diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml new file mode 100644 index 0000000..50c56c7 --- /dev/null +++ b/.forgejo/workflows/build.yml @@ -0,0 +1,49 @@ +name: Build + +# on: +# push: +# tags: +# - "v[0-9]+.[0-9]+.[0-9]+" +# - "v[0-9]+.[0-9]+.[0-9]+*" +# workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }} + IMAGE_VERSION: build + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + config-inline: | + [registry."${{ env.REGISTRY }}"] + ca=["/etc/ssl/certs/ca-certificates.crt"] + - name: Tag Version + run: | + if [[ "${{ github.event_name }}" == "push" ]]; then + echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/v//g' >> $GITHUB_ENV + else + echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + - name: Build and push docker image + uses: docker/build-push-action@v3 + with: + platforms: linux/amd64 + context: . + file: Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}:${{ env.IMAGE_VERSION }} + push: true diff --git a/.forgejo/workflows/ci-cd.yml b/.forgejo/workflows/ci-cd.yml new file mode 100644 index 0000000..2b0e136 --- /dev/null +++ b/.forgejo/workflows/ci-cd.yml @@ -0,0 +1,82 @@ +name: Build & Deploy on Dev + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + workflow_dispatch: + +env: + REGISTRY: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }} + IMAGE_VERSION: latest + DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + config-inline: | + [registry."${{ env.REGISTRY }}"] + ca=["/etc/ssl/certs/ca-certificates.crt"] + - name: Tag Version + run: | + if [ "${{ github.ref_type }}" == "tag" ]; then + echo "IMAGE_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + else + echo "IMAGE_VERSION=latest" >> $GITHUB_ENV + fi + - name: Login in to registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + - name: Build and push docker image + uses: docker/build-push-action@v3 + with: + platforms: linux/amd64 + context: . + file: Dockerfile + tags: ${{ env.CONTAINER_IMAGE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}:${{ env.IMAGE_VERSION }} + push: true + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_RECRUIT "${{ env.IMAGE_VERSION }}" + ./deploy.sh hrms-api-recruit + + - name: Discord Notification + if: always() + run: | + STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" + COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" + TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [{ + \"title\": \"$STATUS\", + \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ github.actor }}\`\", + \"color\": $COLOR, + \"footer\": { + \"text\": \"Release Notification\", + \"icon_url\": \"https://example.com/success-icon.png\" + }, + \"timestamp\": \"$TIMESTAMP\" + }] + }" \ + ${{ env.DISCORD_WEBHOOK }} diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..5861922 --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,29 @@ +name: Build + +on: + workflow_dispatch: + inputs: + version: + description: "Version to deploy" + type: string + required: false + default: "latest" + +env: + IMAGE_VERSION: build + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Remote Deploy + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ vars.SSH_DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ secrets.SSH_DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + ./replace-env.sh API_RECRUIT "${{ inputs.version }}" + ./deploy.sh hrms-api-recruit diff --git a/.idea/.idea.BMA.EHR.Recruit.Service/.idea/.gitignore b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/.gitignore new file mode 100644 index 0000000..889a0d6 --- /dev/null +++ b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/projectSettingsUpdater.xml +/modules.xml +/.idea.BMA.EHR.Recruit.Service.iml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.BMA.EHR.Recruit.Service/.idea/.name b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/.name new file mode 100644 index 0000000..aaf1117 --- /dev/null +++ b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/.name @@ -0,0 +1 @@ +BMA.EHR.Recruit.Service \ No newline at end of file diff --git a/.idea/.idea.BMA.EHR.Recruit.Service/.idea/encodings.xml b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.BMA.EHR.Recruit.Service/.idea/indexLayout.xml b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.BMA.EHR.Recruit.Service/.idea/vcs.xml b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.BMA.EHR.Recruit.Service/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Controllers/RecruitController.cs b/Controllers/RecruitController.cs index 3561b97..39fd3c9 100644 --- a/Controllers/RecruitController.cs +++ b/Controllers/RecruitController.cs @@ -752,6 +752,8 @@ namespace BMA.EHR.Recruit.Service.Controllers var cols = workSheet.GetHeaderColumns(); int row = 2; + int batchCount = 0; + const int batchSize = 500; while (row <= totalRows) { @@ -860,13 +862,23 @@ namespace BMA.EHR.Recruit.Service.Controllers //imported.Recruits.Add(r); row++; + batchCount++; + + // Batch save to prevent OutOfMemoryException on large imports + if (batchCount >= batchSize) + { + _context.SaveChanges(); + _context.ChangeTracker.Clear(); + // Re-attach the import entity after clearing the tracker + _context.RecruitImports.Attach(imported); + batchCount = 0; + } } } } - // finally save to database - + // Save remaining records in the last batch _context.SaveChanges(); return Success(); @@ -1371,6 +1383,8 @@ namespace BMA.EHR.Recruit.Service.Controllers var cols = workSheet.GetHeaderColumns(); int row = 8; + int batchCount = 0; + const int batchSize = 500; var endRow = workSheet.Dimension.End.Row; // แถวสุดท้ายที่มีข้อมูล while (row <= endRow) { @@ -1461,14 +1475,31 @@ namespace BMA.EHR.Recruit.Service.Controllers } row++; + batchCount++; + + // Batch save to prevent OutOfMemoryException on large imports + if (batchCount >= batchSize) + { + rec_import.ScoreImport = imported; + await _context.SaveChangesAsync(); + _context.ChangeTracker.Clear(); + // Re-attach entities after clearing the tracker + _context.Attach(rec_import); + _context.Attach(imported); + imported.Scores.Clear(); + batchCount = 0; + } } // end of sheet loop } // end of all file loop } - // finally save to database - rec_import.ScoreImport = imported; - await _context.SaveChangesAsync(); + // Save remaining records in the last batch + if (imported.Scores.Count > 0) + { + rec_import.ScoreImport = imported; + await _context.SaveChangesAsync(); + } return Success(); diff --git a/obj/BMA.EHR.Recruit.Service.csproj.nuget.dgspec.json b/obj/BMA.EHR.Recruit.Service.csproj.nuget.dgspec.json index c30443d..ea2eb46 100644 --- a/obj/BMA.EHR.Recruit.Service.csproj.nuget.dgspec.json +++ b/obj/BMA.EHR.Recruit.Service.csproj.nuget.dgspec.json @@ -39,7 +39,7 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "9.0.300" + "SdkAnalysisLevel": "10.0.200" }, "frameworks": { "net7.0": { @@ -203,7 +203,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/9.0.305/RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/10.0.203/RuntimeIdentifierGraph.json" } } } diff --git a/obj/BMA.EHR.Recruit.Service.csproj.nuget.g.props b/obj/BMA.EHR.Recruit.Service.csproj.nuget.g.props index 5868417..8714023 100644 --- a/obj/BMA.EHR.Recruit.Service.csproj.nuget.g.props +++ b/obj/BMA.EHR.Recruit.Service.csproj.nuget.g.props @@ -7,7 +7,7 @@ /Users/suphonchaip/.nuget/packages/ /Users/suphonchaip/.nuget/packages/ PackageReference - 6.14.0 + 7.0.0 diff --git a/obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs b/obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs index a9058da..4257f4b 100644 --- a/obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs +++ b/obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs @@ -1,4 +1,4 @@ -// -using System; -using System.Reflection; -[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v7.0", FrameworkDisplayName = ".NET 7.0")] +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v7.0", FrameworkDisplayName = ".NET 7.0")] diff --git a/obj/Debug/net7.0/BMA.EHR.Recruit.Service.AssemblyInfo.cs b/obj/Debug/net7.0/BMA.EHR.Recruit.Service.AssemblyInfo.cs deleted file mode 100644 index b0967b3..0000000 --- a/obj/Debug/net7.0/BMA.EHR.Recruit.Service.AssemblyInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -using System; -using System.Reflection; - -[assembly: Microsoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute("d45c95ce-6b9d-4aa7-aaaf-62fe8b792934")] -[assembly: System.Reflection.AssemblyCompanyAttribute("BMA.EHR.Recruit.Service")] -[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] -[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+e1d869c9329ffd1d9e4fdb73f077eee60b67e09c")] -[assembly: System.Reflection.AssemblyProductAttribute("BMA.EHR.Recruit.Service")] -[assembly: System.Reflection.AssemblyTitleAttribute("BMA.EHR.Recruit.Service")] -[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] - -// Generated by the MSBuild WriteCodeFragment class. - diff --git a/obj/Debug/net7.0/BMA.EHR.Recruit.Service.AssemblyInfoInputs.cache b/obj/Debug/net7.0/BMA.EHR.Recruit.Service.AssemblyInfoInputs.cache deleted file mode 100644 index 0aa2ab4..0000000 --- a/obj/Debug/net7.0/BMA.EHR.Recruit.Service.AssemblyInfoInputs.cache +++ /dev/null @@ -1 +0,0 @@ -115668deb371dd5b1a7e9691f5117e5e4acdd7bef73f049871879b64ab58463e diff --git a/obj/project.assets.json b/obj/project.assets.json index e6f4b38..46e800f 100644 --- a/obj/project.assets.json +++ b/obj/project.assets.json @@ -13782,7 +13782,7 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "9.0.300" + "SdkAnalysisLevel": "10.0.200" }, "frameworks": { "net7.0": { @@ -13946,7 +13946,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/9.0.305/RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/10.0.203/RuntimeIdentifierGraph.json" } } } diff --git a/obj/project.nuget.cache b/obj/project.nuget.cache index 876e06c..4bae79d 100644 --- a/obj/project.nuget.cache +++ b/obj/project.nuget.cache @@ -1,6 +1,6 @@ { "version": 2, - "dgSpecHash": "z4TN+iW9Ey8=", + "dgSpecHash": "6/jNsBvJ3SY=", "success": true, "projectFilePath": "/Users/suphonchaip/Develop/hrms/hrms-api-recruit/BMA.EHR.Recruit.Service.csproj", "expectedPackageFiles": [