diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index ee65dcc41..000000000 --- a/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -$ cat .gitattributes -Swastika-IO-Core/docs/* linguist-documentation -Swastika-IO-Core/src/Swastika.Cms.Web.Mvc/wwwroot/lib/* linguist-vendored -*.* linguist-language=Csharp diff --git a/.github/wiki/Deployment/Infrastructure-as-Code/OpenTofu.md b/.github/wiki/Deployment/Infrastructure-as-Code/OpenTofu.md new file mode 100644 index 000000000..889dcd7be --- /dev/null +++ b/.github/wiki/Deployment/Infrastructure-as-Code/OpenTofu.md @@ -0,0 +1,216 @@ +# Deploying with OpenTofu + +OpenTofu is an open-source infrastructure as code tool that allows you to define and provision infrastructure across multiple cloud providers. This guide will help you deploy Mixcore using OpenTofu. + +## Prerequisites + +- [OpenTofu](https://opentofu.org/docs/intro/install/) installed +- Cloud provider CLI tools installed: + - [AWS CLI](https://aws.amazon.com/cli/) for AWS + - [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) for Azure + - [gcloud CLI](https://cloud.google.com/sdk/docs/install) for GCP +- [kubectl](https://kubernetes.io/docs/tasks/tools/) installed +- [Helm](https://helm.sh/docs/intro/install/) installed + +## Configuration + +### Cloud Provider Setup + +#### AWS +```sh +# Configure AWS credentials +export AWS_ACCESS_KEY_ID=your_access_key +export AWS_SECRET_ACCESS_KEY=your_secret_key +``` + +#### Azure +```sh +# Login to Azure +az login + +# Set environment variables +export ARM_SUBSCRIPTION_ID=your_subscription_id +export ARM_TENANT_ID=your_tenant_id +export ARM_CLIENT_ID=your_client_id +export ARM_CLIENT_SECRET=your_client_secret +``` + +#### GCP +```sh +# Login to GCP +gcloud auth login +gcloud auth application-default login + +# Set project +export GOOGLE_PROJECT=your_project_id +``` + +### OpenTofu Configuration + +1. Create a `terraform.tfvars` file: +```hcl +cloud_provider = "aws" # or "azure" or "gcp" +cluster_name = "mixcore-cluster" +sqlserver_password = "your_password" +mysql_password = "your_password" +redis_password = "your_password" +``` + +2. Review and customize `values.yaml` for Helm configuration: +```yaml +# See cloud/opentofu/values.yaml for configuration options +``` + +## Deployment + +1. Initialize OpenTofu: +```sh +cd cloud/opentofu +tofu init +``` + +2. Plan the deployment: +```sh +tofu plan +``` + +3. Apply the deployment: +```sh +tofu apply +``` + +## Accessing the Application + +1. Get the cluster endpoint: +```sh +tofu output cluster_endpoint +``` + +2. Get the kubeconfig: +```sh +tofu output kubeconfig > kubeconfig.yaml +export KUBECONFIG=kubeconfig.yaml +``` + +3. Access services: +- Main application: http:// +- SQL Server: :1433 +- MySQL: :3306 +- Redis: :6379 + +## Updating Configuration + +1. Edit `values.yaml` for Helm configuration: +```sh +nano cloud/opentofu/values.yaml +``` + +2. Apply changes: +```sh +tofu apply +``` + +## Destroying Resources + +To destroy all resources: +```sh +tofu destroy +``` + +## CI/CD Integration + +### GitHub Actions +```yaml +deploy-infra: + needs: build-and-push + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup OpenTofu + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: "1.5.0" + + - name: Deploy Infrastructure + run: | + cd cloud/opentofu + tofu init + tofu apply -auto-approve +``` + +### GitLab CI/CD +```yaml +deploy-infra: + stage: deploy + image: + name: hashicorp/terraform:light + entrypoint: [""] + script: + - cd cloud/opentofu + - terraform init + - terraform apply -auto-approve + only: + - main +``` + +## Troubleshooting + +### Common Issues + +1. **Authentication Errors** + - Verify cloud provider credentials + - Check environment variables + - Ensure CLI tools are properly configured + +2. **Resource Creation Failures** + - Check resource quotas + - Verify network configurations + - Review cloud provider logs + +3. **Kubernetes Connection Issues** + - Verify kubeconfig + - Check cluster status + - Ensure proper RBAC permissions + +### Logs and Monitoring + +1. **OpenTofu Logs** +```sh +# Enable debug logging +export TF_LOG=DEBUG +tofu apply +``` + +2. **Kubernetes Logs** +```sh +# View pod logs +kubectl logs -f deployment/mixcore -n mixcore + +# View resource usage +kubectl top pods -n mixcore +``` + +## Best Practices + +1. **State Management** + - Use remote state storage + - Enable state locking + - Regular state backups + +2. **Security** + - Use secrets management + - Implement least privilege + - Regular security audits + +3. **Maintenance** + - Regular updates + - Backup procedures + - Disaster recovery planning + +## Additional Resources + +- [OpenTofu Documentation](https://opentofu.org/docs) +- [Kubernetes Documentation](https://kubernetes.io/docs) +- [Helm Documentation](https://helm.sh/docs) +- [Cloud Provider Documentation](https://docs.aws.amazon.com/index.html) \ No newline at end of file diff --git a/.github/wiki/Deployment/Infrastructure-as-Code/Terraform.md b/.github/wiki/Deployment/Infrastructure-as-Code/Terraform.md new file mode 100644 index 000000000..d829bc7d3 --- /dev/null +++ b/.github/wiki/Deployment/Infrastructure-as-Code/Terraform.md @@ -0,0 +1,216 @@ +# Deploying with Terraform + +Terraform is an infrastructure as code tool that allows you to define and provision infrastructure across multiple cloud providers. This guide will help you deploy Mixcore using Terraform. + +## Prerequisites + +- [Terraform](https://www.terraform.io/downloads.html) installed +- Cloud provider CLI tools installed: + - [AWS CLI](https://aws.amazon.com/cli/) for AWS + - [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) for Azure + - [gcloud CLI](https://cloud.google.com/sdk/docs/install) for GCP +- [kubectl](https://kubernetes.io/docs/tasks/tools/) installed +- [Helm](https://helm.sh/docs/intro/install/) installed + +## Configuration + +### Cloud Provider Setup + +#### AWS +```sh +# Configure AWS credentials +export AWS_ACCESS_KEY_ID=your_access_key +export AWS_SECRET_ACCESS_KEY=your_secret_key +``` + +#### Azure +```sh +# Login to Azure +az login + +# Set environment variables +export ARM_SUBSCRIPTION_ID=your_subscription_id +export ARM_TENANT_ID=your_tenant_id +export ARM_CLIENT_ID=your_client_id +export ARM_CLIENT_SECRET=your_client_secret +``` + +#### GCP +```sh +# Login to GCP +gcloud auth login +gcloud auth application-default login + +# Set project +export GOOGLE_PROJECT=your_project_id +``` + +### Terraform Configuration + +1. Create a `terraform.tfvars` file: +```hcl +cloud_provider = "aws" # or "azure" or "gcp" +cluster_name = "mixcore-cluster" +sqlserver_password = "your_password" +mysql_password = "your_password" +redis_password = "your_password" +``` + +2. Review and customize `values.yaml` for Helm configuration: +```yaml +# See cloud/opentofu/values.yaml for configuration options +``` + +## Deployment + +1. Initialize Terraform: +```sh +cd cloud/opentofu +terraform init +``` + +2. Plan the deployment: +```sh +terraform plan +``` + +3. Apply the deployment: +```sh +terraform apply +``` + +## Accessing the Application + +1. Get the cluster endpoint: +```sh +terraform output cluster_endpoint +``` + +2. Get the kubeconfig: +```sh +terraform output kubeconfig > kubeconfig.yaml +export KUBECONFIG=kubeconfig.yaml +``` + +3. Access services: +- Main application: http:// +- SQL Server: :1433 +- MySQL: :3306 +- Redis: :6379 + +## Updating Configuration + +1. Edit `values.yaml` for Helm configuration: +```sh +nano cloud/opentofu/values.yaml +``` + +2. Apply changes: +```sh +terraform apply +``` + +## Destroying Resources + +To destroy all resources: +```sh +terraform destroy +``` + +## CI/CD Integration + +### GitHub Actions +```yaml +deploy-infra: + needs: build-and-push + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: "1.5.0" + + - name: Deploy Infrastructure + run: | + cd cloud/opentofu + terraform init + terraform apply -auto-approve +``` + +### GitLab CI/CD +```yaml +deploy-infra: + stage: deploy + image: + name: hashicorp/terraform:light + entrypoint: [""] + script: + - cd cloud/opentofu + - terraform init + - terraform apply -auto-approve + only: + - main +``` + +## Troubleshooting + +### Common Issues + +1. **Authentication Errors** + - Verify cloud provider credentials + - Check environment variables + - Ensure CLI tools are properly configured + +2. **Resource Creation Failures** + - Check resource quotas + - Verify network configurations + - Review cloud provider logs + +3. **Kubernetes Connection Issues** + - Verify kubeconfig + - Check cluster status + - Ensure proper RBAC permissions + +### Logs and Monitoring + +1. **Terraform Logs** +```sh +# Enable debug logging +export TF_LOG=DEBUG +terraform apply +``` + +2. **Kubernetes Logs** +```sh +# View pod logs +kubectl logs -f deployment/mixcore -n mixcore + +# View resource usage +kubectl top pods -n mixcore +``` + +## Best Practices + +1. **State Management** + - Use remote state storage + - Enable state locking + - Regular state backups + +2. **Security** + - Use secrets management + - Implement least privilege + - Regular security audits + +3. **Maintenance** + - Regular updates + - Backup procedures + - Disaster recovery planning + +## Additional Resources + +- [Terraform Documentation](https://www.terraform.io/docs) +- [Kubernetes Documentation](https://kubernetes.io/docs) +- [Helm Documentation](https://helm.sh/docs) +- [Cloud Provider Documentation](https://docs.aws.amazon.com/index.html) \ No newline at end of file diff --git a/.github/wiki/Home.md b/.github/wiki/Home.md new file mode 100644 index 000000000..86a10cd38 --- /dev/null +++ b/.github/wiki/Home.md @@ -0,0 +1,80 @@ +# Mixcore CMS Documentation + +Welcome to the Mixcore CMS documentation! This wiki contains comprehensive guides and documentation to help you get started with Mixcore CMS. + +## 📚 Getting Started + +- [Installation](Getting-Started/Installation) +- [Quick Start](Getting-Started/Quick-Start) +- [Development Environment](Getting-Started/Development-Environment) +- [First Application](Getting-Started/First-Application) + +## 🏗️ Architecture + +- [Overview](Architecture/Overview) +- [Core Platform](Architecture/Core-Platform) +- [Modules](Architecture/Modules) +- [Services](Architecture/Services) +- [Applications](Architecture/Applications) + +## 🚀 Deployment + +- [Overview](Deployment/Overview) +- [Docker](Deployment/Docker) +- [Kubernetes](Deployment/Kubernetes) +- [Cloud Providers](Deployment/Cloud-Providers) + - [AWS](Deployment/Cloud-Providers/AWS) + - [Azure](Deployment/Cloud-Providers/Azure) + - [GCP](Deployment/Cloud-Providers/GCP) + - [DigitalOcean](Deployment/Cloud-Providers/DigitalOcean) +- [Infrastructure as Code](Deployment/Infrastructure-as-Code) + - [OpenTofu](Deployment/Infrastructure-as-Code/OpenTofu) + - [Terraform](Deployment/Infrastructure-as-Code/Terraform) + +## 💻 Development + +- [Getting Started](Development/Getting-Started) +- [Architecture](Development/Architecture) +- [Modules](Development/Modules) +- [Services](Development/Services) +- [Testing](Development/Testing) +- [Debugging](Development/Debugging) + +## 🔌 API Reference + +- [Overview](API-Reference/Overview) +- [Authentication](API-Reference/Authentication) +- [Endpoints](API-Reference/Endpoints) +- [GraphQL](API-Reference/GraphQL) +- [Webhooks](API-Reference/Webhooks) + +## 🔒 Security + +- [Overview](Security/Overview) +- [Authentication](Security/Authentication) +- [Authorization](Security/Authorization) +- [Data Protection](Security/Data-Protection) +- [Compliance](Security/Compliance) + +## 🛠️ Troubleshooting + +- [Common Issues](Troubleshooting/Common-Issues) +- [Logging](Troubleshooting/Logging) +- [Monitoring](Troubleshooting/Monitoring) +- [Performance](Troubleshooting/Performance) +- [Backup & Recovery](Troubleshooting/Backup-Recovery) + +## 🤝 Contributing + +- [Guidelines](Contributing/Guidelines) +- [Code Style](Contributing/Code-Style) +- [Pull Requests](Contributing/Pull-Requests) +- [Documentation](Contributing/Documentation) + +## 💼 Enterprise Support + +- [Overview](Enterprise-Support/Overview) +- [Support Plans](Enterprise-Support/Support-Plans) +- [SLAs](Enterprise-Support/SLAs) +- [Training](Enterprise-Support/Training) +- [Custom Development](Enterprise-Support/Custom-Development) \ No newline at end of file diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index 69192ccbd..000000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,30 +0,0 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net - -name: .NET - -on: - push: - branches: [ "develop" ] - pull_request: - branches: [ "develop" ] - -jobs: - build: - runs-on: windows-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - path: 'develop' - submodules: recursive - - name: Setup - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 7.0.x - submodules: recursive - - name: Restore - run: dotnet restore develop/src/Mixcore.sln - - name: Build - run: dotnet build develop/src/Mixcore.sln --no-restore diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 000000000..3e86fb0d8 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,200 @@ +name: CI/CD Pipeline + +on: + push: + branches: [ main, develop ] + tags: [ 'v*.*.*' ] + pull_request: + branches: [ main, develop ] + schedule: + - cron: '0 0 * * *' # Daily security scans + +env: + DOCKER_IMAGE: mixcore/mix.core + DOCKER_TAG: ${{ github.sha }} + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Run Dependency Check + uses: dependency-check/Dependency-Check_Action@main + with: + project: 'Mixcore' + path: '.' + format: 'HTML' + out: 'reports' + + - name: Run Container Scan + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + scan-ref: '.' + + - name: Run Secret Scan + uses: github/codeql-action/init@v2 + with: + languages: javascript + + build-and-test: + needs: security + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + dotnet: ['9.0.x'] + + steps: + - uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ matrix.dotnet }} + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore + + - name: Test + run: dotnet test --no-build --verbosity normal + + - name: Run SonarCloud analysis + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: > + -Dsonar.projectKey=mix.core + -Dsonar.organization=mixcore + -Dsonar.verbose=true + + build-and-push: + needs: build-and-test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') + permissions: + contents: read + packages: write + id-token: write + + steps: + - uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ env.DOCKER_IMAGE }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Sign container image + uses: sigstore/cosign-installer@main + with: + cosign-release: 'v2.0.0' + if: github.ref == 'refs/heads/main' + + - name: Sign the published Docker image + env: + COSIGN_EXPERIMENTAL: "true" + run: cosign sign ${{ steps.meta.outputs.tags }}@${{ steps.build-and-push.outputs.digest }} + if: github.ref == 'refs/heads/main' + + deploy: + needs: build-and-push + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + environment: production + + steps: + - uses: actions/checkout@v3 + + - name: Install kubectl + uses: azure/setup-kubectl@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-southeast-1 + + - name: Update kubeconfig + run: aws eks update-kubeconfig --name mixcore-cluster + + - name: Deploy to Kubernetes + run: | + kubectl apply -f k8s/namespace.yaml + kubectl apply -f k8s/secrets.yaml + kubectl apply -f k8s/configmap.yaml + kubectl apply -f k8s/storage.yaml + kubectl apply -f k8s/deployments.yaml + kubectl apply -f k8s/services.yaml + + - name: Verify deployment + run: | + kubectl get all -n mixcore + kubectl rollout status deployment/mixcore -n mixcore + + - name: Run performance tests + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install and run Lighthouse + run: | + npm install -g lighthouse + lighthouse https://mixcore.org --output=html --output-path=./lighthouse-results.html + + notify: + needs: deploy + if: always() + runs-on: ubuntu-latest + steps: + - name: Notify on Slack + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} \ No newline at end of file diff --git a/.github/workflows/develop-v2_mixcore-platform.yml b/.github/workflows/develop-v2_mixcore-platform.yml deleted file mode 100644 index 39ed803de..000000000 --- a/.github/workflows/develop-v2_mixcore-platform.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy -# More GitHub Actions for Azure: https://github.com/Azure/actions - -name: Build and deploy ASP.Net Core app to Azure Web App - mixcore-platform - -on: - push: - branches: - - develop-v2 - workflow_dispatch: - -jobs: - build: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Set up .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '6.0.x' - include-prerelease: true - - - name: Build with dotnet - run: dotnet build src/applications/Mixcore/mixcore.csproj --configuration Release - - - name: dotnet publish - run: dotnet publish src/applications/Mixcore/mixcore.csproj -c Release -o ${{env.DOTNET_ROOT}}/myapp - - - name: Upload artifact for deployment job - uses: actions/upload-artifact@v2 - with: - name: .net-app - path: ${{env.DOTNET_ROOT}}/myapp - - - deploy: - runs-on: windows-latest - needs: build - environment: - name: 'production' - url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} - - steps: - - name: Download artifact from build job - uses: actions/download-artifact@v2 - with: - name: .net-app - - - name: Deploy to Azure Web App - id: deploy-to-webapp - uses: azure/webapps-deploy@v2 - with: - app-name: 'mixcore-platform' - slot-name: 'production' - publish-profile: ${{ secrets.AzureAppService_PublishProfile_eae485bfa224498681bd8915d6bd8cd5 }} - package: . diff --git a/.github/workflows/develop-v2_mixcore-v2-linux.yml b/.github/workflows/develop-v2_mixcore-v2-linux.yml deleted file mode 100644 index 444633910..000000000 --- a/.github/workflows/develop-v2_mixcore-v2-linux.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy -# More GitHub Actions for Azure: https://github.com/Azure/actions - -name: Build and deploy ASP.Net Core app to Azure Web App - mixcore-v2-linux - -on: - push: - branches: - - develop-v2 - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Set up .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '6.0.x' - include-prerelease: true - - - name: Build with dotnet - run: dotnet build src/applications/Mixcore/mixcore.csproj --configuration Release - - - name: dotnet publish - run: dotnet publish src/applications/Mixcore/mixcore.csproj -c Release -o ${{env.DOTNET_ROOT}}/myapp - - - name: Upload artifact for deployment job - uses: actions/upload-artifact@v2 - with: - name: .net-app - path: ${{env.DOTNET_ROOT}}/myapp - - deploy: - runs-on: ubuntu-latest - needs: build - environment: - name: 'Production' - url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} - - steps: - - name: Download artifact from build job - uses: actions/download-artifact@v2 - with: - name: .net-app - - - name: Deploy to Azure Web App - id: deploy-to-webapp - uses: azure/webapps-deploy@v2 - with: - app-name: 'mixcore-v2-linux' - slot-name: 'Production' - publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_A12BEB6F0D0743D48B61BC8B439ADBF1 }} - package: . diff --git a/.github/workflows/mix.spa.portal-dispatch.yml b/.github/workflows/mix.spa.portal-dispatch.yml deleted file mode 100644 index aecfc701b..000000000 --- a/.github/workflows/mix.spa.portal-dispatch.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: CI - -on: [repository_dispatch] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: Run a one-line script - run: echo Hello, world! - - name: Run a multi-line script - run: | - echo Add other actions to build, - echo test, and deploy your project. - - - name: Commit to repository - env: - GITHUB_TOKEN: ${{ secrets.github_token }} - COMMIT_MSG: | - [CI/CD] Commit latest code from mix.spa.portal - skip-checks: true - run: | - # Hard-code user configuration - git config --global user.email "i.love.to.smile.around@gmail.com" - git config --global user.name "smilefounder" - - git clone https://github.com/mixcore/mix.spa.portal.git ${HOME}/work/${GITHUB_REPOSITORY}/mix.spa.portal/ - - git checkout develop - - cp -a ${HOME}/work/${GITHUB_REPOSITORY}/mix.spa.portal/dist/. ${GITHUB_WORKSPACE}/src/ - - # Update origin with token - git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git - git add . - - # Only commit and push if we have changes - git diff --quiet && git diff --staged --quiet || (git commit -m "${COMMIT_MSG}"; git push origin develop) diff --git a/.github/workflows/portal-dispatch.yml b/.github/workflows/portal-dispatch.yml new file mode 100644 index 000000000..b87044790 --- /dev/null +++ b/.github/workflows/portal-dispatch.yml @@ -0,0 +1,203 @@ +name: Portal Dispatch + +on: + push: + branches: [ main, develop ] + paths: + - 'src/applications/mix.spa.portal/**' + - 'src/modules/mix.portal/**' + pull_request: + branches: [ main, develop ] + paths: + - 'src/applications/mix.spa.portal/**' + - 'src/modules/mix.portal/**' + +env: + DOCKER_IMAGE: mixcore/mix.spa.portal + DOCKER_TAG: ${{ github.sha }} + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Run Dependency Check + uses: dependency-check/Dependency-Check_Action@main + with: + project: 'Mixcore Portal' + path: 'src/applications/mix.spa.portal' + format: 'HTML' + out: 'reports' + + - name: Run Container Scan + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + scan-ref: 'src/applications/mix.spa.portal' + + - name: Run Secret Scan + uses: github/codeql-action/init@v2 + with: + languages: javascript + + build-and-test: + needs: security + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + dotnet: ['9.0.x'] + + steps: + - uses: actions/checkout@v3 + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ matrix.dotnet }} + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('src/applications/mix.spa.portal/**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore src/applications/mix.spa.portal/mix.spa.portal.csproj + + - name: Build + run: dotnet build src/applications/mix.spa.portal/mix.spa.portal.csproj --no-restore + + - name: Test + run: dotnet test src/applications/mix.spa.portal/mix.spa.portal.csproj --no-build --verbosity normal + + - name: Run SonarCloud analysis + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: > + -Dsonar.projectKey=mix.spa.portal + -Dsonar.organization=mixcore + -Dsonar.verbose=true + + build-and-push: + needs: build-and-test + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') + permissions: + contents: read + packages: write + id-token: write + + steps: + - uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ env.DOCKER_IMAGE }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: src/applications/mix.spa.portal + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Sign container image + uses: sigstore/cosign-installer@main + with: + cosign-release: 'v2.0.0' + if: github.ref == 'refs/heads/main' + + - name: Sign the published Docker image + env: + COSIGN_EXPERIMENTAL: "true" + run: cosign sign ${{ steps.meta.outputs.tags }}@${{ steps.build-and-push.outputs.digest }} + if: github.ref == 'refs/heads/main' + + deploy: + needs: build-and-push + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + environment: production + + steps: + - uses: actions/checkout@v3 + + - name: Install kubectl + uses: azure/setup-kubectl@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-southeast-1 + + - name: Update kubeconfig + run: aws eks update-kubeconfig --name mixcore-cluster + + - name: Deploy to Kubernetes + run: | + kubectl apply -f src/applications/mix.spa.portal/k8s/namespace.yaml + kubectl apply -f src/applications/mix.spa.portal/k8s/secrets.yaml + kubectl apply -f src/applications/mix.spa.portal/k8s/configmap.yaml + kubectl apply -f src/applications/mix.spa.portal/k8s/storage.yaml + kubectl apply -f src/applications/mix.spa.portal/k8s/deployments.yaml + kubectl apply -f src/applications/mix.spa.portal/k8s/services.yaml + + - name: Verify deployment + run: | + kubectl get all -n mixcore-portal + kubectl rollout status deployment/mixcore-portal -n mixcore-portal + + - name: Run performance tests + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install and run Lighthouse + run: | + npm install -g lighthouse + lighthouse https://portal.mixcore.org --output=html --output-path=./lighthouse-results.html + + notify: + needs: deploy + if: always() + runs-on: ubuntu-latest + steps: + - name: Notify on Slack + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} \ No newline at end of file diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml new file mode 100644 index 000000000..4caf2d0f4 --- /dev/null +++ b/.github/workflows/sonarcloud.yml @@ -0,0 +1,68 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# This workflow helps you trigger a SonarCloud analysis of your code and populates +# GitHub Code Scanning alerts with the vulnerabilities found. +# Free for open source project. + +# 1. Login to SonarCloud.io using your GitHub account + +# 2. Import your project on SonarCloud +# * Add your GitHub organization first, then add your repository as a new project. +# * Please note that many languages are eligible for automatic analysis, +# which means that the analysis will start automatically without the need to set up GitHub Actions. +# * This behavior can be changed in Administration > Analysis Method. +# +# 3. Follow the SonarCloud in-product tutorial +# * a. Copy/paste the Project Key and the Organization Key into the args parameter below +# (You'll find this information in SonarCloud. Click on "Information" at the bottom left) +# +# * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN +# (On SonarCloud, click on your avatar on top-right > My account > Security +# or go directly to https://sonarcloud.io/account/security/) + +# Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/) +# or reach out to our community forum if you need some help (https://community.sonarsource.com/c/help/sc/9) + +name: SonarCloud analysis + +on: + push: + branches: [ "master", "develop" ] + pull_request: + branches: [ "master", "develop" ] + workflow_dispatch: + +permissions: + pull-requests: read # allows SonarCloud to decorate PRs with analysis results + +jobs: + Analysis: + runs-on: ubuntu-latest + + steps: + - name: Analyze with SonarCloud + + # You can pin the exact commit or the version. + # uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 + uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) + with: + # Additional arguments for the sonarcloud scanner + args: + # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu) + # mandatory + -Dsonar.projectKey=mix.core + -Dsonar.organization=mixcore + # Comma-separated paths to directories containing main source files. + #-Dsonar.sources= # optional, default is project base directory + # When you need the analysis to take place in a directory other than the one from which it was launched + #-Dsonar.projectBaseDir= # optional, default is . + # Comma-separated paths to directories containing test source files. + #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/ + # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing. + #-Dsonar.verbose= # optional, default is false diff --git a/.gitignore b/.gitignore index 026556d69..6fa636b45 100644 --- a/.gitignore +++ b/.gitignore @@ -253,3 +253,5 @@ src/**/logs/ src/**/*.sqlite-shm src/**/*.sqlite-wal src/applications/mixcore/wwwroot/**/*.sqlite + +src/applications/mixcore/wwwroot/mixcontent/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..b91b33462 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,58 @@ +image: mcr.microsoft.com/dotnet/sdk:9.0 + +variables: + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: "/certs" + +services: + - docker:dind + +stages: + - build + - test + - build-docker + - deploy + +build: + stage: build + script: + - dotnet restore + - dotnet build --no-restore + +test: + stage: test + script: + - dotnet test --no-build --verbosity normal + +build-docker: + stage: build-docker + image: docker:latest + script: + - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN + - docker build -t $DOCKER_IMAGE:$CI_COMMIT_SHA . + - docker push $DOCKER_IMAGE:$CI_COMMIT_SHA + only: + - main + +deploy: + stage: deploy + image: + name: bitnami/kubectl:latest + entrypoint: [""] + script: + - kubectl config set-cluster k8s --server="$KUBE_URL" --insecure-skip-tls-verify=true + - kubectl config set-credentials admin --token="$KUBE_TOKEN" + - kubectl config set-context default --cluster=k8s --user=admin + - kubectl config use-context default + + - kubectl apply -f k8s/namespace.yaml + - kubectl apply -f k8s/secrets.yaml + - kubectl apply -f k8s/configmap.yaml + - kubectl apply -f k8s/storage.yaml + - kubectl apply -f k8s/deployments.yaml + - kubectl apply -f k8s/services.yaml + + - kubectl get all -n mixcore + - kubectl rollout status deployment/mixcore -n mixcore + only: + - main \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 39ba79484..a64d46766 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,7 @@ [submodule "src/platform/core/mix-heart"] path = src/platform/core/mix-heart url = https://github.com/mixcore/mix.heart.git - branch = develop/v2 \ No newline at end of file + branch = develop/v2 +[submodule "docs/wiki"] + path = docs/wiki + url = https://github.com/mixcore/mix.core.wiki.git diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile deleted file mode 100644 index 9c1e96b1e..000000000 --- a/.gitpod.Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM gitpod/workspace-dotnet:latest diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index e0f5e659a..000000000 --- a/.gitpod.yml +++ /dev/null @@ -1,15 +0,0 @@ -image: - file: .gitpod.Dockerfile - -tasks: - - name: Restore & Build - init: | - dotnet dev-certs https - dotnet restore - dotnet build - - name: Run - command: dotnet run - -vscode: - extensions: - - muhammad-sammy.csharp diff --git a/.project b/.project deleted file mode 100644 index 031d70b2c..000000000 --- a/.project +++ /dev/null @@ -1,11 +0,0 @@ - - - mix.core - - - - - - - - diff --git a/.restyled.yaml b/.restyled.yaml deleted file mode 100644 index 36210706f..000000000 --- a/.restyled.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -enabled: true -auto: true -pull_requests: true -comments: true -request_review: author -labels: ["code-style"] - -restylers: - - prettier - - formatter - - astyle diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b0e7cb4c8..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: csharp -mono: none -dotnet: 5.0 -before_install: - - git clone --depth=50 --branch=master https://github.com/mixcore/mix.heart.git /home/travis/build/mixcore/mix.heart - - git clone --depth=50 --branch=master https://github.com/mixcore/mix.identity.git /home/travis/build/mixcore/mix.identity - - cd src/Mix.Cms.Web -script: - - dotnet restore - - dotnet build diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..36d6b0f79 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/src/Mix.Cms.Web/bin/Debug/net5.0/Mixcore.dll", + "args": [], + "cwd": "${workspaceFolder}/src/Mix.Cms.Web", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] + } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..cba8d6da3 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/Mix.Cms.Web/Mix.Cms.Web.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/src/Mix.Cms.Web/Mix.Cms.Web.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/src/Mix.Cms.Web/Mix.Cms.Web.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/.whitesource b/.whitesource deleted file mode 100644 index c4546b3e5..000000000 --- a/.whitesource +++ /dev/null @@ -1,12 +0,0 @@ -{ - "scanSettings": { - "configMode": "AUTO", - "configExternalURL": "" - }, - "checkRunSettings": { - "vulnerableCheckRunConclusionLevel": "failure" - }, - "issueSettings": { - "minSeverityLevel": "LOW" - } -} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6442b91ef..98baa9b08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,40 @@ # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build-env +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build WORKDIR /app -# Copy everything else and build -COPY src/. ./ -# RUN dotnet restore Mix.Cms.Web/Mixcore.csproj -RUN dotnet publish Mix.Cms.Web/Mixcore.csproj -c Release +# Copy the entire source directory +COPY src/ . -# Build runtime image -FROM mcr.microsoft.com/dotnet/sdk:5.0 AS runtime +# Restore dependencies +RUN dotnet restore + +# Build and publish the main application +RUN dotnet publish applications/mixcore/mixcore.csproj -c Release -o /app/publish + +# Build gateway +FROM build AS gateway +RUN dotnet publish applications/mixcore.gateway/mixcore.gateway.csproj -c Release -o /app/publish + +# Runtime image +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime WORKDIR /app -# COPY --from=node-env /app/Mix.Cms.Web/wwwroot . -COPY --from=build-env /app/Mix.Cms.Web/bin/Release/net5.0/publish . +COPY --from=build /app/publish . + +# Expose ports EXPOSE 80 +EXPOSE 443 + +# Set environment variables +ENV ASPNETCORE_URLS=http://+:80;https://+:443 +ENV ASPNETCORE_ENVIRONMENT=Development + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ + CMD curl -f http://localhost/health || exit 1 + +# Entry point ENTRYPOINT ["dotnet", "mixcore.dll"] diff --git a/README.md b/README.md index cc11a1d9f..e9b2a4214 100644 --- a/README.md +++ b/README.md @@ -1,182 +1,112 @@ -# Mixcore CMS [![Become a Backer](https://opencollective.com/mixcore/tiers/backer.svg?avatarHeight=36)](https://opencollective.com/mixcore#support) +# Mixcore CMS - Enterprise-Grade .NET Core CMS & API Platform [![Become a Backer](https://opencollective.com/mixcore/tiers/backer.svg?avatarHeight=36)](https://opencollective.com/mixcore#support) [![backer](https://opencollective.com/mixcore/tiers/backer/badge.svg?label=backer&color=brightgreen)](https://opencollective.com/mixcore#support) [![Donate](https://img.shields.io/badge/$-donate-ff69b4.svg)](https://www.paypal.me/mixcore) [![Buy us a coffee](https://img.shields.io/badge/$-BuyMeACoffee-orange.svg)](https://www.buymeacoffee.com/mixcore) -### Fully Open Source UI Tools to create multi-purpose Enterprise Web Apps, Mobile Apps & Application Services +> **🚀 Enterprise-Grade CMS & API Platform** - Build scalable, secure, and high-performance web applications with modern .NET Core microservices architecture. Perfect for agencies, enterprises, and developers building complex digital experiences. -## Online Run & Build -Click the button below to start a new development environment: +## 🎯 Quick Links -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/mixcore/mix.core/tree/develop) +- [Documentation](https://github.com/mixcore/mix.core/wiki) +- [Demo](https://demo.mixcore.org) +- [API Reference](https://api.mixcore.org) +- [Community](https://community.mixcore.org) +- [Enterprise Support](https://github.com/mixcore/mix.core/wiki/Enterprise-Support) -## UI Screenshots +## 🚀 Quick Start -### Admin Portal +### Prerequisites +- [.NET 9.0 SDK](https://dotnet.microsoft.com/download) +- [Docker](https://www.docker.com/get-started) +- [Docker Compose](https://docs.docker.com/compose/install/) -> Mixcore CMS Back-office is built on top of the much awaited Bootstrap 5. This makes starting a new project very simple. It also provides benefits if you are already working on a Bootstrap 5 project. - -(New Portal in Angular / Svelte will be coming soon) - -![Mixcore Admin Portal Bootstrap 5.x](https://user-images.githubusercontent.com/3785721/125382627-322d7d00-e3c0-11eb-8ff7-f02316770876.png "Mixcore CMS Admin Portal Bootstrap 5") - - -![Mixcore Admin Portal Bootstrap 5.x](https://user-images.githubusercontent.com/3785721/126033976-28302532-1284-4be8-b8dd-74258a227873.png "Mixcore CMS Admin Portal Bootstrap 5") - - -## Special features (Out of the box) - -- [x] **Databases** -MySQL, SQL Server, PostgreSQL, SQLite -- [x] **Reliability** - Member roles and permissions. -- [x] **High Security** - Strong Data Encryption and Security compliance. -- [x] **Multilingual** - Flexible multilingual content migration. -- [x] **High Performance** - Millisecond response time. -- [x] **Cross Platforms** - Powered by .NET Core and run everywhere. -- [x] **Online Coding** - Visual Studio Code's heart inside. -- [x] **Customizable Designs** - Build any kinds of website. -- [x] **SEO Friendly** - No extra plugin required. -- [x] **Media Management** - Multiple file formats for your website / application. -- [x] **Manage On The Go** - Manage and Code everywhere you want. -- [x] **Easy and Accessible** - Non deep tech knowledge required. -- [x] **Analytics** - Inside Google Analytics dashboard & no extra plugin required. -- [x] **Dynamic Modular Architecture** - Powerful module layers & Attribute sets feature. -- [x] **Extensibility** - API-first architecture for Plug & Play. -- [x] **Easy Backup** - Powerful 1 step export. -- [x] **More Coffee time!** - You can relax and explore more ton of features are not listed here... - -## Architecture -![image](https://user-images.githubusercontent.com/3785721/160266730-f02415a1-870b-45b2-ae4b-ed6c26ca5787.png) - - -### CMS and Dashboards built on top of ASP.Net Core / Dotnet Core, SignalR, Angular / Bootstrap / React / Vue / Svelte. - -|Services |Result |Services |Result | -|---------|---------|---------|---------| -|Github workflow |[![Build Status](https://github.com/mixcore/mix.core/actions/workflows/build-check.yml/badge.svg)](https://github.com/mixcore/mix.core/actions/workflows/build-check.yml)| -|Travis CI |[![Build Status](https://travis-ci.org/mixcore/mix.core.svg?branch=master)](https://travis-ci.org/mixcore/mix.core)|AppVeyor CI |[![Build status](https://ci.appveyor.com/api/projects/status/8o02frivdxa0dgpl/branch/master?svg=true)](https://ci.appveyor.com/project/Smilefounder/mix-core/branch/master) | -Gitter |[![Join the chat at https://gitter.im/mix-core/Lobby](https://badges.gitter.im/mix-core/Lobby.svg)](https://gitter.im/mix-core/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)|Licenses status |[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmixcore%2Fmix.core.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmixcore%2Fmix.core?ref=badge_shield) | -Codefactor |[![CodeFactor](https://www.codefactor.io/repository/github/mixcore/mix.core/badge)](https://www.codefactor.io/repository/github/mixcore/mix.core) |Azure|[![Build Status](https://dev.azure.com/mixcore/mix.core/_apis/build/status/mixcore.mix.core?branchName=master)](https://dev.azure.com/mixcore/mix.core/_build/latest?definitionId=1&branchName=master)| - - -## References - - -| |Links | -|---------|---------| -|STAG / Demo |https://demo.mixcore.org or https://dev.mixcore.org or https://mochahost.demo.mixcore.org/portal (administrator / P@ssw0rd) Ref: [How to install Mixcore CMS with Azure Docker Composer](https://community.mixcore.org/topic/4/install-mixcore-cms-with-azure-and-docker-compose)| -|Dev docs |https://docs.mixcore.org / https://mixcore.dev| -|Community |https://community.mixcore.org| -|Youtube |https://www.youtube.com/channel/UChqzh6JnC8HBUSQ9AWIcZAw| -|Twitter |https://twitter.com/mixcore_cms | -|Medium |https://medium.com/mixcore | - -## Run with Docker - -### Latest Docker Image +### Local Development ```sh -docker pull mixcore/mix.core:latest -docker run -it --rm -p 5000:80 --name mixcore_cms mixcore/mix.core:latest -``` - -### Or with Docker Compose -```sh -docker-compose build -docker-compose up -``` - -## GITs clone -```sh -mkdir mixcore -cd mixcore - +# Clone the repository git clone --branch develop --recursive https://github.com/mixcore/mix.core.git -``` -Optional: +# Navigate to the project directory +cd mix.core -> Optional steps as those packages are Nuget Library +# Build and start all services +docker-compose up --build -```bash -git submodule update --init --recursive +# Access the application at http://localhost:5000 ``` +## 🛠️ Key Features +- **Modern Tech Stack**: .NET 9.0, ASP.NET Core, SignalR, GraphQL +- **Microservices Architecture**: Scalable and maintainable +- **Multi-Tenant Support**: Perfect for SaaS applications +- **Real-time Capabilities**: Powered by SignalR +- **API-First Approach**: RESTful APIs and GraphQL +- **Enterprise Security**: OAuth 2.0, OpenID Connect, JWT +- **Cloud-Native**: Kubernetes and Docker ready -## Build & Run with [Dotnet SDK](https://dotnet.microsoft.com/download) - -### Build & Run Mixcore CMS +## 📦 Tech Stack -> REM Make sure you already read and download Dotnet Core SDK here https://dotnet.microsoft.com/download +| Component | Version | +|-----------|---------| +| .NET Core | 9.0 | +| ASP.NET Core | 9.0 | +| Entity Framework Core | 9.0 | +| SignalR | 9.0 | +| GraphQL | 7.0 | +| Docker | Latest | +| Kubernetes | Latest | +| Redis | 7.0 | +| SQL Server | 2022 | +| MySQL | 8.0 | +| ScyllaDB | Latest | -```sh -cd mix.core/src/Mix.Cms.Web - -dotnet restore -dotnet build -dotnet run -``` -### Modify & Build Portal Front-End source (Optional) - -> This step is optional and only needed in case you would like to modify the portal front-end code +## 📚 Documentation -````sh -cd mix.core/src/portal-app +For detailed documentation, please visit our [Wiki](https://github.com/mixcore/mix.core/wiki): -npm install -npm install --global gulp-cli -gulp build -```` +- [Getting Started](https://github.com/mixcore/mix.core/wiki/Getting-Started) +- [Architecture](https://github.com/mixcore/mix.core/wiki/Architecture) +- [Deployment](https://github.com/mixcore/mix.core/wiki/Deployment) +- [Development](https://github.com/mixcore/mix.core/wiki/Development) +- [API Reference](https://github.com/mixcore/mix.core/wiki/API-Reference) +- [Security](https://github.com/mixcore/mix.core/wiki/Security) +- [Troubleshooting](https://github.com/mixcore/mix.core/wiki/Troubleshooting) -> Note: If you facing any System.Data.SqlClient.SqlException error, please replace all content inside "appsettings.json" file with "{}". +## 💼 Enterprise Support -## Thanks to +We offer various support plans for enterprises and agencies: -> This project has been developed using: -* [.NET](https://www.microsoft.com/net/core) -* [Bootstrap](https://getbootstrap.com/) -* [BrowserStack](https://www.browserstack.com/) -* [Designed by Freepik](https://www.freepik.com) -* And more... +- **Standard Support**: Email support, documentation access +- **Professional Support**: 24/7 support, SLAs, dedicated account manager +- **Enterprise Support**: Custom solutions, on-site support, training +- **Agency Program**: White-labeling, multi-client management, custom features +Contact us at [enterprise@mixcore.org](mailto:enterprise@mixcore.org) for more information. -## License +## 📄 License -Mixcore CMS is licensed under the **[MIT](https://github.com/mixcore/mix.core/blob/master/LICENSE)** +Mixcore CMS is licensed under the **[MIT License](https://github.com/mixcore/mix.core/blob/master/LICENSE)** +## 🤝 Contributing -|Permissions |Limitations |Conditions | -|---------|---------|---------| -|✔ Commercial use |✖ Liability |ℹ License and copyright notice | -|✔ Modification |✖ Warranty | | -|✔ Distribution | | | -|✔ Private use | | | -| | | | +We welcome contributions! Please see our [Contributing Guide](https://github.com/mixcore/mix.core/wiki/Contributing) for details. +## 💖 Support -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmixcore%2Fmix.core.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmixcore%2Fmix.core?ref=badge_large) +If you find this project useful, please consider: +- [Becoming a backer](https://opencollective.com/mixcore#support) +- [Making a donation](https://www.paypal.me/mixcore) +- [Buying us a coffee](https://www.buymeacoffee.com/mixcore) -## Activity +## 📊 Activity ![Alt](https://repobeats.axiom.co/api/embed/4ec425735bae424c69c063f2bac106c3107b6db4.svg "Repobeats analytics image") -======= -## Star History +## ⭐ Star History [![Star History Chart](https://api.star-history.com/svg?repos=mixcore/mix.core&type=Date)](https://star-history.com/#mixcore/mix.core&Date) -## Contributors Wall +## 👥 Contributors + - -## How to contribute - -Fork this repo to your GitHub account, clone it locally and try to follow -the following simple guidelines. - -* **Never** write any code in the master branch -* When writing code, do it in a specific feature branch -* Send your pull request from that feature branch -* After your pull request has been accepted, sync the changes into master from the upstream remote -* Delete you feature branch -* Again, **NEVER** write any code in the master branch ;) -* Ref: https://datasift.github.io/gitflow/IntroducingGitFlow.html diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index d6ad167a2..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,43 +0,0 @@ -version: 1.0.{build} -image: Visual Studio 2017 -configuration: Release -init: -- ps: >- - choco install opencover.portable - - choco install dotnetcore-sdk - - choco install codecov - - choco install "msbuild-sonarqube-runner" -y -before_build: -- cmd: >- - git clone -q --branch=master https://github.com/mixcore/mix.heart.git C:\projects\mix.heart - - git clone -q --branch=master https://github.com/mixcore/mix.identity.git C:\projects\mix.identity - - cd src - - nuget restore - - cd Mix.Cms.Web - - dotnet restore - - dotnet build -build: - project: src\MixCore.sln - verbosity: minimal -test_script: -- cmd: >- -artifacts: -- path: dist - name: web-app -notifications: -- provider: Email - to: - - nhathoang989@gmail.com - subject: '[ mixcore ] Build failure' - on_build_success: false - on_build_failure: true - on_build_status_changed: false \ No newline at end of file diff --git a/cloud/aws/eks-cluster.yaml b/cloud/aws/eks-cluster.yaml new file mode 100644 index 000000000..be80f34d6 --- /dev/null +++ b/cloud/aws/eks-cluster.yaml @@ -0,0 +1,203 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: 'EKS Cluster for Mixcore CMS' + +Parameters: + ClusterName: + Type: String + Default: mixcore-cluster + NodeInstanceType: + Type: String + Default: t3.medium + NodeGroupName: + Type: String + Default: mixcore-nodes + NodeCount: + Type: Number + Default: 3 + VpcBlock: + Type: String + Default: 192.168.0.0/16 + Subnet01Block: + Type: String + Default: 192.168.64.0/18 + Subnet02Block: + Type: String + Default: 192.168.128.0/18 + +Resources: + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !Ref VpcBlock + EnableDnsHostnames: true + EnableDnsSupport: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-VPC + + InternetGateway: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-IGW + + VPCGatewayAttachment: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + InternetGatewayId: !Ref InternetGateway + VpcId: !Ref VPC + + Subnet01: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: !Select [0, !GetAZs ''] + CidrBlock: !Ref Subnet01Block + MapPublicIpOnLaunch: true + VpcId: !Ref VPC + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Subnet01 + + Subnet02: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: !Select [1, !GetAZs ''] + CidrBlock: !Ref Subnet02Block + MapPublicIpOnLaunch: true + VpcId: !Ref VPC + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Subnet02 + + RouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-RouteTable + + Route: + Type: AWS::EC2::Route + DependsOn: VPCGatewayAttachment + Properties: + RouteTableId: !Ref RouteTable + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: !Ref InternetGateway + + Subnet01RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref RouteTable + SubnetId: !Ref Subnet01 + + Subnet02RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref RouteTable + SubnetId: !Ref Subnet02 + + EKSCluster: + Type: AWS::EKS::Cluster + Properties: + Name: !Ref ClusterName + RoleArn: !GetAtt ClusterRole.Arn + Version: "1.28" + ResourcesVpcConfig: + SubnetIds: + - !Ref Subnet01 + - !Ref Subnet02 + SecurityGroupIds: + - !Ref ClusterSecurityGroup + + ClusterRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - eks.amazonaws.com + Action: + - sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AmazonEKSClusterPolicy + + NodeGroupRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Action: + - sts:AssumeRole + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy + - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy + - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly + + NodeGroup: + Type: AWS::EKS::Nodegroup + Properties: + ClusterName: !Ref ClusterName + NodegroupName: !Ref NodeGroupName + NodeRole: !GetAtt NodeGroupRole.Arn + Subnets: + - !Ref Subnet01 + - !Ref Subnet02 + ScalingConfig: + MinSize: !Ref NodeCount + MaxSize: !Ref NodeCount + DesiredSize: !Ref NodeCount + InstanceTypes: + - !Ref NodeInstanceType + Tags: + Name: !Sub ${AWS::StackName}-NodeGroup + + ClusterSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Cluster security group + VpcId: !Ref VPC + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 443 + ToPort: 443 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 1433 + ToPort: 1433 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 3306 + ToPort: 3306 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 6379 + ToPort: 6379 + CidrIp: 0.0.0.0/0 + +Outputs: + ClusterName: + Description: The name of the EKS cluster + Value: !Ref ClusterName + ClusterEndpoint: + Description: The endpoint URL for the EKS cluster + Value: !GetAtt EKSCluster.Endpoint + NodeGroupName: + Description: The name of the node group + Value: !Ref NodeGroupName + VpcId: + Description: The VPC ID + Value: !Ref VPC \ No newline at end of file diff --git a/cloud/azure/aks-cluster.bicep b/cloud/azure/aks-cluster.bicep new file mode 100644 index 000000000..9235a0f08 --- /dev/null +++ b/cloud/azure/aks-cluster.bicep @@ -0,0 +1,82 @@ +@description('The name of the Managed Cluster resource.') +param clusterName string = 'mixcore-cluster' + +@description('The location for all resources.') +param location string = resourceGroup().location + +@description('The name of the resource group containing the VNet.') +param vnetResourceGroupName string = resourceGroup().name + +@description('The name of the VNet.') +param vnetName string = 'mixcore-vnet' + +@description('The name of the subnet.') +param subnetName string = 'mixcore-subnet' + +@description('The CIDR prefix for the VNet.') +param vnetPrefix string = '10.0.0.0/16' + +@description('The CIDR prefix for the subnet.') +param subnetPrefix string = '10.0.0.0/22' + +@description('The size of the VM.') +param vmSize string = 'Standard_D2s_v3' + +@description('The number of nodes in the node pool.') +param nodeCount int = 3 + +@description('The version of Kubernetes.') +param kubernetesVersion string = '1.28.0' + +resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = { + name: vnetName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + vnetPrefix + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: subnetPrefix + } + } + ] + } +} + +resource aks 'Microsoft.ContainerService/managedClusters@2021-05-01' = { + name: clusterName + location: location + properties: { + kubernetesVersion: kubernetesVersion + dnsPrefix: clusterName + agentPoolProfiles: [ + { + name: 'agentpool' + count: nodeCount + vmSize: vmSize + osType: 'Linux' + mode: 'System' + vnetSubnetID: vnet.properties.subnets[0].id + } + ] + networkProfile: { + networkPlugin: 'azure' + serviceCidr: '10.2.0.0/16' + dnsServiceIP: '10.2.0.10' + dockerBridgeCidr: '172.17.0.1/16' + } + servicePrincipalProfile: { + clientId: subscription().servicePrincipalProfile.clientId + secret: subscription().servicePrincipalProfile.secret + } + } +} + +output clusterName string = aks.name +output clusterFqdn string = aks.properties.fqdn +output clusterNodeResourceGroup string = aks.properties.nodeResourceGroup \ No newline at end of file diff --git a/cloud/gcp/gke-cluster.yaml b/cloud/gcp/gke-cluster.yaml new file mode 100644 index 000000000..bd1da38bc --- /dev/null +++ b/cloud/gcp/gke-cluster.yaml @@ -0,0 +1,57 @@ +apiVersion: container.cnrm.cloud.google.com/v1beta1 +kind: ContainerCluster +metadata: + name: mixcore-cluster + labels: + environment: production +spec: + location: asia-southeast1 + initialNodeCount: 3 + minMasterVersion: "1.28" + networkRef: + name: mixcore-network + subnetworkRef: + name: mixcore-subnet + ipAllocationPolicy: + clusterIpv4CidrBlock: "10.0.0.0/16" + servicesIpv4CidrBlock: "10.1.0.0/16" + nodeConfig: + machineType: e2-medium + diskSizeGb: 100 + oauthScopes: + - https://www.googleapis.com/auth/cloud-platform + tags: + - mixcore + masterAuthorizedNetworksConfig: + cidrBlocks: + - displayName: all + cidrBlock: 0.0.0.0/0 + privateClusterConfig: + enablePrivateNodes: false + enablePrivateEndpoint: false + releaseChannel: + channel: REGULAR +--- +apiVersion: compute.cnrm.cloud.google.com/v1beta1 +kind: ComputeNetwork +metadata: + name: mixcore-network +spec: + routingMode: REGIONAL + autoCreateSubnetworks: false +--- +apiVersion: compute.cnrm.cloud.google.com/v1beta1 +kind: ComputeSubnetwork +metadata: + name: mixcore-subnet +spec: + networkRef: + name: mixcore-network + region: asia-southeast1 + ipCidrRange: 10.0.0.0/22 + privateIpGoogleAccess: true + secondaryIpRanges: + - rangeName: pods + ipCidrRange: 10.0.4.0/22 + - rangeName: services + ipCidrRange: 10.0.8.0/22 \ No newline at end of file diff --git a/cloud/opentofu/main.tf b/cloud/opentofu/main.tf new file mode 100644 index 000000000..67f535200 --- /dev/null +++ b/cloud/opentofu/main.tf @@ -0,0 +1,207 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + azurerm = { + source = "hashicorp/azurerm" + version = "~> 3.0" + } + google = { + source = "hashicorp/google" + version = "~> 5.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.0" + } + helm = { + source = "hashicorp/helm" + version = "~> 2.0" + } + } +} + +# AWS Provider +provider "aws" { + region = var.aws_region +} + +# Azure Provider +provider "azurerm" { + features {} +} + +# GCP Provider +provider "google" { + project = var.gcp_project_id + region = var.gcp_region +} + +# Kubernetes Provider +provider "kubernetes" { + config_path = local.kubeconfig_path +} + +# Helm Provider +provider "helm" { + kubernetes { + config_path = local.kubeconfig_path + } +} + +# Variables +variable "aws_region" { + description = "AWS region" + default = "ap-southeast-1" +} + +variable "gcp_project_id" { + description = "GCP project ID" +} + +variable "gcp_region" { + description = "GCP region" + default = "asia-southeast1" +} + +variable "cluster_name" { + description = "Kubernetes cluster name" + default = "mixcore-cluster" +} + +# Locals +locals { + kubeconfig_path = var.cloud_provider == "aws" ? "~/.kube/config-eks" : + var.cloud_provider == "azure" ? "~/.kube/config-aks" : + "~/.kube/config-gke" +} + +# AWS EKS Cluster +module "aws_eks" { + count = var.cloud_provider == "aws" ? 1 : 0 + source = "terraform-aws-modules/eks/aws" + version = "~> 19.0" + + cluster_name = var.cluster_name + cluster_version = "1.28" + + vpc_id = module.vpc[0].vpc_id + subnet_ids = module.vpc[0].private_subnets + + eks_managed_node_groups = { + mixcore = { + min_size = 3 + max_size = 5 + desired_size = 3 + + instance_types = ["t3.medium"] + } + } +} + +# Azure AKS Cluster +module "azure_aks" { + count = var.cloud_provider == "azure" ? 1 : 0 + source = "Azure/aks/azurerm" + version = "~> 7.0" + + resource_group_name = "mixcore-rg" + cluster_name = var.cluster_name + kubernetes_version = "1.28.0" + + default_node_pool = { + name = "mixcore" + node_count = 3 + vm_size = "Standard_D2s_v3" + } + + network_plugin = "azure" + network_policy = "azure" +} + +# GCP GKE Cluster +module "gcp_gke" { + count = var.cloud_provider == "gcp" ? 1 : 0 + source = "terraform-google-modules/kubernetes-engine/google" + version = "~> 30.0" + + project_id = var.gcp_project_id + name = var.cluster_name + region = var.gcp_region + zones = ["${var.gcp_region}-a", "${var.gcp_region}-b"] + network = "mixcore-vpc" + subnetwork = "mixcore-subnet" + ip_range_pods = "mixcore-pods" + ip_range_services = "mixcore-services" + + node_pools = [ + { + name = "mixcore" + machine_type = "e2-medium" + node_count = 3 + } + ] +} + +# Kubernetes Resources +resource "kubernetes_namespace" "mixcore" { + metadata { + name = "mixcore" + } +} + +resource "kubernetes_secret" "mixcore_secrets" { + metadata { + name = "mixcore-secrets" + namespace = kubernetes_namespace.mixcore.metadata[0].name + } + + data = { + sqlserver-password = base64encode(var.sqlserver_password) + mysql-password = base64encode(var.mysql_password) + redis-password = base64encode(var.redis_password) + } +} + +resource "kubernetes_config_map" "mixcore_config" { + metadata { + name = "mixcore-config" + namespace = kubernetes_namespace.mixcore.metadata[0].name + } + + data = { + ASPNETCORE_ENVIRONMENT = "Production" + ASPNETCORE_URLS = "http://+:80;https://+:443" + ConnectionStrings__DefaultConnection = "Server=mixcore-sqlserver;Database=mixcore;User=sa;Password=${var.sqlserver_password};" + ConnectionStrings__MySqlConnection = "Server=mixcore-mysql;Database=mixcore;User=root;Password=${var.mysql_password};" + Redis__ConnectionString = "mixcore-redis:6379" + } +} + +# Helm Releases +resource "helm_release" "mixcore" { + name = "mixcore" + namespace = kubernetes_namespace.mixcore.metadata[0].name + repository = "https://charts.mixcore.org" + chart = "mixcore" + version = "1.0.0" + + values = [ + file("${path.module}/values.yaml") + ] +} + +# Outputs +output "cluster_endpoint" { + value = var.cloud_provider == "aws" ? module.aws_eks[0].cluster_endpoint : + var.cloud_provider == "azure" ? module.azure_aks[0].cluster_endpoint : + module.gcp_gke[0].endpoint +} + +output "kubeconfig" { + value = var.cloud_provider == "aws" ? module.aws_eks[0].kubeconfig : + var.cloud_provider == "azure" ? module.azure_aks[0].kubeconfig : + module.gcp_gke[0].kubeconfig +} \ No newline at end of file diff --git a/cloud/opentofu/values.yaml b/cloud/opentofu/values.yaml new file mode 100644 index 000000000..d8a5725eb --- /dev/null +++ b/cloud/opentofu/values.yaml @@ -0,0 +1,87 @@ +replicaCount: 3 + +image: + repository: mixcore/mix.core + tag: latest + pullPolicy: Always + +service: + type: LoadBalancer + port: 80 + targetPort: 80 + +ingress: + enabled: true + className: nginx + annotations: + kubernetes.io/ingress.class: nginx + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: mixcore.example.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: mixcore-tls + hosts: + - mixcore.example.com + +resources: + requests: + cpu: 500m + memory: 1Gi + limits: + cpu: 1 + memory: 2Gi + +autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +sqlserver: + enabled: true + persistence: + enabled: true + size: 10Gi + resources: + requests: + cpu: 1 + memory: 2Gi + limits: + cpu: 2 + memory: 4Gi + +mysql: + enabled: true + persistence: + enabled: true + size: 10Gi + resources: + requests: + cpu: 500m + memory: 1Gi + limits: + cpu: 1 + memory: 2Gi + +redis: + enabled: true + persistence: + enabled: true + size: 5Gi + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi \ No newline at end of file diff --git a/cloud/opentofu/variables.tf b/cloud/opentofu/variables.tf new file mode 100644 index 000000000..2cf94c9b2 --- /dev/null +++ b/cloud/opentofu/variables.tf @@ -0,0 +1,64 @@ +variable "cloud_provider" { + description = "Cloud provider to deploy to (aws, azure, gcp)" + type = string + default = "aws" +} + +variable "aws_region" { + description = "AWS region" + type = string + default = "ap-southeast-1" +} + +variable "gcp_project_id" { + description = "GCP project ID" + type = string +} + +variable "gcp_region" { + description = "GCP region" + type = string + default = "asia-southeast1" +} + +variable "cluster_name" { + description = "Kubernetes cluster name" + type = string + default = "mixcore-cluster" +} + +variable "sqlserver_password" { + description = "SQL Server password" + type = string + sensitive = true +} + +variable "mysql_password" { + description = "MySQL password" + type = string + sensitive = true +} + +variable "redis_password" { + description = "Redis password" + type = string + sensitive = true +} + +variable "node_count" { + description = "Number of nodes in the cluster" + type = number + default = 3 +} + +variable "node_size" { + description = "Size of the nodes" + type = string + default = "t3.medium" +} + +variable "environment" { + description = "Environment (dev, staging, prod)" + type = string + default = "prod" +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1e703f42e..88dc8dcff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,36 +1,76 @@ -version: "3" +version: "3.8" + services: mixcore: # image: "mixcore/mix.core:develop" - build: . + build: + context: . + dockerfile: Dockerfile ports: - "5000:80" - "5001:443" + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://+:80;https://+:443 + - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx + - ASPNETCORE_Kestrel__Certificates__Default__Password=P@ssw0rd + volumes: + - ./src:/app/src + - ~/.aspnet/https:/https:ro depends_on: - sqlserver - mysql + - redis + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80/health"] + interval: 30s + timeout: 10s + retries: 3 sqlserver: - image: "mcr.microsoft.com/mssql/server" + image: "mcr.microsoft.com/mssql/server:2022-latest" environment: - SA_PASSWORD: "P@ssw0rd" - ACCEPT_EULA: "Y" + - SA_PASSWORD=P@ssw0rd + - ACCEPT_EULA=Y + - MSSQL_PID=Developer ports: - "1433:1433" + volumes: + - sqlserver_data:/var/opt/mssql restart: always - # redis: - # image: redis + healthcheck: + test: ["CMD", "/opt/mssql-tools/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "P@ssw0rd", "-Q", "SELECT 1"] + interval: 30s + timeout: 10s + retries: 3 + redis: + image: redis + volumes: + - redis_data:/data + restart: always + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 30s + timeout: 10s + retries: 3 mysql: image: mysql:8.0 volumes: - - db_data:/var/lib/mysql + - mysql_data:/var/lib/mysql restart: always environment: - MYSQL_ROOT_PASSWORD: P@ssw0rd - MYSQL_DATABASE: mixcore - MYSQL_USER: mixcore - MYSQL_PASSWORD: P@ssw0rd + - MYSQL_ROOT_PASSWORD=P@ssw0rd + - MYSQL_DATABASE=mixcore + - MYSQL_USER=mixcore + - MYSQL_PASSWORD=P@ssw0rd ports: - 3306:3306 + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pP@ssw0rd"] + interval: 30s + timeout: 10s + retries: 3 # phpmyadmin phpmyadmin: depends_on: @@ -40,7 +80,14 @@ services: ports: - '8080:80' environment: - PMA_HOST: mysql - MYSQL_ROOT_PASSWORD: P@ssw0rd + - PMA_HOST=mysql + - MYSQL_ROOT_PASSWORD=P@ssw0rd + volumes: - db_data: {} \ No newline at end of file + sqlserver_data: + mysql_data: + redis_data: + +networks: + default: + driver: bridge \ No newline at end of file diff --git a/docs/wiki b/docs/wiki new file mode 160000 index 000000000..8b96abb3a --- /dev/null +++ b/docs/wiki @@ -0,0 +1 @@ +Subproject commit 8b96abb3aae260e1a2270dec004657eaafcea47d diff --git a/k8s/configmap.yaml b/k8s/configmap.yaml new file mode 100644 index 000000000..01472e28d --- /dev/null +++ b/k8s/configmap.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mixcore-config + namespace: mixcore +data: + ASPNETCORE_ENVIRONMENT: "Production" + ASPNETCORE_URLS: "http://+:80;https://+:443" + ConnectionStrings__DefaultConnection: "Server=mixcore-sqlserver;Database=mixcore;User=sa;Password=P@ssw0rd;" + ConnectionStrings__MySqlConnection: "Server=mixcore-mysql;Database=mixcore;User=root;Password=root;" + Redis__ConnectionString: "mixcore-redis:6379" \ No newline at end of file diff --git a/k8s/deployments.yaml b/k8s/deployments.yaml new file mode 100644 index 000000000..cd6898c49 --- /dev/null +++ b/k8s/deployments.yaml @@ -0,0 +1,164 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mixcore + namespace: mixcore +spec: + replicas: 2 + selector: + matchLabels: + app: mixcore + template: + metadata: + labels: + app: mixcore + spec: + containers: + - name: mixcore + image: mixcore/mix.core:latest + ports: + - containerPort: 80 + - containerPort: 443 + envFrom: + - configMapRef: + name: mixcore-config + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "1" + memory: "2Gi" + livenessProbe: + httpGet: + path: /health + port: 80 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 80 + initialDelaySeconds: 5 + periodSeconds: 5 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mixcore-sqlserver + namespace: mixcore +spec: + replicas: 1 + selector: + matchLabels: + app: mixcore-sqlserver + template: + metadata: + labels: + app: mixcore-sqlserver + spec: + containers: + - name: sqlserver + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - containerPort: 1433 + env: + - name: ACCEPT_EULA + value: "Y" + - name: SA_PASSWORD + valueFrom: + secretKeyRef: + name: mixcore-secrets + key: sqlserver-password + volumeMounts: + - name: sqlserver-data + mountPath: /var/opt/mssql + resources: + requests: + cpu: "1" + memory: "2Gi" + limits: + cpu: "2" + memory: "4Gi" + volumes: + - name: sqlserver-data + persistentVolumeClaim: + claimName: mixcore-sqlserver-pvc +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mixcore-mysql + namespace: mixcore +spec: + replicas: 1 + selector: + matchLabels: + app: mixcore-mysql + template: + metadata: + labels: + app: mixcore-mysql + spec: + containers: + - name: mysql + image: mysql:8.0 + ports: + - containerPort: 3306 + env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mixcore-secrets + key: mysql-password + - name: MYSQL_DATABASE + value: "mixcore" + volumeMounts: + - name: mysql-data + mountPath: /var/lib/mysql + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "1" + memory: "2Gi" + volumes: + - name: mysql-data + persistentVolumeClaim: + claimName: mixcore-mysql-pvc +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mixcore-redis + namespace: mixcore +spec: + replicas: 1 + selector: + matchLabels: + app: mixcore-redis + template: + metadata: + labels: + app: mixcore-redis + spec: + containers: + - name: redis + image: redis:7.0 + ports: + - containerPort: 6379 + volumeMounts: + - name: redis-data + mountPath: /data + resources: + requests: + cpu: "200m" + memory: "256Mi" + limits: + cpu: "500m" + memory: "512Mi" + volumes: + - name: redis-data + persistentVolumeClaim: + claimName: mixcore-redis-pvc \ No newline at end of file diff --git a/k8s/namespace.yaml b/k8s/namespace.yaml new file mode 100644 index 000000000..0b5872b3f --- /dev/null +++ b/k8s/namespace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: mixcore + labels: + name: mixcore + environment: production \ No newline at end of file diff --git a/k8s/secrets.yaml b/k8s/secrets.yaml new file mode 100644 index 000000000..e04dbee57 --- /dev/null +++ b/k8s/secrets.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mixcore-secrets + namespace: mixcore +type: Opaque +data: + sqlserver-password: UEBzc3cwcmQ= # Base64 encoded "P@ssw0rd" + mysql-password: cm9vdA== # Base64 encoded "root" + redis-password: UEBzc3cwcmQ= # Base64 encoded "P@ssw0rd" \ No newline at end of file diff --git a/k8s/services.yaml b/k8s/services.yaml new file mode 100644 index 000000000..6efd7da58 --- /dev/null +++ b/k8s/services.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +kind: Service +metadata: + name: mixcore + namespace: mixcore +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 80 + name: http + - port: 443 + targetPort: 443 + name: https + selector: + app: mixcore +--- +apiVersion: v1 +kind: Service +metadata: + name: mixcore-sqlserver + namespace: mixcore +spec: + ports: + - port: 1433 + targetPort: 1433 + selector: + app: mixcore-sqlserver +--- +apiVersion: v1 +kind: Service +metadata: + name: mixcore-mysql + namespace: mixcore +spec: + ports: + - port: 3306 + targetPort: 3306 + selector: + app: mixcore-mysql +--- +apiVersion: v1 +kind: Service +metadata: + name: mixcore-redis + namespace: mixcore +spec: + ports: + - port: 6379 + targetPort: 6379 + selector: + app: mixcore-redis \ No newline at end of file diff --git a/k8s/storage.yaml b/k8s/storage.yaml new file mode 100644 index 000000000..ecfc7d9ba --- /dev/null +++ b/k8s/storage.yaml @@ -0,0 +1,83 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: mixcore-sqlserver-pv + namespace: mixcore +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: standard + hostPath: + path: /data/mixcore/sqlserver +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mixcore-sqlserver-pvc + namespace: mixcore +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: standard +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: mixcore-mysql-pv + namespace: mixcore +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: standard + hostPath: + path: /data/mixcore/mysql +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mixcore-mysql-pvc + namespace: mixcore +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: standard +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: mixcore-redis-pv + namespace: mixcore +spec: + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: standard + hostPath: + path: /data/mixcore/redis +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mixcore-redis-pvc + namespace: mixcore +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi + storageClassName: standard \ No newline at end of file diff --git a/scripts/create-droplet.sh b/scripts/create-droplet.sh new file mode 100644 index 000000000..042bb6823 --- /dev/null +++ b/scripts/create-droplet.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +# Check if doctl is installed +if ! command -v doctl &> /dev/null; then + echo "doctl is not installed. Please install it first: https://docs.digitalocean.com/reference/doctl/how-to/install/" + exit 1 +fi + +# Check if DO_TOKEN is set +if [ -z "$DO_TOKEN" ]; then + echo "Please set your DigitalOcean API token:" + echo "export DO_TOKEN=your_token_here" + exit 1 +fi + +# Set variables +DROPLET_NAME="mixcore" +REGION="sgp1" # Singapore region +SIZE="s-2vcpu-4gb" # 2 vCPUs, 4GB RAM +IMAGE="ubuntu-22-04-x64" +SSH_KEY_ID="" # You need to add your SSH key ID here + +# Create droplet +echo "Creating droplet..." +doctl compute droplet create $DROPLET_NAME \ + --region $REGION \ + --size $SIZE \ + --image $IMAGE \ + --ssh-keys $SSH_KEY_ID \ + --wait + +# Get droplet IP +DROPLET_IP=$(doctl compute droplet get $DROPLET_NAME --format PublicIPv4 --no-header) + +echo "Droplet created successfully!" +echo "IP Address: $DROPLET_IP" + +# Wait for SSH to be available +echo "Waiting for SSH to be available..." +sleep 30 + +# Setup the server +echo "Setting up the server..." +ssh root@$DROPLET_IP << 'EOF' + # Update system + apt-get update && apt-get upgrade -y + + # Install required packages + apt-get install -y \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + software-properties-common + + # Install Docker + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io + + # Install Docker Compose + curl -L "https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + + # Install .NET 9.0 SDK + wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + dpkg -i packages-microsoft-prod.deb + rm packages-microsoft-prod.deb + apt-get update + apt-get install -y dotnet-sdk-9.0 + + # Create non-root user + useradd -m -s /bin/bash mixcore + usermod -aG docker mixcore + + # Setup firewall + ufw allow 22/tcp + ufw allow 80/tcp + ufw allow 443/tcp + ufw allow 5000/tcp + ufw allow 5001/tcp + ufw allow 1433/tcp + ufw allow 3306/tcp + ufw allow 6379/tcp + ufw allow 8080/tcp + ufw --force enable + + # Create application directory + mkdir -p /opt/mixcore + chown -R mixcore:mixcore /opt/mixcore +EOF + +echo "Server setup completed!" +echo "You can now SSH into the server using:" +echo "ssh root@$DROPLET_IP" +echo "Or as the mixcore user:" +echo "ssh mixcore@$DROPLET_IP" \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 000000000..40c91ae68 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# Check if SSH key and droplet IP are provided +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Usage: ./deploy.sh " + exit 1 +fi + +SSH_KEY=$1 +DROPLET_IP=$2 + +# Build the application +echo "Building the application..." +docker-compose build + +# Create deployment archive +echo "Creating deployment archive..." +tar -czf deploy.tar.gz \ + docker-compose.yml \ + Dockerfile \ + .dockerignore \ + src/ + +# Copy files to server +echo "Copying files to server..." +scp -i $SSH_KEY deploy.tar.gz mixcore@$DROPLET_IP:/opt/mixcore/ + +# Deploy on server +echo "Deploying on server..." +ssh -i $SSH_KEY mixcore@$DROPLET_IP << 'EOF' + cd /opt/mixcore + tar -xzf deploy.tar.gz + rm deploy.tar.gz + + # Start the application + docker-compose up -d + + # Check if containers are running + docker-compose ps +EOF + +echo "Deployment completed!" +echo "Application is running at:" +echo "http://$DROPLET_IP:5000" +echo "phpMyAdmin: http://$DROPLET_IP:8080" \ No newline at end of file diff --git a/src/Mixcore.sln b/src/Mixcore.sln index 9bcd3656a..ae5692a8d 100644 --- a/src/Mixcore.sln +++ b/src/Mixcore.sln @@ -36,7 +36,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mix.grpc", "modules\mix.grp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mix.queue", "platform\mix.queue\mix.queue.csproj", "{2E843175-B948-4D90-A11B-415EE67D9E1A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mix.heart", "platform\core\mix-heart\src\mix.heart\mix.heart.csproj", "{D4938E22-22C3-4364-A710-557B63954B5E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mix.heart", "platform\core\mix-heart\src\mix.heart.csproj", "{D4938E22-22C3-4364-A710-557B63954B5E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mix.tenancy", "modules\mix.tenancy\mix.tenancy.csproj", "{2D254550-CE54-43A2-8AE9-FAA72F2C0FD6}" EndProject @@ -131,6 +131,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution items", "solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mix.scylladb", "platform\mix.scylladb\mix.scylladb.csproj", "{A15A6930-30D6-46DC-B02B-0015D059A0C8}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mix.automation", "mix.automation", "{7F7A886F-E0E7-4CF1-B303-E2789FB0421E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mix.automation.api", "services\mix.automation\mix.automation.api\mix.automation.api.csproj", "{003DC8DF-59D4-4E31-8BF5-397AFB3654B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mix.automation.lib", "services\mix.automation\mix.automation.lib\mix.automation.lib.csproj", "{820D972E-A0DB-4014-8F7A-48952140D50C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -297,6 +303,14 @@ Global {A15A6930-30D6-46DC-B02B-0015D059A0C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {A15A6930-30D6-46DC-B02B-0015D059A0C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {A15A6930-30D6-46DC-B02B-0015D059A0C8}.Release|Any CPU.Build.0 = Release|Any CPU + {003DC8DF-59D4-4E31-8BF5-397AFB3654B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {003DC8DF-59D4-4E31-8BF5-397AFB3654B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {003DC8DF-59D4-4E31-8BF5-397AFB3654B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {003DC8DF-59D4-4E31-8BF5-397AFB3654B7}.Release|Any CPU.Build.0 = Release|Any CPU + {820D972E-A0DB-4014-8F7A-48952140D50C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {820D972E-A0DB-4014-8F7A-48952140D50C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {820D972E-A0DB-4014-8F7A-48952140D50C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {820D972E-A0DB-4014-8F7A-48952140D50C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -363,6 +377,9 @@ Global {4B4B73DD-9014-4609-89A0-3297289157A6} = {7E1C5D8B-58B4-40B1-8BA7-F8F80B76F276} {737720EE-DF6F-4457-84D6-45E0E2F209E6} = {C93898B7-2001-4C29-9BBE-33A2E61C350A} {A15A6930-30D6-46DC-B02B-0015D059A0C8} = {69169108-92D3-411D-87EB-DF2DD026FE64} + {7F7A886F-E0E7-4CF1-B303-E2789FB0421E} = {C0A05428-767E-46C5-A31F-0D220E41F7C5} + {003DC8DF-59D4-4E31-8BF5-397AFB3654B7} = {7F7A886F-E0E7-4CF1-B303-E2789FB0421E} + {820D972E-A0DB-4014-8F7A-48952140D50C} = {7F7A886F-E0E7-4CF1-B303-E2789FB0421E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0143C230-7F40-44B2-8BA3-EF5B92D55848} diff --git a/src/applications/mixcore.gateway/Program.cs b/src/applications/mixcore.gateway/Program.cs index 6581c37b6..5c0f39b9c 100644 --- a/src/applications/mixcore.gateway/Program.cs +++ b/src/applications/mixcore.gateway/Program.cs @@ -37,7 +37,7 @@ { builder.Services.AddEndpointsApiExplorer(); } -if (isInit) +if (builder.Configuration.IsInit()) { builder.Services.AddCors(options => { @@ -55,7 +55,6 @@ } var app = builder.Build(); -app.MapDefaultEndpoints(); Configure(app, builder.Environment, builder.Configuration); diff --git a/src/applications/mixcore.gateway/mixcore.gateway.csproj b/src/applications/mixcore.gateway/mixcore.gateway.csproj index 6f11ee60e..fdae21436 100644 --- a/src/applications/mixcore.gateway/mixcore.gateway.csproj +++ b/src/applications/mixcore.gateway/mixcore.gateway.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/src/applications/mixcore.host.aspire.AppHost/Program.cs b/src/applications/mixcore.host.aspire.AppHost/Program.cs index c67370b40..14367bda8 100644 --- a/src/applications/mixcore.host.aspire.AppHost/Program.cs +++ b/src/applications/mixcore.host.aspire.AppHost/Program.cs @@ -1,6 +1,8 @@ var builder = DistributedApplication.CreateBuilder(args); var mixcore = builder.AddProject("mixcore"); -builder.AddProject("mixcore-gateway").WithReference(mixcore); +//builder.AddProject("mixcore-gateway").WithReference(mixcore); + +//builder.AddProject("mix-automation-api"); builder.Build().Run(); diff --git a/src/applications/mixcore.host.aspire.AppHost/mixcore.host.aspire.AppHost.csproj b/src/applications/mixcore.host.aspire.AppHost/mixcore.host.aspire.AppHost.csproj index da953b480..157e56843 100644 --- a/src/applications/mixcore.host.aspire.AppHost/mixcore.host.aspire.AppHost.csproj +++ b/src/applications/mixcore.host.aspire.AppHost/mixcore.host.aspire.AppHost.csproj @@ -10,12 +10,13 @@ - + + diff --git a/src/applications/mixcore.host.aspire.ServiceDefaults/Extensions.cs b/src/applications/mixcore.host.aspire.ServiceDefaults/Extensions.cs index cdd1bce48..efed87237 100644 --- a/src/applications/mixcore.host.aspire.ServiceDefaults/Extensions.cs +++ b/src/applications/mixcore.host.aspire.ServiceDefaults/Extensions.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging; @@ -43,7 +44,12 @@ public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicati .WithMetrics(metrics => { metrics.AddRuntimeInstrumentation() - .AddBuiltInMeters(); + .AddHttpClientInstrumentation() + .AddOtlpExporter() + .AddAspNetCoreInstrumentation() + .AddProcessInstrumentation() + .AddBuiltInMeters() + .AddPrometheusExporter(); }) .WithTracing(tracing => { @@ -75,8 +81,8 @@ private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostAppli } // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) - builder.Services.AddOpenTelemetry() - .WithMetrics(metrics => metrics.AddPrometheusExporter()); + //builder.Services.AddOpenTelemetry() + // .WithMetrics(metrics => metrics.AddPrometheusExporter()); // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package) // builder.Services.AddOpenTelemetry() @@ -97,7 +103,9 @@ public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicati public static WebApplication MapDefaultEndpoints(this WebApplication app) { // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) - // app.MapPrometheusScrapingEndpoint(); + app.MapPrometheusScrapingEndpoint() + .DisableHttpMetrics(); + //.RequireAuthorization(); // All health checks must pass for app to be considered ready to accept traffic after starting app.MapHealthChecks("/health"); diff --git a/src/applications/mixcore.host.aspire.ServiceDefaults/mixcore.host.aspire.ServiceDefaults.csproj b/src/applications/mixcore.host.aspire.ServiceDefaults/mixcore.host.aspire.ServiceDefaults.csproj index 3180ec292..b0b3fb040 100644 --- a/src/applications/mixcore.host.aspire.ServiceDefaults/mixcore.host.aspire.ServiceDefaults.csproj +++ b/src/applications/mixcore.host.aspire.ServiceDefaults/mixcore.host.aspire.ServiceDefaults.csproj @@ -10,19 +10,21 @@ - - + + - - - + + + + + - - + + - - - + + + diff --git a/src/applications/mixcore/Controllers/PostContentApiController.cs b/src/applications/mixcore/Controllers/PostContentApiController.cs index f6be592f0..c5209cd17 100644 --- a/src/applications/mixcore/Controllers/PostContentApiController.cs +++ b/src/applications/mixcore/Controllers/PostContentApiController.cs @@ -45,50 +45,7 @@ public PostContentApiController( _mixDbDataService = mixDbDataService; } - [HttpPost("filter")] - public async Task>> Filter([FromBody] FilterContentRequestDto req, CancellationToken cancellationToken = default) - { - try - { - var searchRequest = BuildSearchRequest(req); - searchRequest.Predicate = searchRequest.Predicate.AndAlsoIf( - !string.IsNullOrEmpty(req.MixDatabaseName), m => m.MixDatabaseName == req.MixDatabaseName); - if (!string.IsNullOrEmpty(req.MixDatabaseName) && req.Queries.Count > 0) - { - var listData = await _mixDbDataService.GetListByAsync( - new SearchMixDbRequestModel() - { - TableName = req.MixDatabaseName, - Queries = req.Queries, - },cancellationToken); - if (listData != null) - { - List allowIds = new(); - foreach (var data in listData) - { - // used JObject.FromObject to keep original reponse fieldName - allowIds.Add(JObject.FromObject(data).Value("ParentId")); - } - searchRequest.Predicate = searchRequest.Predicate.AndAlso(m => allowIds.Contains(m.Id)); - } - } - var result = await Repository.GetPagingAsync(searchRequest.Predicate, searchRequest.PagingData); - foreach (var item in result.Items) - { - await item.LoadAdditionalDataAsync(_mixDbDataService, _metadataService, CacheService, cancellationToken); - } - return Ok(ParseSearchResult(req, result)); - } - catch (MixException) - { - throw; - } - catch (Exception ex) - { - throw new MixException(MixErrorStatus.Badrequest, ex); - } - } - + protected override async Task> SearchHandler(SearchRequestDto req, CancellationToken cancellationToken = default) { var searchPostQuery = new SearchPostQueryModel(Request, req, CurrentTenant.Id); diff --git a/src/applications/mixcore/Domain/Extensions/ServiceExtension.cs b/src/applications/mixcore/Domain/Extensions/ServiceExtension.cs index 2676790cd..0a483c93f 100644 --- a/src/applications/mixcore/Domain/Extensions/ServiceExtension.cs +++ b/src/applications/mixcore/Domain/Extensions/ServiceExtension.cs @@ -21,7 +21,7 @@ public static void AddMixRoutes(this IServiceCollection services) public static void UseMixMVCEndpoints(this IEndpointRouteBuilder routes) { - string notStartWithPattern = "regex(^(?!(mixcontent|mix-app|graph|app|init|page|post|security|portal|api|vue|error|swagger|graphql|ReDoc|OpenAPI|.+Hub))(.+)$)"; + string notStartWithPattern = "regex(^(?!(metrics|mixcontent|mix-app|graph|app|init|page|post|security|portal|api|vue|error|swagger|graphql|ReDoc|OpenAPI|.+Hub))(.+)$)"; //string urlPathPattern = @"regex((([A-z0-9\-\%]+\/)*[A-z0-9\-\%]+$)?)"; routes.MapDynamicControllerRoute( diff --git a/src/applications/mixcore/Program.cs b/src/applications/mixcore/Program.cs index 8c21154b4..4679a0ad9 100644 --- a/src/applications/mixcore/Program.cs +++ b/src/applications/mixcore/Program.cs @@ -63,8 +63,8 @@ void Configure(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); + app.UseHttpsRedirection(); } - app.UseHttpsRedirection(); app.UseMixTenant(); app.UseRouting(); diff --git a/src/applications/mixcore/Views/Portal/Index.cshtml b/src/applications/mixcore/Views/Portal/Index.cshtml index f26d229fb..8cf6e3341 100644 --- a/src/applications/mixcore/Views/Portal/Index.cshtml +++ b/src/applications/mixcore/Views/Portal/Index.cshtml @@ -2,7 +2,7 @@ @{ Layout = null; var menus = MixFileHelper.GetFile("portal-menus", ".json", MixFolders.JsonDataFolder); - var logo = configService.GetConfig("Favicon", ViewData["Culture"]?.ToString(), 1, "/mix-app/css/portal/img/mixcore-logo-green.svg"); + var logo = await configService.GetConfig("Favicon", ViewData["Culture"]?.ToString(), 1, "/mix-app/css/portal/img/mixcore-logo-green.svg"); } diff --git a/src/applications/mixcore/appsettings.json b/src/applications/mixcore/appsettings.json index 21cae1d99..145712e23 100644 --- a/src/applications/mixcore/appsettings.json +++ b/src/applications/mixcore/appsettings.json @@ -1,15 +1 @@ -{ - "Logging": { - "Console": { "TimestampFormat": "[yyyy-MM-dd HH:mm:ss.fffffff] " }, - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "Microsoft.AspNetCore.Mvc": "Information" - } - }, - "AllowAnyOrigin": false, - "DefaultCulture": "en-us", - "HttpScheme": "https", - "DatabaseProvider": "SQLITE", - "IsInit": true -} +{"Logging":{"Console":{"TimestampFormat":"[yyyy-MM-dd HH:mm:ss.fffffff] "},"LogLevel":{"Default":"Information","Microsoft.AspNetCore":"Warning","Microsoft.AspNetCore.Mvc":"Information"}},"AllowAnyOrigin":false,"DefaultCulture":"en-us","HttpScheme":"https","DatabaseProvider":"SQLITE","IsInit":true,"AesKey":"RHhjdXVHZmFpSVlmZmdhcHdNNFVQUT09LEIvN0FGTXB3MEM4TnZSbXdCSC9LT0s4STJ4ZUFSSFNpRnQzTzh4Y3BkSVE9"} diff --git a/src/applications/mixcore/mixcore.csproj b/src/applications/mixcore/mixcore.csproj index 337aa1c2d..4dbe2f155 100644 --- a/src/applications/mixcore/mixcore.csproj +++ b/src/applications/mixcore/mixcore.csproj @@ -29,6 +29,7 @@ + @@ -64,15 +65,15 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + diff --git a/src/applications/mixcore/web.config b/src/applications/mixcore/web.config new file mode 100644 index 000000000..c8fcecc36 --- /dev/null +++ b/src/applications/mixcore/web.config @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/default-mixcontent/shared/json/portal-menus.json b/src/applications/mixcore/wwwroot/default-mixcontent/shared/json/portal-menus.json index e44ae9c49..b9941c7c7 100644 --- a/src/applications/mixcore/wwwroot/default-mixcontent/shared/json/portal-menus.json +++ b/src/applications/mixcore/wwwroot/default-mixcontent/shared/json/portal-menus.json @@ -14,6 +14,13 @@ "title": "Scheduler", "subMenus": [] }, + { + "path": "/admin/workflow-trigger", + "icon": "mi mi-Tiles", + "svg": "tabler-icon-smart-home.svg", + "title": "Workflow Trigger", + "subMenus": [] + }, { "title": "Tenants", "path": "#", @@ -100,10 +107,10 @@ { "title": "Create New", "icon": "mi mi-Add", - "path": "/admin/mix-database-data/create?mixDatabaseName=metadata&mixDatabaseTitle=Metadata" + "path": "/admin/mix-database-data/create?mixDatabaseName=mix_metadata&mixDatabaseTitle=Metadata" }, { - "path": "/admin/mix-database-data/list?mixDatabaseName=Metadata&mixDatabaseTitle=Metadata&isGroup=true", + "path": "/admin/mix-database-data/list?mixDatabaseName=mix_metadata&mixDatabaseTitle=Metadata&isGroup=true", "title": "List", "icon": "mi mi-List" } @@ -212,10 +219,10 @@ { "title": "Create New", "icon": "mi mi-Add", - "path": "/admin/mix-database-data/create?mixDatabaseId=2&mixDatabaseName=sysNavigation&mixDatabaseTitle=Navigation&dataId=default" + "path": "/admin/mix-database-data/create?mixDatabaseId=2&mixDatabaseName=mix_navigation&mixDatabaseTitle=Navigation&dataId=default" }, { - "path": "/admin/mix-database-data/list?mixDatabaseId=2&mixDatabaseName=sysNavigation&mixDatabaseTitle=Navigation", + "path": "/admin/mix-database-data/list?mixDatabaseId=2&mixDatabaseName=mix_navigation&mixDatabaseTitle=Navigation", "title": "List", "icon": "mi mi-List" @@ -268,7 +275,7 @@ "icon": "mi mi-GuestUser" }, { - "path": "/admin/mix-database-data/list?mixDatabaseId=6&mixDatabaseName=sysPermission&mixDatabaseTitle=Permission", + "path": "/admin/mix-database-data/list?mixDatabaseId=6&mixDatabaseName=mix_permission&mixDatabaseTitle=Permission", "title": "Permission", "icon": "mi mi-Permissions" } @@ -332,7 +339,7 @@ "path": "/admin/store/list" }, { - "path": "/admin/mix-database-data/list?mixDatabaseId=2&mixDatabaseName=sysNavigation&mixDatabaseTitle=Navigation", + "path": "/admin/mix-database-data/list?mixDatabaseId=2&mixDatabaseName=mix_navigation&mixDatabaseTitle=Navigation", "title": "Navigations", "icon": "mi mi-ButtonMenu" @@ -348,7 +355,7 @@ "icon": "mi mi-Wheel" }, { - "path": "/admin/mix-database/list", + "path": "/admin/mix-database/list?mixDatabaseContextId=1", "title": "Databases", "icon": "mi mi-Wheel" } @@ -441,6 +448,21 @@ "templatePath": "/mix-app/views/app-portal/pages/scheduler/details.html", "controller": "SchedulerController" }, + { + "path": "/admin/workflow-trigger", + "templatePath": "/mix-app/views/app-portal/pages/workflow-trigger/list.html", + "controller": "WorkflowTriggerController" + }, + { + "path": "/admin/workflow-trigger/create", + "templatePath": "/mix-app/views/app-portal/pages/workflow-trigger/details.html", + "controller": "WorkflowTriggerController" + }, + { + "path": "/admin/workflow-trigger/details/:id", + "templatePath": "/mix-app/views/app-portal/pages/workflow-trigger/details.html", + "controller": "WorkflowTriggerController" + }, { "path": "/admin/import", "templatePath": "/mix-app/views/app-portal/pages/import/details.html", @@ -867,7 +889,7 @@ "controller": "PermissionController" }, { - "path": "/admin/mix-database-data/list?mixDatabaseId=6&mixDatabaseName=sysPermission&mixDatabaseTitle=Permission", + "path": "/admin/mix-database-data/list?mixDatabaseId=6&mixDatabaseName=mix_permission&mixDatabaseTitle=Permission", "templatePath": "/mix-app/views/app-portal/pages/permission/list.html", "controller": "PermissionController" }, diff --git a/src/applications/mixcore/wwwroot/mix-app/assets/img/svg/mixcore-logo.svg b/src/applications/mixcore/wwwroot/mix-app/assets/img/svg/mixcore-logo.svg index ac1111ac8..d17e67445 100644 --- a/src/applications/mixcore/wwwroot/mix-app/assets/img/svg/mixcore-logo.svg +++ b/src/applications/mixcore/wwwroot/mix-app/assets/img/svg/mixcore-logo.svg @@ -1,4 +1,44 @@ - - PIZZA 4P'S - + + + + + + + + + + + + + + diff --git a/src/applications/mixcore/wwwroot/mix-app/assets/mixcore-logo-red-2.svg b/src/applications/mixcore/wwwroot/mix-app/assets/mixcore-logo.svg similarity index 100% rename from src/applications/mixcore/wwwroot/mix-app/assets/mixcore-logo-red-2.svg rename to src/applications/mixcore/wwwroot/mix-app/assets/mixcore-logo.svg diff --git a/src/applications/mixcore/wwwroot/mix-app/js/app-portal-required.min.js b/src/applications/mixcore/wwwroot/mix-app/js/app-portal-required.min.js index 97a10ce8e..717247c4b 100644 --- a/src/applications/mixcore/wwwroot/mix-app/js/app-portal-required.min.js +++ b/src/applications/mixcore/wwwroot/mix-app/js/app-portal-required.min.js @@ -1 +1 @@ -/* Fri Aug 23 2024 10:53:58 GMT+0700 (Indochina Time) */!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],i=Object.getPrototypeOf,o=n.slice,r=n.flat?function(e){return n.flat.call(e)}:function(e){return n.concat.apply([],e)},s=n.push,a=n.indexOf,l={},u=l.toString,c=l.hasOwnProperty,d=c.toString,f=d.call(Object),h={},p=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},g=function(e){return null!=e&&e===e.window},m=e.document,b={type:!0,src:!0,nonce:!0,noModule:!0};function x(e,t,n){var i,o,r=(n=n||m).createElement("script");if(r.text=e,t)for(i in b)(o=t[i]||t.getAttribute&&t.getAttribute(i))&&r.setAttribute(i,o);n.head.appendChild(r).parentNode.removeChild(r)}function v(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[u.call(e)]||"object":typeof e}var y="3.7.1",_=/HTML$/i,F=function(e,t){return new F.fn.init(e,t)};function w(e){var t=!!e&&"length"in e&&e.length,n=v(e);return!p(e)&&!g(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+T+")"+T+"*"),$=new RegExp(T+"|>"),z=new RegExp(M),q=new RegExp("^"+N+"$"),W={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N+"|[*])"),ATTR:new RegExp("^"+B),PSEUDO:new RegExp("^"+M),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+T+"*(even|odd|(([+-]|)(\\d*)n|)"+T+"*(?:([+-]|)"+T+"*(\\d+)|))"+T+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+T+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+T+"*((?:-\\d)?\\d*)"+T+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,U=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,V=/[+~]/,K=new RegExp("\\\\[\\da-fA-F]{1,6}"+T+"?|\\\\([^\\r\\n\\f])","g"),Q=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},Y=function(){le()},J=fe(function(e){return!0===e.disabled&&C(e,"fieldset")},{dir:"parentNode",next:"legend"});try{m.apply(n=o.call(j.childNodes),j.childNodes),n[j.childNodes.length].nodeType}catch(t){m={apply:function(e,t){P.apply(e,o.call(t))},call:function(e){P.apply(e,o.call(arguments,1))}}}function Z(e,t,n,i){var o,r,s,a,l,c,d,g=t&&t.ownerDocument,x=t?t.nodeType:9;if(n=n||[],"string"!=typeof e||!e||1!==x&&9!==x&&11!==x)return n;if(!i&&(le(t),t=t||u,f)){if(11!==x&&(l=U.exec(e)))if(o=l[1]){if(9===x){if(!(s=t.getElementById(o)))return n;if(s.id===o)return m.call(n,s),n}else if(g&&(s=g.getElementById(o))&&Z.contains(t,s)&&s.id===o)return m.call(n,s),n}else{if(l[2])return m.apply(n,t.getElementsByTagName(e)),n;if((o=l[3])&&t.getElementsByClassName)return m.apply(n,t.getElementsByClassName(o)),n}if(!(S[e+" "]||p&&p.test(e))){if(d=e,g=t,1===x&&($.test(e)||H.test(e))){for((g=V.test(e)&&ae(t.parentNode)||t)==t&&h.scope||((a=t.getAttribute("id"))?a=F.escapeSelector(a):t.setAttribute("id",a=b)),r=(c=ce(e)).length;r--;)c[r]=(a?"#"+a:":scope")+" "+de(c[r]);d=c.join(",")}try{return m.apply(n,g.querySelectorAll(d)),n}catch(t){S(e,!0)}finally{a===b&&t.removeAttribute("id")}}}return xe(e.replace(O,"$1"),t,n,i)}function ee(){var e=[];return function t(n,o){return e.push(n+" ")>i.cacheLength&&delete t[e.shift()],t[n+" "]=o}}function te(e){return e[b]=!0,e}function ne(e){var t=u.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ie(e){return function(t){return C(t,"input")&&t.type===e}}function oe(e){return function(t){return(C(t,"input")||C(t,"button"))&&t.type===e}}function re(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&J(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function se(e){return te(function(t){return t=+t,te(function(n,i){for(var o,r=e([],n.length,t),s=r.length;s--;)n[o=r[s]]&&(n[o]=!(i[o]=n[o]))})})}function ae(e){return e&&void 0!==e.getElementsByTagName&&e}function le(e){var t,n=e?e.ownerDocument||e:j;return n!=u&&9===n.nodeType&&n.documentElement&&(d=(u=n).documentElement,f=!F.isXMLDoc(u),g=d.matches||d.webkitMatchesSelector||d.msMatchesSelector,d.msMatchesSelector&&j!=u&&(t=u.defaultView)&&t.top!==t&&t.addEventListener("unload",Y),h.getById=ne(function(e){return d.appendChild(e).id=F.expando,!u.getElementsByName||!u.getElementsByName(F.expando).length}),h.disconnectedMatch=ne(function(e){return g.call(e,"*")}),h.scope=ne(function(){return u.querySelectorAll(":scope")}),h.cssHas=ne(function(){try{return u.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),h.getById?(i.filter.ID=function(e){var t=e.replace(K,Q);return function(e){return e.getAttribute("id")===t}},i.find.ID=function(e,t){if(void 0!==t.getElementById&&f){var n=t.getElementById(e);return n?[n]:[]}}):(i.filter.ID=function(e){var t=e.replace(K,Q);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},i.find.ID=function(e,t){if(void 0!==t.getElementById&&f){var n,i,o,r=t.getElementById(e);if(r){if((n=r.getAttributeNode("id"))&&n.value===e)return[r];for(o=t.getElementsByName(e),i=0;r=o[i++];)if((n=r.getAttributeNode("id"))&&n.value===e)return[r]}return[]}}),i.find.TAG=function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},i.find.CLASS=function(e,t){if(void 0!==t.getElementsByClassName&&f)return t.getElementsByClassName(e)},p=[],ne(function(e){var t;d.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||p.push("\\["+T+"*(?:value|"+L+")"),e.querySelectorAll("[id~="+b+"-]").length||p.push("~="),e.querySelectorAll("a#"+b+"+*").length||p.push(".#.+[+~]"),e.querySelectorAll(":checked").length||p.push(":checked"),(t=u.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),d.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&p.push(":enabled",":disabled"),(t=u.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||p.push("\\["+T+"*name"+T+"*="+T+"*(?:''|\"\")")}),h.cssHas||p.push(":has"),p=p.length&&new RegExp(p.join("|")),D=function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!h.sortDetached&&t.compareDocumentPosition(e)===n?e===u||e.ownerDocument==j&&Z.contains(j,e)?-1:t===u||t.ownerDocument==j&&Z.contains(j,t)?1:s?a.call(s,e)-a.call(s,t):0:4&n?-1:1)}),u}for(t in Z.matches=function(e,t){return Z(e,null,null,t)},Z.matchesSelector=function(e,t){if(le(e),f&&!S[t+" "]&&(!p||!p.test(t)))try{var n=g.call(e,t);if(n||h.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){S(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(K,Q),e[3]=(e[3]||e[4]||e[5]||"").replace(K,Q),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||Z.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&Z.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return W.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&z.test(n)&&(t=ce(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(K,Q).toLowerCase();return"*"===e?function(){return!0}:function(e){return C(e,t)}},CLASS:function(e){var t=y[e+" "];return t||(t=new RegExp("(^|"+T+")"+e+"("+T+"|$)"))&&y(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(i){var o=Z.attr(i,e);return null==o?"!="===t:!t||(o+="","="===t?o===n:"!="===t?o!==n:"^="===t?n&&0===o.indexOf(n):"*="===t?n&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function I(e,t,n){return p(t)?F.grep(e,function(e,i){return!!t.call(e,i,e)!==n}):t.nodeType?F.grep(e,function(e){return e===t!==n}):"string"!=typeof t?F.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(F.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||R,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:H.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof F?t[0]:t,F.merge(this,F.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:m,!0)),M.test(i[1])&&F.isPlainObject(t))for(i in t)p(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=m.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):p(e)?void 0!==n.ready?n.ready(e):e(F):F.makeArray(e,this)}).prototype=F.fn,R=F(m);var $=/^(?:parents|prev(?:Until|All))/,z={children:!0,contents:!0,next:!0,prev:!0};function q(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}F.fn.extend({has:function(e){var t=F(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,we=/^$|^module$|\/(?:java|ecma)script/i;ve=m.createDocumentFragment().appendChild(m.createElement("div")),(ye=m.createElement("input")).setAttribute("type","radio"),ye.setAttribute("checked","checked"),ye.setAttribute("name","t"),ve.appendChild(ye),h.checkClone=ve.cloneNode(!0).cloneNode(!0).lastChild.checked,ve.innerHTML="",h.noCloneChecked=!!ve.cloneNode(!0).lastChild.defaultValue,ve.innerHTML="",h.option=!!ve.lastChild;var Ce={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Ee(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&C(e,t)?F.merge([e],n):n}function Ae(e,t){for(var n=0,i=e.length;n",""]);var ke=/<|&#?\w+;/;function Te(e,t,n,i,o){for(var r,s,a,l,u,c,d=t.createDocumentFragment(),f=[],h=0,p=e.length;h\s*$/g;function Me(e,t){return C(e,"table")&&C(11!==t.nodeType?t:t.firstChild,"tr")&&F(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function He(e,t){var n,i,o,r,s,a;if(1===t.nodeType){if(oe.hasData(e)&&(a=oe.get(e).events))for(o in oe.remove(t,"handle events"),a)for(n=0,i=a[o].length;n").attr(e.scriptAttrs||{}).prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&o("error"===e.type?404:200,e.type)}),m.head.appendChild(t[0])},abort:function(){n&&n()}}});var Yt,Jt=[],Zt=/(=)\?(?=&|$)|\?\?/;F.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Jt.pop()||F.expando+"_"+kt.guid++;return this[e]=!0,e}}),F.ajaxPrefilter("json jsonp",function(t,n,i){var o,r,s,a=!1!==t.jsonp&&(Zt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(t.data)&&"data");if(a||"jsonp"===t.dataTypes[0])return o=t.jsonpCallback=p(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,a?t[a]=t[a].replace(Zt,"$1"+o):!1!==t.jsonp&&(t.url+=(Tt.test(t.url)?"&":"?")+t.jsonp+"="+o),t.converters["script json"]=function(){return s||F.error(o+" was not called"),s[0]},t.dataTypes[0]="json",r=e[o],e[o]=function(){s=arguments},i.always(function(){void 0===r?F(e).removeProp(o):e[o]=r,t[o]&&(t.jsonpCallback=n.jsonpCallback,Jt.push(o)),s&&p(r)&&r(s[0]),s=r=void 0}),"script"}),h.createHTMLDocument=((Yt=m.implementation.createHTMLDocument("").body).innerHTML="
",2===Yt.childNodes.length),F.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(h.createHTMLDocument?((i=(t=m.implementation.createHTMLDocument("")).createElement("base")).href=m.location.href,t.head.appendChild(i)):t=m),r=!n&&[],(o=M.exec(e))?[t.createElement(o[1])]:(o=Te([e],t,r),r&&r.length&&F(r).remove(),F.merge([],o.childNodes)));var i,o,r},F.fn.load=function(e,t,n){var i,o,r,s=this,a=e.indexOf(" ");return-1").append(F.parseHTML(e)).find(i):e)}).always(n&&function(e,t){s.each(function(){n.apply(this,r||[e.responseText,t,e])})}),this},F.expr.pseudos.animated=function(e){return F.grep(F.timers,function(t){return e===t.elem}).length},F.offset={setOffset:function(e,t,n){var i,o,r,s,a,l,u=F.css(e,"position"),c=F(e),d={};"static"===u&&(e.style.position="relative"),a=c.offset(),r=F.css(e,"top"),l=F.css(e,"left"),("absolute"===u||"fixed"===u)&&-1<(r+l).indexOf("auto")?(s=(i=c.position()).top,o=i.left):(s=parseFloat(r)||0,o=parseFloat(l)||0),p(t)&&(t=t.call(e,n,F.extend({},a))),null!=t.top&&(d.top=t.top-a.top+s),null!=t.left&&(d.left=t.left-a.left+o),"using"in t?t.using.call(e,d):c.css(d)}},F.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){F.offset.setOffset(this,e,t)});var t,n,i=this[0];return i?i.getClientRects().length?(t=i.getBoundingClientRect(),n=i.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,i=this[0],o={top:0,left:0};if("fixed"===F.css(i,"position"))t=i.getBoundingClientRect();else{for(t=this.offset(),n=i.ownerDocument,e=i.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===F.css(e,"position");)e=e.parentNode;e&&e!==i&&1===e.nodeType&&((o=F(e).offset()).top+=F.css(e,"borderTopWidth",!0),o.left+=F.css(e,"borderLeftWidth",!0))}return{top:t.top-o.top-F.css(i,"marginTop",!0),left:t.left-o.left-F.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===F.css(e,"position");)e=e.offsetParent;return e||fe})}}),F.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;F.fn[e]=function(i){return Y(this,function(e,i,o){var r;if(g(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===o)return r?r[t]:e[i];r?r.scrollTo(n?r.pageXOffset:o,n?o:r.pageYOffset):e[i]=o},e,i,arguments.length)}}),F.each(["top","left"],function(e,t){F.cssHooks[t]=Ke(h.pixelPosition,function(e,n){if(n)return n=Ve(e,t),qe.test(n)?F(e).position()[t]+"px":n})}),F.each({Height:"height",Width:"width"},function(e,t){F.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,i){F.fn[i]=function(o,r){var s=arguments.length&&(n||"boolean"!=typeof o),a=n||(!0===o||!0===r?"margin":"border");return Y(this,function(t,n,o){var r;return g(t)?0===i.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(r=t.documentElement,Math.max(t.body["scroll"+e],r["scroll"+e],t.body["offset"+e],r["offset"+e],r["client"+e])):void 0===o?F.css(t,n,a):F.style(t,n,o,a)},t,s?o:void 0,s)}})}),F.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){F.fn[t]=function(e){return this.on(t,e)}}),F.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,i){return this.on(t,e,n,i)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),F.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){F.fn[t]=function(e,n){return 0e[n]})}return t.default=e,Object.freeze(t)}(e),n=new Map,i={set(e,t,i){n.has(e)||n.set(e,new Map);const o=n.get(e);o.has(t)||0===o.size?o.set(t,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(o.keys())[0]}.`)},get:(e,t)=>n.has(e)&&n.get(e).get(t)||null,remove(e,t){if(!n.has(e))return;const i=n.get(e);i.delete(t),0===i.size&&n.delete(e)}},o="transitionend",r=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,(e,t)=>"#"+CSS.escape(t))),e),s=e=>{e.dispatchEvent(new Event(o))},a=e=>!(!e||"object"!=typeof e)&&(void 0!==e.jquery&&(e=e[0]),void 0!==e.nodeType),l=e=>a(e)?e.jquery?e[0]:e:"string"==typeof e&&e.length>0?document.querySelector(r(e)):null,u=e=>{if(!a(e)||0===e.getClientRects().length)return!1;const t="visible"===getComputedStyle(e).getPropertyValue("visibility"),n=e.closest("details:not([open])");if(!n)return t;if(n!==e){const t=e.closest("summary");if(t&&t.parentNode!==n)return!1;if(null===t)return!1}return t},c=e=>!e||e.nodeType!==Node.ELEMENT_NODE||!!e.classList.contains("disabled")||(void 0!==e.disabled?e.disabled:e.hasAttribute("disabled")&&"false"!==e.getAttribute("disabled")),d=e=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof e.getRootNode){const t=e.getRootNode();return t instanceof ShadowRoot?t:null}return e instanceof ShadowRoot?e:e.parentNode?d(e.parentNode):null},f=()=>{},h=e=>{e.offsetHeight},p=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,g=[],m=()=>"rtl"===document.documentElement.dir,b=e=>{var t;t=(()=>{const t=p();if(t){const n=e.NAME,i=t.fn[n];t.fn[n]=e.jQueryInterface,t.fn[n].Constructor=e,t.fn[n].noConflict=(()=>(t.fn[n]=i,e.jQueryInterface))}}),"loading"===document.readyState?(g.length||document.addEventListener("DOMContentLoaded",()=>{for(const e of g)e()}),g.push(t)):t()},x=(e,t=[],n=e)=>"function"==typeof e?e(...t):n,v=(e,t,n=!0)=>{if(!n)return void x(e);const i=(e=>{if(!e)return 0;let{transitionDuration:t,transitionDelay:n}=window.getComputedStyle(e);const i=Number.parseFloat(t),o=Number.parseFloat(n);return i||o?(t=t.split(",")[0],n=n.split(",")[0],1e3*(Number.parseFloat(t)+Number.parseFloat(n))):0})(t)+5;let r=!1;const a=({target:n})=>{n===t&&(r=!0,t.removeEventListener(o,a),x(e))};t.addEventListener(o,a),setTimeout(()=>{r||s(t)},i)},y=(e,t,n,i)=>{const o=e.length;let r=e.indexOf(t);return-1===r?!n&&i?e[o-1]:e[0]:(r+=n?1:-1,i&&(r=(r+o)%o),e[Math.max(0,Math.min(r,o-1))])},_=/[^.]*(?=\..*)\.|.*/,F=/\..*/,w=/::\d+$/,C={};let E=1;const A={mouseenter:"mouseover",mouseleave:"mouseout"},k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function T(e,t){return t&&`${t}::${E++}`||e.uidEvent||E++}function O(e){const t=T(e);return e.uidEvent=t,C[t]=C[t]||{},C[t]}function S(e,t,n=null){return Object.values(e).find(e=>e.callable===t&&e.delegationSelector===n)}function D(e,t,n){const i="string"==typeof t,o=i?n:t||n;let r=N(e);return k.has(r)||(r=e),[i,o,r]}function j(e,t,n,i,o){if("string"!=typeof t||!e)return;let[r,s,a]=D(t,n,i);t in A&&(s=(e=>(function(t){if(!t.relatedTarget||t.relatedTarget!==t.delegateTarget&&!t.delegateTarget.contains(t.relatedTarget))return e.call(this,t)}))(s));const l=O(e),u=l[a]||(l[a]={}),c=S(u,s,r?n:null);if(c)return void(c.oneOff=c.oneOff&&o);const d=T(s,t.replace(_,"")),f=r?function(e,t,n){return function i(o){const r=e.querySelectorAll(t);for(let{target:s}=o;s&&s!==this;s=s.parentNode)for(const a of r)if(a===s)return M(o,{delegateTarget:s}),i.oneOff&&B.off(e,o.type,t,n),n.apply(s,[o])}}(e,n,s):function(e,t){return function n(i){return M(i,{delegateTarget:e}),n.oneOff&&B.off(e,i.type,t),t.apply(e,[i])}}(e,s);f.delegationSelector=r?n:null,f.callable=s,f.oneOff=o,f.uidEvent=d,u[d]=f,e.addEventListener(a,f,r)}function P(e,t,n,i,o){const r=S(t[n],i,o);r&&(e.removeEventListener(n,r,Boolean(o)),delete t[n][r.uidEvent])}function L(e,t,n,i){const o=t[n]||{};for(const[r,s]of Object.entries(o))r.includes(i)&&P(e,t,n,s.callable,s.delegationSelector)}function N(e){return e=e.replace(F,""),A[e]||e}const B={on(e,t,n,i){j(e,t,n,i,!1)},one(e,t,n,i){j(e,t,n,i,!0)},off(e,t,n,i){if("string"!=typeof t||!e)return;const[o,r,s]=D(t,n,i),a=s!==t,l=O(e),u=l[s]||{},c=t.startsWith(".");if(void 0===r){if(c)for(const n of Object.keys(l))L(e,l,n,t.slice(1));for(const[n,i]of Object.entries(u)){const o=n.replace(w,"");a&&!t.includes(o)||P(e,l,s,i.callable,i.delegationSelector)}}else{if(!Object.keys(u).length)return;P(e,l,s,r,o?n:null)}},trigger(e,t,n){if("string"!=typeof t||!e)return null;const i=p();let o=null,r=!0,s=!0,a=!1;t!==N(t)&&i&&(o=i.Event(t,n),i(e).trigger(o),r=!o.isPropagationStopped(),s=!o.isImmediatePropagationStopped(),a=o.isDefaultPrevented());const l=M(new Event(t,{bubbles:r,cancelable:!0}),n);return a&&l.preventDefault(),s&&e.dispatchEvent(l),l.defaultPrevented&&o&&o.preventDefault(),l}};function M(e,t={}){for(const[n,i]of Object.entries(t))try{e[n]=i}catch(t){Object.defineProperty(e,n,{configurable:!0,get:()=>i})}return e}function I(e){if("true"===e)return!0;if("false"===e)return!1;if(e===Number(e).toString())return Number(e);if(""===e||"null"===e)return null;if("string"!=typeof e)return e;try{return JSON.parse(decodeURIComponent(e))}catch(t){return e}}function R(e){return e.replace(/[A-Z]/g,e=>"-"+e.toLowerCase())}const H={setDataAttribute(e,t,n){e.setAttribute("data-bs-"+R(t),n)},removeDataAttribute(e,t){e.removeAttribute("data-bs-"+R(t))},getDataAttributes(e){if(!e)return{};const t={},n=Object.keys(e.dataset).filter(e=>e.startsWith("bs")&&!e.startsWith("bsConfig"));for(const i of n){let n=i.replace(/^bs/,"");t[n=n.charAt(0).toLowerCase()+n.slice(1,n.length)]=I(e.dataset[i])}return t},getDataAttribute:(e,t)=>I(e.getAttribute("data-bs-"+R(t)))};class ${static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){const n=a(t)?H.getDataAttribute(t,"config"):{};return{...this.constructor.Default,..."object"==typeof n?n:{},...a(t)?H.getDataAttributes(t):{},..."object"==typeof e?e:{}}}_typeCheckConfig(e,t=this.constructor.DefaultType){for(const[i,o]of Object.entries(t)){const t=e[i],r=a(t)?"element":null==(n=t)?""+n:Object.prototype.toString.call(n).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${i}" provided type "${r}" but expected type "${o}".`)}var n}}class z extends ${constructor(e,t){super(),(e=l(e))&&(this._element=e,this._config=this._getConfig(t),i.set(this._element,this.constructor.DATA_KEY,this))}dispose(){i.remove(this._element,this.constructor.DATA_KEY),B.off(this._element,this.constructor.EVENT_KEY);for(const e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,n=!0){v(e,t,n)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return i.get(l(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,"object"==typeof t?t:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return"bs."+this.NAME}static get EVENT_KEY(){return"."+this.DATA_KEY}static eventName(e){return`${e}${this.EVENT_KEY}`}}const q=e=>{let t=e.getAttribute("data-bs-target");if(!t||"#"===t){let n=e.getAttribute("href");if(!n||!n.includes("#")&&!n.startsWith("."))return null;n.includes("#")&&!n.startsWith("#")&&(n="#"+n.split("#")[1]),t=n&&"#"!==n?n.trim():null}return t?t.split(",").map(e=>r(e)).join(","):null},W={find:(e,t=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(t,e)),findOne:(e,t=document.documentElement)=>Element.prototype.querySelector.call(t,e),children:(e,t)=>[].concat(...e.children).filter(e=>e.matches(t)),parents(e,t){const n=[];let i=e.parentNode.closest(t);for(;i;)n.push(i),i=i.parentNode.closest(t);return n},prev(e,t){let n=e.previousElementSibling;for(;n;){if(n.matches(t))return[n];n=n.previousElementSibling}return[]},next(e,t){let n=e.nextElementSibling;for(;n;){if(n.matches(t))return[n];n=n.nextElementSibling}return[]},focusableChildren(e){const t=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(e=>e+':not([tabindex^="-"])').join(",");return this.find(t,e).filter(e=>!c(e)&&u(e))},getSelectorFromElement(e){const t=q(e);return t&&W.findOne(t)?t:null},getElementFromSelector(e){const t=q(e);return t?W.findOne(t):null},getMultipleElementsFromSelector(e){const t=q(e);return t?W.find(t):[]}},G=(e,t="hide")=>{const n="click.dismiss"+e.EVENT_KEY,i=e.NAME;B.on(document,n,`[data-bs-dismiss="${i}"]`,function(n){if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),c(this))return;const o=W.getElementFromSelector(this)||this.closest("."+i);e.getOrCreateInstance(o)[t]()})};class X extends z{static get NAME(){return"alert"}close(){if(B.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const e=this._element.classList.contains("fade");this._queueCallback(()=>this._destroyElement(),this._element,e)}_destroyElement(){this._element.remove(),B.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(e){return this.each(function(){const t=X.getOrCreateInstance(this);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e](this)}})}}G(X,"close"),b(X);const U='[data-bs-toggle="button"]';class V extends z{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(e){return this.each(function(){const t=V.getOrCreateInstance(this);"toggle"===e&&t[e]()})}}B.on(document,"click.bs.button.data-api",U,e=>{e.preventDefault();const t=e.target.closest(U);V.getOrCreateInstance(t).toggle()}),b(V);const K=".bs.swipe",Q={endCallback:null,leftCallback:null,rightCallback:null},Y={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class J extends ${constructor(e,t){super(),this._element=e,e&&J.isSupported()&&(this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Q}static get DefaultType(){return Y}static get NAME(){return"swipe"}dispose(){B.off(this._element,K)}_start(e){this._supportPointerEvents?this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX):this._deltaX=e.touches[0].clientX}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),x(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){const e=Math.abs(this._deltaX);if(e<=40)return;const t=e/this._deltaX;this._deltaX=0,t&&x(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(B.on(this._element,"pointerdown.bs.swipe",e=>this._start(e)),B.on(this._element,"pointerup.bs.swipe",e=>this._end(e)),this._element.classList.add("pointer-event")):(B.on(this._element,"touchstart.bs.swipe",e=>this._start(e)),B.on(this._element,"touchmove.bs.swipe",e=>this._move(e)),B.on(this._element,"touchend.bs.swipe",e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&("pen"===e.pointerType||"touch"===e.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const Z=".bs.carousel",ee=".data-api",te="next",ne="prev",ie="left",oe="right",re="slid"+Z,se=`load${Z}${ee}`,ae=`click${Z}${ee}`,le="carousel",ue="active",ce=".active",de=".carousel-item",fe={ArrowLeft:oe,ArrowRight:ie},he={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},pe={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class ge extends z{constructor(e,t){super(e,t),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=W.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===le&&this.cycle()}static get Default(){return he}static get DefaultType(){return pe}static get NAME(){return"carousel"}next(){this._slide(te)}nextWhenVisible(){!document.hidden&&u(this._element)&&this.next()}prev(){this._slide(ne)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?B.one(this._element,re,()=>this.cycle()):this.cycle())}to(e){const t=this._getItems();if(e>t.length-1||e<0)return;if(this._isSliding)return void B.one(this._element,re,()=>this.to(e));const n=this._getItemIndex(this._getActive());if(n===e)return;const i=e>n?te:ne;this._slide(i,t[e])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&B.on(this._element,"keydown.bs.carousel",e=>this._keydown(e)),"hover"===this._config.pause&&(B.on(this._element,"mouseenter.bs.carousel",()=>this.pause()),B.on(this._element,"mouseleave.bs.carousel",()=>this._maybeEnableCycle())),this._config.touch&&J.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const e of W.find(".carousel-item img",this._element))B.on(e,"dragstart.bs.carousel",e=>e.preventDefault());const e={leftCallback:()=>this._slide(this._directionToOrder(ie)),rightCallback:()=>this._slide(this._directionToOrder(oe)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),500+this._config.interval))}};this._swipeHelper=new J(this._element,e)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;const t=fe[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;const t=W.findOne(ce,this._indicatorsElement);t.classList.remove(ue),t.removeAttribute("aria-current");const n=W.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);n&&(n.classList.add(ue),n.setAttribute("aria-current","true"))}_updateInterval(){const e=this._activeElement||this._getActive();if(!e)return;const t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(e,t=null){if(this._isSliding)return;const n=this._getActive(),i=e===te,o=t||y(this._getItems(),n,i,this._config.wrap);if(o===n)return;const r=this._getItemIndex(o),s=t=>B.trigger(this._element,t,{relatedTarget:o,direction:this._orderToDirection(e),from:this._getItemIndex(n),to:r});if(s("slide.bs.carousel").defaultPrevented)return;if(!n||!o)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(r),this._activeElement=o;const l=i?"carousel-item-start":"carousel-item-end",u=i?"carousel-item-next":"carousel-item-prev";o.classList.add(u),h(o),n.classList.add(l),o.classList.add(l),this._queueCallback(()=>{o.classList.remove(l,u),o.classList.add(ue),n.classList.remove(ue,u,l),this._isSliding=!1,s(re)},n,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return W.findOne(".active.carousel-item",this._element)}_getItems(){return W.find(de,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return m()?e===ie?ne:te:e===ie?te:ne}_orderToDirection(e){return m()?e===ne?ie:oe:e===ne?oe:ie}static jQueryInterface(e){return this.each(function(){const t=ge.getOrCreateInstance(this,e);if("number"!=typeof e){if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}}else t.to(e)})}}B.on(document,ae,"[data-bs-slide], [data-bs-slide-to]",function(e){const t=W.getElementFromSelector(this);if(!t||!t.classList.contains(le))return;e.preventDefault();const n=ge.getOrCreateInstance(t),i=this.getAttribute("data-bs-slide-to");return i?(n.to(i),void n._maybeEnableCycle()):"next"===H.getDataAttribute(this,"slide")?(n.next(),void n._maybeEnableCycle()):(n.prev(),void n._maybeEnableCycle())}),B.on(window,se,()=>{const e=W.find('[data-bs-ride="carousel"]');for(const t of e)ge.getOrCreateInstance(t)}),b(ge);const me="show",be="collapse",xe="collapsing",ve=`:scope .${be} .${be}`,ye='[data-bs-toggle="collapse"]',_e={parent:null,toggle:!0},Fe={parent:"(null|element)",toggle:"boolean"};class we extends z{constructor(e,t){super(e,t),this._isTransitioning=!1,this._triggerArray=[];const n=W.find(ye);for(const e of n){const t=W.getSelectorFromElement(e),n=W.find(t).filter(e=>e===this._element);null!==t&&n.length&&this._triggerArray.push(e)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return _e}static get DefaultType(){return Fe}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let e=[];if(this._config.parent&&(e=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter(e=>e!==this._element).map(e=>we.getOrCreateInstance(e,{toggle:!1}))),e.length&&e[0]._isTransitioning)return;if(B.trigger(this._element,"show.bs.collapse").defaultPrevented)return;for(const t of e)t.hide();const t=this._getDimension();this._element.classList.remove(be),this._element.classList.add(xe),this._element.style[t]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const n="scroll"+(t[0].toUpperCase()+t.slice(1));this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(xe),this._element.classList.add(be,me),this._element.style[t]="",B.trigger(this._element,"shown.bs.collapse")},this._element,!0),this._element.style[t]=this._element[n]+"px"}hide(){if(this._isTransitioning||!this._isShown())return;if(B.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const e=this._getDimension();this._element.style[e]=this._element.getBoundingClientRect()[e]+"px",h(this._element),this._element.classList.add(xe),this._element.classList.remove(be,me);for(const e of this._triggerArray){const t=W.getElementFromSelector(e);t&&!this._isShown(t)&&this._addAriaAndCollapsedClass([e],!1)}this._isTransitioning=!0,this._element.style[e]="",this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(xe),this._element.classList.add(be),B.trigger(this._element,"hidden.bs.collapse")},this._element,!0)}_isShown(e=this._element){return e.classList.contains(me)}_configAfterMerge(e){return e.toggle=Boolean(e.toggle),e.parent=l(e.parent),e}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const e=this._getFirstLevelChildren(ye);for(const t of e){const e=W.getElementFromSelector(t);e&&this._addAriaAndCollapsedClass([t],this._isShown(e))}}_getFirstLevelChildren(e){const t=W.find(ve,this._config.parent);return W.find(e,this._config.parent).filter(e=>!t.includes(e))}_addAriaAndCollapsedClass(e,t){if(e.length)for(const n of e)n.classList.toggle("collapsed",!t),n.setAttribute("aria-expanded",t)}static jQueryInterface(e){const t={};return"string"==typeof e&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){const n=we.getOrCreateInstance(this,t);if("string"==typeof e){if(void 0===n[e])throw new TypeError(`No method named "${e}"`);n[e]()}})}}B.on(document,"click.bs.collapse.data-api",ye,function(e){("A"===e.target.tagName||e.delegateTarget&&"A"===e.delegateTarget.tagName)&&e.preventDefault();for(const e of W.getMultipleElementsFromSelector(this))we.getOrCreateInstance(e,{toggle:!1}).toggle()}),b(we);const Ce="dropdown",Ee=".bs.dropdown",Ae=".data-api",ke="ArrowUp",Te="ArrowDown",Oe=`click${Ee}${Ae}`,Se=`keydown${Ee}${Ae}`,De=`keyup${Ee}${Ae}`,je="show",Pe='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Le=".dropdown-menu",Ne=m()?"top-end":"top-start",Be=m()?"top-start":"top-end",Me=m()?"bottom-end":"bottom-start",Ie=m()?"bottom-start":"bottom-end",Re=m()?"left-start":"right-start",He=m()?"right-start":"left-start",$e={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},ze={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class qe extends z{constructor(e,t){super(e,t),this._popper=null,this._parent=this._element.parentNode,this._menu=W.next(this._element,Le)[0]||W.prev(this._element,Le)[0]||W.findOne(Le,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return $e}static get DefaultType(){return ze}static get NAME(){return Ce}toggle(){return this._isShown()?this.hide():this.show()}show(){if(c(this._element)||this._isShown())return;const e={relatedTarget:this._element};if(!B.trigger(this._element,"show.bs.dropdown",e).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const e of[].concat(...document.body.children))B.on(e,"mouseover",f);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(je),this._element.classList.add(je),B.trigger(this._element,"shown.bs.dropdown",e)}}hide(){if(c(this._element)||!this._isShown())return;const e={relatedTarget:this._element};this._completeHide(e)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(e){if(!B.trigger(this._element,"hide.bs.dropdown",e).defaultPrevented){if("ontouchstart"in document.documentElement)for(const e of[].concat(...document.body.children))B.off(e,"mouseover",f);this._popper&&this._popper.destroy(),this._menu.classList.remove(je),this._element.classList.remove(je),this._element.setAttribute("aria-expanded","false"),H.removeDataAttribute(this._menu,"popper"),B.trigger(this._element,"hidden.bs.dropdown",e)}}_getConfig(e){if("object"==typeof(e=super._getConfig(e)).reference&&!a(e.reference)&&"function"!=typeof e.reference.getBoundingClientRect)throw new TypeError(Ce.toUpperCase()+': Option "reference" provided type "object" without a required "getBoundingClientRect" method.');return e}_createPopper(){if(void 0===t)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;"parent"===this._config.reference?e=this._parent:a(this._config.reference)?e=l(this._config.reference):"object"==typeof this._config.reference&&(e=this._config.reference);const n=this._getPopperConfig();this._popper=t.createPopper(e,this._menu,n)}_isShown(){return this._menu.classList.contains(je)}_getPlacement(){const e=this._parent;if(e.classList.contains("dropend"))return Re;if(e.classList.contains("dropstart"))return He;if(e.classList.contains("dropup-center"))return"top";if(e.classList.contains("dropdown-center"))return"bottom";const t="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return e.classList.contains("dropup")?t?Be:Ne:t?Ie:Me}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:e}=this._config;return"string"==typeof e?e.split(",").map(e=>Number.parseInt(e,10)):"function"==typeof e?t=>e(t,this._element):e}_getPopperConfig(){const e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(H.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),{...e,...x(this._config.popperConfig,[e])}}_selectMenuItem({key:e,target:t}){const n=W.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(e=>u(e));n.length&&y(n,t,e===Te,!n.includes(t)).focus()}static jQueryInterface(e){return this.each(function(){const t=qe.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}static clearMenus(e){if(2===e.button||"keyup"===e.type&&"Tab"!==e.key)return;const t=W.find('[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled).show');for(const n of t){const t=qe.getInstance(n);if(!t||!1===t._config.autoClose)continue;const i=e.composedPath(),o=i.includes(t._menu);if(i.includes(t._element)||"inside"===t._config.autoClose&&!o||"outside"===t._config.autoClose&&o)continue;if(t._menu.contains(e.target)&&("keyup"===e.type&&"Tab"===e.key||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;const r={relatedTarget:t._element};"click"===e.type&&(r.clickEvent=e),t._completeHide(r)}}static dataApiKeydownHandler(e){const t=/input|textarea/i.test(e.target.tagName),n="Escape"===e.key,i=[ke,Te].includes(e.key);if(!i&&!n)return;if(t&&!n)return;e.preventDefault();const o=this.matches(Pe)?this:W.prev(this,Pe)[0]||W.next(this,Pe)[0]||W.findOne(Pe,e.delegateTarget.parentNode),r=qe.getOrCreateInstance(o);if(i)return e.stopPropagation(),r.show(),void r._selectMenuItem(e);r._isShown()&&(e.stopPropagation(),r.hide(),o.focus())}}B.on(document,Se,Pe,qe.dataApiKeydownHandler),B.on(document,Se,Le,qe.dataApiKeydownHandler),B.on(document,Oe,qe.clearMenus),B.on(document,De,qe.clearMenus),B.on(document,Oe,Pe,function(e){e.preventDefault(),qe.getOrCreateInstance(this).toggle()}),b(qe);const We="backdrop",Ge="mousedown.bs."+We,Xe={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ue={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ve extends ${constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return Xe}static get DefaultType(){return Ue}static get NAME(){return We}show(e){if(!this._config.isVisible)return void x(e);this._append();const t=this._getElement();this._config.isAnimated&&h(t),t.classList.add("show"),this._emulateAnimation(()=>{x(e)})}hide(e){this._config.isVisible?(this._getElement().classList.remove("show"),this._emulateAnimation(()=>{this.dispose(),x(e)})):x(e)}dispose(){this._isAppended&&(B.off(this._element,Ge),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add("fade"),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=l(e.rootElement),e}_append(){if(this._isAppended)return;const e=this._getElement();this._config.rootElement.append(e),B.on(e,Ge,()=>{x(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){v(e,this._getElement(),this._config.isAnimated)}}const Ke=".bs.focustrap",Qe="backward",Ye={autofocus:!0,trapElement:null},Je={autofocus:"boolean",trapElement:"element"};class Ze extends ${constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Ye}static get DefaultType(){return Je}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),B.off(document,Ke),B.on(document,"focusin.bs.focustrap",e=>this._handleFocusin(e)),B.on(document,"keydown.tab.bs.focustrap",e=>this._handleKeydown(e)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,B.off(document,Ke))}_handleFocusin(e){const{trapElement:t}=this._config;if(e.target===document||e.target===t||t.contains(e.target))return;const n=W.focusableChildren(t);0===n.length?t.focus():this._lastTabNavDirection===Qe?n[n.length-1].focus():n[0].focus()}_handleKeydown(e){"Tab"===e.key&&(this._lastTabNavDirection=e.shiftKey?Qe:"forward")}}const et=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",tt=".sticky-top",nt="padding-right",it="margin-right";class ot{constructor(){this._element=document.body}getWidth(){const e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){const e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,nt,t=>t+e),this._setElementAttributes(et,nt,t=>t+e),this._setElementAttributes(tt,it,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,nt),this._resetElementAttributes(et,nt),this._resetElementAttributes(tt,it)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,n){const i=this.getWidth();this._applyManipulationCallback(e,e=>{if(e!==this._element&&window.innerWidth>e.clientWidth+i)return;this._saveInitialAttribute(e,t);const o=window.getComputedStyle(e).getPropertyValue(t);e.style.setProperty(t,n(Number.parseFloat(o))+"px")})}_saveInitialAttribute(e,t){const n=e.style.getPropertyValue(t);n&&H.setDataAttribute(e,t,n)}_resetElementAttributes(e,t){this._applyManipulationCallback(e,e=>{const n=H.getDataAttribute(e,t);null!==n?(H.removeDataAttribute(e,t),e.style.setProperty(t,n)):e.style.removeProperty(t)})}_applyManipulationCallback(e,t){if(a(e))t(e);else for(const n of W.find(e,this._element))t(n)}}const rt=".bs.modal",st="hidden"+rt,at="show"+rt,lt=`click${rt}.data-api`,ut="modal-open",ct="modal-static",dt={backdrop:!0,focus:!0,keyboard:!0},ft={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class ht extends z{constructor(e,t){super(e,t),this._dialog=W.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new ot,this._addEventListeners()}static get Default(){return dt}static get DefaultType(){return ft}static get NAME(){return"modal"}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){this._isShown||this._isTransitioning||B.trigger(this._element,at,{relatedTarget:e}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(ut),this._adjustDialog(),this._backdrop.show(()=>this._showElement(e)))}hide(){this._isShown&&!this._isTransitioning&&(B.trigger(this._element,"hide.bs.modal").defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove("show"),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated())))}dispose(){B.off(window,rt),B.off(this._dialog,rt),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ve({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Ze({trapElement:this._element})}_showElement(e){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const t=W.findOne(".modal-body",this._dialog);t&&(t.scrollTop=0),h(this._element),this._element.classList.add("show"),this._queueCallback(()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,B.trigger(this._element,"shown.bs.modal",{relatedTarget:e})},this._dialog,this._isAnimated())}_addEventListeners(){B.on(this._element,"keydown.dismiss.bs.modal",e=>{"Escape"===e.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())}),B.on(window,"resize.bs.modal",()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),B.on(this._element,"mousedown.dismiss.bs.modal",e=>{B.one(this._element,"click.dismiss.bs.modal",t=>{this._element===e.target&&this._element===t.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(ut),this._resetAdjustments(),this._scrollBar.reset(),B.trigger(this._element,st)})}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(B.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const e=this._element.scrollHeight>document.documentElement.clientHeight,t=this._element.style.overflowY;"hidden"===t||this._element.classList.contains(ct)||(e||(this._element.style.overflowY="hidden"),this._element.classList.add(ct),this._queueCallback(()=>{this._element.classList.remove(ct),this._queueCallback(()=>{this._element.style.overflowY=t},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){const e=this._element.scrollHeight>document.documentElement.clientHeight,t=this._scrollBar.getWidth(),n=t>0;if(n&&!e){const e=m()?"paddingLeft":"paddingRight";this._element.style[e]=t+"px"}if(!n&&e){const e=m()?"paddingRight":"paddingLeft";this._element.style[e]=t+"px"}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){const n=ht.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===n[e])throw new TypeError(`No method named "${e}"`);n[e](t)}})}}B.on(document,lt,'[data-bs-toggle="modal"]',function(e){const t=W.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&e.preventDefault(),B.one(t,at,e=>{e.defaultPrevented||B.one(t,st,()=>{u(this)&&this.focus()})});const n=W.findOne(".modal.show");n&&ht.getInstance(n).hide(),ht.getOrCreateInstance(t).toggle(this)}),G(ht),b(ht);const pt=".bs.offcanvas",gt=".data-api",mt=`load${pt}${gt}`,bt="showing",xt=".offcanvas.show",vt="hidePrevented"+pt,yt="hidden"+pt,_t=`click${pt}${gt}`,Ft={backdrop:!0,keyboard:!0,scroll:!1},wt={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Ct extends z{constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Ft}static get DefaultType(){return wt}static get NAME(){return"offcanvas"}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){this._isShown||B.trigger(this._element,"show.bs.offcanvas",{relatedTarget:e}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new ot).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(bt),this._queueCallback(()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add("show"),this._element.classList.remove(bt),B.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:e})},this._element,!0))}hide(){this._isShown&&(B.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add("hiding"),this._backdrop.hide(),this._queueCallback(()=>{this._element.classList.remove("show","hiding"),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new ot).reset(),B.trigger(this._element,yt)},this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const e=Boolean(this._config.backdrop);return new Ve({className:"offcanvas-backdrop",isVisible:e,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:e?()=>{"static"!==this._config.backdrop?this.hide():B.trigger(this._element,vt)}:null})}_initializeFocusTrap(){return new Ze({trapElement:this._element})}_addEventListeners(){B.on(this._element,"keydown.dismiss.bs.offcanvas",e=>{"Escape"===e.key&&(this._config.keyboard?this.hide():B.trigger(this._element,vt))})}static jQueryInterface(e){return this.each(function(){const t=Ct.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e](this)}})}}B.on(document,_t,'[data-bs-toggle="offcanvas"]',function(e){const t=W.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),c(this))return;B.one(t,yt,()=>{u(this)&&this.focus()});const n=W.findOne(xt);n&&n!==t&&Ct.getInstance(n).hide(),Ct.getOrCreateInstance(t).toggle(this)}),B.on(window,mt,()=>{for(const e of W.find(xt))Ct.getOrCreateInstance(e).show()}),B.on(window,"resize.bs.offcanvas",()=>{for(const e of W.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(e).position&&Ct.getOrCreateInstance(e).hide()}),G(Ct),b(Ct);const Et={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},At=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),kt=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Tt=(e,t)=>{const n=e.nodeName.toLowerCase();return t.includes(n)?!At.has(n)||Boolean(kt.test(e.nodeValue)):t.filter(e=>e instanceof RegExp).some(e=>e.test(n))},Ot={allowList:Et,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},St={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Dt={entry:"(string|element|function|null)",selector:"(string|element)"};class jt extends ${constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return Ot}static get DefaultType(){return St}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content={...this._config.content,...e},this}toHtml(){const e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(const[t,n]of Object.entries(this._config.content))this._setContent(e,n,t);const t=e.children[0],n=this._resolvePossibleFunction(this._config.extraClass);return n&&t.classList.add(...n.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(const[t,n]of Object.entries(e))super._typeCheckConfig({selector:t,entry:n},Dt)}_setContent(e,t,n){const i=W.findOne(n,e);i&&((t=this._resolvePossibleFunction(t))?a(t)?this._putElementInTemplate(l(t),i):this._config.html?i.innerHTML=this._maybeSanitize(t):i.textContent=t:i.remove())}_maybeSanitize(e){return this._config.sanitize?function(e,t,n){if(!e.length)return e;if(n&&"function"==typeof n)return n(e);const i=(new window.DOMParser).parseFromString(e,"text/html"),o=[].concat(...i.body.querySelectorAll("*"));for(const e of o){const n=e.nodeName.toLowerCase();if(!Object.keys(t).includes(n)){e.remove();continue}const i=[].concat(...e.attributes),o=[].concat(t["*"]||[],t[n]||[]);for(const t of i)Tt(t,o)||e.removeAttribute(t.nodeName)}return i.body.innerHTML}(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return x(e,[this])}_putElementInTemplate(e,t){if(this._config.html)return t.innerHTML="",void t.append(e);t.textContent=e.textContent}}const Pt=new Set(["sanitize","allowList","sanitizeFn"]),Lt="fade",Nt="show",Bt="hide.bs.modal",Mt="hover",It="focus",Rt={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},Ht={allowList:Et,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},$t={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class zt extends z{constructor(e,n){if(void 0===t)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(e,n),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return Ht}static get DefaultType(){return $t}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),B.off(this._element.closest(".modal"),Bt,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const e=B.trigger(this._element,this.constructor.eventName("show")),t=(d(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(e.defaultPrevented||!t)return;this._disposePopper();const n=this._getTipElement();this._element.setAttribute("aria-describedby",n.getAttribute("id"));const{container:i}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(i.append(n),B.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(n),n.classList.add(Nt),"ontouchstart"in document.documentElement)for(const e of[].concat(...document.body.children))B.on(e,"mouseover",f);this._queueCallback(()=>{B.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1},this.tip,this._isAnimated())}hide(){if(this._isShown()&&!B.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(Nt),"ontouchstart"in document.documentElement)for(const e of[].concat(...document.body.children))B.off(e,"mouseover",f);this._activeTrigger.click=!1,this._activeTrigger[It]=!1,this._activeTrigger[Mt]=!1,this._isHovered=null,this._queueCallback(()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),B.trigger(this._element,this.constructor.eventName("hidden")))},this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){const t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(Lt,Nt),t.classList.add(`bs-${this.constructor.NAME}-auto`);const n=(e=>{do{e+=Math.floor(1e6*Math.random())}while(document.getElementById(e));return e})(this.constructor.NAME).toString();return t.setAttribute("id",n),this._isAnimated()&&t.classList.add(Lt),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new jt({...this._config,content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Lt)}_isShown(){return this.tip&&this.tip.classList.contains(Nt)}_createPopper(e){const n=x(this._config.placement,[this,e,this._element]),i=Rt[n.toUpperCase()];return t.createPopper(this._element,e,this._getPopperConfig(i))}_getOffset(){const{offset:e}=this._config;return"string"==typeof e?e.split(",").map(e=>Number.parseInt(e,10)):"function"==typeof e?t=>e(t,this._element):e}_resolvePossibleFunction(e){return x(e,[this._element])}_getPopperConfig(e){const t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:e=>{this._getTipElement().setAttribute("data-popper-placement",e.state.placement)}}]};return{...t,...x(this._config.popperConfig,[t])}}_setListeners(){const e=this._config.trigger.split(" ");for(const t of e)if("click"===t)B.on(this._element,this.constructor.eventName("click"),this._config.selector,e=>{this._initializeOnDelegatedTarget(e).toggle()});else if("manual"!==t){const e=t===Mt?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),n=t===Mt?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");B.on(this._element,e,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger["focusin"===e.type?It:Mt]=!0,t._enter()}),B.on(this._element,n,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger["focusout"===e.type?It:Mt]=t._element.contains(e.relatedTarget),t._leave()})}this._hideModalHandler=(()=>{this._element&&this.hide()}),B.on(this._element.closest(".modal"),Bt,this._hideModalHandler)}_fixTitle(){const e=this._element.getAttribute("title");e&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){const t=H.getDataAttributes(this._element);for(const e of Object.keys(t))Pt.has(e)&&delete t[e];return e={...t,..."object"==typeof e&&e?e:{}},e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=!1===e.container?document.body:l(e.container),"number"==typeof e.delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),e}_getDelegateConfig(){const e={};for(const[t,n]of Object.entries(this._config))this.constructor.Default[t]!==n&&(e[t]=n);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){const t=zt.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}}b(zt);const qt={...zt.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},Wt={...zt.DefaultType,content:"(null|string|element|function)"};class Gt extends zt{static get Default(){return qt}static get DefaultType(){return Wt}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){const t=Gt.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}}b(Gt);const Xt=".bs.scrollspy",Ut="click"+Xt,Vt=`load${Xt}.data-api`,Kt="active",Qt="[href]",Yt=".nav-link",Jt=`${Yt}, .nav-item > ${Yt}, .list-group-item`,Zt={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},en={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class tn extends z{constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return Zt}static get DefaultType(){return en}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=l(e.target)||document.body,e.rootMargin=e.offset?e.offset+"px 0px -30%":e.rootMargin,"string"==typeof e.threshold&&(e.threshold=e.threshold.split(",").map(e=>Number.parseFloat(e))),e}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(B.off(this._config.target,Ut),B.on(this._config.target,Ut,Qt,e=>{const t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();const n=this._rootElement||window,i=t.offsetTop-this._element.offsetTop;if(n.scrollTo)return void n.scrollTo({top:i,behavior:"smooth"});n.scrollTop=i}}))}_getNewObserver(){const e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(e=>this._observerCallback(e),e)}_observerCallback(e){const t=e=>this._targetLinks.get("#"+e.target.id),n=e=>{this._previousScrollData.visibleEntryTop=e.target.offsetTop,this._process(t(e))},i=(this._rootElement||document.documentElement).scrollTop,o=i>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=i;for(const r of e){if(!r.isIntersecting){this._activeTarget=null,this._clearActiveClass(t(r));continue}const e=r.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&e){if(n(r),!i)return}else o||e||n(r)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const e=W.find(Qt,this._config.target);for(const t of e){if(!t.hash||c(t))continue;const e=W.findOne(decodeURI(t.hash),this._element);u(e)&&(this._targetLinks.set(decodeURI(t.hash),t),this._observableSections.set(t.hash,e))}}_process(e){this._activeTarget!==e&&(this._clearActiveClass(this._config.target),this._activeTarget=e,e.classList.add(Kt),this._activateParents(e),B.trigger(this._element,"activate.bs.scrollspy",{relatedTarget:e}))}_activateParents(e){if(e.classList.contains("dropdown-item"))W.findOne(".dropdown-toggle",e.closest(".dropdown")).classList.add(Kt);else for(const t of W.parents(e,".nav, .list-group"))for(const e of W.prev(t,Jt))e.classList.add(Kt)}_clearActiveClass(e){e.classList.remove(Kt);const t=W.find(`${Qt}.${Kt}`,e);for(const e of t)e.classList.remove(Kt)}static jQueryInterface(e){return this.each(function(){const t=tn.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}})}}B.on(window,Vt,()=>{for(const e of W.find('[data-bs-spy="scroll"]'))tn.getOrCreateInstance(e)}),b(tn);const nn="ArrowLeft",on="ArrowRight",rn="ArrowUp",sn="ArrowDown",an="Home",ln="End",un="active",cn="show",dn=".dropdown-toggle",fn=`:not(${dn})`,hn='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',pn=`.nav-link${fn}, .list-group-item${fn}, [role="tab"]${fn}, ${hn}`,gn=`.${un}[data-bs-toggle="tab"], .${un}[data-bs-toggle="pill"], .${un}[data-bs-toggle="list"]`;class mn extends z{constructor(e){super(e),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),B.on(this._element,"keydown.bs.tab",e=>this._keydown(e)))}static get NAME(){return"tab"}show(){const e=this._element;if(this._elemIsActive(e))return;const t=this._getActiveElem(),n=t?B.trigger(t,"hide.bs.tab",{relatedTarget:e}):null;B.trigger(e,"show.bs.tab",{relatedTarget:t}).defaultPrevented||n&&n.defaultPrevented||(this._deactivate(t,e),this._activate(e,t))}_activate(e,t){e&&(e.classList.add(un),this._activate(W.getElementFromSelector(e)),this._queueCallback(()=>{"tab"===e.getAttribute("role")?(e.removeAttribute("tabindex"),e.setAttribute("aria-selected",!0),this._toggleDropDown(e,!0),B.trigger(e,"shown.bs.tab",{relatedTarget:t})):e.classList.add(cn)},e,e.classList.contains("fade")))}_deactivate(e,t){e&&(e.classList.remove(un),e.blur(),this._deactivate(W.getElementFromSelector(e)),this._queueCallback(()=>{"tab"===e.getAttribute("role")?(e.setAttribute("aria-selected",!1),e.setAttribute("tabindex","-1"),this._toggleDropDown(e,!1),B.trigger(e,"hidden.bs.tab",{relatedTarget:t})):e.classList.remove(cn)},e,e.classList.contains("fade")))}_keydown(e){if(![nn,on,rn,sn,an,ln].includes(e.key))return;e.stopPropagation(),e.preventDefault();const t=this._getChildren().filter(e=>!c(e));let n;if([an,ln].includes(e.key))n=t[e.key===an?0:t.length-1];else{const i=[on,sn].includes(e.key);n=y(t,e.target,i,!0)}n&&(n.focus({preventScroll:!0}),mn.getOrCreateInstance(n).show())}_getChildren(){return W.find(pn,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(const e of t)this._setInitialAttributesOnChild(e)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);const t=this._elemIsActive(e),n=this._getOuterElement(e);e.setAttribute("aria-selected",t),n!==e&&this._setAttributeIfNotExists(n,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){const t=W.getElementFromSelector(e);t&&(this._setAttributeIfNotExists(t,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(t,"aria-labelledby",""+e.id))}_toggleDropDown(e,t){const n=this._getOuterElement(e);if(!n.classList.contains("dropdown"))return;const i=(e,i)=>{const o=W.findOne(e,n);o&&o.classList.toggle(i,t)};i(dn,un),i(".dropdown-menu",cn),n.setAttribute("aria-expanded",t)}_setAttributeIfNotExists(e,t,n){e.hasAttribute(t)||e.setAttribute(t,n)}_elemIsActive(e){return e.classList.contains(un)}_getInnerElement(e){return e.matches(pn)?e:W.findOne(pn,e)}_getOuterElement(e){return e.closest(".nav-item, .list-group-item")||e}static jQueryInterface(e){return this.each(function(){const t=mn.getOrCreateInstance(this);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}})}}B.on(document,"click.bs.tab",hn,function(e){["A","AREA"].includes(this.tagName)&&e.preventDefault(),c(this)||mn.getOrCreateInstance(this).show()}),B.on(window,"load.bs.tab",()=>{for(const e of W.find(gn))mn.getOrCreateInstance(e)}),b(mn);const bn="show",xn="showing",vn={animation:"boolean",autohide:"boolean",delay:"number"},yn={animation:!0,autohide:!0,delay:5e3};class _n extends z{constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return yn}static get DefaultType(){return vn}static get NAME(){return"toast"}show(){B.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove("hide"),h(this._element),this._element.classList.add(bn,xn),this._queueCallback(()=>{this._element.classList.remove(xn),B.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()},this._element,this._config.animation))}hide(){this.isShown()&&(B.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(xn),this._queueCallback(()=>{this._element.classList.add("hide"),this._element.classList.remove(xn,bn),B.trigger(this._element,"hidden.bs.toast")},this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(bn),super.dispose()}isShown(){return this._element.classList.contains(bn)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout(()=>{this.hide()},this._config.delay)))}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":this._hasMouseInteraction=t;break;case"focusin":case"focusout":this._hasKeyboardInteraction=t}if(t)return void this._clearTimeout();const n=e.relatedTarget;this._element===n||this._element.contains(n)||this._maybeScheduleHide()}_setListeners(){B.on(this._element,"mouseover.bs.toast",e=>this._onInteraction(e,!0)),B.on(this._element,"mouseout.bs.toast",e=>this._onInteraction(e,!1)),B.on(this._element,"focusin.bs.toast",e=>this._onInteraction(e,!0)),B.on(this._element,"focusout.bs.toast",e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){const t=_n.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e](this)}})}}return G(_n),b(_n),{Alert:X,Button:V,Carousel:ge,Collapse:we,Dropdown:qe,Modal:ht,Offcanvas:Ct,Popover:Gt,ScrollSpy:tn,Tab:mn,Toast:_n,Tooltip:zt}}),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){function t(t,i,o){i={content:{message:"object"==typeof i?i.message:i,title:i.title?i.title:"",icon:i.icon?i.icon:"",url:i.url?i.url:"#",target:i.target?i.target:"-"}},o=e.extend(!0,{},i,o),this.settings=e.extend(!0,{},n,o),this._defaults=n,"-"==this.settings.content.target&&(this.settings.content.target=this.settings.url_target),this.animations={start:"webkitAnimationStart oanimationstart MSAnimationStart animationstart",end:"webkitAnimationEnd oanimationend MSAnimationEnd animationend"},"number"==typeof this.settings.offset&&(this.settings.offset={x:this.settings.offset,y:this.settings.offset}),this.init()}var n={element:"body",position:null,type:"info",allow_dismiss:!0,newest_on_top:!1,showProgressbar:!1,placement:{from:"top",align:"right"},offset:20,spacing:10,z_index:1031,delay:5e3,timer:1e3,url_target:"_blank",mouse_over:null,animate:{enter:"animated fadeInDown",exit:"animated fadeOutUp"},onShow:null,onShown:null,onClose:null,onClosed:null,icon_type:"class",template:''};String.format=function(){for(var e=arguments[0],t=1;t .progress-bar').removeClass("progress-bar-"+e.settings.type),e.settings.type=i[t],this.$ele.addClass("alert-"+i[t]).find('[data-notify="progressbar"] > .progress-bar').addClass("progress-bar-"+i[t]);break;case"icon":var o=this.$ele.find('[data-notify="icon"]');"class"==e.settings.icon_type.toLowerCase()?o.removeClass(e.settings.content.icon).addClass(i[t]):(o.is("img")||o.find("img"),o.attr("src",i[t]));break;case"progress":var r=e.settings.delay-e.settings.delay*(i[t]/100);this.$ele.data("notify-delay",r),this.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",i[t]).css("width",i[t]+"%");break;case"url":this.$ele.find('[data-notify="url"]').attr("href",i[t]);break;case"target":this.$ele.find('[data-notify="url"]').attr("target",i[t]);break;default:this.$ele.find('[data-notify="'+t+'"]').html(i[t])}var s=this.$ele.outerHeight()+parseInt(e.settings.spacing)+parseInt(e.settings.offset.y);e.reposition(s)},close:function(){e.close()}}},buildNotify:function(){var t=this.settings.content;this.$ele=e(String.format(this.settings.template,this.settings.type,t.title,t.message,t.url,t.target)),this.$ele.attr("data-notify-position",this.settings.placement.from+"-"+this.settings.placement.align),this.settings.allow_dismiss||this.$ele.find('[data-notify="dismiss"]').css("display","none"),(this.settings.delay<=0&&!this.settings.showProgressbar||!this.settings.showProgressbar)&&this.$ele.find('[data-notify="progressbar"]').remove()},setIcon:function(){"class"==this.settings.icon_type.toLowerCase()?this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon):this.$ele.find('[data-notify="icon"]').is("img")?this.$ele.find('[data-notify="icon"]').attr("src",this.settings.content.icon):this.$ele.find('[data-notify="icon"]').append('Notify Icon')},styleDismiss:function(){this.$ele.find('[data-notify="dismiss"]').css({position:"absolute",right:"10px",top:"5px",zIndex:this.settings.z_index+2})},styleURL:function(){this.$ele.find('[data-notify="url"]').css({backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)",height:"100%",left:"0px",position:"absolute",top:"0px",width:"100%",zIndex:this.settings.z_index+1})},placement:function(){var t=this,n=this.settings.offset.y,i={display:"inline-block",margin:"0px auto",position:this.settings.position?this.settings.position:"body"===this.settings.element?"fixed":"absolute",transition:"all .5s ease-in-out",zIndex:this.settings.z_index},o=!1,r=this.settings;switch(e('[data-notify-position="'+this.settings.placement.from+"-"+this.settings.placement.align+'"]:not([data-closing="true"])').each(function(){return n=Math.max(n,parseInt(e(this).css(r.placement.from))+parseInt(e(this).outerHeight())+parseInt(r.spacing))}),1==this.settings.newest_on_top&&(n=this.settings.offset.y),i[this.settings.placement.from]=n+"px",this.settings.placement.align){case"left":case"right":i[this.settings.placement.align]=this.settings.offset.x+"px";break;case"center":i.left=0,i.right=0}this.$ele.css(i).addClass(this.settings.animate.enter),e.each(Array("webkit","moz","o","ms",""),function(e,n){t.$ele[0].style[n+"AnimationIterationCount"]=1}),e(this.settings.element).append(this.$ele),1==this.settings.newest_on_top&&(n=parseInt(n)+parseInt(this.settings.spacing)+this.$ele.outerHeight(),this.reposition(n)),e.isFunction(t.settings.onShow)&&t.settings.onShow.call(this.$ele),this.$ele.one(this.animations.start,function(e){o=!0}).one(this.animations.end,function(n){e.isFunction(t.settings.onShown)&&t.settings.onShown.call(this)}),setTimeout(function(){o||e.isFunction(t.settings.onShown)&&t.settings.onShown.call(this)},600)},bind:function(){var t=this;if(this.$ele.find('[data-notify="dismiss"]').on("click",function(){t.close()}),this.$ele.mouseover(function(t){e(this).data("data-hover","true")}).mouseout(function(t){e(this).data("data-hover","false")}),this.$ele.data("data-hover","false"),this.settings.delay>0){t.$ele.data("notify-delay",t.settings.delay);var n=setInterval(function(){var e=parseInt(t.$ele.data("notify-delay"))-t.settings.timer;if("false"===t.$ele.data("data-hover")&&"pause"==t.settings.mouse_over||"pause"!=t.settings.mouse_over){var i=(t.settings.delay-e)/t.settings.delay*100;t.$ele.data("notify-delay",e),t.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",i).css("width",i+"%")}e<=-t.settings.timer&&(clearInterval(n),t.close())},t.settings.timer)}},close:function(){var t=this,n=parseInt(this.$ele.css(this.settings.placement.from)),i=!1;this.$ele.data("closing","true").addClass(this.settings.animate.exit),t.reposition(n),e.isFunction(t.settings.onClose)&&t.settings.onClose.call(this.$ele),this.$ele.one(this.animations.start,function(e){i=!0}).one(this.animations.end,function(n){e(this).remove(),e.isFunction(t.settings.onClosed)&&t.settings.onClosed.call(this)}),setTimeout(function(){i||(t.$ele.remove(),t.settings.onClosed&&t.settings.onClosed(t.$ele))},600)},reposition:function(t){var n=this,i='[data-notify-position="'+this.settings.placement.from+"-"+this.settings.placement.align+'"]:not([data-closing="true"])',o=this.$ele.nextAll(i);1==this.settings.newest_on_top&&(o=this.$ele.prevAll(i)),o.each(function(){e(this).css(n.settings.placement.from,t),t=parseInt(t)+parseInt(n.settings.spacing)+e(this).outerHeight()})}}),e.notify=function(e,n){return new t(this,e,n).notify},e.notifyDefaults=function(t){return n=e.extend(!0,{},n,t)},e.notifyClose=function(t){void 0===t||"all"==t?e("[data-notify]").find('[data-notify="dismiss"]').trigger("click"):e('[data-notify-position="'+t+'"]').find('[data-notify="dismiss"]').trigger("click")}}),function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=15)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function e(t,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.data=t,this.text=n.text||t,this.options=n}},function(e,t,n){"use strict";var i;function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}Object.defineProperty(t,"__esModule",{value:!0});var r=t.SET_A=0,s=t.SET_B=1,a=t.SET_C=2,l=(t.SHIFT=98,t.START_A=103),u=t.START_B=104,c=t.START_C=105;t.MODULO=103,t.STOP=106,t.FNC1=207,t.SET_BY_CODE=(o(i={},l,r),o(i,u,s),o(i,c,a),i),t.SWAP={101:r,100:s,99:a},t.A_START_CHAR=String.fromCharCode(208),t.B_START_CHAR=String.fromCharCode(209),t.C_START_CHAR=String.fromCharCode(210),t.A_CHARS="[\0-_È-Ï]",t.B_CHARS="[ -È-Ï]",t.C_CHARS="(Ï*[0-9]{2}Ï*)",t.BARS=[11011001100,11001101100,11001100110,10010011e3,10010001100,10001001100,10011001e3,10011000100,10001100100,11001001e3,11001000100,11000100100,10110011100,10011011100,10011001110,10111001100,10011101100,10011100110,11001110010,11001011100,11001001110,11011100100,11001110100,11101101110,11101001100,11100101100,11100100110,11101100100,11100110100,11100110010,11011011e3,11011000110,11000110110,10100011e3,10001011e3,10001000110,10110001e3,10001101e3,10001100010,11010001e3,11000101e3,11000100010,10110111e3,10110001110,10001101110,10111011e3,10111000110,10001110110,11101110110,11010001110,11000101110,11011101e3,11011100010,11011101110,11101011e3,11101000110,11100010110,11101101e3,11101100010,11100011010,11101111010,11001000010,11110001010,1010011e4,10100001100,1001011e4,10010000110,10000101100,10000100110,1011001e4,10110000100,1001101e4,10011000010,10000110100,10000110010,11000010010,1100101e4,11110111010,11000010100,10001111010,10100111100,10010111100,10010011110,10111100100,10011110100,10011110010,11110100100,11110010100,11110010010,11011011110,11011110110,11110110110,10101111e3,10100011110,10001011110,10111101e3,10111100010,11110101e3,11110100010,10111011110,10111101110,11101011110,11110101110,11010000100,1101001e4,11010011100,1100011101011]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SIDE_BIN="101",t.MIDDLE_BIN="01010",t.BINARIES={L:["0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011"],G:["0100111","0110011","0011011","0100001","0011101","0111001","0000101","0010001","0001001","0010111"],R:["1110010","1100110","1101100","1000010","1011100","1001110","1010000","1000100","1001000","1110100"],O:["0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011"],E:["0100111","0110011","0011011","0100001","0011101","0111001","0000101","0010001","0001001","0010111"]},t.EAN2_STRUCTURE=["LL","LG","GL","GG"],t.EAN5_STRUCTURE=["GGLLL","GLGLL","GLLGL","GLLLG","LGGLL","LLGGL","LLLGG","LGLGL","LGLLG","LLGLG"],t.EAN13_STRUCTURE=["LLLLLL","LLGLGG","LLGGLG","LLGGGL","LGLLGG","LGGLLG","LGGGLL","LGLGLG","LGLGGL","LGGLGL"]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(2);t.default=function(e,t,n){var o=e.split("").map(function(e,n){return i.BINARIES[t[n]]}).map(function(t,n){return t?t[e[n]]:""});if(n){var r=e.length-1;o=o.map(function(e,t){return t=200){r=e.shift()-105;var a=s.SWAP[r];void 0!==a?o=t.next(e,n+1,a):(i!==s.SET_A&&i!==s.SET_B||r!==s.SHIFT||(e[0]=i===s.SET_A?e[0]>95?e[0]-96:e[0]:e[0]<32?e[0]+96:e[0]),o=t.next(e,n+1,i))}else r=t.correctIndex(e,i),o=t.next(e,n+1,i);var l=r*n;return{result:t.getBar(r)+o.result,checksum:l+o.checksum}}}]),t}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.mod10=function(e){for(var t=0,n=0;n10*n.width?10*n.width:n.fontSize,i.guardHeight=n.height+i.fontSize/2+n.textMargin,i}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,s(n(0)).default),i(t,[{key:"encode",value:function(){return this.options.flat?this.encodeFlat():this.encodeGuarded()}},{key:"leftText",value:function(e,t){return this.text.substr(e,t)}},{key:"leftEncode",value:function(e,t){return(0,r.default)(e,t)}},{key:"rightText",value:function(e,t){return this.text.substr(e,t)}},{key:"rightEncode",value:function(e,t){return(0,r.default)(e,t)}},{key:"encodeGuarded",value:function(){var e={fontSize:this.fontSize},t={height:this.guardHeight};return[{data:o.SIDE_BIN,options:t},{data:this.leftEncode(),text:this.leftText(),options:e},{data:o.MIDDLE_BIN,options:t},{data:this.rightEncode(),text:this.rightText(),options:e},{data:o.SIDE_BIN,options:t}]}},{key:"encodeFlat",value:function(){return{data:[o.SIDE_BIN,this.leftEncode(),o.MIDDLE_BIN,this.rightEncode(),o.SIDE_BIN].join(""),text:this.text}}}]),t}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n10*n.width?i.fontSize=10*n.width:i.fontSize=n.fontSize,i.guardHeight=n.height+i.fontSize/2+n.textMargin,i}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,r(n(0)).default),i(t,[{key:"valid",value:function(){return-1!==this.data.search(/^[0-9]{12}$/)&&this.data[11]==a(this.data)}},{key:"encode",value:function(){return this.options.flat?this.flatEncoding():this.guardedEncoding()}},{key:"flatEncoding",value:function(){var e="";return e+="101",e+=(0,o.default)(this.data.substr(0,6),"LLLLLL"),e+="01010",e+=(0,o.default)(this.data.substr(6,6),"RRRRRR"),{data:e+="101",text:this.text}}},{key:"guardedEncoding",value:function(){var e=[];return this.displayValue&&e.push({data:"00000000",text:this.text.substr(0,1),options:{textAlign:"left",fontSize:this.fontSize}}),e.push({data:"101"+(0,o.default)(this.data[0],"L"),options:{height:this.guardHeight}}),e.push({data:(0,o.default)(this.data.substr(1,5),"LLLLL"),text:this.text.substr(1,5),options:{fontSize:this.fontSize}}),e.push({data:"01010",options:{height:this.guardHeight}}),e.push({data:(0,o.default)(this.data.substr(6,5),"RRRRR"),text:this.text.substr(6,5),options:{fontSize:this.fontSize}}),e.push({data:(0,o.default)(this.data[11],"R")+"101",options:{height:this.guardHeight}}),this.displayValue&&e.push({data:"00000000",text:this.text.substr(11,1),options:{textAlign:"right",fontSize:this.fontSize}}),e}}]),t}();function a(e){var t,n=0;for(t=1;t<11;t+=2)n+=parseInt(e[t]);for(t=0;t<11;t+=2)n+=3*parseInt(e[t]);return(10-n%10)%10}t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,o=function(){function e(e,t){for(var n=0;n0?t.fontSize+t.textMargin:0)+t.marginTop+t.marginBottom}function s(e,t,n){if(n.displayValue&&tt&&(t=e[n].height);return t},t.getEncodingHeight=r,t.getBarcodePadding=s,t.calculateEncodingAttributes=function(e,t,n){for(var i=0;i=r(e);return t+String.fromCharCode(i?206:205)+a(e,i)}t.default=function(e){var t=void 0;if(s(e).length>=2)t=i.C_START_CHAR+l(e);else{var n=o(e)>r(e);t=(n?i.A_START_CHAR:i.B_START_CHAR)+a(e,n)}return t.replace(/[\xCD\xCE]([^])[\xCD\xCE]/,function(e,t){return String.fromCharCode(203)+t})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,o=function(){function e(e,t){for(var n=0;n10*n.width?i.fontSize=10*n.width:i.fontSize=n.fontSize,i.guardHeight=n.height+i.fontSize/2+n.textMargin,i}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,r.default),i(t,[{key:"valid",value:function(){return this.isValid}},{key:"encode",value:function(){return this.options.flat?this.flatEncoding():this.guardedEncoding()}},{key:"flatEncoding",value:function(){var e="";return e+="101",e+=this.encodeMiddleDigits(),{data:e+="010101",text:this.text}}},{key:"guardedEncoding",value:function(){var e=[];return this.displayValue&&e.push({data:"00000000",text:this.text[0],options:{textAlign:"left",fontSize:this.fontSize}}),e.push({data:"101",options:{height:this.guardHeight}}),e.push({data:this.encodeMiddleDigits(),text:this.text.substring(1,7),options:{fontSize:this.fontSize}}),e.push({data:"010101",options:{height:this.guardHeight}}),this.displayValue&&e.push({data:"00000000",text:this.text[7],options:{textAlign:"right",fontSize:this.fontSize}}),e}},{key:"encodeMiddleDigits",value:function(){var e=this.upcA[0],t=this.upcA[this.upcA.length-1],n=c[parseInt(t)][parseInt(e)];return(0,o.default)(this.middleDigits,n)}}]),t}();function f(e,t){for(var n=parseInt(e[e.length-1]),i=u[n],o="",r=0,a=0;a=3&&this.number<=131070}}]),t}();t.pharmacode=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.codabar=void 0;var i,o=function(){function e(e,t){for(var n=0;n0?(n=0,o.textAlign="left"):"right"==e.textAlign?(n=t.width-1,o.textAlign="right"):(n=t.width/2,o.textAlign="center"),o.fillText(t.text,n,i))}},{key:"moveCanvasDrawing",value:function(e){this.canvas.getContext("2d").translate(e.width,0)}},{key:"restoreCanvas",value:function(){this.canvas.getContext("2d").restore()}}]),e}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,o=function(){function e(e,t){for(var n=0;n0&&(this.drawRect(s-t.width*r,i,t.width*r,t.height,e),r=0);r>0&&this.drawRect(s-t.width*(r-1),i,t.width*r,t.height,e)}},{key:"drawSVGText",value:function(e,t,n){var i,o,r=this.document.createElementNS(a,"text");t.displayValue&&(r.setAttribute("style","font:"+t.fontOptions+" "+t.fontSize+"px "+t.font),o="top"==t.textPosition?t.fontSize-t.textMargin:t.height+t.textMargin+t.fontSize,"left"==t.textAlign||n.barcodePadding>0?(i=0,r.setAttribute("text-anchor","start")):"right"==t.textAlign?(i=n.width-1,r.setAttribute("text-anchor","end")):(i=n.width/2,r.setAttribute("text-anchor","middle")),r.setAttribute("x",i),r.setAttribute("y",o),r.appendChild(this.document.createTextNode(n.text)),e.appendChild(r))}},{key:"setSvgAttributes",value:function(e,t){var n=this.svg;n.setAttribute("width",e+"px"),n.setAttribute("height",t+"px"),n.setAttribute("x","0px"),n.setAttribute("y","0px"),n.setAttribute("viewBox","0 0 "+e+" "+t),n.setAttribute("xmlns",a),n.setAttribute("version","1.1"),n.setAttribute("style","transform: translate(0,0)")}},{key:"createGroup",value:function(e,t,n){var i=this.document.createElementNS(a,"g");return i.setAttribute("transform","translate("+e+", "+t+")"),n.appendChild(i),i}},{key:"setGroupOptions",value:function(e,t){e.setAttribute("style","fill:"+t.lineColor+";")}},{key:"drawRect",value:function(e,t,n,i,o){var r=this.document.createElementNS(a,"rect");return r.setAttribute("x",e),r.setAttribute("y",t),r.setAttribute("width",n),r.setAttribute("height",i),o.appendChild(r),r}}]),e}();t.default=l},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;ne.length)return;if(!(_ instanceof l)){if(g&&v!=t.length-1){if(f.lastIndex=y,!(k=f.exec(e)))break;for(var F=k.index+(p?k[1].length:0),w=k.index+k[0].length,C=v,E=y,A=t.length;C"+r.content+""},!_self.document)return _self.addEventListener&&(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),i=t.language,o=t.code,r=t.immediateClose;_self.postMessage(n.highlight(o,n.languages[i],i)),r&&_self.close()},!1)),_self.Prism;var o=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return o&&(n.filename=o.src,n.manual||o.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism),Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/(^|[^\\])["']/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^{}\s][^{};]*?(?=\s*\{)/,string:{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/\B!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.languages.css,Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css",greedy:!0}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)),Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/[a-z0-9_]+(?=\()/i,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,function:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,alias:"function"},constant:/\b[A-Z][A-Z\d_]*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${[^}]+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}}}),Prism.languages.javascript["template-string"].inside.interpolation.inside.rest=Prism.languages.javascript,Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript",greedy:!0}}),Prism.languages.js=Prism.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var n,i=t.getAttribute("data-src"),o=t,r=/\blang(?:uage)?-([\w-]+)\b/i;o&&!r.test(o.className);)o=o.parentNode;if(o&&(n=(t.className.match(r)||[,""])[1]),!n){var s=(i.match(/\.(\w+)$/)||[,""])[1];n=e[s]||s}var a=document.createElement("code");a.className="language-"+n,t.textContent="",a.textContent="Loading…",t.appendChild(a);var l=new XMLHttpRequest;l.open("GET",i,!0),l.onreadystatechange=function(){4==l.readyState&&(l.status<400&&l.responseText?(a.textContent=l.responseText,Prism.highlightElement(a)):400<=l.status?a.textContent="✖ Error "+l.status+" while fetching file: "+l.statusText:a.textContent="✖ Error: File does not exist or is empty")},l.send(null)}),Prism.plugins.toolbar&&Prism.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),i=document.createElement("a");return i.textContent=t.getAttribute("data-download-link-label")||"Download",i.setAttribute("download",""),i.href=n,i}})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight)),function(e){"use strict";function t(e,t){e.className+=" "+t}function n(e,t){for(var n=e.className.split(" "),i=t.split(" "),o=0;o-1&&n.splice(r,1)}e.className=n.join(" ")}function i(){return"rtl"===e.getComputedStyle(document.body).direction}function o(){return document.documentElement&&document.documentElement.scrollTop||document.body.scrollTop}function r(){return document.documentElement&&document.documentElement.scrollLeft||document.body.scrollLeft}function s(e){for(;e.lastChild;)e.removeChild(e.lastChild)}function a(e){if(null===e)return e;var t;if(Array.isArray(e)){t=[];for(var n=0;n0){for(var n=[],i=0;i=0?(n(document.body,Fe.noOverflow),k(!1)):e>0&&document.body.className.indexOf(Fe.noOverflow)<0&&(k(!0),t(document.body,Fe.noOverflow))}function k(i){T.defaults.preventBodyShift&&(i&&document.documentElement.scrollHeight>document.documentElement.clientHeight?(Ce=be,we=e.getComputedStyle(document.body).top,t(document.body,Fe.fixed),document.body.style.top=-be+"px"):i||(be=Ce,document.body.style.top=we,n(document.body,Fe.fixed),F()))}function O(e,t){for(var n=y.indexOf(t)+1;n200&&(Ae=e.timeStamp)&&!Ee){var n=e.srcElement||e.target;!0===t.get("closableByDimmer")&&n===t.elements.modal&&j(t)}Ee=!1}function z(e,t){if(Date.now()-ke>200&&(ke=Date.now()))for(var n=0;n-1?(z(t,function(e){return e.key===n}),!1):void 0}Te=!1}function G(e){var t=y[y.length-1],n=e.keyCode;if(n===m||n===b){for(var i=t.__internal.buttons,o=0;op-1&&xe.indexOf(n)>-1)return e.preventDefault(),e.stopPropagation(),z(t,function(e){return e.key===n}),!1}function X(e,t){if(t)t.focus();else{var n=e.__internal.focus,i=n.element;switch(typeof n.element){case"number":e.__internal.buttons.length>n.element&&(i=!0===e.get("basic")?e.elements.reset[0]:e.__internal.buttons[n.element].element);break;case"string":i=e.elements.body.querySelector(n.element);break;case"function":i=n.element.call(e)}!0!==e.get("defaultFocusOff")&&(null!=i||0!==e.__internal.buttons.length)||(i=e.elements.reset[0]),i&&i.focus&&(i.focus(),n.select&&i.select&&i.select())}}function U(e,t){if(!t)for(var n=y.length-1;n>-1;n-=1)if(y[n].isModal()){t=y[n];break}if(t&&t.isModal()){var i,o=t.elements.reset[0],r=t.elements.reset[1],s=e.relatedTarget,a=t.elements.root.contains(s),l=e.srcElement||e.target;if(l===o&&!a||l===r&&s===o)return;l===r||l===document.body?i=o:l===o&&s===r?i=V(t):l===o&&a&&(i=V(t,!0)),X(t,i)}}function V(e,t){var n=[].slice.call(e.elements.dialog.querySelectorAll(v.tabbable));t&&n.reverse();for(var i=0;iRe?t.style.left=Ie+c+"px":t.offsetWidth>=He&&(t.style.left=Ie-c+"px")}}(t,Me.elements.dialog,!Me.get("modal")&&!Me.get("pinned")))}function se(){if(Me){var e=Me;Me=null,n(document.body,Fe.noSelection),n(e.elements.dialog,Fe.capture),Ee=!0,d("onresized",e)}}function ae(e){Me=null;var t=e.elements.dialog;"none"===t.style.maxWidth&&(t.style.maxWidth=t.style.minWidth=t.style.width=t.style.height=t.style.minHeight=t.style.left="",Ie=Number.Nan,Re=He=$e=0)}function le(){for(var e=0;e-1&&e.navigator.userAgent.indexOf("Chrome")<0,_e={dimmer:'
',modal:'
',dialog:'
',reset:'',commands:'
',header:'
',body:'
',content:'
',footer:'',buttons:{primary:'
',auxiliary:'
'},button:'',resizeHandle:'
'},Fe={animationIn:"ajs-in",animationOut:"ajs-out",base:"alertify",basic:"ajs-basic",capture:"ajs-capture",closable:"ajs-closable",fixed:"ajs-fixed",frameless:"ajs-frameless",hidden:"ajs-hidden",maximize:"ajs-maximize",maximized:"ajs-maximized",maximizable:"ajs-maximizable",modeless:"ajs-modeless",movable:"ajs-movable",noSelection:"ajs-no-selection",noOverflow:"ajs-no-overflow",noPadding:"ajs-no-padding",pin:"ajs-pin",pinnable:"ajs-pinnable",prefix:"ajs-",resizable:"ajs-resizable",restore:"ajs-restore",shake:"ajs-shake",unpinned:"ajs-unpinned",noTransition:"ajs-no-transition"},we="",Ce=0,Ee=!1,Ae=0,ke=0,Te=!1,Oe=null,Se=0,De=0,je="pageX",Pe="pageY",Le=null,Ne=!1,Be=null,Me=null,Ie=Number.Nan,Re=0,He=0,$e=0;return{__init:f,isOpen:function(){return this.__internal.isOpen},isModal:function(){return this.elements.root.className.indexOf(Fe.modeless)<0},isMaximized:function(){return this.elements.root.className.indexOf(Fe.maximized)>-1},isPinned:function(){return this.elements.root.className.indexOf(Fe.unpinned)<0},maximize:function(){return this.isMaximized()||B(this),this},restore:function(){return this.isMaximized()&&M(this),this},pin:function(){return this.isPinned()||L(this),this},unpin:function(){return this.isPinned()&&N(this),this},bringToFront:function(){return O(0,this),this},moveTo:function(e,t){if(!isNaN(e)&&!isNaN(t)){d("onmove",this);var n=this.elements.dialog,o=n,r=0,s=0;n.style.left&&(r-=parseInt(n.style.left,10)),n.style.top&&(s-=parseInt(n.style.top,10));do{r+=o.offsetLeft,s+=o.offsetTop}while(o=o.offsetParent);var a=e-r,l=t-s;i()&&(a*=-1),n.style.left=a+"px",n.style.top=l+"px",d("onmoved",this)}return this},resizeTo:function(e,t){var n=parseFloat(e),i=parseFloat(t),o=/(\d*\.\d+|\d+)%/;if(!isNaN(n)&&!isNaN(i)&&!0===this.get("resizable")){d("onresize",this),(""+e).match(o)&&(n=n/100*document.documentElement.clientWidth),(""+t).match(o)&&(i=i/100*document.documentElement.clientHeight);var r=this.elements.dialog;"none"!==r.style.maxWidth&&(r.style.minWidth=(He=r.offsetWidth)+"px"),r.style.maxWidth="none",r.style.minHeight=this.elements.header.offsetHeight+this.elements.footer.offsetHeight+"px",r.style.width=n+"px",r.style.height=i+"px",d("onresized",this)}return this},setting:function(e,t){var n=this,i=D(this,this.__internal.options,function(e,t,i){S(n,e,t,i)},e,t);if("get"===i.op)return i.found?i.value:void 0!==this.settings?D(this,this.settings,this.settingUpdated||function(){},e,t).value:void 0;if("set"===i.op){if(i.items.length>0)for(var o=this.settingUpdated||function(){},r=0;r0){var t=this;this.__internal.timer=setTimeout(function(){t.dismiss()},1e3*this.__internal.delay)}return this},setContent:function(n){if("string"==typeof n?(s(this.element),this.element.innerHTML=n):n instanceof e.HTMLElement&&this.element.firstChild!==n&&(s(this.element),this.element.appendChild(n)),this.__internal.closeButton){var i=document.createElement("span");t(i,f.close),i.setAttribute("data-close",!0),this.element.appendChild(i)}return this},dismissOthers:function(){return k.dismissAll(this),this}})}var c,d=[],f=v.notifier.classes,h=f.base;return{setting:function(e,t){if(i(this),void 0===t)return this.__internal[e];switch(e){case"position":this.__internal.position=t,a(this);break;case"delay":this.__internal.delay=t}return this},set:function(e,t){return this.setting(e,t),this},get:function(e){return this.setting(e)},create:function(e,t){i(this);var n=document.createElement("div");return n.className=f.message+("string"==typeof e&&""!==e?" "+f.prefix+e:""),l(n,t)},dismissAll:function(e){for(var t=d.slice(0),n=0;n>t?1:0;return n}function h(e,t,n,i,o,r,s){var a=p;a(e,t,n[0],i-2,o-2,r,s),a(e,t,n[1],i-2,o-1,r,s),a(e,t,n[2],i-1,o-2,r,s),a(e,t,n[3],i-1,o-1,r,s),a(e,t,n[4],i-1,o,r,s),a(e,t,n[5],i,o-2,r,s),a(e,t,n[6],i,o-1,r,s),a(e,t,n[7],i,o,r,s)}function p(e,t,n,i,o,r,s){i<0&&(i+=r,o+=4-(r+4)%8),o<0&&(o+=s,i+=4-(s+4)%8),1!==t[i][o]&&(e[i][o]=n,t[i][o]=1)}return function(g,m){var b,x=function(e){var t,n,i=[],o=0;for(t=0;t>=8;return i}function F(e,t,n){return String.fromCharCode(n)+String.fromCharCode(t)+String.fromCharCode(e)}function w(e){var t=parseInt("0x"+e.substr(1),16),n=255&t;return F((t>>=8)>>8,255&t,n)}function C(e){return e.match(/#[0-91-F]/gi)}function E(e){for(var t,n,i,o,r,s,a,l="",u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",c=0;c>2,r=(3&t)<<4|(n=e.charCodeAt(c++))>>4,s=(15&n)<<2|(i=e.charCodeAt(c++))>>6,a=63&i,isNaN(n)?s=a=64:isNaN(i)&&(a=64),l+=u.charAt(o)+u.charAt(r)+u.charAt(s)+u.charAt(a);return l}function A(e){var t,n=[];for(n[0]=[],t=0;t',p='
';for(a=0;a'+i+""),k(e,o*d).html(f)}function S(e,t,n,i,o,r){var s,a,l,u,c,d,f,h,p=n.length,g=n[0].length,m=o*g,b=r*p;for(t.showHRI&&(l=y(t.fontSize),b+=y(t.marginHRI)+l),u='',u+='',c='',a=0;a',u+=''+i+"",u+=""),u+="",(h=document.createElement("img")).setAttribute("src","data:image/svg+xml;base64,"+E(u)),k(e,m).append(h)}function D(e,t,n,i,o,r,s,a){var l,u,c,d,f,h,p=e.get(0),g=n.length,m=n[0].length;if(p&&p.getContext){for((c=p.getContext("2d")).lineWidth=1,c.lineCap="butt",c.fillStyle=t.bgColor,c.fillRect(o,r,m*s,g*a),c.fillStyle=t.color,u=0;u?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",a=0,l=0,u=0;for(t=0;te||this.moduleCount<=e||0>t||this.moduleCount<=t)throw Error(e+","+t);return this.modules[e][t]},getModuleCount:function(){return this.moduleCount},make:function(){if(1>this.typeNumber){var e=1;for(e=1;40>e;e++){for(var t=s.getRSBlocks(e,this.errorCorrectLevel),n=new a,i=0,o=0;o=n;n++)if(!(-1>=e+n||this.moduleCount<=e+n))for(var i=-1;7>=i;i++)-1>=t+i||this.moduleCount<=t+i||(this.modules[e+n][t+i]=0<=n&&6>=n&&(0==i||6==i)||0<=i&&6>=i&&(0==n||6==n)||2<=n&&4>=n&&2<=i&&4>=i)},getBestMaskPattern:function(){for(var e=0,t=0,n=0;8>n;n++){this.makeImpl(!0,n);var i=l.getLostPoint(this);(0==n||e>i)&&(e=i,t=n)}return t},createMovieClip:function(e,t,n){for(e=e.createEmptyMovieClip(t,n),this.make(),t=0;t=r;r++)for(var s=-2;2>=s;s++)this.modules[i+r][o+s]=-2==r||2==r||-2==s||2==s||0==r&&0==s}},setupTypeNumber:function(e){for(var t=l.getBCHTypeNumber(this.typeNumber),n=0;18>n;n++){var i=!e&&1==(t>>n&1);this.modules[Math.floor(n/3)][n%3+this.moduleCount-8-3]=i}for(n=0;18>n;n++)i=!e&&1==(t>>n&1),this.modules[n%3+this.moduleCount-8-3][Math.floor(n/3)]=i},setupTypeInfo:function(e,t){for(var n=l.getBCHTypeInfo(this.errorCorrectLevel<<3|t),i=0;15>i;i++){var o=!e&&1==(n>>i&1);6>i?this.modules[i][8]=o:8>i?this.modules[i+1][8]=o:this.modules[this.moduleCount-15+i][8]=o}for(i=0;15>i;i++)o=!e&&1==(n>>i&1),8>i?this.modules[8][this.moduleCount-i-1]=o:9>i?this.modules[8][15-i-1+1]=o:this.modules[8][15-i-1]=o;this.modules[this.moduleCount-8][8]=!e},mapData:function(e,t){for(var n=-1,i=this.moduleCount-1,o=7,r=0,s=this.moduleCount-1;0a;a++)if(null==this.modules[i][s-a]){var u=!1;r>>o&1)),l.getMask(t,i,s-a)&&(u=!u),this.modules[i][s-a]=u,-1==--o&&(r++,o=7)}if(0>(i+=n)||this.moduleCount<=i){i-=n,n=-n;break}}}},o.PAD0=236,o.PAD1=17,o.createData=function(e,t,n){t=s.getRSBlocks(e,t);for(var i=new a,r=0;r8*e)throw Error("code length overflow. ("+i.getLengthInBits()+">"+8*e+")");for(i.getLengthInBits()+4<=8*e&&i.put(0,4);0!=i.getLengthInBits()%8;)i.putBit(!1);for(;!(i.getLengthInBits()>=8*e||(i.put(o.PAD0,8),i.getLengthInBits()>=8*e));)i.put(o.PAD1,8);return o.createBytes(i,t)},o.createBytes=function(e,t){for(var n=0,i=0,o=0,s=Array(t.length),a=Array(t.length),u=0;u>>=1;return t},getPatternPosition:function(e){return l.PATTERN_POSITION_TABLE[e-1]},getMask:function(e,t,n){switch(e){case 0:return 0==(t+n)%2;case 1:return 0==t%2;case 2:return 0==n%3;case 3:return 0==(t+n)%3;case 4:return 0==(Math.floor(t/2)+Math.floor(n/3))%2;case 5:return 0==t*n%2+t*n%3;case 6:return 0==(t*n%2+t*n%3)%2;case 7:return 0==(t*n%3+(t+n)%2)%2;default:throw Error("bad maskPattern:"+e)}},getErrorCorrectPolynomial:function(e){for(var t=new r([1],0),n=0;nt)switch(e){case 1:return 10;case 2:return 9;case n:case 8:return 8;default:throw Error("mode:"+e)}else if(27>t)switch(e){case 1:return 12;case 2:return 11;case n:return 16;case 8:return 10;default:throw Error("mode:"+e)}else{if(!(41>t))throw Error("type:"+t);switch(e){case 1:return 14;case 2:return 13;case n:return 16;case 8:return 12;default:throw Error("mode:"+e)}}},getLostPoint:function(e){for(var t=e.getModuleCount(),n=0,i=0;i=a;a++)if(!(0>i+a||t<=i+a))for(var l=-1;1>=l;l++)0>o+l||t<=o+l||0==a&&0==l||s==e.isDark(i+a,o+l)&&r++;5e)throw Error("glog("+e+")");return u.LOG_TABLE[e]},gexp:function(e){for(;0>e;)e+=255;for(;256<=e;)e-=255;return u.EXP_TABLE[e]},EXP_TABLE:Array(256),LOG_TABLE:Array(256)},c=0;8>c;c++)u.EXP_TABLE[c]=1<c;c++)u.EXP_TABLE[c]=u.EXP_TABLE[c-4]^u.EXP_TABLE[c-5]^u.EXP_TABLE[c-6]^u.EXP_TABLE[c-8];for(c=0;255>c;c++)u.LOG_TABLE[u.EXP_TABLE[c]]=c;return r.prototype={get:function(e){return this.num[e]},getLength:function(){return this.num.length},multiply:function(e){for(var t=Array(this.getLength()+e.getLength()-1),n=0;nthis.getLength()-e.getLength())return this;for(var t=u.glog(this.get(0))-u.glog(e.get(0)),n=Array(this.getLength()),i=0;i>>7-e%8&1)},put:function(e,t){for(var n=0;n>>t-n-1&1))},getLengthInBits:function(){return this.length},putBit:function(e){var t=Math.floor(this.length/8);this.buffer.length<=t&&this.buffer.push(0),e&&(this.buffer[t]|=128>>>this.length%8),this.length++}},"string"==typeof t&&(t={text:t}),t=e.extend({},{render:"canvas",width:256,height:256,typeNumber:-1,correctLevel:2,background:"#ffffff",foreground:"#000000"},t),this.each(function(){var n;if("canvas"==t.render){(n=new o(t.typeNumber,t.correctLevel)).addData(t.text),n.make();var i=document.createElement("canvas");i.width=t.width,i.height=t.height;for(var r=i.getContext("2d"),s=t.width/n.getModuleCount(),a=t.height/n.getModuleCount(),l=0;l").css("width",t.width+"px").css("height",t.height+"px").css("border","0px").css("border-collapse","collapse").css("background-color",t.background),r=t.width/n.getModuleCount(),s=t.height/n.getModuleCount(),a=0;a").css("height",s+"px").appendTo(i),u=0;u").css("width",r+"px").css("background-color",n.isDark(a,u)?t.foreground:t.background).appendTo(l);n=i,jQuery(n).appendTo(this)})}}(jQuery),function(){"use strict";var e="undefined"!=typeof window&&void 0!==window.document?window.document:{},t="undefined"!=typeof module&&module.exports,n=function(){for(var t,n=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],i=0,o=n.length,r={};i",{class:n+"box "+n+"editor-visible "+n+e.o.lang+" trumbowyg"}),e.isTextarea=e.$ta.is("textarea"),e.isTextarea?(o=e.$ta.val(),e.$ed=i("
"),e.$box.insertAfter(e.$ta).append(e.$ed,e.$ta)):(e.$ed=e.$ta,o=e.$ed.html(),e.$ta=i("",h.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",h.option=!!xe.lastChild;var Ce={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Ee(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&C(e,t)?F.merge([e],n):n}function Ae(e,t){for(var n=0,i=e.length;n",""]);var ke=/<|&#?\w+;/;function Te(e,t,n,i,o){for(var r,s,a,l,u,c,d=t.createDocumentFragment(),f=[],h=0,p=e.length;h\s*$/g;function Be(e,t){return C(e,"table")&&C(11!==t.nodeType?t:t.firstChild,"tr")&&F(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function He(e,t){var n,i,o,r,s,a;if(1===t.nodeType){if(oe.hasData(e)&&(a=oe.get(e).events))for(o in oe.remove(t,"handle events"),a)for(n=0,i=a[o].length;n").attr(e.scriptAttrs||{}).prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&o("error"===e.type?404:200,e.type)}),m.head.appendChild(t[0])},abort:function(){n&&n()}}});var Qt,Zt=[],Jt=/(=)\?(?=&|$)|\?\?/;F.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Zt.pop()||F.expando+"_"+kt.guid++;return this[e]=!0,e}}),F.ajaxPrefilter("json jsonp",function(t,n,i){var o,r,s,a=!1!==t.jsonp&&(Jt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Jt.test(t.data)&&"data");if(a||"jsonp"===t.dataTypes[0])return o=t.jsonpCallback=p(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,a?t[a]=t[a].replace(Jt,"$1"+o):!1!==t.jsonp&&(t.url+=(Tt.test(t.url)?"&":"?")+t.jsonp+"="+o),t.converters["script json"]=function(){return s||F.error(o+" was not called"),s[0]},t.dataTypes[0]="json",r=e[o],e[o]=function(){s=arguments},i.always(function(){void 0===r?F(e).removeProp(o):e[o]=r,t[o]&&(t.jsonpCallback=n.jsonpCallback,Zt.push(o)),s&&p(r)&&r(s[0]),s=r=void 0}),"script"}),h.createHTMLDocument=((Qt=m.implementation.createHTMLDocument("").body).innerHTML="
",2===Qt.childNodes.length),F.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(h.createHTMLDocument?((i=(t=m.implementation.createHTMLDocument("")).createElement("base")).href=m.location.href,t.head.appendChild(i)):t=m),r=!n&&[],(o=B.exec(e))?[t.createElement(o[1])]:(o=Te([e],t,r),r&&r.length&&F(r).remove(),F.merge([],o.childNodes)));var i,o,r},F.fn.load=function(e,t,n){var i,o,r,s=this,a=e.indexOf(" ");return-1").append(F.parseHTML(e)).find(i):e)}).always(n&&function(e,t){s.each(function(){n.apply(this,r||[e.responseText,t,e])})}),this},F.expr.pseudos.animated=function(e){return F.grep(F.timers,function(t){return e===t.elem}).length},F.offset={setOffset:function(e,t,n){var i,o,r,s,a,l,u=F.css(e,"position"),c=F(e),d={};"static"===u&&(e.style.position="relative"),a=c.offset(),r=F.css(e,"top"),l=F.css(e,"left"),("absolute"===u||"fixed"===u)&&-1<(r+l).indexOf("auto")?(s=(i=c.position()).top,o=i.left):(s=parseFloat(r)||0,o=parseFloat(l)||0),p(t)&&(t=t.call(e,n,F.extend({},a))),null!=t.top&&(d.top=t.top-a.top+s),null!=t.left&&(d.left=t.left-a.left+o),"using"in t?t.using.call(e,d):c.css(d)}},F.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){F.offset.setOffset(this,e,t)});var t,n,i=this[0];return i?i.getClientRects().length?(t=i.getBoundingClientRect(),n=i.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,i=this[0],o={top:0,left:0};if("fixed"===F.css(i,"position"))t=i.getBoundingClientRect();else{for(t=this.offset(),n=i.ownerDocument,e=i.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===F.css(e,"position");)e=e.parentNode;e&&e!==i&&1===e.nodeType&&((o=F(e).offset()).top+=F.css(e,"borderTopWidth",!0),o.left+=F.css(e,"borderLeftWidth",!0))}return{top:t.top-o.top-F.css(i,"marginTop",!0),left:t.left-o.left-F.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===F.css(e,"position");)e=e.offsetParent;return e||fe})}}),F.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;F.fn[e]=function(i){return Q(this,function(e,i,o){var r;if(g(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===o)return r?r[t]:e[i];r?r.scrollTo(n?r.pageXOffset:o,n?o:r.pageYOffset):e[i]=o},e,i,arguments.length)}}),F.each(["top","left"],function(e,t){F.cssHooks[t]=Ve(h.pixelPosition,function(e,n){if(n)return n=Xe(e,t),We.test(n)?F(e).position()[t]+"px":n})}),F.each({Height:"height",Width:"width"},function(e,t){F.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,i){F.fn[i]=function(o,r){var s=arguments.length&&(n||"boolean"!=typeof o),a=n||(!0===o||!0===r?"margin":"border");return Q(this,function(t,n,o){var r;return g(t)?0===i.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(r=t.documentElement,Math.max(t.body["scroll"+e],r["scroll"+e],t.body["offset"+e],r["offset"+e],r["client"+e])):void 0===o?F.css(t,n,a):F.style(t,n,o,a)},t,s?o:void 0,s)}})}),F.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){F.fn[t]=function(e){return this.on(t,e)}}),F.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,i){return this.on(t,e,n,i)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),F.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){F.fn[t]=function(e,n){return 0e[n]})}return t.default=e,Object.freeze(t)}(e),n=new Map,i={set(e,t,i){n.has(e)||n.set(e,new Map);const o=n.get(e);o.has(t)||0===o.size?o.set(t,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(o.keys())[0]}.`)},get:(e,t)=>n.has(e)&&n.get(e).get(t)||null,remove(e,t){if(!n.has(e))return;const i=n.get(e);i.delete(t),0===i.size&&n.delete(e)}},o="transitionend",r=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,(e,t)=>"#"+CSS.escape(t))),e),s=e=>{e.dispatchEvent(new Event(o))},a=e=>!(!e||"object"!=typeof e)&&(void 0!==e.jquery&&(e=e[0]),void 0!==e.nodeType),l=e=>a(e)?e.jquery?e[0]:e:"string"==typeof e&&e.length>0?document.querySelector(r(e)):null,u=e=>{if(!a(e)||0===e.getClientRects().length)return!1;const t="visible"===getComputedStyle(e).getPropertyValue("visibility"),n=e.closest("details:not([open])");if(!n)return t;if(n!==e){const t=e.closest("summary");if(t&&t.parentNode!==n)return!1;if(null===t)return!1}return t},c=e=>!e||e.nodeType!==Node.ELEMENT_NODE||!!e.classList.contains("disabled")||(void 0!==e.disabled?e.disabled:e.hasAttribute("disabled")&&"false"!==e.getAttribute("disabled")),d=e=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof e.getRootNode){const t=e.getRootNode();return t instanceof ShadowRoot?t:null}return e instanceof ShadowRoot?e:e.parentNode?d(e.parentNode):null},f=()=>{},h=e=>{e.offsetHeight},p=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,g=[],m=()=>"rtl"===document.documentElement.dir,b=e=>{var t;t=(()=>{const t=p();if(t){const n=e.NAME,i=t.fn[n];t.fn[n]=e.jQueryInterface,t.fn[n].Constructor=e,t.fn[n].noConflict=(()=>(t.fn[n]=i,e.jQueryInterface))}}),"loading"===document.readyState?(g.length||document.addEventListener("DOMContentLoaded",()=>{for(const e of g)e()}),g.push(t)):t()},v=(e,t=[],n=e)=>"function"==typeof e?e(...t):n,x=(e,t,n=!0)=>{if(!n)return void v(e);const i=(e=>{if(!e)return 0;let{transitionDuration:t,transitionDelay:n}=window.getComputedStyle(e);const i=Number.parseFloat(t),o=Number.parseFloat(n);return i||o?(t=t.split(",")[0],n=n.split(",")[0],1e3*(Number.parseFloat(t)+Number.parseFloat(n))):0})(t)+5;let r=!1;const a=({target:n})=>{n===t&&(r=!0,t.removeEventListener(o,a),v(e))};t.addEventListener(o,a),setTimeout(()=>{r||s(t)},i)},y=(e,t,n,i)=>{const o=e.length;let r=e.indexOf(t);return-1===r?!n&&i?e[o-1]:e[0]:(r+=n?1:-1,i&&(r=(r+o)%o),e[Math.max(0,Math.min(r,o-1))])},_=/[^.]*(?=\..*)\.|.*/,F=/\..*/,w=/::\d+$/,C={};let E=1;const A={mouseenter:"mouseover",mouseleave:"mouseout"},k=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function T(e,t){return t&&`${t}::${E++}`||e.uidEvent||E++}function O(e){const t=T(e);return e.uidEvent=t,C[t]=C[t]||{},C[t]}function S(e,t,n=null){return Object.values(e).find(e=>e.callable===t&&e.delegationSelector===n)}function D(e,t,n){const i="string"==typeof t,o=i?n:t||n;let r=L(e);return k.has(r)||(r=e),[i,o,r]}function j(e,t,n,i,o){if("string"!=typeof t||!e)return;let[r,s,a]=D(t,n,i);t in A&&(s=(e=>(function(t){if(!t.relatedTarget||t.relatedTarget!==t.delegateTarget&&!t.delegateTarget.contains(t.relatedTarget))return e.call(this,t)}))(s));const l=O(e),u=l[a]||(l[a]={}),c=S(u,s,r?n:null);if(c)return void(c.oneOff=c.oneOff&&o);const d=T(s,t.replace(_,"")),f=r?function(e,t,n){return function i(o){const r=e.querySelectorAll(t);for(let{target:s}=o;s&&s!==this;s=s.parentNode)for(const a of r)if(a===s)return B(o,{delegateTarget:s}),i.oneOff&&N.off(e,o.type,t,n),n.apply(s,[o])}}(e,n,s):function(e,t){return function n(i){return B(i,{delegateTarget:e}),n.oneOff&&N.off(e,i.type,t),t.apply(e,[i])}}(e,s);f.delegationSelector=r?n:null,f.callable=s,f.oneOff=o,f.uidEvent=d,u[d]=f,e.addEventListener(a,f,r)}function P(e,t,n,i,o){const r=S(t[n],i,o);r&&(e.removeEventListener(n,r,Boolean(o)),delete t[n][r.uidEvent])}function M(e,t,n,i){const o=t[n]||{};for(const[r,s]of Object.entries(o))r.includes(i)&&P(e,t,n,s.callable,s.delegationSelector)}function L(e){return e=e.replace(F,""),A[e]||e}const N={on(e,t,n,i){j(e,t,n,i,!1)},one(e,t,n,i){j(e,t,n,i,!0)},off(e,t,n,i){if("string"!=typeof t||!e)return;const[o,r,s]=D(t,n,i),a=s!==t,l=O(e),u=l[s]||{},c=t.startsWith(".");if(void 0===r){if(c)for(const n of Object.keys(l))M(e,l,n,t.slice(1));for(const[n,i]of Object.entries(u)){const o=n.replace(w,"");a&&!t.includes(o)||P(e,l,s,i.callable,i.delegationSelector)}}else{if(!Object.keys(u).length)return;P(e,l,s,r,o?n:null)}},trigger(e,t,n){if("string"!=typeof t||!e)return null;const i=p();let o=null,r=!0,s=!0,a=!1;t!==L(t)&&i&&(o=i.Event(t,n),i(e).trigger(o),r=!o.isPropagationStopped(),s=!o.isImmediatePropagationStopped(),a=o.isDefaultPrevented());const l=B(new Event(t,{bubbles:r,cancelable:!0}),n);return a&&l.preventDefault(),s&&e.dispatchEvent(l),l.defaultPrevented&&o&&o.preventDefault(),l}};function B(e,t={}){for(const[n,i]of Object.entries(t))try{e[n]=i}catch(t){Object.defineProperty(e,n,{configurable:!0,get:()=>i})}return e}function I(e){if("true"===e)return!0;if("false"===e)return!1;if(e===Number(e).toString())return Number(e);if(""===e||"null"===e)return null;if("string"!=typeof e)return e;try{return JSON.parse(decodeURIComponent(e))}catch(t){return e}}function R(e){return e.replace(/[A-Z]/g,e=>"-"+e.toLowerCase())}const H={setDataAttribute(e,t,n){e.setAttribute("data-bs-"+R(t),n)},removeDataAttribute(e,t){e.removeAttribute("data-bs-"+R(t))},getDataAttributes(e){if(!e)return{};const t={},n=Object.keys(e.dataset).filter(e=>e.startsWith("bs")&&!e.startsWith("bsConfig"));for(const i of n){let n=i.replace(/^bs/,"");t[n=n.charAt(0).toLowerCase()+n.slice(1,n.length)]=I(e.dataset[i])}return t},getDataAttribute:(e,t)=>I(e.getAttribute("data-bs-"+R(t)))};class z{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(e){return e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e}_mergeConfigObj(e,t){const n=a(t)?H.getDataAttribute(t,"config"):{};return{...this.constructor.Default,..."object"==typeof n?n:{},...a(t)?H.getDataAttributes(t):{},..."object"==typeof e?e:{}}}_typeCheckConfig(e,t=this.constructor.DefaultType){for(const[i,o]of Object.entries(t)){const t=e[i],r=a(t)?"element":null==(n=t)?""+n:Object.prototype.toString.call(n).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${i}" provided type "${r}" but expected type "${o}".`)}var n}}class $ extends z{constructor(e,t){super(),(e=l(e))&&(this._element=e,this._config=this._getConfig(t),i.set(this._element,this.constructor.DATA_KEY,this))}dispose(){i.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const e of Object.getOwnPropertyNames(this))this[e]=null}_queueCallback(e,t,n=!0){x(e,t,n)}_getConfig(e){return e=this._mergeConfigObj(e,this._element),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}static getInstance(e){return i.get(l(e),this.DATA_KEY)}static getOrCreateInstance(e,t={}){return this.getInstance(e)||new this(e,"object"==typeof t?t:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return"bs."+this.NAME}static get EVENT_KEY(){return"."+this.DATA_KEY}static eventName(e){return`${e}${this.EVENT_KEY}`}}const W=e=>{let t=e.getAttribute("data-bs-target");if(!t||"#"===t){let n=e.getAttribute("href");if(!n||!n.includes("#")&&!n.startsWith("."))return null;n.includes("#")&&!n.startsWith("#")&&(n="#"+n.split("#")[1]),t=n&&"#"!==n?n.trim():null}return t?t.split(",").map(e=>r(e)).join(","):null},q={find:(e,t=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(t,e)),findOne:(e,t=document.documentElement)=>Element.prototype.querySelector.call(t,e),children:(e,t)=>[].concat(...e.children).filter(e=>e.matches(t)),parents(e,t){const n=[];let i=e.parentNode.closest(t);for(;i;)n.push(i),i=i.parentNode.closest(t);return n},prev(e,t){let n=e.previousElementSibling;for(;n;){if(n.matches(t))return[n];n=n.previousElementSibling}return[]},next(e,t){let n=e.nextElementSibling;for(;n;){if(n.matches(t))return[n];n=n.nextElementSibling}return[]},focusableChildren(e){const t=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(e=>e+':not([tabindex^="-"])').join(",");return this.find(t,e).filter(e=>!c(e)&&u(e))},getSelectorFromElement(e){const t=W(e);return t&&q.findOne(t)?t:null},getElementFromSelector(e){const t=W(e);return t?q.findOne(t):null},getMultipleElementsFromSelector(e){const t=W(e);return t?q.find(t):[]}},Y=(e,t="hide")=>{const n="click.dismiss"+e.EVENT_KEY,i=e.NAME;N.on(document,n,`[data-bs-dismiss="${i}"]`,function(n){if(["A","AREA"].includes(this.tagName)&&n.preventDefault(),c(this))return;const o=q.getElementFromSelector(this)||this.closest("."+i);e.getOrCreateInstance(o)[t]()})};class U extends ${static get NAME(){return"alert"}close(){if(N.trigger(this._element,"close.bs.alert").defaultPrevented)return;this._element.classList.remove("show");const e=this._element.classList.contains("fade");this._queueCallback(()=>this._destroyElement(),this._element,e)}_destroyElement(){this._element.remove(),N.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(e){return this.each(function(){const t=U.getOrCreateInstance(this);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e](this)}})}}Y(U,"close"),b(U);const G='[data-bs-toggle="button"]';class X extends ${static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(e){return this.each(function(){const t=X.getOrCreateInstance(this);"toggle"===e&&t[e]()})}}N.on(document,"click.bs.button.data-api",G,e=>{e.preventDefault();const t=e.target.closest(G);X.getOrCreateInstance(t).toggle()}),b(X);const V=".bs.swipe",K={endCallback:null,leftCallback:null,rightCallback:null},Q={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Z extends z{constructor(e,t){super(),this._element=e,e&&Z.isSupported()&&(this._config=this._getConfig(t),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return K}static get DefaultType(){return Q}static get NAME(){return"swipe"}dispose(){N.off(this._element,V)}_start(e){this._supportPointerEvents?this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX):this._deltaX=e.touches[0].clientX}_end(e){this._eventIsPointerPenTouch(e)&&(this._deltaX=e.clientX-this._deltaX),this._handleSwipe(),v(this._config.endCallback)}_move(e){this._deltaX=e.touches&&e.touches.length>1?0:e.touches[0].clientX-this._deltaX}_handleSwipe(){const e=Math.abs(this._deltaX);if(e<=40)return;const t=e/this._deltaX;this._deltaX=0,t&&v(t>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,"pointerdown.bs.swipe",e=>this._start(e)),N.on(this._element,"pointerup.bs.swipe",e=>this._end(e)),this._element.classList.add("pointer-event")):(N.on(this._element,"touchstart.bs.swipe",e=>this._start(e)),N.on(this._element,"touchmove.bs.swipe",e=>this._move(e)),N.on(this._element,"touchend.bs.swipe",e=>this._end(e)))}_eventIsPointerPenTouch(e){return this._supportPointerEvents&&("pen"===e.pointerType||"touch"===e.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const J=".bs.carousel",ee=".data-api",te="next",ne="prev",ie="left",oe="right",re="slid"+J,se=`load${J}${ee}`,ae=`click${J}${ee}`,le="carousel",ue="active",ce=".active",de=".carousel-item",fe={ArrowLeft:oe,ArrowRight:ie},he={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},pe={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class ge extends ${constructor(e,t){super(e,t),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=q.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===le&&this.cycle()}static get Default(){return he}static get DefaultType(){return pe}static get NAME(){return"carousel"}next(){this._slide(te)}nextWhenVisible(){!document.hidden&&u(this._element)&&this.next()}prev(){this._slide(ne)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,re,()=>this.cycle()):this.cycle())}to(e){const t=this._getItems();if(e>t.length-1||e<0)return;if(this._isSliding)return void N.one(this._element,re,()=>this.to(e));const n=this._getItemIndex(this._getActive());if(n===e)return;const i=e>n?te:ne;this._slide(i,t[e])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(e){return e.defaultInterval=e.interval,e}_addEventListeners(){this._config.keyboard&&N.on(this._element,"keydown.bs.carousel",e=>this._keydown(e)),"hover"===this._config.pause&&(N.on(this._element,"mouseenter.bs.carousel",()=>this.pause()),N.on(this._element,"mouseleave.bs.carousel",()=>this._maybeEnableCycle())),this._config.touch&&Z.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const e of q.find(".carousel-item img",this._element))N.on(e,"dragstart.bs.carousel",e=>e.preventDefault());const e={leftCallback:()=>this._slide(this._directionToOrder(ie)),rightCallback:()=>this._slide(this._directionToOrder(oe)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),500+this._config.interval))}};this._swipeHelper=new Z(this._element,e)}_keydown(e){if(/input|textarea/i.test(e.target.tagName))return;const t=fe[e.key];t&&(e.preventDefault(),this._slide(this._directionToOrder(t)))}_getItemIndex(e){return this._getItems().indexOf(e)}_setActiveIndicatorElement(e){if(!this._indicatorsElement)return;const t=q.findOne(ce,this._indicatorsElement);t.classList.remove(ue),t.removeAttribute("aria-current");const n=q.findOne(`[data-bs-slide-to="${e}"]`,this._indicatorsElement);n&&(n.classList.add(ue),n.setAttribute("aria-current","true"))}_updateInterval(){const e=this._activeElement||this._getActive();if(!e)return;const t=Number.parseInt(e.getAttribute("data-bs-interval"),10);this._config.interval=t||this._config.defaultInterval}_slide(e,t=null){if(this._isSliding)return;const n=this._getActive(),i=e===te,o=t||y(this._getItems(),n,i,this._config.wrap);if(o===n)return;const r=this._getItemIndex(o),s=t=>N.trigger(this._element,t,{relatedTarget:o,direction:this._orderToDirection(e),from:this._getItemIndex(n),to:r});if(s("slide.bs.carousel").defaultPrevented)return;if(!n||!o)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(r),this._activeElement=o;const l=i?"carousel-item-start":"carousel-item-end",u=i?"carousel-item-next":"carousel-item-prev";o.classList.add(u),h(o),n.classList.add(l),o.classList.add(l),this._queueCallback(()=>{o.classList.remove(l,u),o.classList.add(ue),n.classList.remove(ue,u,l),this._isSliding=!1,s(re)},n,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return q.findOne(".active.carousel-item",this._element)}_getItems(){return q.find(de,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(e){return m()?e===ie?ne:te:e===ie?te:ne}_orderToDirection(e){return m()?e===ne?ie:oe:e===ne?oe:ie}static jQueryInterface(e){return this.each(function(){const t=ge.getOrCreateInstance(this,e);if("number"!=typeof e){if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}}else t.to(e)})}}N.on(document,ae,"[data-bs-slide], [data-bs-slide-to]",function(e){const t=q.getElementFromSelector(this);if(!t||!t.classList.contains(le))return;e.preventDefault();const n=ge.getOrCreateInstance(t),i=this.getAttribute("data-bs-slide-to");return i?(n.to(i),void n._maybeEnableCycle()):"next"===H.getDataAttribute(this,"slide")?(n.next(),void n._maybeEnableCycle()):(n.prev(),void n._maybeEnableCycle())}),N.on(window,se,()=>{const e=q.find('[data-bs-ride="carousel"]');for(const t of e)ge.getOrCreateInstance(t)}),b(ge);const me="show",be="collapse",ve="collapsing",xe=`:scope .${be} .${be}`,ye='[data-bs-toggle="collapse"]',_e={parent:null,toggle:!0},Fe={parent:"(null|element)",toggle:"boolean"};class we extends ${constructor(e,t){super(e,t),this._isTransitioning=!1,this._triggerArray=[];const n=q.find(ye);for(const e of n){const t=q.getSelectorFromElement(e),n=q.find(t).filter(e=>e===this._element);null!==t&&n.length&&this._triggerArray.push(e)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return _e}static get DefaultType(){return Fe}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let e=[];if(this._config.parent&&(e=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter(e=>e!==this._element).map(e=>we.getOrCreateInstance(e,{toggle:!1}))),e.length&&e[0]._isTransitioning)return;if(N.trigger(this._element,"show.bs.collapse").defaultPrevented)return;for(const t of e)t.hide();const t=this._getDimension();this._element.classList.remove(be),this._element.classList.add(ve),this._element.style[t]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const n="scroll"+(t[0].toUpperCase()+t.slice(1));this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(ve),this._element.classList.add(be,me),this._element.style[t]="",N.trigger(this._element,"shown.bs.collapse")},this._element,!0),this._element.style[t]=this._element[n]+"px"}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const e=this._getDimension();this._element.style[e]=this._element.getBoundingClientRect()[e]+"px",h(this._element),this._element.classList.add(ve),this._element.classList.remove(be,me);for(const e of this._triggerArray){const t=q.getElementFromSelector(e);t&&!this._isShown(t)&&this._addAriaAndCollapsedClass([e],!1)}this._isTransitioning=!0,this._element.style[e]="",this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(ve),this._element.classList.add(be),N.trigger(this._element,"hidden.bs.collapse")},this._element,!0)}_isShown(e=this._element){return e.classList.contains(me)}_configAfterMerge(e){return e.toggle=Boolean(e.toggle),e.parent=l(e.parent),e}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const e=this._getFirstLevelChildren(ye);for(const t of e){const e=q.getElementFromSelector(t);e&&this._addAriaAndCollapsedClass([t],this._isShown(e))}}_getFirstLevelChildren(e){const t=q.find(xe,this._config.parent);return q.find(e,this._config.parent).filter(e=>!t.includes(e))}_addAriaAndCollapsedClass(e,t){if(e.length)for(const n of e)n.classList.toggle("collapsed",!t),n.setAttribute("aria-expanded",t)}static jQueryInterface(e){const t={};return"string"==typeof e&&/show|hide/.test(e)&&(t.toggle=!1),this.each(function(){const n=we.getOrCreateInstance(this,t);if("string"==typeof e){if(void 0===n[e])throw new TypeError(`No method named "${e}"`);n[e]()}})}}N.on(document,"click.bs.collapse.data-api",ye,function(e){("A"===e.target.tagName||e.delegateTarget&&"A"===e.delegateTarget.tagName)&&e.preventDefault();for(const e of q.getMultipleElementsFromSelector(this))we.getOrCreateInstance(e,{toggle:!1}).toggle()}),b(we);const Ce="dropdown",Ee=".bs.dropdown",Ae=".data-api",ke="ArrowUp",Te="ArrowDown",Oe=`click${Ee}${Ae}`,Se=`keydown${Ee}${Ae}`,De=`keyup${Ee}${Ae}`,je="show",Pe='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Me=".dropdown-menu",Le=m()?"top-end":"top-start",Ne=m()?"top-start":"top-end",Be=m()?"bottom-end":"bottom-start",Ie=m()?"bottom-start":"bottom-end",Re=m()?"left-start":"right-start",He=m()?"right-start":"left-start",ze={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},$e={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class We extends ${constructor(e,t){super(e,t),this._popper=null,this._parent=this._element.parentNode,this._menu=q.next(this._element,Me)[0]||q.prev(this._element,Me)[0]||q.findOne(Me,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return ze}static get DefaultType(){return $e}static get NAME(){return Ce}toggle(){return this._isShown()?this.hide():this.show()}show(){if(c(this._element)||this._isShown())return;const e={relatedTarget:this._element};if(!N.trigger(this._element,"show.bs.dropdown",e).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const e of[].concat(...document.body.children))N.on(e,"mouseover",f);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(je),this._element.classList.add(je),N.trigger(this._element,"shown.bs.dropdown",e)}}hide(){if(c(this._element)||!this._isShown())return;const e={relatedTarget:this._element};this._completeHide(e)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(e){if(!N.trigger(this._element,"hide.bs.dropdown",e).defaultPrevented){if("ontouchstart"in document.documentElement)for(const e of[].concat(...document.body.children))N.off(e,"mouseover",f);this._popper&&this._popper.destroy(),this._menu.classList.remove(je),this._element.classList.remove(je),this._element.setAttribute("aria-expanded","false"),H.removeDataAttribute(this._menu,"popper"),N.trigger(this._element,"hidden.bs.dropdown",e)}}_getConfig(e){if("object"==typeof(e=super._getConfig(e)).reference&&!a(e.reference)&&"function"!=typeof e.reference.getBoundingClientRect)throw new TypeError(Ce.toUpperCase()+': Option "reference" provided type "object" without a required "getBoundingClientRect" method.');return e}_createPopper(){if(void 0===t)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let e=this._element;"parent"===this._config.reference?e=this._parent:a(this._config.reference)?e=l(this._config.reference):"object"==typeof this._config.reference&&(e=this._config.reference);const n=this._getPopperConfig();this._popper=t.createPopper(e,this._menu,n)}_isShown(){return this._menu.classList.contains(je)}_getPlacement(){const e=this._parent;if(e.classList.contains("dropend"))return Re;if(e.classList.contains("dropstart"))return He;if(e.classList.contains("dropup-center"))return"top";if(e.classList.contains("dropdown-center"))return"bottom";const t="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return e.classList.contains("dropup")?t?Ne:Le:t?Ie:Be}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:e}=this._config;return"string"==typeof e?e.split(",").map(e=>Number.parseInt(e,10)):"function"==typeof e?t=>e(t,this._element):e}_getPopperConfig(){const e={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(H.setDataAttribute(this._menu,"popper","static"),e.modifiers=[{name:"applyStyles",enabled:!1}]),{...e,...v(this._config.popperConfig,[e])}}_selectMenuItem({key:e,target:t}){const n=q.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(e=>u(e));n.length&&y(n,t,e===Te,!n.includes(t)).focus()}static jQueryInterface(e){return this.each(function(){const t=We.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}static clearMenus(e){if(2===e.button||"keyup"===e.type&&"Tab"!==e.key)return;const t=q.find('[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled).show');for(const n of t){const t=We.getInstance(n);if(!t||!1===t._config.autoClose)continue;const i=e.composedPath(),o=i.includes(t._menu);if(i.includes(t._element)||"inside"===t._config.autoClose&&!o||"outside"===t._config.autoClose&&o)continue;if(t._menu.contains(e.target)&&("keyup"===e.type&&"Tab"===e.key||/input|select|option|textarea|form/i.test(e.target.tagName)))continue;const r={relatedTarget:t._element};"click"===e.type&&(r.clickEvent=e),t._completeHide(r)}}static dataApiKeydownHandler(e){const t=/input|textarea/i.test(e.target.tagName),n="Escape"===e.key,i=[ke,Te].includes(e.key);if(!i&&!n)return;if(t&&!n)return;e.preventDefault();const o=this.matches(Pe)?this:q.prev(this,Pe)[0]||q.next(this,Pe)[0]||q.findOne(Pe,e.delegateTarget.parentNode),r=We.getOrCreateInstance(o);if(i)return e.stopPropagation(),r.show(),void r._selectMenuItem(e);r._isShown()&&(e.stopPropagation(),r.hide(),o.focus())}}N.on(document,Se,Pe,We.dataApiKeydownHandler),N.on(document,Se,Me,We.dataApiKeydownHandler),N.on(document,Oe,We.clearMenus),N.on(document,De,We.clearMenus),N.on(document,Oe,Pe,function(e){e.preventDefault(),We.getOrCreateInstance(this).toggle()}),b(We);const qe="backdrop",Ye="mousedown.bs."+qe,Ue={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ge={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Xe extends z{constructor(e){super(),this._config=this._getConfig(e),this._isAppended=!1,this._element=null}static get Default(){return Ue}static get DefaultType(){return Ge}static get NAME(){return qe}show(e){if(!this._config.isVisible)return void v(e);this._append();const t=this._getElement();this._config.isAnimated&&h(t),t.classList.add("show"),this._emulateAnimation(()=>{v(e)})}hide(e){this._config.isVisible?(this._getElement().classList.remove("show"),this._emulateAnimation(()=>{this.dispose(),v(e)})):v(e)}dispose(){this._isAppended&&(N.off(this._element,Ye),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const e=document.createElement("div");e.className=this._config.className,this._config.isAnimated&&e.classList.add("fade"),this._element=e}return this._element}_configAfterMerge(e){return e.rootElement=l(e.rootElement),e}_append(){if(this._isAppended)return;const e=this._getElement();this._config.rootElement.append(e),N.on(e,Ye,()=>{v(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(e){x(e,this._getElement(),this._config.isAnimated)}}const Ve=".bs.focustrap",Ke="backward",Qe={autofocus:!0,trapElement:null},Ze={autofocus:"boolean",trapElement:"element"};class Je extends z{constructor(e){super(),this._config=this._getConfig(e),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Qe}static get DefaultType(){return Ze}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Ve),N.on(document,"focusin.bs.focustrap",e=>this._handleFocusin(e)),N.on(document,"keydown.tab.bs.focustrap",e=>this._handleKeydown(e)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Ve))}_handleFocusin(e){const{trapElement:t}=this._config;if(e.target===document||e.target===t||t.contains(e.target))return;const n=q.focusableChildren(t);0===n.length?t.focus():this._lastTabNavDirection===Ke?n[n.length-1].focus():n[0].focus()}_handleKeydown(e){"Tab"===e.key&&(this._lastTabNavDirection=e.shiftKey?Ke:"forward")}}const et=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",tt=".sticky-top",nt="padding-right",it="margin-right";class ot{constructor(){this._element=document.body}getWidth(){const e=document.documentElement.clientWidth;return Math.abs(window.innerWidth-e)}hide(){const e=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,nt,t=>t+e),this._setElementAttributes(et,nt,t=>t+e),this._setElementAttributes(tt,it,t=>t-e)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,nt),this._resetElementAttributes(et,nt),this._resetElementAttributes(tt,it)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(e,t,n){const i=this.getWidth();this._applyManipulationCallback(e,e=>{if(e!==this._element&&window.innerWidth>e.clientWidth+i)return;this._saveInitialAttribute(e,t);const o=window.getComputedStyle(e).getPropertyValue(t);e.style.setProperty(t,n(Number.parseFloat(o))+"px")})}_saveInitialAttribute(e,t){const n=e.style.getPropertyValue(t);n&&H.setDataAttribute(e,t,n)}_resetElementAttributes(e,t){this._applyManipulationCallback(e,e=>{const n=H.getDataAttribute(e,t);null!==n?(H.removeDataAttribute(e,t),e.style.setProperty(t,n)):e.style.removeProperty(t)})}_applyManipulationCallback(e,t){if(a(e))t(e);else for(const n of q.find(e,this._element))t(n)}}const rt=".bs.modal",st="hidden"+rt,at="show"+rt,lt=`click${rt}.data-api`,ut="modal-open",ct="modal-static",dt={backdrop:!0,focus:!0,keyboard:!0},ft={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class ht extends ${constructor(e,t){super(e,t),this._dialog=q.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new ot,this._addEventListeners()}static get Default(){return dt}static get DefaultType(){return ft}static get NAME(){return"modal"}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){this._isShown||this._isTransitioning||N.trigger(this._element,at,{relatedTarget:e}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(ut),this._adjustDialog(),this._backdrop.show(()=>this._showElement(e)))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,"hide.bs.modal").defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove("show"),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated())))}dispose(){N.off(window,rt),N.off(this._dialog,rt),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Xe({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Je({trapElement:this._element})}_showElement(e){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const t=q.findOne(".modal-body",this._dialog);t&&(t.scrollTop=0),h(this._element),this._element.classList.add("show"),this._queueCallback(()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,"shown.bs.modal",{relatedTarget:e})},this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,"keydown.dismiss.bs.modal",e=>{"Escape"===e.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())}),N.on(window,"resize.bs.modal",()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),N.on(this._element,"mousedown.dismiss.bs.modal",e=>{N.one(this._element,"click.dismiss.bs.modal",t=>{this._element===e.target&&this._element===t.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(ut),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,st)})}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const e=this._element.scrollHeight>document.documentElement.clientHeight,t=this._element.style.overflowY;"hidden"===t||this._element.classList.contains(ct)||(e||(this._element.style.overflowY="hidden"),this._element.classList.add(ct),this._queueCallback(()=>{this._element.classList.remove(ct),this._queueCallback(()=>{this._element.style.overflowY=t},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){const e=this._element.scrollHeight>document.documentElement.clientHeight,t=this._scrollBar.getWidth(),n=t>0;if(n&&!e){const e=m()?"paddingLeft":"paddingRight";this._element.style[e]=t+"px"}if(!n&&e){const e=m()?"paddingRight":"paddingLeft";this._element.style[e]=t+"px"}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(e,t){return this.each(function(){const n=ht.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===n[e])throw new TypeError(`No method named "${e}"`);n[e](t)}})}}N.on(document,lt,'[data-bs-toggle="modal"]',function(e){const t=q.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&e.preventDefault(),N.one(t,at,e=>{e.defaultPrevented||N.one(t,st,()=>{u(this)&&this.focus()})});const n=q.findOne(".modal.show");n&&ht.getInstance(n).hide(),ht.getOrCreateInstance(t).toggle(this)}),Y(ht),b(ht);const pt=".bs.offcanvas",gt=".data-api",mt=`load${pt}${gt}`,bt="showing",vt=".offcanvas.show",xt="hidePrevented"+pt,yt="hidden"+pt,_t=`click${pt}${gt}`,Ft={backdrop:!0,keyboard:!0,scroll:!1},wt={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Ct extends ${constructor(e,t){super(e,t),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Ft}static get DefaultType(){return wt}static get NAME(){return"offcanvas"}toggle(e){return this._isShown?this.hide():this.show(e)}show(e){this._isShown||N.trigger(this._element,"show.bs.offcanvas",{relatedTarget:e}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new ot).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(bt),this._queueCallback(()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add("show"),this._element.classList.remove(bt),N.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:e})},this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add("hiding"),this._backdrop.hide(),this._queueCallback(()=>{this._element.classList.remove("show","hiding"),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new ot).reset(),N.trigger(this._element,yt)},this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const e=Boolean(this._config.backdrop);return new Xe({className:"offcanvas-backdrop",isVisible:e,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:e?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,xt)}:null})}_initializeFocusTrap(){return new Je({trapElement:this._element})}_addEventListeners(){N.on(this._element,"keydown.dismiss.bs.offcanvas",e=>{"Escape"===e.key&&(this._config.keyboard?this.hide():N.trigger(this._element,xt))})}static jQueryInterface(e){return this.each(function(){const t=Ct.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e](this)}})}}N.on(document,_t,'[data-bs-toggle="offcanvas"]',function(e){const t=q.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&e.preventDefault(),c(this))return;N.one(t,yt,()=>{u(this)&&this.focus()});const n=q.findOne(vt);n&&n!==t&&Ct.getInstance(n).hide(),Ct.getOrCreateInstance(t).toggle(this)}),N.on(window,mt,()=>{for(const e of q.find(vt))Ct.getOrCreateInstance(e).show()}),N.on(window,"resize.bs.offcanvas",()=>{for(const e of q.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(e).position&&Ct.getOrCreateInstance(e).hide()}),Y(Ct),b(Ct);const Et={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},At=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),kt=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Tt=(e,t)=>{const n=e.nodeName.toLowerCase();return t.includes(n)?!At.has(n)||Boolean(kt.test(e.nodeValue)):t.filter(e=>e instanceof RegExp).some(e=>e.test(n))},Ot={allowList:Et,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},St={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Dt={entry:"(string|element|function|null)",selector:"(string|element)"};class jt extends z{constructor(e){super(),this._config=this._getConfig(e)}static get Default(){return Ot}static get DefaultType(){return St}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map(e=>this._resolvePossibleFunction(e)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(e){return this._checkContent(e),this._config.content={...this._config.content,...e},this}toHtml(){const e=document.createElement("div");e.innerHTML=this._maybeSanitize(this._config.template);for(const[t,n]of Object.entries(this._config.content))this._setContent(e,n,t);const t=e.children[0],n=this._resolvePossibleFunction(this._config.extraClass);return n&&t.classList.add(...n.split(" ")),t}_typeCheckConfig(e){super._typeCheckConfig(e),this._checkContent(e.content)}_checkContent(e){for(const[t,n]of Object.entries(e))super._typeCheckConfig({selector:t,entry:n},Dt)}_setContent(e,t,n){const i=q.findOne(n,e);i&&((t=this._resolvePossibleFunction(t))?a(t)?this._putElementInTemplate(l(t),i):this._config.html?i.innerHTML=this._maybeSanitize(t):i.textContent=t:i.remove())}_maybeSanitize(e){return this._config.sanitize?function(e,t,n){if(!e.length)return e;if(n&&"function"==typeof n)return n(e);const i=(new window.DOMParser).parseFromString(e,"text/html"),o=[].concat(...i.body.querySelectorAll("*"));for(const e of o){const n=e.nodeName.toLowerCase();if(!Object.keys(t).includes(n)){e.remove();continue}const i=[].concat(...e.attributes),o=[].concat(t["*"]||[],t[n]||[]);for(const t of i)Tt(t,o)||e.removeAttribute(t.nodeName)}return i.body.innerHTML}(e,this._config.allowList,this._config.sanitizeFn):e}_resolvePossibleFunction(e){return v(e,[this])}_putElementInTemplate(e,t){if(this._config.html)return t.innerHTML="",void t.append(e);t.textContent=e.textContent}}const Pt=new Set(["sanitize","allowList","sanitizeFn"]),Mt="fade",Lt="show",Nt="hide.bs.modal",Bt="hover",It="focus",Rt={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},Ht={allowList:Et,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},zt={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class $t extends ${constructor(e,n){if(void 0===t)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(e,n),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return Ht}static get DefaultType(){return zt}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(".modal"),Nt,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const e=N.trigger(this._element,this.constructor.eventName("show")),t=(d(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(e.defaultPrevented||!t)return;this._disposePopper();const n=this._getTipElement();this._element.setAttribute("aria-describedby",n.getAttribute("id"));const{container:i}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(i.append(n),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(n),n.classList.add(Lt),"ontouchstart"in document.documentElement)for(const e of[].concat(...document.body.children))N.on(e,"mouseover",f);this._queueCallback(()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1},this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(Lt),"ontouchstart"in document.documentElement)for(const e of[].concat(...document.body.children))N.off(e,"mouseover",f);this._activeTrigger.click=!1,this._activeTrigger[It]=!1,this._activeTrigger[Bt]=!1,this._isHovered=null,this._queueCallback(()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))},this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(e){const t=this._getTemplateFactory(e).toHtml();if(!t)return null;t.classList.remove(Mt,Lt),t.classList.add(`bs-${this.constructor.NAME}-auto`);const n=(e=>{do{e+=Math.floor(1e6*Math.random())}while(document.getElementById(e));return e})(this.constructor.NAME).toString();return t.setAttribute("id",n),this._isAnimated()&&t.classList.add(Mt),t}setContent(e){this._newContent=e,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(e){return this._templateFactory?this._templateFactory.changeContent(e):this._templateFactory=new jt({...this._config,content:e,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(e){return this.constructor.getOrCreateInstance(e.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Mt)}_isShown(){return this.tip&&this.tip.classList.contains(Lt)}_createPopper(e){const n=v(this._config.placement,[this,e,this._element]),i=Rt[n.toUpperCase()];return t.createPopper(this._element,e,this._getPopperConfig(i))}_getOffset(){const{offset:e}=this._config;return"string"==typeof e?e.split(",").map(e=>Number.parseInt(e,10)):"function"==typeof e?t=>e(t,this._element):e}_resolvePossibleFunction(e){return v(e,[this._element])}_getPopperConfig(e){const t={placement:e,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:e=>{this._getTipElement().setAttribute("data-popper-placement",e.state.placement)}}]};return{...t,...v(this._config.popperConfig,[t])}}_setListeners(){const e=this._config.trigger.split(" ");for(const t of e)if("click"===t)N.on(this._element,this.constructor.eventName("click"),this._config.selector,e=>{this._initializeOnDelegatedTarget(e).toggle()});else if("manual"!==t){const e=t===Bt?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),n=t===Bt?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,e,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger["focusin"===e.type?It:Bt]=!0,t._enter()}),N.on(this._element,n,this._config.selector,e=>{const t=this._initializeOnDelegatedTarget(e);t._activeTrigger["focusout"===e.type?It:Bt]=t._element.contains(e.relatedTarget),t._leave()})}this._hideModalHandler=(()=>{this._element&&this.hide()}),N.on(this._element.closest(".modal"),Nt,this._hideModalHandler)}_fixTitle(){const e=this._element.getAttribute("title");e&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",e),this._element.setAttribute("data-bs-original-title",e),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(e,t){clearTimeout(this._timeout),this._timeout=setTimeout(e,t)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(e){const t=H.getDataAttributes(this._element);for(const e of Object.keys(t))Pt.has(e)&&delete t[e];return e={...t,..."object"==typeof e&&e?e:{}},e=this._mergeConfigObj(e),e=this._configAfterMerge(e),this._typeCheckConfig(e),e}_configAfterMerge(e){return e.container=!1===e.container?document.body:l(e.container),"number"==typeof e.delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),e}_getDelegateConfig(){const e={};for(const[t,n]of Object.entries(this._config))this.constructor.Default[t]!==n&&(e[t]=n);return e.selector=!1,e.trigger="manual",e}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(e){return this.each(function(){const t=$t.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}}b($t);const Wt={...$t.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},qt={...$t.DefaultType,content:"(null|string|element|function)"};class Yt extends $t{static get Default(){return Wt}static get DefaultType(){return qt}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(e){return this.each(function(){const t=Yt.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e]()}})}}b(Yt);const Ut=".bs.scrollspy",Gt="click"+Ut,Xt=`load${Ut}.data-api`,Vt="active",Kt="[href]",Qt=".nav-link",Zt=`${Qt}, .nav-item > ${Qt}, .list-group-item`,Jt={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},en={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class tn extends ${constructor(e,t){super(e,t),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return Jt}static get DefaultType(){return en}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const e of this._observableSections.values())this._observer.observe(e)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(e){return e.target=l(e.target)||document.body,e.rootMargin=e.offset?e.offset+"px 0px -30%":e.rootMargin,"string"==typeof e.threshold&&(e.threshold=e.threshold.split(",").map(e=>Number.parseFloat(e))),e}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,Gt),N.on(this._config.target,Gt,Kt,e=>{const t=this._observableSections.get(e.target.hash);if(t){e.preventDefault();const n=this._rootElement||window,i=t.offsetTop-this._element.offsetTop;if(n.scrollTo)return void n.scrollTo({top:i,behavior:"smooth"});n.scrollTop=i}}))}_getNewObserver(){const e={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(e=>this._observerCallback(e),e)}_observerCallback(e){const t=e=>this._targetLinks.get("#"+e.target.id),n=e=>{this._previousScrollData.visibleEntryTop=e.target.offsetTop,this._process(t(e))},i=(this._rootElement||document.documentElement).scrollTop,o=i>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=i;for(const r of e){if(!r.isIntersecting){this._activeTarget=null,this._clearActiveClass(t(r));continue}const e=r.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(o&&e){if(n(r),!i)return}else o||e||n(r)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const e=q.find(Kt,this._config.target);for(const t of e){if(!t.hash||c(t))continue;const e=q.findOne(decodeURI(t.hash),this._element);u(e)&&(this._targetLinks.set(decodeURI(t.hash),t),this._observableSections.set(t.hash,e))}}_process(e){this._activeTarget!==e&&(this._clearActiveClass(this._config.target),this._activeTarget=e,e.classList.add(Vt),this._activateParents(e),N.trigger(this._element,"activate.bs.scrollspy",{relatedTarget:e}))}_activateParents(e){if(e.classList.contains("dropdown-item"))q.findOne(".dropdown-toggle",e.closest(".dropdown")).classList.add(Vt);else for(const t of q.parents(e,".nav, .list-group"))for(const e of q.prev(t,Zt))e.classList.add(Vt)}_clearActiveClass(e){e.classList.remove(Vt);const t=q.find(`${Kt}.${Vt}`,e);for(const e of t)e.classList.remove(Vt)}static jQueryInterface(e){return this.each(function(){const t=tn.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}})}}N.on(window,Xt,()=>{for(const e of q.find('[data-bs-spy="scroll"]'))tn.getOrCreateInstance(e)}),b(tn);const nn="ArrowLeft",on="ArrowRight",rn="ArrowUp",sn="ArrowDown",an="Home",ln="End",un="active",cn="show",dn=".dropdown-toggle",fn=`:not(${dn})`,hn='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',pn=`.nav-link${fn}, .list-group-item${fn}, [role="tab"]${fn}, ${hn}`,gn=`.${un}[data-bs-toggle="tab"], .${un}[data-bs-toggle="pill"], .${un}[data-bs-toggle="list"]`;class mn extends ${constructor(e){super(e),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,"keydown.bs.tab",e=>this._keydown(e)))}static get NAME(){return"tab"}show(){const e=this._element;if(this._elemIsActive(e))return;const t=this._getActiveElem(),n=t?N.trigger(t,"hide.bs.tab",{relatedTarget:e}):null;N.trigger(e,"show.bs.tab",{relatedTarget:t}).defaultPrevented||n&&n.defaultPrevented||(this._deactivate(t,e),this._activate(e,t))}_activate(e,t){e&&(e.classList.add(un),this._activate(q.getElementFromSelector(e)),this._queueCallback(()=>{"tab"===e.getAttribute("role")?(e.removeAttribute("tabindex"),e.setAttribute("aria-selected",!0),this._toggleDropDown(e,!0),N.trigger(e,"shown.bs.tab",{relatedTarget:t})):e.classList.add(cn)},e,e.classList.contains("fade")))}_deactivate(e,t){e&&(e.classList.remove(un),e.blur(),this._deactivate(q.getElementFromSelector(e)),this._queueCallback(()=>{"tab"===e.getAttribute("role")?(e.setAttribute("aria-selected",!1),e.setAttribute("tabindex","-1"),this._toggleDropDown(e,!1),N.trigger(e,"hidden.bs.tab",{relatedTarget:t})):e.classList.remove(cn)},e,e.classList.contains("fade")))}_keydown(e){if(![nn,on,rn,sn,an,ln].includes(e.key))return;e.stopPropagation(),e.preventDefault();const t=this._getChildren().filter(e=>!c(e));let n;if([an,ln].includes(e.key))n=t[e.key===an?0:t.length-1];else{const i=[on,sn].includes(e.key);n=y(t,e.target,i,!0)}n&&(n.focus({preventScroll:!0}),mn.getOrCreateInstance(n).show())}_getChildren(){return q.find(pn,this._parent)}_getActiveElem(){return this._getChildren().find(e=>this._elemIsActive(e))||null}_setInitialAttributes(e,t){this._setAttributeIfNotExists(e,"role","tablist");for(const e of t)this._setInitialAttributesOnChild(e)}_setInitialAttributesOnChild(e){e=this._getInnerElement(e);const t=this._elemIsActive(e),n=this._getOuterElement(e);e.setAttribute("aria-selected",t),n!==e&&this._setAttributeIfNotExists(n,"role","presentation"),t||e.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(e,"role","tab"),this._setInitialAttributesOnTargetPanel(e)}_setInitialAttributesOnTargetPanel(e){const t=q.getElementFromSelector(e);t&&(this._setAttributeIfNotExists(t,"role","tabpanel"),e.id&&this._setAttributeIfNotExists(t,"aria-labelledby",""+e.id))}_toggleDropDown(e,t){const n=this._getOuterElement(e);if(!n.classList.contains("dropdown"))return;const i=(e,i)=>{const o=q.findOne(e,n);o&&o.classList.toggle(i,t)};i(dn,un),i(".dropdown-menu",cn),n.setAttribute("aria-expanded",t)}_setAttributeIfNotExists(e,t,n){e.hasAttribute(t)||e.setAttribute(t,n)}_elemIsActive(e){return e.classList.contains(un)}_getInnerElement(e){return e.matches(pn)?e:q.findOne(pn,e)}_getOuterElement(e){return e.closest(".nav-item, .list-group-item")||e}static jQueryInterface(e){return this.each(function(){const t=mn.getOrCreateInstance(this);if("string"==typeof e){if(void 0===t[e]||e.startsWith("_")||"constructor"===e)throw new TypeError(`No method named "${e}"`);t[e]()}})}}N.on(document,"click.bs.tab",hn,function(e){["A","AREA"].includes(this.tagName)&&e.preventDefault(),c(this)||mn.getOrCreateInstance(this).show()}),N.on(window,"load.bs.tab",()=>{for(const e of q.find(gn))mn.getOrCreateInstance(e)}),b(mn);const bn="show",vn="showing",xn={animation:"boolean",autohide:"boolean",delay:"number"},yn={animation:!0,autohide:!0,delay:5e3};class _n extends ${constructor(e,t){super(e,t),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return yn}static get DefaultType(){return xn}static get NAME(){return"toast"}show(){N.trigger(this._element,"show.bs.toast").defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove("hide"),h(this._element),this._element.classList.add(bn,vn),this._queueCallback(()=>{this._element.classList.remove(vn),N.trigger(this._element,"shown.bs.toast"),this._maybeScheduleHide()},this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,"hide.bs.toast").defaultPrevented||(this._element.classList.add(vn),this._queueCallback(()=>{this._element.classList.add("hide"),this._element.classList.remove(vn,bn),N.trigger(this._element,"hidden.bs.toast")},this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(bn),super.dispose()}isShown(){return this._element.classList.contains(bn)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout(()=>{this.hide()},this._config.delay)))}_onInteraction(e,t){switch(e.type){case"mouseover":case"mouseout":this._hasMouseInteraction=t;break;case"focusin":case"focusout":this._hasKeyboardInteraction=t}if(t)return void this._clearTimeout();const n=e.relatedTarget;this._element===n||this._element.contains(n)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,"mouseover.bs.toast",e=>this._onInteraction(e,!0)),N.on(this._element,"mouseout.bs.toast",e=>this._onInteraction(e,!1)),N.on(this._element,"focusin.bs.toast",e=>this._onInteraction(e,!0)),N.on(this._element,"focusout.bs.toast",e=>this._onInteraction(e,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(e){return this.each(function(){const t=_n.getOrCreateInstance(this,e);if("string"==typeof e){if(void 0===t[e])throw new TypeError(`No method named "${e}"`);t[e](this)}})}}return Y(_n),b(_n),{Alert:U,Button:X,Carousel:ge,Collapse:we,Dropdown:We,Modal:ht,Offcanvas:Ct,Popover:Yt,ScrollSpy:tn,Tab:mn,Toast:_n,Tooltip:$t}}),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){function t(t,i,o){i={content:{message:"object"==typeof i?i.message:i,title:i.title?i.title:"",icon:i.icon?i.icon:"",url:i.url?i.url:"#",target:i.target?i.target:"-"}},o=e.extend(!0,{},i,o),this.settings=e.extend(!0,{},n,o),this._defaults=n,"-"==this.settings.content.target&&(this.settings.content.target=this.settings.url_target),this.animations={start:"webkitAnimationStart oanimationstart MSAnimationStart animationstart",end:"webkitAnimationEnd oanimationend MSAnimationEnd animationend"},"number"==typeof this.settings.offset&&(this.settings.offset={x:this.settings.offset,y:this.settings.offset}),this.init()}var n={element:"body",position:null,type:"info",allow_dismiss:!0,newest_on_top:!1,showProgressbar:!1,placement:{from:"top",align:"right"},offset:20,spacing:10,z_index:1031,delay:5e3,timer:1e3,url_target:"_blank",mouse_over:null,animate:{enter:"animated fadeInDown",exit:"animated fadeOutUp"},onShow:null,onShown:null,onClose:null,onClosed:null,icon_type:"class",template:''};String.format=function(){for(var e=arguments[0],t=1;t .progress-bar').removeClass("progress-bar-"+e.settings.type),e.settings.type=i[t],this.$ele.addClass("alert-"+i[t]).find('[data-notify="progressbar"] > .progress-bar').addClass("progress-bar-"+i[t]);break;case"icon":var o=this.$ele.find('[data-notify="icon"]');"class"==e.settings.icon_type.toLowerCase()?o.removeClass(e.settings.content.icon).addClass(i[t]):(o.is("img")||o.find("img"),o.attr("src",i[t]));break;case"progress":var r=e.settings.delay-e.settings.delay*(i[t]/100);this.$ele.data("notify-delay",r),this.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",i[t]).css("width",i[t]+"%");break;case"url":this.$ele.find('[data-notify="url"]').attr("href",i[t]);break;case"target":this.$ele.find('[data-notify="url"]').attr("target",i[t]);break;default:this.$ele.find('[data-notify="'+t+'"]').html(i[t])}var s=this.$ele.outerHeight()+parseInt(e.settings.spacing)+parseInt(e.settings.offset.y);e.reposition(s)},close:function(){e.close()}}},buildNotify:function(){var t=this.settings.content;this.$ele=e(String.format(this.settings.template,this.settings.type,t.title,t.message,t.url,t.target)),this.$ele.attr("data-notify-position",this.settings.placement.from+"-"+this.settings.placement.align),this.settings.allow_dismiss||this.$ele.find('[data-notify="dismiss"]').css("display","none"),(this.settings.delay<=0&&!this.settings.showProgressbar||!this.settings.showProgressbar)&&this.$ele.find('[data-notify="progressbar"]').remove()},setIcon:function(){"class"==this.settings.icon_type.toLowerCase()?this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon):this.$ele.find('[data-notify="icon"]').is("img")?this.$ele.find('[data-notify="icon"]').attr("src",this.settings.content.icon):this.$ele.find('[data-notify="icon"]').append('Notify Icon')},styleDismiss:function(){this.$ele.find('[data-notify="dismiss"]').css({position:"absolute",right:"10px",top:"5px",zIndex:this.settings.z_index+2})},styleURL:function(){this.$ele.find('[data-notify="url"]').css({backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)",height:"100%",left:"0px",position:"absolute",top:"0px",width:"100%",zIndex:this.settings.z_index+1})},placement:function(){var t=this,n=this.settings.offset.y,i={display:"inline-block",margin:"0px auto",position:this.settings.position?this.settings.position:"body"===this.settings.element?"fixed":"absolute",transition:"all .5s ease-in-out",zIndex:this.settings.z_index},o=!1,r=this.settings;switch(e('[data-notify-position="'+this.settings.placement.from+"-"+this.settings.placement.align+'"]:not([data-closing="true"])').each(function(){return n=Math.max(n,parseInt(e(this).css(r.placement.from))+parseInt(e(this).outerHeight())+parseInt(r.spacing))}),1==this.settings.newest_on_top&&(n=this.settings.offset.y),i[this.settings.placement.from]=n+"px",this.settings.placement.align){case"left":case"right":i[this.settings.placement.align]=this.settings.offset.x+"px";break;case"center":i.left=0,i.right=0}this.$ele.css(i).addClass(this.settings.animate.enter),e.each(Array("webkit","moz","o","ms",""),function(e,n){t.$ele[0].style[n+"AnimationIterationCount"]=1}),e(this.settings.element).append(this.$ele),1==this.settings.newest_on_top&&(n=parseInt(n)+parseInt(this.settings.spacing)+this.$ele.outerHeight(),this.reposition(n)),e.isFunction(t.settings.onShow)&&t.settings.onShow.call(this.$ele),this.$ele.one(this.animations.start,function(e){o=!0}).one(this.animations.end,function(n){e.isFunction(t.settings.onShown)&&t.settings.onShown.call(this)}),setTimeout(function(){o||e.isFunction(t.settings.onShown)&&t.settings.onShown.call(this)},600)},bind:function(){var t=this;if(this.$ele.find('[data-notify="dismiss"]').on("click",function(){t.close()}),this.$ele.mouseover(function(t){e(this).data("data-hover","true")}).mouseout(function(t){e(this).data("data-hover","false")}),this.$ele.data("data-hover","false"),this.settings.delay>0){t.$ele.data("notify-delay",t.settings.delay);var n=setInterval(function(){var e=parseInt(t.$ele.data("notify-delay"))-t.settings.timer;if("false"===t.$ele.data("data-hover")&&"pause"==t.settings.mouse_over||"pause"!=t.settings.mouse_over){var i=(t.settings.delay-e)/t.settings.delay*100;t.$ele.data("notify-delay",e),t.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",i).css("width",i+"%")}e<=-t.settings.timer&&(clearInterval(n),t.close())},t.settings.timer)}},close:function(){var t=this,n=parseInt(this.$ele.css(this.settings.placement.from)),i=!1;this.$ele.data("closing","true").addClass(this.settings.animate.exit),t.reposition(n),e.isFunction(t.settings.onClose)&&t.settings.onClose.call(this.$ele),this.$ele.one(this.animations.start,function(e){i=!0}).one(this.animations.end,function(n){e(this).remove(),e.isFunction(t.settings.onClosed)&&t.settings.onClosed.call(this)}),setTimeout(function(){i||(t.$ele.remove(),t.settings.onClosed&&t.settings.onClosed(t.$ele))},600)},reposition:function(t){var n=this,i='[data-notify-position="'+this.settings.placement.from+"-"+this.settings.placement.align+'"]:not([data-closing="true"])',o=this.$ele.nextAll(i);1==this.settings.newest_on_top&&(o=this.$ele.prevAll(i)),o.each(function(){e(this).css(n.settings.placement.from,t),t=parseInt(t)+parseInt(n.settings.spacing)+e(this).outerHeight()})}}),e.notify=function(e,n){return new t(this,e,n).notify},e.notifyDefaults=function(t){return n=e.extend(!0,{},n,t)},e.notifyClose=function(t){void 0===t||"all"==t?e("[data-notify]").find('[data-notify="dismiss"]').trigger("click"):e('[data-notify-position="'+t+'"]').find('[data-notify="dismiss"]').trigger("click")}}),function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=15)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function e(t,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.data=t,this.text=n.text||t,this.options=n}},function(e,t,n){"use strict";var i;function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}Object.defineProperty(t,"__esModule",{value:!0});var r=t.SET_A=0,s=t.SET_B=1,a=t.SET_C=2,l=(t.SHIFT=98,t.START_A=103),u=t.START_B=104,c=t.START_C=105;t.MODULO=103,t.STOP=106,t.FNC1=207,t.SET_BY_CODE=(o(i={},l,r),o(i,u,s),o(i,c,a),i),t.SWAP={101:r,100:s,99:a},t.A_START_CHAR=String.fromCharCode(208),t.B_START_CHAR=String.fromCharCode(209),t.C_START_CHAR=String.fromCharCode(210),t.A_CHARS="[\0-_È-Ï]",t.B_CHARS="[ -È-Ï]",t.C_CHARS="(Ï*[0-9]{2}Ï*)",t.BARS=[11011001100,11001101100,11001100110,10010011e3,10010001100,10001001100,10011001e3,10011000100,10001100100,11001001e3,11001000100,11000100100,10110011100,10011011100,10011001110,10111001100,10011101100,10011100110,11001110010,11001011100,11001001110,11011100100,11001110100,11101101110,11101001100,11100101100,11100100110,11101100100,11100110100,11100110010,11011011e3,11011000110,11000110110,10100011e3,10001011e3,10001000110,10110001e3,10001101e3,10001100010,11010001e3,11000101e3,11000100010,10110111e3,10110001110,10001101110,10111011e3,10111000110,10001110110,11101110110,11010001110,11000101110,11011101e3,11011100010,11011101110,11101011e3,11101000110,11100010110,11101101e3,11101100010,11100011010,11101111010,11001000010,11110001010,1010011e4,10100001100,1001011e4,10010000110,10000101100,10000100110,1011001e4,10110000100,1001101e4,10011000010,10000110100,10000110010,11000010010,1100101e4,11110111010,11000010100,10001111010,10100111100,10010111100,10010011110,10111100100,10011110100,10011110010,11110100100,11110010100,11110010010,11011011110,11011110110,11110110110,10101111e3,10100011110,10001011110,10111101e3,10111100010,11110101e3,11110100010,10111011110,10111101110,11101011110,11110101110,11010000100,1101001e4,11010011100,1100011101011]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SIDE_BIN="101",t.MIDDLE_BIN="01010",t.BINARIES={L:["0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011"],G:["0100111","0110011","0011011","0100001","0011101","0111001","0000101","0010001","0001001","0010111"],R:["1110010","1100110","1101100","1000010","1011100","1001110","1010000","1000100","1001000","1110100"],O:["0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011"],E:["0100111","0110011","0011011","0100001","0011101","0111001","0000101","0010001","0001001","0010111"]},t.EAN2_STRUCTURE=["LL","LG","GL","GG"],t.EAN5_STRUCTURE=["GGLLL","GLGLL","GLLGL","GLLLG","LGGLL","LLGGL","LLLGG","LGLGL","LGLLG","LLGLG"],t.EAN13_STRUCTURE=["LLLLLL","LLGLGG","LLGGLG","LLGGGL","LGLLGG","LGGLLG","LGGGLL","LGLGLG","LGLGGL","LGGLGL"]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(2);t.default=function(e,t,n){var o=e.split("").map(function(e,n){return i.BINARIES[t[n]]}).map(function(t,n){return t?t[e[n]]:""});if(n){var r=e.length-1;o=o.map(function(e,t){return t=200){r=e.shift()-105;var a=s.SWAP[r];void 0!==a?o=t.next(e,n+1,a):(i!==s.SET_A&&i!==s.SET_B||r!==s.SHIFT||(e[0]=i===s.SET_A?e[0]>95?e[0]-96:e[0]:e[0]<32?e[0]+96:e[0]),o=t.next(e,n+1,i))}else r=t.correctIndex(e,i),o=t.next(e,n+1,i);var l=r*n;return{result:t.getBar(r)+o.result,checksum:l+o.checksum}}}]),t}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.mod10=function(e){for(var t=0,n=0;n10*n.width?10*n.width:n.fontSize,i.guardHeight=n.height+i.fontSize/2+n.textMargin,i}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,s(n(0)).default),i(t,[{key:"encode",value:function(){return this.options.flat?this.encodeFlat():this.encodeGuarded()}},{key:"leftText",value:function(e,t){return this.text.substr(e,t)}},{key:"leftEncode",value:function(e,t){return(0,r.default)(e,t)}},{key:"rightText",value:function(e,t){return this.text.substr(e,t)}},{key:"rightEncode",value:function(e,t){return(0,r.default)(e,t)}},{key:"encodeGuarded",value:function(){var e={fontSize:this.fontSize},t={height:this.guardHeight};return[{data:o.SIDE_BIN,options:t},{data:this.leftEncode(),text:this.leftText(),options:e},{data:o.MIDDLE_BIN,options:t},{data:this.rightEncode(),text:this.rightText(),options:e},{data:o.SIDE_BIN,options:t}]}},{key:"encodeFlat",value:function(){return{data:[o.SIDE_BIN,this.leftEncode(),o.MIDDLE_BIN,this.rightEncode(),o.SIDE_BIN].join(""),text:this.text}}}]),t}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n10*n.width?i.fontSize=10*n.width:i.fontSize=n.fontSize,i.guardHeight=n.height+i.fontSize/2+n.textMargin,i}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,r(n(0)).default),i(t,[{key:"valid",value:function(){return-1!==this.data.search(/^[0-9]{12}$/)&&this.data[11]==a(this.data)}},{key:"encode",value:function(){return this.options.flat?this.flatEncoding():this.guardedEncoding()}},{key:"flatEncoding",value:function(){var e="";return e+="101",e+=(0,o.default)(this.data.substr(0,6),"LLLLLL"),e+="01010",e+=(0,o.default)(this.data.substr(6,6),"RRRRRR"),{data:e+="101",text:this.text}}},{key:"guardedEncoding",value:function(){var e=[];return this.displayValue&&e.push({data:"00000000",text:this.text.substr(0,1),options:{textAlign:"left",fontSize:this.fontSize}}),e.push({data:"101"+(0,o.default)(this.data[0],"L"),options:{height:this.guardHeight}}),e.push({data:(0,o.default)(this.data.substr(1,5),"LLLLL"),text:this.text.substr(1,5),options:{fontSize:this.fontSize}}),e.push({data:"01010",options:{height:this.guardHeight}}),e.push({data:(0,o.default)(this.data.substr(6,5),"RRRRR"),text:this.text.substr(6,5),options:{fontSize:this.fontSize}}),e.push({data:(0,o.default)(this.data[11],"R")+"101",options:{height:this.guardHeight}}),this.displayValue&&e.push({data:"00000000",text:this.text.substr(11,1),options:{textAlign:"right",fontSize:this.fontSize}}),e}}]),t}();function a(e){var t,n=0;for(t=1;t<11;t+=2)n+=parseInt(e[t]);for(t=0;t<11;t+=2)n+=3*parseInt(e[t]);return(10-n%10)%10}t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,o=function(){function e(e,t){for(var n=0;n0?t.fontSize+t.textMargin:0)+t.marginTop+t.marginBottom}function s(e,t,n){if(n.displayValue&&tt&&(t=e[n].height);return t},t.getEncodingHeight=r,t.getBarcodePadding=s,t.calculateEncodingAttributes=function(e,t,n){for(var i=0;i=r(e);return t+String.fromCharCode(i?206:205)+a(e,i)}t.default=function(e){var t=void 0;if(s(e).length>=2)t=i.C_START_CHAR+l(e);else{var n=o(e)>r(e);t=(n?i.A_START_CHAR:i.B_START_CHAR)+a(e,n)}return t.replace(/[\xCD\xCE]([^])[\xCD\xCE]/,function(e,t){return String.fromCharCode(203)+t})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,o=function(){function e(e,t){for(var n=0;n10*n.width?i.fontSize=10*n.width:i.fontSize=n.fontSize,i.guardHeight=n.height+i.fontSize/2+n.textMargin,i}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,r.default),i(t,[{key:"valid",value:function(){return this.isValid}},{key:"encode",value:function(){return this.options.flat?this.flatEncoding():this.guardedEncoding()}},{key:"flatEncoding",value:function(){var e="";return e+="101",e+=this.encodeMiddleDigits(),{data:e+="010101",text:this.text}}},{key:"guardedEncoding",value:function(){var e=[];return this.displayValue&&e.push({data:"00000000",text:this.text[0],options:{textAlign:"left",fontSize:this.fontSize}}),e.push({data:"101",options:{height:this.guardHeight}}),e.push({data:this.encodeMiddleDigits(),text:this.text.substring(1,7),options:{fontSize:this.fontSize}}),e.push({data:"010101",options:{height:this.guardHeight}}),this.displayValue&&e.push({data:"00000000",text:this.text[7],options:{textAlign:"right",fontSize:this.fontSize}}),e}},{key:"encodeMiddleDigits",value:function(){var e=this.upcA[0],t=this.upcA[this.upcA.length-1],n=c[parseInt(t)][parseInt(e)];return(0,o.default)(this.middleDigits,n)}}]),t}();function f(e,t){for(var n=parseInt(e[e.length-1]),i=u[n],o="",r=0,a=0;a=3&&this.number<=131070}}]),t}();t.pharmacode=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.codabar=void 0;var i,o=function(){function e(e,t){for(var n=0;n0?(n=0,o.textAlign="left"):"right"==e.textAlign?(n=t.width-1,o.textAlign="right"):(n=t.width/2,o.textAlign="center"),o.fillText(t.text,n,i))}},{key:"moveCanvasDrawing",value:function(e){this.canvas.getContext("2d").translate(e.width,0)}},{key:"restoreCanvas",value:function(){this.canvas.getContext("2d").restore()}}]),e}();t.default=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,o=function(){function e(e,t){for(var n=0;n0&&(this.drawRect(s-t.width*r,i,t.width*r,t.height,e),r=0);r>0&&this.drawRect(s-t.width*(r-1),i,t.width*r,t.height,e)}},{key:"drawSVGText",value:function(e,t,n){var i,o,r=this.document.createElementNS(a,"text");t.displayValue&&(r.setAttribute("style","font:"+t.fontOptions+" "+t.fontSize+"px "+t.font),o="top"==t.textPosition?t.fontSize-t.textMargin:t.height+t.textMargin+t.fontSize,"left"==t.textAlign||n.barcodePadding>0?(i=0,r.setAttribute("text-anchor","start")):"right"==t.textAlign?(i=n.width-1,r.setAttribute("text-anchor","end")):(i=n.width/2,r.setAttribute("text-anchor","middle")),r.setAttribute("x",i),r.setAttribute("y",o),r.appendChild(this.document.createTextNode(n.text)),e.appendChild(r))}},{key:"setSvgAttributes",value:function(e,t){var n=this.svg;n.setAttribute("width",e+"px"),n.setAttribute("height",t+"px"),n.setAttribute("x","0px"),n.setAttribute("y","0px"),n.setAttribute("viewBox","0 0 "+e+" "+t),n.setAttribute("xmlns",a),n.setAttribute("version","1.1"),n.setAttribute("style","transform: translate(0,0)")}},{key:"createGroup",value:function(e,t,n){var i=this.document.createElementNS(a,"g");return i.setAttribute("transform","translate("+e+", "+t+")"),n.appendChild(i),i}},{key:"setGroupOptions",value:function(e,t){e.setAttribute("style","fill:"+t.lineColor+";")}},{key:"drawRect",value:function(e,t,n,i,o){var r=this.document.createElementNS(a,"rect");return r.setAttribute("x",e),r.setAttribute("y",t),r.setAttribute("width",n),r.setAttribute("height",i),o.appendChild(r),r}}]),e}();t.default=l},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;ne.length)return;if(!(_ instanceof l)){if(g&&x!=t.length-1){if(f.lastIndex=y,!(k=f.exec(e)))break;for(var F=k.index+(p?k[1].length:0),w=k.index+k[0].length,C=x,E=y,A=t.length;C"+r.content+""},!_self.document)return _self.addEventListener&&(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),i=t.language,o=t.code,r=t.immediateClose;_self.postMessage(n.highlight(o,n.languages[i],i)),r&&_self.close()},!1)),_self.Prism;var o=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return o&&(n.filename=o.src,n.manual||o.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism),Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/(^|[^\\])["']/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^{}\s][^{};]*?(?=\s*\{)/,string:{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/\B!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.languages.css,Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css",greedy:!0}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)),Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/[a-z0-9_]+(?=\()/i,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/},Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,function:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,alias:"function"},constant:/\b[A-Z][A-Z\d_]*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${[^}]+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}}}),Prism.languages.javascript["template-string"].inside.interpolation.inside.rest=Prism.languages.javascript,Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript",greedy:!0}}),Prism.languages.js=Prism.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(){var e={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(document.querySelectorAll("pre[data-src]")).forEach(function(t){for(var n,i=t.getAttribute("data-src"),o=t,r=/\blang(?:uage)?-([\w-]+)\b/i;o&&!r.test(o.className);)o=o.parentNode;if(o&&(n=(t.className.match(r)||[,""])[1]),!n){var s=(i.match(/\.(\w+)$/)||[,""])[1];n=e[s]||s}var a=document.createElement("code");a.className="language-"+n,t.textContent="",a.textContent="Loading…",t.appendChild(a);var l=new XMLHttpRequest;l.open("GET",i,!0),l.onreadystatechange=function(){4==l.readyState&&(l.status<400&&l.responseText?(a.textContent=l.responseText,Prism.highlightElement(a)):400<=l.status?a.textContent="✖ Error "+l.status+" while fetching file: "+l.statusText:a.textContent="✖ Error: File does not exist or is empty")},l.send(null)}),Prism.plugins.toolbar&&Prism.plugins.toolbar.registerButton("download-file",function(e){var t=e.element.parentNode;if(t&&/pre/i.test(t.nodeName)&&t.hasAttribute("data-src")&&t.hasAttribute("data-download-link")){var n=t.getAttribute("data-src"),i=document.createElement("a");return i.textContent=t.getAttribute("data-download-link-label")||"Download",i.setAttribute("download",""),i.href=n,i}})},document.addEventListener("DOMContentLoaded",self.Prism.fileHighlight)),function(e){"use strict";function t(e,t){e.className+=" "+t}function n(e,t){for(var n=e.className.split(" "),i=t.split(" "),o=0;o-1&&n.splice(r,1)}e.className=n.join(" ")}function i(){return"rtl"===e.getComputedStyle(document.body).direction}function o(){return document.documentElement&&document.documentElement.scrollTop||document.body.scrollTop}function r(){return document.documentElement&&document.documentElement.scrollLeft||document.body.scrollLeft}function s(e){for(;e.lastChild;)e.removeChild(e.lastChild)}function a(e){if(null===e)return e;var t;if(Array.isArray(e)){t=[];for(var n=0;n0){for(var n=[],i=0;i=0?(n(document.body,Fe.noOverflow),k(!1)):e>0&&document.body.className.indexOf(Fe.noOverflow)<0&&(k(!0),t(document.body,Fe.noOverflow))}function k(i){T.defaults.preventBodyShift&&(i&&document.documentElement.scrollHeight>document.documentElement.clientHeight?(Ce=be,we=e.getComputedStyle(document.body).top,t(document.body,Fe.fixed),document.body.style.top=-be+"px"):i||(be=Ce,document.body.style.top=we,n(document.body,Fe.fixed),F()))}function O(e,t){for(var n=y.indexOf(t)+1;n200&&(Ae=e.timeStamp)&&!Ee){var n=e.srcElement||e.target;!0===t.get("closableByDimmer")&&n===t.elements.modal&&j(t)}Ee=!1}function $(e,t){if(Date.now()-ke>200&&(ke=Date.now()))for(var n=0;n-1?($(t,function(e){return e.key===n}),!1):void 0}Te=!1}function Y(e){var t=y[y.length-1],n=e.keyCode;if(n===m||n===b){for(var i=t.__internal.buttons,o=0;op-1&&ve.indexOf(n)>-1)return e.preventDefault(),e.stopPropagation(),$(t,function(e){return e.key===n}),!1}function U(e,t){if(t)t.focus();else{var n=e.__internal.focus,i=n.element;switch(typeof n.element){case"number":e.__internal.buttons.length>n.element&&(i=!0===e.get("basic")?e.elements.reset[0]:e.__internal.buttons[n.element].element);break;case"string":i=e.elements.body.querySelector(n.element);break;case"function":i=n.element.call(e)}!0!==e.get("defaultFocusOff")&&(null!=i||0!==e.__internal.buttons.length)||(i=e.elements.reset[0]),i&&i.focus&&(i.focus(),n.select&&i.select&&i.select())}}function G(e,t){if(!t)for(var n=y.length-1;n>-1;n-=1)if(y[n].isModal()){t=y[n];break}if(t&&t.isModal()){var i,o=t.elements.reset[0],r=t.elements.reset[1],s=e.relatedTarget,a=t.elements.root.contains(s),l=e.srcElement||e.target;if(l===o&&!a||l===r&&s===o)return;l===r||l===document.body?i=o:l===o&&s===r?i=X(t):l===o&&a&&(i=X(t,!0)),U(t,i)}}function X(e,t){var n=[].slice.call(e.elements.dialog.querySelectorAll(x.tabbable));t&&n.reverse();for(var i=0;iRe?t.style.left=Ie+c+"px":t.offsetWidth>=He&&(t.style.left=Ie-c+"px")}}(t,Be.elements.dialog,!Be.get("modal")&&!Be.get("pinned")))}function se(){if(Be){var e=Be;Be=null,n(document.body,Fe.noSelection),n(e.elements.dialog,Fe.capture),Ee=!0,d("onresized",e)}}function ae(e){Be=null;var t=e.elements.dialog;"none"===t.style.maxWidth&&(t.style.maxWidth=t.style.minWidth=t.style.width=t.style.height=t.style.minHeight=t.style.left="",Ie=Number.Nan,Re=He=ze=0)}function le(){for(var e=0;e-1&&e.navigator.userAgent.indexOf("Chrome")<0,_e={dimmer:'
',modal:'
',dialog:'
',reset:'',commands:'
',header:'
',body:'
',content:'
',footer:'',buttons:{primary:'
',auxiliary:'
'},button:'',resizeHandle:'
'},Fe={animationIn:"ajs-in",animationOut:"ajs-out",base:"alertify",basic:"ajs-basic",capture:"ajs-capture",closable:"ajs-closable",fixed:"ajs-fixed",frameless:"ajs-frameless",hidden:"ajs-hidden",maximize:"ajs-maximize",maximized:"ajs-maximized",maximizable:"ajs-maximizable",modeless:"ajs-modeless",movable:"ajs-movable",noSelection:"ajs-no-selection",noOverflow:"ajs-no-overflow",noPadding:"ajs-no-padding",pin:"ajs-pin",pinnable:"ajs-pinnable",prefix:"ajs-",resizable:"ajs-resizable",restore:"ajs-restore",shake:"ajs-shake",unpinned:"ajs-unpinned",noTransition:"ajs-no-transition"},we="",Ce=0,Ee=!1,Ae=0,ke=0,Te=!1,Oe=null,Se=0,De=0,je="pageX",Pe="pageY",Me=null,Le=!1,Ne=null,Be=null,Ie=Number.Nan,Re=0,He=0,ze=0;return{__init:f,isOpen:function(){return this.__internal.isOpen},isModal:function(){return this.elements.root.className.indexOf(Fe.modeless)<0},isMaximized:function(){return this.elements.root.className.indexOf(Fe.maximized)>-1},isPinned:function(){return this.elements.root.className.indexOf(Fe.unpinned)<0},maximize:function(){return this.isMaximized()||N(this),this},restore:function(){return this.isMaximized()&&B(this),this},pin:function(){return this.isPinned()||M(this),this},unpin:function(){return this.isPinned()&&L(this),this},bringToFront:function(){return O(0,this),this},moveTo:function(e,t){if(!isNaN(e)&&!isNaN(t)){d("onmove",this);var n=this.elements.dialog,o=n,r=0,s=0;n.style.left&&(r-=parseInt(n.style.left,10)),n.style.top&&(s-=parseInt(n.style.top,10));do{r+=o.offsetLeft,s+=o.offsetTop}while(o=o.offsetParent);var a=e-r,l=t-s;i()&&(a*=-1),n.style.left=a+"px",n.style.top=l+"px",d("onmoved",this)}return this},resizeTo:function(e,t){var n=parseFloat(e),i=parseFloat(t),o=/(\d*\.\d+|\d+)%/;if(!isNaN(n)&&!isNaN(i)&&!0===this.get("resizable")){d("onresize",this),(""+e).match(o)&&(n=n/100*document.documentElement.clientWidth),(""+t).match(o)&&(i=i/100*document.documentElement.clientHeight);var r=this.elements.dialog;"none"!==r.style.maxWidth&&(r.style.minWidth=(He=r.offsetWidth)+"px"),r.style.maxWidth="none",r.style.minHeight=this.elements.header.offsetHeight+this.elements.footer.offsetHeight+"px",r.style.width=n+"px",r.style.height=i+"px",d("onresized",this)}return this},setting:function(e,t){var n=this,i=D(this,this.__internal.options,function(e,t,i){S(n,e,t,i)},e,t);if("get"===i.op)return i.found?i.value:void 0!==this.settings?D(this,this.settings,this.settingUpdated||function(){},e,t).value:void 0;if("set"===i.op){if(i.items.length>0)for(var o=this.settingUpdated||function(){},r=0;r0){var t=this;this.__internal.timer=setTimeout(function(){t.dismiss()},1e3*this.__internal.delay)}return this},setContent:function(n){if("string"==typeof n?(s(this.element),this.element.innerHTML=n):n instanceof e.HTMLElement&&this.element.firstChild!==n&&(s(this.element),this.element.appendChild(n)),this.__internal.closeButton){var i=document.createElement("span");t(i,f.close),i.setAttribute("data-close",!0),this.element.appendChild(i)}return this},dismissOthers:function(){return k.dismissAll(this),this}})}var c,d=[],f=x.notifier.classes,h=f.base;return{setting:function(e,t){if(i(this),void 0===t)return this.__internal[e];switch(e){case"position":this.__internal.position=t,a(this);break;case"delay":this.__internal.delay=t}return this},set:function(e,t){return this.setting(e,t),this},get:function(e){return this.setting(e)},create:function(e,t){i(this);var n=document.createElement("div");return n.className=f.message+("string"==typeof e&&""!==e?" "+f.prefix+e:""),l(n,t)},dismissAll:function(e){for(var t=d.slice(0),n=0;n>t?1:0;return n}function h(e,t,n,i,o,r,s){var a=p;a(e,t,n[0],i-2,o-2,r,s),a(e,t,n[1],i-2,o-1,r,s),a(e,t,n[2],i-1,o-2,r,s),a(e,t,n[3],i-1,o-1,r,s),a(e,t,n[4],i-1,o,r,s),a(e,t,n[5],i,o-2,r,s),a(e,t,n[6],i,o-1,r,s),a(e,t,n[7],i,o,r,s)}function p(e,t,n,i,o,r,s){i<0&&(i+=r,o+=4-(r+4)%8),o<0&&(o+=s,i+=4-(s+4)%8),1!==t[i][o]&&(e[i][o]=n,t[i][o]=1)}return function(g,m){var b,v=function(e){var t,n,i=[],o=0;for(t=0;t>=8;return i}function F(e,t,n){return String.fromCharCode(n)+String.fromCharCode(t)+String.fromCharCode(e)}function w(e){var t=parseInt("0x"+e.substr(1),16),n=255&t;return F((t>>=8)>>8,255&t,n)}function C(e){return e.match(/#[0-91-F]/gi)}function E(e){for(var t,n,i,o,r,s,a,l="",u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",c=0;c>2,r=(3&t)<<4|(n=e.charCodeAt(c++))>>4,s=(15&n)<<2|(i=e.charCodeAt(c++))>>6,a=63&i,isNaN(n)?s=a=64:isNaN(i)&&(a=64),l+=u.charAt(o)+u.charAt(r)+u.charAt(s)+u.charAt(a);return l}function A(e){var t,n=[];for(n[0]=[],t=0;t
',p='
';for(a=0;a'+i+""),k(e,o*d).html(f)}function S(e,t,n,i,o,r){var s,a,l,u,c,d,f,h,p=n.length,g=n[0].length,m=o*g,b=r*p;for(t.showHRI&&(l=y(t.fontSize),b+=y(t.marginHRI)+l),u='',u+='',c='',a=0;a',u+=''+i+"",u+=""),u+="",(h=document.createElement("img")).setAttribute("src","data:image/svg+xml;base64,"+E(u)),k(e,m).append(h)}function D(e,t,n,i,o,r,s,a){var l,u,c,d,f,h,p=e.get(0),g=n.length,m=n[0].length;if(p&&p.getContext){for((c=p.getContext("2d")).lineWidth=1,c.lineCap="butt",c.fillStyle=t.bgColor,c.fillRect(o,r,m*s,g*a),c.fillStyle=t.color,u=0;u?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",a=0,l=0,u=0;for(t=0;te||this.moduleCount<=e||0>t||this.moduleCount<=t)throw Error(e+","+t);return this.modules[e][t]},getModuleCount:function(){return this.moduleCount},make:function(){if(1>this.typeNumber){var e=1;for(e=1;40>e;e++){for(var t=s.getRSBlocks(e,this.errorCorrectLevel),n=new a,i=0,o=0;o=n;n++)if(!(-1>=e+n||this.moduleCount<=e+n))for(var i=-1;7>=i;i++)-1>=t+i||this.moduleCount<=t+i||(this.modules[e+n][t+i]=0<=n&&6>=n&&(0==i||6==i)||0<=i&&6>=i&&(0==n||6==n)||2<=n&&4>=n&&2<=i&&4>=i)},getBestMaskPattern:function(){for(var e=0,t=0,n=0;8>n;n++){this.makeImpl(!0,n);var i=l.getLostPoint(this);(0==n||e>i)&&(e=i,t=n)}return t},createMovieClip:function(e,t,n){for(e=e.createEmptyMovieClip(t,n),this.make(),t=0;t=r;r++)for(var s=-2;2>=s;s++)this.modules[i+r][o+s]=-2==r||2==r||-2==s||2==s||0==r&&0==s}},setupTypeNumber:function(e){for(var t=l.getBCHTypeNumber(this.typeNumber),n=0;18>n;n++){var i=!e&&1==(t>>n&1);this.modules[Math.floor(n/3)][n%3+this.moduleCount-8-3]=i}for(n=0;18>n;n++)i=!e&&1==(t>>n&1),this.modules[n%3+this.moduleCount-8-3][Math.floor(n/3)]=i},setupTypeInfo:function(e,t){for(var n=l.getBCHTypeInfo(this.errorCorrectLevel<<3|t),i=0;15>i;i++){var o=!e&&1==(n>>i&1);6>i?this.modules[i][8]=o:8>i?this.modules[i+1][8]=o:this.modules[this.moduleCount-15+i][8]=o}for(i=0;15>i;i++)o=!e&&1==(n>>i&1),8>i?this.modules[8][this.moduleCount-i-1]=o:9>i?this.modules[8][15-i-1+1]=o:this.modules[8][15-i-1]=o;this.modules[this.moduleCount-8][8]=!e},mapData:function(e,t){for(var n=-1,i=this.moduleCount-1,o=7,r=0,s=this.moduleCount-1;0a;a++)if(null==this.modules[i][s-a]){var u=!1;r>>o&1)),l.getMask(t,i,s-a)&&(u=!u),this.modules[i][s-a]=u,-1==--o&&(r++,o=7)}if(0>(i+=n)||this.moduleCount<=i){i-=n,n=-n;break}}}},o.PAD0=236,o.PAD1=17,o.createData=function(e,t,n){t=s.getRSBlocks(e,t);for(var i=new a,r=0;r8*e)throw Error("code length overflow. ("+i.getLengthInBits()+">"+8*e+")");for(i.getLengthInBits()+4<=8*e&&i.put(0,4);0!=i.getLengthInBits()%8;)i.putBit(!1);for(;!(i.getLengthInBits()>=8*e||(i.put(o.PAD0,8),i.getLengthInBits()>=8*e));)i.put(o.PAD1,8);return o.createBytes(i,t)},o.createBytes=function(e,t){for(var n=0,i=0,o=0,s=Array(t.length),a=Array(t.length),u=0;u>>=1;return t},getPatternPosition:function(e){return l.PATTERN_POSITION_TABLE[e-1]},getMask:function(e,t,n){switch(e){case 0:return 0==(t+n)%2;case 1:return 0==t%2;case 2:return 0==n%3;case 3:return 0==(t+n)%3;case 4:return 0==(Math.floor(t/2)+Math.floor(n/3))%2;case 5:return 0==t*n%2+t*n%3;case 6:return 0==(t*n%2+t*n%3)%2;case 7:return 0==(t*n%3+(t+n)%2)%2;default:throw Error("bad maskPattern:"+e)}},getErrorCorrectPolynomial:function(e){for(var t=new r([1],0),n=0;nt)switch(e){case 1:return 10;case 2:return 9;case n:case 8:return 8;default:throw Error("mode:"+e)}else if(27>t)switch(e){case 1:return 12;case 2:return 11;case n:return 16;case 8:return 10;default:throw Error("mode:"+e)}else{if(!(41>t))throw Error("type:"+t);switch(e){case 1:return 14;case 2:return 13;case n:return 16;case 8:return 12;default:throw Error("mode:"+e)}}},getLostPoint:function(e){for(var t=e.getModuleCount(),n=0,i=0;i=a;a++)if(!(0>i+a||t<=i+a))for(var l=-1;1>=l;l++)0>o+l||t<=o+l||0==a&&0==l||s==e.isDark(i+a,o+l)&&r++;5e)throw Error("glog("+e+")");return u.LOG_TABLE[e]},gexp:function(e){for(;0>e;)e+=255;for(;256<=e;)e-=255;return u.EXP_TABLE[e]},EXP_TABLE:Array(256),LOG_TABLE:Array(256)},c=0;8>c;c++)u.EXP_TABLE[c]=1<c;c++)u.EXP_TABLE[c]=u.EXP_TABLE[c-4]^u.EXP_TABLE[c-5]^u.EXP_TABLE[c-6]^u.EXP_TABLE[c-8];for(c=0;255>c;c++)u.LOG_TABLE[u.EXP_TABLE[c]]=c;return r.prototype={get:function(e){return this.num[e]},getLength:function(){return this.num.length},multiply:function(e){for(var t=Array(this.getLength()+e.getLength()-1),n=0;nthis.getLength()-e.getLength())return this;for(var t=u.glog(this.get(0))-u.glog(e.get(0)),n=Array(this.getLength()),i=0;i>>7-e%8&1)},put:function(e,t){for(var n=0;n>>t-n-1&1))},getLengthInBits:function(){return this.length},putBit:function(e){var t=Math.floor(this.length/8);this.buffer.length<=t&&this.buffer.push(0),e&&(this.buffer[t]|=128>>>this.length%8),this.length++}},"string"==typeof t&&(t={text:t}),t=e.extend({},{render:"canvas",width:256,height:256,typeNumber:-1,correctLevel:2,background:"#ffffff",foreground:"#000000"},t),this.each(function(){var n;if("canvas"==t.render){(n=new o(t.typeNumber,t.correctLevel)).addData(t.text),n.make();var i=document.createElement("canvas");i.width=t.width,i.height=t.height;for(var r=i.getContext("2d"),s=t.width/n.getModuleCount(),a=t.height/n.getModuleCount(),l=0;l").css("width",t.width+"px").css("height",t.height+"px").css("border","0px").css("border-collapse","collapse").css("background-color",t.background),r=t.width/n.getModuleCount(),s=t.height/n.getModuleCount(),a=0;a").css("height",s+"px").appendTo(i),u=0;u").css("width",r+"px").css("background-color",n.isDark(a,u)?t.foreground:t.background).appendTo(l);n=i,jQuery(n).appendTo(this)})}}(jQuery),function(e){function t(e,t){return function(n){return l(e.call(this,n),t)}}function n(e,t){return function(n){return this.lang().ordinal(e.call(this,n),t)}}function i(){}function o(e){s(this,e)}function r(e){var t=e.years||e.year||e.y||0,n=e.months||e.month||e.M||0,i=e.weeks||e.week||e.w||0,o=e.days||e.day||e.d||0,r=e.hours||e.hour||e.h||0,s=e.minutes||e.minute||e.m||0,a=e.seconds||e.second||e.s||0,l=e.milliseconds||e.millisecond||e.ms||0;this._input=e,this._milliseconds=+l+1e3*a+6e4*s+36e5*r,this._days=+o+7*i,this._months=+n+12*t,this._data={},this._bubble()}function s(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function a(e){return 0>e?Math.ceil(e):Math.floor(e)}function l(e,t){for(var n=e+"";n.lengthn;n++)~~e[n]!=~~t[n]&&r++;return r+o}function f(e){return e?Q[e]||e.toLowerCase().replace(/(.)s$/,"$1"):e}function h(e){if(!e)return T.fn._lang;if(!D[e]&&j)try{require("./lang/"+e)}catch(e){return T.fn._lang}return D[e]||T.fn._lang}function p(e){return e.match(/\[.*\]/)?e.replace(/^\[|\]$/g,""):e.replace(/\\/g,"")}function g(e,t){return t=m(t,e.lang()),Z[t]||(Z[t]=function(e){var t,n,i=e.match(L);for(t=0,n=i.length;n>t;t++)i[t]=te[i[t]]?te[i[t]]:p(i[t]);return function(o){var r="";for(t=0;n>t;t++)r+=i[t]instanceof Function?i[t].call(o,e):i[t];return r}}(t)),Z[t](e)}function m(e,t){function n(e){return t.longDateFormat(e)||e}for(var i=5;i--&&(N.lastIndex=0,N.test(e));)e=e.replace(N,n);return e}function b(e,t){switch(e){case"DDDD":return R;case"YYYY":return H;case"YYYYY":return z;case"S":case"SS":case"SSS":case"DDD":return I;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return $;case"a":case"A":return h(t._l)._meridiemParse;case"X":return Y;case"Z":case"ZZ":return W;case"T":return q;case"MM":case"DD":case"YY":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":return B;default:return new RegExp(e.replace("\\",""))}}function v(e){var t=((W.exec(e)||[])[0]+"").match(X)||["-",0,0],n=60*t[1]+~~t[2];return"+"===t[0]?-n:n}function x(e,t,n){var i,o=n._a;switch(e){case"M":case"MM":null!=t&&(o[1]=~~t-1);break;case"MMM":case"MMMM":null!=(i=h(n._l).monthsParse(t))?o[1]=i:n._isValid=!1;break;case"D":case"DD":null!=t&&(o[2]=~~t);break;case"DDD":case"DDDD":null!=t&&(o[1]=0,o[2]=~~t);break;case"YY":o[0]=~~t+(~~t>68?1900:2e3);break;case"YYYY":case"YYYYY":o[0]=~~t;break;case"a":case"A":n._isPm=h(n._l).isPM(t);break;case"H":case"HH":case"h":case"hh":o[3]=~~t;break;case"m":case"mm":o[4]=~~t;break;case"s":case"ss":o[5]=~~t;break;case"S":case"SS":case"SSS":o[6]=~~(1e3*("0."+t));break;case"X":n._d=new Date(1e3*parseFloat(t));break;case"Z":case"ZZ":n._useUTC=!0,n._tzm=v(t)}null==t&&(n._isValid=!1)}function y(e){var t,n,i,o=[];if(!e._d){for(i=function(e){var t=new Date;return e._useUTC?[t.getUTCFullYear(),t.getUTCMonth(),t.getUTCDate()]:[t.getFullYear(),t.getMonth(),t.getDate()]}(e),t=0;3>t&&null==e._a[t];++t)e._a[t]=o[t]=i[t];for(;7>t;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];o[3]+=~~((e._tzm||0)/60),o[4]+=~~((e._tzm||0)%60),n=new Date(0),e._useUTC?(n.setUTCFullYear(o[0],o[1],o[2]),n.setUTCHours(o[3],o[4],o[5],o[6])):(n.setFullYear(o[0],o[1],o[2]),n.setHours(o[3],o[4],o[5],o[6])),e._d=n}}function _(e){var t,n,i,o=h(e._l),r=""+e._i;for(i=m(e._f,o).match(L),e._a=[],t=0;tt;t++)if(G[t][1].exec(n)){e._f+=G[t][0];break}W.exec(n)&&(e._f+=" Z"),_(e)}else e._d=new Date(n)}(t):c(n)?(t._a=n.slice(0),y(t)):n instanceof Date?t._d=new Date(+n):"object"==typeof n?function(e){var t=e._i;e._d||(e._a=[t.years||t.year||t.y,t.months||t.month||t.M,t.days||t.day||t.d,t.hours||t.hour||t.h,t.minutes||t.minute||t.m,t.seconds||t.second||t.s,t.milliseconds||t.millisecond||t.ms],y(e))}(t):t._d=new Date(n)}function w(e,t,n){var i,o=n-t,r=n-e.day();return r>o&&(r-=7),o-7>r&&(r+=7),i=T(e).add("d",r),{week:Math.ceil(i.dayOfYear()/7),year:i.year()}}function C(e){var t=e._i,n=e._f;return null===t||""===t?null:("string"==typeof t&&(e._i=t=h().preparse(t)),T.isMoment(t)?(e=s({},t))._d=new Date(+t._d):n?c(n)?function(e){var t,n,i,r,a,l=99;for(r=0;ra&&(l=a,i=n);s(e,i)}(e):_(e):F(e),new o(e))}function E(e,t){T.fn[e]=T.fn[e+"s"]=function(e){var n=this._isUTC?"UTC":"";return null!=e?(this._d["set"+n+t](e),T.updateOffset(this),this):this._d["get"+n+t]()}}function A(e){T.duration.fn[e]=function(){return this._data[e]}}function k(e,t){T.duration.fn["as"+e]=function(){return+this/t}}for(var T,O,S=Math.round,D={},j="undefined"!=typeof module&&module.exports,P=/^\/?Date\((\-?\d+)/i,M=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)\:(\d+)\.?(\d{3})?/,L=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|.)/g,N=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,B=/\d\d?/,I=/\d{1,3}/,R=/\d{3}/,H=/\d{1,4}/,z=/[+\-]?\d{1,6}/,$=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,W=/Z|[\+\-]\d\d:?\d\d/i,q=/T/i,Y=/[\+\-]?\d+(\.\d{1,3})?/,U=/^\s*\d{4}-\d\d-\d\d((T| )(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,G=[["HH:mm:ss.S",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],X=/([\+\-]|\d\d)/gi,V="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),K={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},Q={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",w:"week",W:"isoweek",M:"month",y:"year"},Z={},J="DDD w W M D d".split(" "),ee="M D H h m s w W".split(" "),te={M:function(){return this.month()+1},MMM:function(e){return this.lang().monthsShort(this,e)},MMMM:function(e){return this.lang().months(this,e)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(e){return this.lang().weekdaysMin(this,e)},ddd:function(e){return this.lang().weekdaysShort(this,e)},dddd:function(e){return this.lang().weekdays(this,e)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return l(this.year()%100,2)},YYYY:function(){return l(this.year(),4)},YYYYY:function(){return l(this.year(),5)},gg:function(){return l(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return l(this.weekYear(),5)},GG:function(){return l(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return l(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return~~(this.milliseconds()/100)},SS:function(){return l(~~(this.milliseconds()/10),2)},SSS:function(){return l(this.milliseconds(),3)},Z:function(){var e=-this.zone(),t="+";return 0>e&&(e=-e,t="-"),t+l(~~(e/60),2)+":"+l(~~e%60,2)},ZZ:function(){var e=-this.zone(),t="+";return 0>e&&(e=-e,t="-"),t+l(~~(10*e/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}};J.length;)O=J.pop(),te[O+"o"]=n(te[O],O);for(;ee.length;)O=ee.pop(),te[O+O]=t(te[O],2);for(te.DDDD=t(te.DDD,3),s(i.prototype,{set:function(e){var t,n;for(n in e)"function"==typeof(t=e[n])?this[n]=t:this["_"+n]=t},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(e){return this._months[e.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(e){return this._monthsShort[e.month()]},monthsParse:function(e){var t,n,i;for(this._monthsParse||(this._monthsParse=[]),t=0;12>t;t++)if(this._monthsParse[t]||(n=T.utc([2e3,t]),i="^"+this.months(n,"")+"|^"+this.monthsShort(n,""),this._monthsParse[t]=new RegExp(i.replace(".",""),"i")),this._monthsParse[t].test(e))return t},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(e){return this._weekdays[e.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(e){return this._weekdaysShort[e.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(e){return this._weekdaysMin[e.day()]},weekdaysParse:function(e){var t,n,i;for(this._weekdaysParse||(this._weekdaysParse=[]),t=0;7>t;t++)if(this._weekdaysParse[t]||(n=T([2e3,1]).day(t),i="^"+this.weekdays(n,"")+"|^"+this.weekdaysShort(n,"")+"|^"+this.weekdaysMin(n,""),this._weekdaysParse[t]=new RegExp(i.replace(".",""),"i")),this._weekdaysParse[t].test(e))return t},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(e){var t=this._longDateFormat[e];return!t&&this._longDateFormat[e.toUpperCase()]&&(t=this._longDateFormat[e.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e]=t),t},isPM:function(e){return"p"===(e+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(e,t,n){return e>11?n?"pm":"PM":n?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(e,t){var n=this._calendar[e];return"function"==typeof n?n.apply(t):n},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(e,t,n,i){var o=this._relativeTime[n];return"function"==typeof o?o(e,t,n,i):o.replace(/%d/i,e)},pastFuture:function(e,t){var n=this._relativeTime[e>0?"future":"past"];return"function"==typeof n?n(t):n.replace(/%s/i,t)},ordinal:function(e){return this._ordinal.replace("%d",e)},_ordinal:"%d",preparse:function(e){return e},postformat:function(e){return e},week:function(e){return w(e,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6}}),(T=function(e,t,n){return C({_i:e,_f:t,_l:n,_isUTC:!1})}).utc=function(e,t,n){return C({_useUTC:!0,_isUTC:!0,_l:n,_i:e,_f:t}).utc()},T.unix=function(e){return T(1e3*e)},T.duration=function(e,t){var n,i,o=T.isDuration(e),s="number"==typeof e,a=o?e._input:s?{}:e,l=M.exec(e);return s?t?a[t]=e:a.milliseconds=e:l&&(n="-"===l[1]?-1:1,a={y:0,d:~~l[2]*n,h:~~l[3]*n,m:~~l[4]*n,s:~~l[5]*n,ms:~~l[6]*n}),i=new r(a),o&&e.hasOwnProperty("_lang")&&(i._lang=e._lang),i},T.version="2.2.1",T.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",T.updateOffset=function(){},T.lang=function(e,t){return e?(e=(e=e.toLowerCase()).replace("_","-"),t?function(e,t){t.abbr=e,D[e]||(D[e]=new i),D[e].set(t),D[e]}(e,t):null===t?(function(e){delete D[e]}(e),e="en"):D[e]||h(e),void(T.duration.fn._lang=T.fn._lang=h(e))):T.fn._lang._abbr},T.langData=function(e){return e&&e._lang&&e._lang._abbr&&(e=e._lang._abbr),h(e)},T.isMoment=function(e){return e instanceof o},T.isDuration=function(e){return e instanceof r},s(T.fn=o.prototype,{clone:function(){return T(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return g(T(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var e=this;return[e.year(),e.month(),e.date(),e.hours(),e.minutes(),e.seconds(),e.milliseconds()]},isValid:function(){return null==this._isValid&&(this._isValid=this._a?!d(this._a,(this._isUTC?T.utc(this._a):T(this._a)).toArray()):!isNaN(this._d.getTime())),!!this._isValid},invalidAt:function(){var e,t=this._a,n=(this._isUTC?T.utc(this._a):T(this._a)).toArray();for(e=6;e>=0&&t[e]===n[e];--e);return e},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(e){var t=g(this,e||T.defaultFormat);return this.lang().postformat(t)},add:function(e,t){return u(this,"string"==typeof e?T.duration(+t,e):T.duration(e,t),1),this},subtract:function(e,t){return u(this,"string"==typeof e?T.duration(+t,e):T.duration(e,t),-1),this},diff:function(e,t,n){var i,o,r=this._isUTC?T(e).zone(this._offset||0):T(e).local(),s=6e4*(this.zone()-r.zone());return"year"===(t=f(t))||"month"===t?(i=432e5*(this.daysInMonth()+r.daysInMonth()),o=12*(this.year()-r.year())+(this.month()-r.month()),o+=(this-T(this).startOf("month")-(r-T(r).startOf("month")))/i,o-=6e4*(this.zone()-T(this).startOf("month").zone()-(r.zone()-T(r).startOf("month").zone()))/i,"year"===t&&(o/=12)):(i=this-r,o="second"===t?i/1e3:"minute"===t?i/6e4:"hour"===t?i/36e5:"day"===t?(i-s)/864e5:"week"===t?(i-s)/6048e5:i),n?o:a(o)},from:function(e,t){return T.duration(this.diff(e)).lang(this.lang()._abbr).humanize(!t)},fromNow:function(e){return this.from(T(),e)},calendar:function(){var e=this.diff(T().zone(this.zone()).startOf("day"),"days",!0),t=-6>e?"sameElse":-1>e?"lastWeek":0>e?"lastDay":1>e?"sameDay":2>e?"nextDay":7>e?"nextWeek":"sameElse";return this.format(this.lang().calendar(t,this))},isLeapYear:function(){var e=this.year();return 0==e%4&&0!=e%100||0==e%400},isDST:function(){return this.zone()+T(e).startOf(t)},isBefore:function(e,t){return t=void 0!==t?t:"millisecond",+this.clone().startOf(t)<+T(e).startOf(t)},isSame:function(e,t){return t=void 0!==t?t:"millisecond",+this.clone().startOf(t)==+T(e).startOf(t)},min:function(e){return this>(e=T.apply(null,arguments))?this:e},max:function(e){return(e=T.apply(null,arguments))>this?this:e},zone:function(e){var t=this._offset||0;return null==e?this._isUTC?t:this._d.getTimezoneOffset():("string"==typeof e&&(e=v(e)),Math.abs(e)<16&&(e*=60),this._offset=e,this._isUTC=!0,t!==e&&u(this,T.duration(t-e,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},hasAlignedHourOffset:function(e){return e=e?T(e).zone():0,0==(this.zone()-e)%60},daysInMonth:function(){return T.utc([this.year(),this.month()+1,0]).date()},dayOfYear:function(e){var t=S((T(this).startOf("day")-T(this).startOf("year"))/864e5)+1;return null==e?t:this.add("d",e-t)},weekYear:function(e){var t=w(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==e?t:this.add("y",e-t)},isoWeekYear:function(e){var t=w(this,1,4).year;return null==e?t:this.add("y",e-t)},week:function(e){var t=this.lang().week(this);return null==e?t:this.add("d",7*(e-t))},isoWeek:function(e){var t=w(this,1,4).week;return null==e?t:this.add("d",7*(e-t))},weekday:function(e){var t=(this._d.getDay()+7-this.lang()._week.dow)%7;return null==e?t:this.add("d",e-t)},isoWeekday:function(e){return null==e?this.day()||7:this.day(this.day()%7?e:e-7)},get:function(e){return this[(e=f(e)).toLowerCase()]()},set:function(e,t){this[(e=f(e)).toLowerCase()](t)},lang:function(t){return t===e?this._lang:(this._lang=h(t),this)}}),O=0;Oi&&["s",i]||1===o&&["m"]||45>o&&["mm",o]||1===r&&["h"]||22>r&&["hh",r]||1===s&&["d"]||25>=s&&["dd",s]||45>=s&&["M"]||345>s&&["MM",S(s/30)]||1===a&&["y"]||["yy",a];return l[2]=t,l[3]=e>0,l[4]=n,function(e,t,n,i,o){return o.relativeTime(t||1,!!n,e,i)}.apply({},l)}(t,!e,this.lang());return e&&(n=this.lang().pastFuture(t,n)),this.lang().postformat(n)},add:function(e,t){var n=T.duration(e,t);return this._milliseconds+=n._milliseconds,this._days+=n._days,this._months+=n._months,this._bubble(),this},subtract:function(e,t){var n=T.duration(e,t);return this._milliseconds-=n._milliseconds,this._days-=n._days,this._months-=n._months,this._bubble(),this},get:function(e){return this[(e=f(e)).toLowerCase()+"s"]()},as:function(e){return this["as"+(e=f(e)).charAt(0).toUpperCase()+e.slice(1)+"s"]()},lang:T.fn.lang}),K)K.hasOwnProperty(O)&&(k(O,K[O]),A(O.toLowerCase()));k("Weeks",6048e5),T.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},T.lang("en",{ordinal:function(e){var t=e%10;return e+(1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")}}),j&&(module.exports=T),"undefined"==typeof ender&&(this.moment=T),"function"==typeof define&&define.amd&&define("moment",[],function(){return T})}.call(this),function(){"use strict";var e="undefined"!=typeof window&&void 0!==window.document?window.document:{},t="undefined"!=typeof module&&module.exports,n=function(){for(var t,n=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],i=0,o=n.length,r={};i",{class:n+"box "+n+"editor-visible "+n+e.o.lang+" trumbowyg"}),e.isTextarea=e.$ta.is("textarea"),e.isTextarea?(o=e.$ta.val(),e.$ed=i("
"),e.$box.insertAfter(e.$ta).append(e.$ed,e.$ta)):(e.$ed=e.$ta,o=e.$ed.html(),e.$ta=i("

\ No newline at end of file +
({{$ctrl.mixDatabaseDataValue.dataType}})

\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-data-values/view.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-data-values/view.html index d455c5487..48e079dd5 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-data-values/view.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-data-values/view.html @@ -1 +1 @@ -
Total items:
Next
\ No newline at end of file +
Total items:
Next
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-form/view.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-form/view.html index fb37497a5..902770a40 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-form/view.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-database-form/view.html @@ -1 +1 @@ -
List data Search links
{{$ctrl.translate(ref.destinateDatabaseName)}}
Total items:
\ No newline at end of file +
List data Search links
{{$ctrl.translate(ref.destinateDatabaseName)}}
Total items:
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-value-editor/view.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-value-editor/view.html index 74a077938..bdfc6d3cb 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-value-editor/view.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/components/mix-value-editor/view.html @@ -1 +1 @@ -

\ No newline at end of file +

\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/dashboard/dashboard.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/dashboard/dashboard.html index 6e66dda86..8ba45d322 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/dashboard/dashboard.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/dashboard/dashboard.html @@ -1,3 +1 @@ -
\ No newline at end of file + \ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/details.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/details.html index d4a5f17f8..e24f4588e 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/details.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/details.html @@ -1 +1 @@ -

Create {{mixDatabaseTitle}}'s data

Fill all of the following columns to create a new data for {{mixDatabaseTitle}} database.

\ No newline at end of file +

Create {{mixDatabaseTitle}}'s data

Fill all of the following columns to create a new data for {{mixDatabaseTitle}} database.

\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/list.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/list.html index 3ab9f192c..a775a09dd 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/list.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database-data/list.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database/list.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database/list.html index d3bb2675d..9cf3f3f15 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database/list.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mix-database/list.html @@ -1 +1 @@ -
DatabaseNameTypeAuthor
{{item.displayName}}{{item.systemName}}{{item.type}}{{item.createdBy}}🟢 🟡 🔴 ⚫️
\ No newline at end of file +
DatabaseNameTypeAuthor
{{item.displayName}}{{item.systemName}}{{item.type}}{{item.createdBy}}🟢 🟡 🔴 ⚫️
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/components/mix-databases/view.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/components/mix-databases/view.html index 0c4933d09..9662e14a4 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/components/mix-databases/view.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/components/mix-databases/view.html @@ -1 +1 @@ -
DatabaseNameTypeAuthor
{{item.displayName}}{{item.systemName}}{{item.type}}{{item.createdBy}}🟢 🟡 🔴 ⚫️
\ No newline at end of file +
DatabaseNameTypeAuthor
{{item.displayName}}{{item.systemName}}{{item.type}}{{item.createdBy}}🟢 🟡 🔴 ⚫️
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/list.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/list.html index eabdc37b7..478009a64 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/list.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/mixdb-context/list.html @@ -1 +1 @@ -
DatabaseNameTypeAuthor
{{item.displayName}}{{item.systemName}}{{item.type}}{{item.createdBy}}🟢 🟡 🔴 ⚫️
\ No newline at end of file +
DatabaseNameTypeAuthor
{{item.displayName}}{{item.systemName}}{{item.type}}{{item.createdBy}}🟢 🟡 🔴 ⚫️
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/post/details.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/post/details.html index af849645d..9aea9b740 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/post/details.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/post/details.html @@ -1 +1 @@ -
Please save the Post then continue with this section
\ No newline at end of file +
Please save the Post then continue with this section
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/scheduler/details.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/scheduler/details.html index 51e53e642..9d0ebc456 100644 --- a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/scheduler/details.html +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/scheduler/details.html @@ -1,4 +1,4 @@ -
+
 {{trigger|json}}
 {{schedule|json}}
 
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/workflow-trigger/details.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/workflow-trigger/details.html new file mode 100644 index 000000000..cc5d6817a --- /dev/null +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/workflow-trigger/details.html @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/workflow-trigger/list.html b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/workflow-trigger/list.html new file mode 100644 index 000000000..52a4f5d5b --- /dev/null +++ b/src/applications/mixcore/wwwroot/mix-app/views/app-portal/pages/workflow-trigger/list.html @@ -0,0 +1 @@ +
Workflow IdCreatedAuthor
{{item.createdDateTime | utcToLocal:'dd.MM.yy hh:mm a'}}{{item.createdBy}}🟢 🟡 🔴 ⚫️
\ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mixcontent/staticfiles/readme.md b/src/applications/mixcore/wwwroot/mixcontent/staticfiles/readme.md deleted file mode 100644 index d8217155b..000000000 --- a/src/applications/mixcore/wwwroot/mixcontent/staticfiles/readme.md +++ /dev/null @@ -1 +0,0 @@ -used for staticfiles \ No newline at end of file diff --git a/src/applications/mixcore/wwwroot/mixcontent/templates/readme.md b/src/applications/mixcore/wwwroot/mixcontent/templates/readme.md deleted file mode 100644 index 4ae0d97a7..000000000 --- a/src/applications/mixcore/wwwroot/mixcontent/templates/readme.md +++ /dev/null @@ -1 +0,0 @@ -Upload folder to store files from user;s upload files. diff --git a/src/modules/mix.common/Domain/Models/GlobalSettings.cs b/src/modules/mix.common/Domain/Models/GlobalSettings.cs index d73cc4625..743940591 100644 --- a/src/modules/mix.common/Domain/Models/GlobalSettings.cs +++ b/src/modules/mix.common/Domain/Models/GlobalSettings.cs @@ -1,4 +1,6 @@ -namespace Mix.Common.Models +using Mix.Shared.Models.Configurations; + +namespace Mix.Common.Models { public class GlobalSettings @@ -9,7 +11,7 @@ public class GlobalSettings public string LangIcon { get; set; } - public JObject PortalThemeSettings { get; set; } + public PortalConfigurationModel PortalThemeSettings { get; set; } public string ApiEncryptKey { get; set; } diff --git a/src/modules/mix.grpc/mix.grpc.csproj b/src/modules/mix.grpc/mix.grpc.csproj index 572bccd99..526c7f59c 100644 --- a/src/modules/mix.grpc/mix.grpc.csproj +++ b/src/modules/mix.grpc/mix.grpc.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/modules/mix.portal/mix.portal.csproj b/src/modules/mix.portal/mix.portal.csproj index 3b77e307a..b091ba3c3 100644 --- a/src/modules/mix.portal/mix.portal.csproj +++ b/src/modules/mix.portal/mix.portal.csproj @@ -63,7 +63,7 @@ - + diff --git a/src/modules/mix.scheduler/Domain/Jobs/WorkflowHanlderJob.cs b/src/modules/mix.scheduler/Domain/Jobs/WorkflowHanlderJob.cs new file mode 100644 index 000000000..5470a2fea --- /dev/null +++ b/src/modules/mix.scheduler/Domain/Jobs/WorkflowHanlderJob.cs @@ -0,0 +1,221 @@ +using Mix.Queue.Interfaces; +using Mix.SignalR.Models; +using Newtonsoft.Json.Linq; +using Quartz; +using System; +using System.Threading.Tasks; +using Mix.Quartz.Jobs; +using Mix.SignalR.Interfaces; +using Mix.Mq.Lib.Models; +using Mix.Shared.Models; +using Mix.Shared.Services; +using System.Collections.Generic; +using System.Linq; +using Mix.Mixdb.Services; +using Mix.Database.Services.MixGlobalSettings; +using Mix.Mixdb.Interfaces; +using Mix.Heart.Models; +using Microsoft.Identity.Client; +using Mix.Heart.Extensions; +using Newtonsoft.Json; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Routing.Matching; + +namespace Mix.Scheduler.Domain.Jobs +{ + public class WorkflowHanlderJob : MixJobBase + { + private readonly HttpService _httpService; + private readonly IPortalHubClientService _portalHub; + protected readonly IMixDbDataService _mixDbDataService; + public WorkflowHanlderJob( + IServiceProvider serviceProvider, + IMemoryQueueService queueService, + IPortalHubClientService portalHub, + HttpService httpService, + DatabaseService databaseService, + MixDbDataServiceFactory mixDbDataFactory) + : base(serviceProvider, queueService) + { + _portalHub = portalHub; + _httpService = httpService; + _mixDbDataService = mixDbDataFactory.GetDataService(databaseService.DatabaseProvider, databaseService.GetConnectionString(MixConstants.CONST_MIXDB_CONNECTION)); + } + + public override async Task ExceptionHandler(Exception ex) + { + await _portalHub.SendMessageAsync(new SignalRMessageModel() + { + Action = SignalR.Enums.MessageAction.NewMessage, + Type = SignalR.Enums.MessageType.Error, + From = new HubUserModel(nameof(WorkflowTriggerModel)), + Title = "Exception", + Message = ex.Message + }); + await base.ExceptionHandler(ex); + } + + public override async Task ExecuteHandler(IJobExecutionContext context) + { + var obj = context.Trigger.JobDataMap.GetString("data"); + if (!string.IsNullOrWhiteSpace(obj)) + { + var msg = JObject.Parse(obj).ToObject(); + var wf = await _mixDbDataService.GetByIdAsync("mix_workflow", msg.WorkflowId, + "title,actions", System.Threading.CancellationToken.None); + JArray responses = new JArray(); + if (wf != null) + { + foreach (var action in wf.Actions.Items.OrderBy(m => m.Index)) + { + if (action.Index == 0) + { + if (msg.Input != null) + { + action.Request = InjectParameters(action.Request, msg.Input); + action.Body = InjectParameters(action.Body, msg.Input); + } + } + else + { + action.Request = InjectParameters(action.Request, responses); + action.Body = InjectParameters(action.Body, responses); + } + if (action.Type == "Request") + { + var request = action.Request.ToObject(); + request.Body = action.Body; + var resp = await _httpService.SendHttpRequestModel(request); + responses.Add(resp); + } + if (action.Type == "ParseMarkdownJson") + { + var markdownContent = ExtractInput(action.InputPath, responses); + responses.Add(JObject.Parse(Regex.Match(markdownContent, "\\`{3}(\\w+)?\\n([^\\`]+)\\n\\`{3}").Groups[2].Value)); + } + if (action.Type == "PortalNotification") + { + var portalMsg = InjectParameters(action.Body, responses); + await _portalHub.SendMessageAsync(portalMsg.ToObject()); + } + } + } + } + } + + + // pattern: {responseIndex}.{propertyName} + private JObject InjectParameters(JObject body, JToken responses) + { + string pattern = "(\\[{2})((\\d|\\.|\\w)+)(\\]{2})"; + string strBody = body.ToString(Formatting.None); + if (!Regex.IsMatch(strBody, pattern)) + { + return body; + } + else + { + foreach (Match match in Regex.Matches(strBody, pattern)) + { + strBody = strBody.Replace(match.Value, @ExtractInput(match.Groups[2].Value, responses)); + } + return JObject.Parse(strBody); + } + } + + private T ExtractInput(string inputPath, JToken jObject) + { + if (string.IsNullOrEmpty(inputPath)) + { + return jObject.ToObject(); + } + + var keys = inputPath.Split('.'); + JToken? token = jObject; + foreach (var key in keys) + { + if (int.TryParse(key, out var index)) + { + if (token.Type == JTokenType.Array) + { + token = JArray.FromObject(token)[index]; + } + else + { + token = token[key]; + } + } + else + { + token = token[key]; + } + } + return token != null ? token.ToObject() : default; + } + + public class WorkflowTriggerModel + { + public int WorkflowId { get; set; } + public JObject? Input { get; set; } + } + public class WorkflowModel + { + public string Title { get; set; } + public string Description { get; set; } + public PagingResponseModel Actions { get; set; } + } + public class WorkflowActionModel + { + [JsonProperty("index")] + public int Index { get; set; } + [JsonProperty("title")] + public string Title { get; set; } + [JsonProperty("type")] + public string Type { get; set; } + [JsonProperty("request")] + public JObject? Request { get; set; } + [JsonProperty("input_path")] + public string? InputPath { get; set; } // from ancestor action + [JsonProperty("body")] + public JObject? Body { get; set; } // current action + } + public class ChatCompletion + { + public string Id { get; set; } + public string Object { get; set; } + public long Created { get; set; } + public string Model { get; set; } + public List Choices { get; set; } + public Usage Usage { get; set; } + } + + public class Choice + { + public int Index { get; set; } + public Message Message { get; set; } + public object Logprobs { get; set; } // Use object if you’re unsure of the exact type + public string FinishReason { get; set; } + } + + public class Message + { + public string Role { get; set; } + public string Content { get; set; } + } + + public class Usage + { + public int PromptTokens { get; set; } + public int CompletionTokens { get; set; } + public int TotalTokens { get; set; } + public PromptTokensDetails PromptTokensDetails { get; set; } + } + + public class PromptTokensDetails + { + public int CachedTokens { get; set; } + } + } + + +} \ No newline at end of file diff --git a/src/modules/mix.tenancy/Controllers/InitApiController.cs b/src/modules/mix.tenancy/Controllers/InitApiController.cs index 0fc614cbf..98e5456db 100644 --- a/src/modules/mix.tenancy/Controllers/InitApiController.cs +++ b/src/modules/mix.tenancy/Controllers/InitApiController.cs @@ -103,7 +103,7 @@ public async Task> InitTenant([FromBody] InitCmsDto model) await _quartzService.LoadScheduler(); await _uow.CompleteAsync(); await _mixTenantService.Reload(); - _mixEndpointService.SetDefaultDomain($"https://{model.PrimaryDomain}"); + _mixEndpointService.InitDomain($"https://{model.PrimaryDomain}"); _appSettingsService.SetConfig(nameof(AppSettingsModel.DatabaseProvider), model.DatabaseProvider.ToString()); _appSettingsService.SetConfig(nameof(AppSettingsModel.InitStatus), InitStep.InitAccount); _appSettingsService.SaveSettings(); diff --git a/src/platform/core/mix-heart b/src/platform/core/mix-heart index 7c8119714..b788d17a0 160000 --- a/src/platform/core/mix-heart +++ b/src/platform/core/mix-heart @@ -1 +1 @@ -Subproject commit 7c8119714e7826a781c7836b6da0d17b8ce028a1 +Subproject commit b788d17a047e0686832dcdde92b576403048c219 diff --git a/src/platform/core/mix.mixdb.event/Services/MixDbEventService.cs b/src/platform/core/mix.mixdb.event/Services/MixDbEventService.cs index f31040a6e..457e9c4cf 100644 --- a/src/platform/core/mix.mixdb.event/Services/MixDbEventService.cs +++ b/src/platform/core/mix.mixdb.event/Services/MixDbEventService.cs @@ -125,7 +125,7 @@ private JObject ParseBody(JObject body, JObject data) { if (strBody.Contains($"[[{prop.Name}]]", StringComparison.OrdinalIgnoreCase)) { - strBody = strBody.Replace($"[[{prop.Name.ToTitleCase()}]]", data.GetValue(prop.Name)!.ToString()); + strBody = strBody.Replace($"[[{prop.Name}]]", data.GetValue(prop.Name)!.ToString()); } } return JObject.Parse(strBody); diff --git a/src/platform/mix.communicator/mix.communicator.csproj b/src/platform/mix.communicator/mix.communicator.csproj index 8e6f499f0..10c64c63e 100644 --- a/src/platform/mix.communicator/mix.communicator.csproj +++ b/src/platform/mix.communicator/mix.communicator.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/src/platform/mix.constant/Constants/MixAppSettingsSection.cs b/src/platform/mix.constant/Constants/MixAppSettingsSection.cs index 67a3a8775..378997134 100644 --- a/src/platform/mix.constant/Constants/MixAppSettingsSection.cs +++ b/src/platform/mix.constant/Constants/MixAppSettingsSection.cs @@ -2,6 +2,7 @@ { public class MixAppSettingsSection { + public const string MessageQueueSettings = "MessageQueueSettings"; public const string Database = "Database"; public const string MixConfigurations = "MixConfigurations"; public const string Authentication = "Authentication"; diff --git a/src/platform/mix.constant/Constants/MixDatabaseNames.cs b/src/platform/mix.constant/Constants/MixDatabaseNames.cs index 77962226d..833d91c86 100644 --- a/src/platform/mix.constant/Constants/MixDatabaseNames.cs +++ b/src/platform/mix.constant/Constants/MixDatabaseNames.cs @@ -20,5 +20,7 @@ public class MixDatabaseNames public const string SYSTEM_DATA_RELATIONSHIP = "mix_data_relationship"; public const string DATA_RELATIONSHIP_TITLE_CASE = "data_relationship"; public const string DATA_RELATIONSHIP_SNAKE_CASE = "data_relationship"; + public const string AUDIT_LOG = "mix_audit_log"; + public const string QUEUE_LOG = "mix_queue_log"; } } \ No newline at end of file diff --git a/src/platform/mix.constant/mix.constant.csproj b/src/platform/mix.constant/mix.constant.csproj index ef555f797..0c5e8c435 100644 --- a/src/platform/mix.constant/mix.constant.csproj +++ b/src/platform/mix.constant/mix.constant.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/platform/mix.database/Entities/AuditLog/EntityConfigurations/AuditLogConfiguration.cs b/src/platform/mix.database/Entities/AuditLog/EntityConfigurations/AuditLogConfiguration.cs index 52be38698..bd9718d69 100644 --- a/src/platform/mix.database/Entities/AuditLog/EntityConfigurations/AuditLogConfiguration.cs +++ b/src/platform/mix.database/Entities/AuditLog/EntityConfigurations/AuditLogConfiguration.cs @@ -17,7 +17,7 @@ public AuditLogConfiguration(DatabaseService databaseService) : base(databaseSer public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); - + builder.ToTable(MixDatabaseNames.AUDIT_LOG); builder.Property(e => e.Success) .HasColumnName("success") .HasColumnType(Config.Boolean); diff --git a/src/platform/mix.database/Entities/QueueLog/EntityConfigurations/QueueLogConfiguration.cs b/src/platform/mix.database/Entities/QueueLog/EntityConfigurations/QueueLogConfiguration.cs index e4861988e..102dea5de 100644 --- a/src/platform/mix.database/Entities/QueueLog/EntityConfigurations/QueueLogConfiguration.cs +++ b/src/platform/mix.database/Entities/QueueLog/EntityConfigurations/QueueLogConfiguration.cs @@ -20,7 +20,7 @@ public QueueLogConfiguration(DatabaseService databaseService): base(databaseServ public override void Configure(EntityTypeBuilder builder) { base.Configure(builder); - + builder.ToTable(MixDatabaseNames.QUEUE_LOG); builder.Property(e => e.TenantId) .HasColumnName("tenant_id"); diff --git a/src/platform/mix.database/Migrations/AuditLog/MySql/20250304082555_Init.Designer.cs b/src/platform/mix.database/Migrations/AuditLog/MySql/20250304082555_Init.Designer.cs new file mode 100644 index 000000000..66a3bb348 --- /dev/null +++ b/src/platform/mix.database/Migrations/AuditLog/MySql/20250304082555_Init.Designer.cs @@ -0,0 +1,115 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.AuditLog; + +#nullable disable + +namespace Mix.Database.Migrations.AuditLog.MySql +{ + [DbContext(typeof(MySqlAuditLogDbContext))] + [Migration("20250304082555_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id") + .HasDefaultValueSql("(uuid())"); + + b.Property("Body") + .HasColumnType("longtext") + .HasColumnName("body"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime") + .HasColumnName("created_date_time"); + + b.Property("Endpoint") + .HasColumnType("varchar(2000)") + .HasColumnName("endpoint"); + + b.Property("Exception") + .HasColumnType("longtext") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("tinyint") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("datetime") + .HasColumnName("last_modified"); + + b.Property("Method") + .HasColumnType("varchar(50)") + .HasColumnName("method"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueryString") + .HasColumnType("varchar(2000)") + .HasColumnName("query_string"); + + b.Property("RequestIp") + .HasColumnType("varchar(50)") + .HasColumnName("request_ip"); + + b.Property("Response") + .HasColumnType("longtext") + .HasColumnName("response"); + + b.Property("ResponseTime") + .HasColumnType("int") + .HasColumnName("response_time"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); + + b.Property("StatusCode") + .HasColumnType("int") + .HasColumnName("status_code"); + + b.Property("Success") + .HasColumnType("tinyint") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_audit_log"); + + b.ToTable("mix_audit_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/AuditLog/MySql/20250304082555_Init.cs b/src/platform/mix.database/Migrations/AuditLog/MySql/20250304082555_Init.cs new file mode 100644 index 000000000..a4da6d09b --- /dev/null +++ b/src/platform/mix.database/Migrations/AuditLog/MySql/20250304082555_Init.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Mix.Database.Migrations.AuditLog.MySql +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "mix_audit_log", + columns: table => new + { + id = table.Column(type: "char(36)", nullable: false, defaultValueSql: "(uuid())", collation: "ascii_general_ci"), + success = table.Column(type: "tinyint", nullable: false), + status_code = table.Column(type: "int", nullable: false), + response_time = table.Column(type: "int", nullable: false), + request_ip = table.Column(type: "varchar(50)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + endpoint = table.Column(type: "varchar(2000)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + method = table.Column(type: "varchar(50)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + query_string = table.Column(type: "varchar(2000)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + body = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + response = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + exception = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + created_date_time = table.Column(type: "datetime", nullable: false), + last_modified = table.Column(type: "datetime", nullable: true), + created_by = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + modified_by = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + priority = table.Column(type: "int", nullable: false), + status = table.Column(type: "varchar(50)", nullable: false) + .Annotation("MySql:CharSet", "utf8"), + is_deleted = table.Column(type: "tinyint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_audit_log", x => x.id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "mix_audit_log"); + } + } +} diff --git a/src/platform/mix.database/Migrations/AuditLog/MySql/MySqlAuditLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/AuditLog/MySql/MySqlAuditLogDbContextModelSnapshot.cs new file mode 100644 index 000000000..9fc415c9a --- /dev/null +++ b/src/platform/mix.database/Migrations/AuditLog/MySql/MySqlAuditLogDbContextModelSnapshot.cs @@ -0,0 +1,112 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.AuditLog; + +#nullable disable + +namespace Mix.Database.Migrations.AuditLog.MySql +{ + [DbContext(typeof(MySqlAuditLogDbContext))] + partial class MySqlAuditLogDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id") + .HasDefaultValueSql("(uuid())"); + + b.Property("Body") + .HasColumnType("longtext") + .HasColumnName("body"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime") + .HasColumnName("created_date_time"); + + b.Property("Endpoint") + .HasColumnType("varchar(2000)") + .HasColumnName("endpoint"); + + b.Property("Exception") + .HasColumnType("longtext") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("tinyint") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("datetime") + .HasColumnName("last_modified"); + + b.Property("Method") + .HasColumnType("varchar(50)") + .HasColumnName("method"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueryString") + .HasColumnType("varchar(2000)") + .HasColumnName("query_string"); + + b.Property("RequestIp") + .HasColumnType("varchar(50)") + .HasColumnName("request_ip"); + + b.Property("Response") + .HasColumnType("longtext") + .HasColumnName("response"); + + b.Property("ResponseTime") + .HasColumnType("int") + .HasColumnName("response_time"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); + + b.Property("StatusCode") + .HasColumnType("int") + .HasColumnName("status_code"); + + b.Property("Success") + .HasColumnType("tinyint") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_audit_log"); + + b.ToTable("mix_audit_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/AuditLog/Postgres/20250304082709_Init.Designer.cs b/src/platform/mix.database/Migrations/AuditLog/Postgres/20250304082709_Init.Designer.cs new file mode 100644 index 000000000..ceab82fc3 --- /dev/null +++ b/src/platform/mix.database/Migrations/AuditLog/Postgres/20250304082709_Init.Designer.cs @@ -0,0 +1,114 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.AuditLog; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Mix.Database.Migrations.AuditLog.Postgres +{ + [DbContext(typeof(PostgresAuditLogDbContext))] + [Migration("20250331040444_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Body") + .HasColumnType("text") + .HasColumnName("body"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_date_time"); + + b.Property("Endpoint") + .HasColumnType("varchar(4000)") + .HasColumnName("endpoint"); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("Method") + .HasColumnType("varchar(50)") + .HasColumnName("method"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueryString") + .HasColumnType("varchar(4000)") + .HasColumnName("query_string"); + + b.Property("RequestIp") + .HasColumnType("varchar(50)") + .HasColumnName("request_ip"); + + b.Property("Response") + .HasColumnType("text") + .HasColumnName("response"); + + b.Property("ResponseTime") + .HasColumnType("int") + .HasColumnName("response_time"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status") + .HasAnnotation("MySql:CharSet", "utf8"); + + b.Property("StatusCode") + .HasColumnType("int") + .HasColumnName("status_code"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_audit_log"); + + b.ToTable("mix_audit_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/AuditLog/Postgres/20250129124341_Init.cs b/src/platform/mix.database/Migrations/AuditLog/Postgres/20250304082709_Init.cs similarity index 96% rename from src/platform/mix.database/Migrations/AuditLog/Postgres/20250129124341_Init.cs rename to src/platform/mix.database/Migrations/AuditLog/Postgres/20250304082709_Init.cs index 7b6b0cdd9..2bdbc2f8d 100644 --- a/src/platform/mix.database/Migrations/AuditLog/Postgres/20250129124341_Init.cs +++ b/src/platform/mix.database/Migrations/AuditLog/Postgres/20250304082709_Init.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -12,7 +12,7 @@ public partial class Init : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "audit_log", + name: "mix_audit_log", columns: table => new { id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), @@ -44,7 +44,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "audit_log"); + name: "mix_audit_log"); } } } diff --git a/src/platform/mix.database/Migrations/AuditLog/Postgres/PostgresAuditLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/AuditLog/Postgres/PostgresAuditLogDbContextModelSnapshot.cs index 0fa6cd909..bb4996c77 100644 --- a/src/platform/mix.database/Migrations/AuditLog/Postgres/PostgresAuditLogDbContextModelSnapshot.cs +++ b/src/platform/mix.database/Migrations/AuditLog/Postgres/PostgresAuditLogDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -17,94 +17,94 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("ProductVersion", "9.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("Body") - .HasColumnType("text") - .HasColumnName("body"); - - b.Property("CreatedBy") - .HasColumnType("varchar(250)") - .HasColumnName("created_by"); - - b.Property("CreatedDateTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("created_date_time"); - - b.Property("Endpoint") - .HasColumnType("varchar(4000)") - .HasColumnName("endpoint"); - - b.Property("Exception") - .HasColumnType("text") - .HasColumnName("exception"); - - b.Property("IsDeleted") - .HasColumnType("boolean") - .HasColumnName("is_deleted"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_modified"); - - b.Property("Method") - .HasColumnType("varchar(50)") - .HasColumnName("method"); - - b.Property("ModifiedBy") - .HasColumnType("varchar(250)") - .HasColumnName("modified_by"); - - b.Property("Priority") - .HasColumnType("int") - .HasColumnName("priority"); - - b.Property("QueryString") - .HasColumnType("varchar(4000)") - .HasColumnName("query_string"); - - b.Property("RequestIp") - .HasColumnType("varchar(50)") - .HasColumnName("request_ip"); - - b.Property("Response") - .HasColumnType("text") - .HasColumnName("response"); - - b.Property("ResponseTime") - .HasColumnType("int") - .HasColumnName("response_time"); - - b.Property("Status") - .IsRequired() - .HasColumnType("varchar(50)") - .HasColumnName("status") - .HasAnnotation("MySql:CharSet", "utf8"); - - b.Property("StatusCode") - .HasColumnType("int") - .HasColumnName("status_code"); - - b.Property("Success") - .HasColumnType("boolean") - .HasColumnName("success"); - - b.HasKey("Id") - .HasName("pk_audit_log"); - - b.ToTable("audit_log", (string)null); - }); + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Body") + .HasColumnType("text") + .HasColumnName("body"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_date_time"); + + b.Property("Endpoint") + .HasColumnType("varchar(4000)") + .HasColumnName("endpoint"); + + b.Property("Exception") + .HasColumnType("text") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("Method") + .HasColumnType("varchar(50)") + .HasColumnName("method"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueryString") + .HasColumnType("varchar(4000)") + .HasColumnName("query_string"); + + b.Property("RequestIp") + .HasColumnType("varchar(50)") + .HasColumnName("request_ip"); + + b.Property("Response") + .HasColumnType("text") + .HasColumnName("response"); + + b.Property("ResponseTime") + .HasColumnType("int") + .HasColumnName("response_time"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status") + .HasAnnotation("MySql:CharSet", "utf8"); + + b.Property("StatusCode") + .HasColumnType("int") + .HasColumnName("status_code"); + + b.Property("Success") + .HasColumnType("boolean") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_audit_log"); + + b.ToTable("mix_audit_log", (string)null); + }); #pragma warning restore 612, 618 } } diff --git a/src/platform/mix.database/Migrations/AuditLog/Postgres/20250129124341_Init.Designer.cs b/src/platform/mix.database/Migrations/AuditLog/SqlServer/20250304082824_Init.Designer.cs similarity index 75% rename from src/platform/mix.database/Migrations/AuditLog/Postgres/20250129124341_Init.Designer.cs rename to src/platform/mix.database/Migrations/AuditLog/SqlServer/20250304082824_Init.Designer.cs index 8a74ec7ca..fe97df8d6 100644 --- a/src/platform/mix.database/Migrations/AuditLog/Postgres/20250129124341_Init.Designer.cs +++ b/src/platform/mix.database/Migrations/AuditLog/SqlServer/20250304082824_Init.Designer.cs @@ -2,17 +2,17 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Mix.Database.Entities.AuditLog; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable -namespace Mix.Database.Migrations.AuditLog.Postgres +namespace Mix.Database.Migrations.AuditLog.SqlServer { - [DbContext(typeof(PostgresAuditLogDbContext))] - [Migration("20250129124341_Init")] + [DbContext(typeof(SqlServerAuditLogDbContext))] + [Migration("20250304082824_Init")] partial class Init { /// @@ -20,21 +20,21 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 63); + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 128); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("uuid") + .HasColumnType("uniqueidentifier") .HasColumnName("id") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("newid()"); b.Property("Body") - .HasColumnType("text") + .HasColumnType("ntext") .HasColumnName("body"); b.Property("CreatedBy") @@ -42,27 +42,27 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnName("created_by"); b.Property("CreatedDateTime") - .HasColumnType("timestamp with time zone") + .HasColumnType("datetime") .HasColumnName("created_date_time"); b.Property("Endpoint") - .HasColumnType("varchar(4000)") + .HasColumnType("nvarchar(4000)") .HasColumnName("endpoint"); b.Property("Exception") - .HasColumnType("text") + .HasColumnType("ntext") .HasColumnName("exception"); b.Property("IsDeleted") - .HasColumnType("boolean") + .HasColumnType("bit") .HasColumnName("is_deleted"); b.Property("LastModified") - .HasColumnType("timestamp with time zone") + .HasColumnType("datetime") .HasColumnName("last_modified"); b.Property("Method") - .HasColumnType("varchar(50)") + .HasColumnType("nvarchar(50)") .HasColumnName("method"); b.Property("ModifiedBy") @@ -74,7 +74,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnName("priority"); b.Property("QueryString") - .HasColumnType("varchar(4000)") + .HasColumnType("nvarchar(4000)") .HasColumnName("query_string"); b.Property("RequestIp") @@ -82,7 +82,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnName("request_ip"); b.Property("Response") - .HasColumnType("text") + .HasColumnType("ntext") .HasColumnName("response"); b.Property("ResponseTime") @@ -100,13 +100,13 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnName("status_code"); b.Property("Success") - .HasColumnType("boolean") + .HasColumnType("bit") .HasColumnName("success"); b.HasKey("Id") .HasName("pk_audit_log"); - b.ToTable("audit_log", (string)null); + b.ToTable("mix_audit_log", (string)null); }); #pragma warning restore 612, 618 } diff --git a/src/platform/mix.database/Migrations/AuditLog/SqlServer/20250304082824_Init.cs b/src/platform/mix.database/Migrations/AuditLog/SqlServer/20250304082824_Init.cs new file mode 100644 index 000000000..04a4d8c24 --- /dev/null +++ b/src/platform/mix.database/Migrations/AuditLog/SqlServer/20250304082824_Init.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Mix.Database.Migrations.AuditLog.SqlServer +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "mix_audit_log", + columns: table => new + { + id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "newid()"), + success = table.Column(type: "bit", nullable: false), + status_code = table.Column(type: "int", nullable: false), + response_time = table.Column(type: "int", nullable: false), + request_ip = table.Column(type: "varchar(50)", nullable: true), + endpoint = table.Column(type: "nvarchar(4000)", nullable: true), + method = table.Column(type: "nvarchar(50)", nullable: true), + query_string = table.Column(type: "nvarchar(4000)", nullable: true), + body = table.Column(type: "ntext", nullable: true), + response = table.Column(type: "ntext", nullable: true), + exception = table.Column(type: "ntext", nullable: true), + created_date_time = table.Column(type: "datetime", nullable: false), + last_modified = table.Column(type: "datetime", nullable: true), + created_by = table.Column(type: "varchar(250)", nullable: true), + modified_by = table.Column(type: "varchar(250)", nullable: true), + priority = table.Column(type: "int", nullable: false), + status = table.Column(type: "varchar(50)", nullable: false), + is_deleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_audit_log", x => x.id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "mix_audit_log"); + } + } +} diff --git a/src/platform/mix.database/Migrations/AuditLog/SqlServer/SqlServerAuditLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/AuditLog/SqlServer/SqlServerAuditLogDbContextModelSnapshot.cs new file mode 100644 index 000000000..5050a37c9 --- /dev/null +++ b/src/platform/mix.database/Migrations/AuditLog/SqlServer/SqlServerAuditLogDbContextModelSnapshot.cs @@ -0,0 +1,111 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.AuditLog; + +#nullable disable + +namespace Mix.Database.Migrations.AuditLog.SqlServer +{ + [DbContext(typeof(SqlServerAuditLogDbContext))] + partial class SqlServerAuditLogDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasColumnName("id") + .HasDefaultValueSql("newid()"); + + b.Property("Body") + .HasColumnType("ntext") + .HasColumnName("body"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime") + .HasColumnName("created_date_time"); + + b.Property("Endpoint") + .HasColumnType("nvarchar(4000)") + .HasColumnName("endpoint"); + + b.Property("Exception") + .HasColumnType("ntext") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("bit") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("datetime") + .HasColumnName("last_modified"); + + b.Property("Method") + .HasColumnType("nvarchar(50)") + .HasColumnName("method"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueryString") + .HasColumnType("nvarchar(4000)") + .HasColumnName("query_string"); + + b.Property("RequestIp") + .HasColumnType("varchar(50)") + .HasColumnName("request_ip"); + + b.Property("Response") + .HasColumnType("ntext") + .HasColumnName("response"); + + b.Property("ResponseTime") + .HasColumnType("int") + .HasColumnName("response_time"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status") + .HasAnnotation("MySql:CharSet", "utf8"); + + b.Property("StatusCode") + .HasColumnType("int") + .HasColumnName("status_code"); + + b.Property("Success") + .HasColumnType("bit") + .HasColumnName("success"); + + b.HasKey("Id") + .HasName("pk_audit_log"); + + b.ToTable("mix_audit_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250123095831_Init.Designer.cs b/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250304082331_Init.Designer.cs similarity index 95% rename from src/platform/mix.database/Migrations/AuditLog/Sqlite/20250123095831_Init.Designer.cs rename to src/platform/mix.database/Migrations/AuditLog/Sqlite/20250304082331_Init.Designer.cs index 7dd5e88ea..964b5d66e 100644 --- a/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250123095831_Init.Designer.cs +++ b/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250304082331_Init.Designer.cs @@ -11,14 +11,14 @@ namespace Mix.Database.Migrations.AuditLog.Sqlite { [DbContext(typeof(SqliteAuditLogDbContext))] - [Migration("20250123095831_Init")] + [Migration("20250304082331_Init")] partial class Init { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.2"); modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => { @@ -101,7 +101,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_audit_log"); - b.ToTable("audit_log", (string)null); + b.ToTable("mix_audit_log", (string)null); }); #pragma warning restore 612, 618 } diff --git a/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250123095831_Init.cs b/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250304082331_Init.cs similarity index 96% rename from src/platform/mix.database/Migrations/AuditLog/Sqlite/20250123095831_Init.cs rename to src/platform/mix.database/Migrations/AuditLog/Sqlite/20250304082331_Init.cs index d91d5c459..d343aad35 100644 --- a/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250123095831_Init.cs +++ b/src/platform/mix.database/Migrations/AuditLog/Sqlite/20250304082331_Init.cs @@ -12,7 +12,7 @@ public partial class Init : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "audit_log", + name: "mix_audit_log", columns: table => new { id = table.Column(type: "TEXT", nullable: false, defaultValueSql: "newid()"), @@ -44,7 +44,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "audit_log"); + name: "mix_audit_log"); } } } diff --git a/src/platform/mix.database/Migrations/AuditLog/Sqlite/SqliteAuditLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/AuditLog/Sqlite/SqliteAuditLogDbContextModelSnapshot.cs index 435709602..e751a5866 100644 --- a/src/platform/mix.database/Migrations/AuditLog/Sqlite/SqliteAuditLogDbContextModelSnapshot.cs +++ b/src/platform/mix.database/Migrations/AuditLog/Sqlite/SqliteAuditLogDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class SqliteAuditLogDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.2"); modelBuilder.Entity("Mix.Database.Entities.AuditLog.AuditLog", b => { @@ -98,7 +98,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_audit_log"); - b.ToTable("audit_log", (string)null); + b.ToTable("mix_audit_log", (string)null); }); #pragma warning restore 612, 618 } diff --git a/src/platform/mix.database/Migrations/QueueLog/MySql/20250304083310_Init.Designer.cs b/src/platform/mix.database/Migrations/QueueLog/MySql/20250304083310_Init.Designer.cs new file mode 100644 index 000000000..6165e87a5 --- /dev/null +++ b/src/platform/mix.database/Migrations/QueueLog/MySql/20250304083310_Init.Designer.cs @@ -0,0 +1,126 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.QueueLog; + +#nullable disable + +namespace Mix.Database.Migrations.QueueLog.MySql +{ + [DbContext(typeof(MySqlQueueLogDbContext))] + [Migration("20250304083310_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.QueueLog.QueueLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id") + .HasDefaultValueSql("(uuid())"); + + b.Property("Action") + .HasColumnType("varchar(250)") + .HasColumnName("action"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime") + .HasColumnName("created_date_time"); + + b.Property("DataTypeFullName") + .HasColumnType("varchar(250)") + .HasColumnName("data_type_full_name"); + + b.Property("Exception") + .HasColumnType("longtext") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("tinyint") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("datetime") + .HasColumnName("last_modified"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Note") + .HasColumnType("varchar(250)") + .HasColumnName("note"); + + b.Property("ObjectData") + .HasColumnType("longtext") + .HasColumnName("object_data"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueueMessageId") + .HasColumnType("varchar(255)") + .HasColumnName("queue_message_id"); + + b.Property("State") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("state"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("State"), "utf8"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); + + b.Property("StringData") + .HasColumnType("longtext") + .HasColumnName("string_data"); + + b.Property("SubscriptionId") + .HasColumnType("varchar(250)") + .HasColumnName("subscription_id"); + + b.Property("Subscriptions") + .HasColumnType("longtext") + .HasColumnName("subscriptions"); + + b.Property("TenantId") + .HasColumnType("int") + .HasColumnName("tenant_id"); + + b.Property("TopicId") + .HasColumnType("varchar(250)") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_queue_log"); + + b.ToTable("mix_queue_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/QueueLog/MySql/20250304083310_Init.cs b/src/platform/mix.database/Migrations/QueueLog/MySql/20250304083310_Init.cs new file mode 100644 index 000000000..dc9e7fd72 --- /dev/null +++ b/src/platform/mix.database/Migrations/QueueLog/MySql/20250304083310_Init.cs @@ -0,0 +1,70 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Mix.Database.Migrations.QueueLog.MySql +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "mix_queue_log", + columns: table => new + { + id = table.Column(type: "char(36)", nullable: false, defaultValueSql: "(uuid())", collation: "ascii_general_ci"), + queue_message_id = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + topic_id = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + subscription_id = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + action = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + string_data = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + object_data = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + exception = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + subscriptions = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + data_type_full_name = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + note = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + state = table.Column(type: "varchar(50)", nullable: false) + .Annotation("MySql:CharSet", "utf8"), + tenant_id = table.Column(type: "int", nullable: false), + created_date_time = table.Column(type: "datetime", nullable: false), + last_modified = table.Column(type: "datetime", nullable: true), + created_by = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + modified_by = table.Column(type: "varchar(250)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + priority = table.Column(type: "int", nullable: false), + status = table.Column(type: "varchar(50)", nullable: false) + .Annotation("MySql:CharSet", "utf8"), + is_deleted = table.Column(type: "tinyint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_queue_log", x => x.id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "mix_queue_log"); + } + } +} diff --git a/src/platform/mix.database/Migrations/QueueLog/MySql/MySqlQueueLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/QueueLog/MySql/MySqlQueueLogDbContextModelSnapshot.cs new file mode 100644 index 000000000..cd36d341b --- /dev/null +++ b/src/platform/mix.database/Migrations/QueueLog/MySql/MySqlQueueLogDbContextModelSnapshot.cs @@ -0,0 +1,123 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.QueueLog; + +#nullable disable + +namespace Mix.Database.Migrations.QueueLog.MySql +{ + [DbContext(typeof(MySqlQueueLogDbContext))] + partial class MySqlQueueLogDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.QueueLog.QueueLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id") + .HasDefaultValueSql("(uuid())"); + + b.Property("Action") + .HasColumnType("varchar(250)") + .HasColumnName("action"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime") + .HasColumnName("created_date_time"); + + b.Property("DataTypeFullName") + .HasColumnType("varchar(250)") + .HasColumnName("data_type_full_name"); + + b.Property("Exception") + .HasColumnType("longtext") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("tinyint") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("datetime") + .HasColumnName("last_modified"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Note") + .HasColumnType("varchar(250)") + .HasColumnName("note"); + + b.Property("ObjectData") + .HasColumnType("longtext") + .HasColumnName("object_data"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueueMessageId") + .HasColumnType("varchar(255)") + .HasColumnName("queue_message_id"); + + b.Property("State") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("state"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("State"), "utf8"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status"); + + MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); + + b.Property("StringData") + .HasColumnType("longtext") + .HasColumnName("string_data"); + + b.Property("SubscriptionId") + .HasColumnType("varchar(250)") + .HasColumnName("subscription_id"); + + b.Property("Subscriptions") + .HasColumnType("longtext") + .HasColumnName("subscriptions"); + + b.Property("TenantId") + .HasColumnType("int") + .HasColumnName("tenant_id"); + + b.Property("TopicId") + .HasColumnType("varchar(250)") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_queue_log"); + + b.ToTable("mix_queue_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/QueueLog/Postgres/20250129125128_Upd.Designer.cs b/src/platform/mix.database/Migrations/QueueLog/Postgres/20250304083219_Init.Designer.cs similarity index 96% rename from src/platform/mix.database/Migrations/QueueLog/Postgres/20250129125128_Upd.Designer.cs rename to src/platform/mix.database/Migrations/QueueLog/Postgres/20250304083219_Init.Designer.cs index 568f48d69..9e21f2425 100644 --- a/src/platform/mix.database/Migrations/QueueLog/Postgres/20250129125128_Upd.Designer.cs +++ b/src/platform/mix.database/Migrations/QueueLog/Postgres/20250304083219_Init.Designer.cs @@ -12,15 +12,15 @@ namespace Mix.Database.Migrations.QueueLog.Postgres { [DbContext(typeof(PostgresQueueLogDbContext))] - [Migration("20250129125128_Upd")] - partial class Upd + [Migration("20250304083219_Init")] + partial class Init { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("ProductVersion", "9.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -116,7 +116,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_queue_log"); - b.ToTable("queue_log", (string)null); + b.ToTable("mix_queue_log", (string)null); }); #pragma warning restore 612, 618 } diff --git a/src/platform/mix.database/Migrations/QueueLog/Postgres/20250129125128_Upd.cs b/src/platform/mix.database/Migrations/QueueLog/Postgres/20250304083219_Init.cs similarity index 95% rename from src/platform/mix.database/Migrations/QueueLog/Postgres/20250129125128_Upd.cs rename to src/platform/mix.database/Migrations/QueueLog/Postgres/20250304083219_Init.cs index b76fb2fc6..c963b0a90 100644 --- a/src/platform/mix.database/Migrations/QueueLog/Postgres/20250129125128_Upd.cs +++ b/src/platform/mix.database/Migrations/QueueLog/Postgres/20250304083219_Init.cs @@ -6,13 +6,13 @@ namespace Mix.Database.Migrations.QueueLog.Postgres { /// - public partial class Upd : Migration + public partial class Init : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "queue_log", + name: "mix_queue_log", columns: table => new { id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), @@ -46,7 +46,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "queue_log"); + name: "mix_queue_log"); } } } diff --git a/src/platform/mix.database/Migrations/QueueLog/Postgres/PostgresQueueLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/QueueLog/Postgres/PostgresQueueLogDbContextModelSnapshot.cs index 90bc31ce1..5c47b1785 100644 --- a/src/platform/mix.database/Migrations/QueueLog/Postgres/PostgresQueueLogDbContextModelSnapshot.cs +++ b/src/platform/mix.database/Migrations/QueueLog/Postgres/PostgresQueueLogDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("ProductVersion", "9.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -113,7 +113,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_queue_log"); - b.ToTable("queue_log", (string)null); + b.ToTable("mix_queue_log", (string)null); }); #pragma warning restore 612, 618 } diff --git a/src/platform/mix.database/Migrations/QueueLog/SqlServer/20250304083130_Init.Designer.cs b/src/platform/mix.database/Migrations/QueueLog/SqlServer/20250304083130_Init.Designer.cs new file mode 100644 index 000000000..584790232 --- /dev/null +++ b/src/platform/mix.database/Migrations/QueueLog/SqlServer/20250304083130_Init.Designer.cs @@ -0,0 +1,124 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.QueueLog; + +#nullable disable + +namespace Mix.Database.Migrations.QueueLog.SqlServer +{ + [DbContext(typeof(SqlServerQueueLogDbContext))] + [Migration("20250304083130_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.QueueLog.QueueLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasColumnName("id") + .HasDefaultValueSql("newid()"); + + b.Property("Action") + .HasColumnType("varchar(250)") + .HasColumnName("action"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime") + .HasColumnName("created_date_time"); + + b.Property("DataTypeFullName") + .HasColumnType("varchar(250)") + .HasColumnName("data_type_full_name"); + + b.Property("Exception") + .HasColumnType("ntext") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("bit") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("datetime") + .HasColumnName("last_modified"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Note") + .HasColumnType("varchar(250)") + .HasColumnName("note"); + + b.Property("ObjectData") + .HasColumnType("ntext") + .HasColumnName("object_data"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueueMessageId") + .HasColumnType("uniqueidentifier") + .HasColumnName("queue_message_id"); + + b.Property("State") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("state") + .HasAnnotation("MySql:CharSet", "utf8"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status") + .HasAnnotation("MySql:CharSet", "utf8"); + + b.Property("StringData") + .HasColumnType("ntext") + .HasColumnName("string_data"); + + b.Property("SubscriptionId") + .HasColumnType("varchar(250)") + .HasColumnName("subscription_id"); + + b.Property("Subscriptions") + .HasColumnType("ntext") + .HasColumnName("subscriptions"); + + b.Property("TenantId") + .HasColumnType("int") + .HasColumnName("tenant_id"); + + b.Property("TopicId") + .HasColumnType("varchar(250)") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_queue_log"); + + b.ToTable("mix_queue_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/QueueLog/SqlServer/20250304083130_Init.cs b/src/platform/mix.database/Migrations/QueueLog/SqlServer/20250304083130_Init.cs new file mode 100644 index 000000000..a5c2da671 --- /dev/null +++ b/src/platform/mix.database/Migrations/QueueLog/SqlServer/20250304083130_Init.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Mix.Database.Migrations.QueueLog.SqlServer +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "mix_queue_log", + columns: table => new + { + id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "newid()"), + queue_message_id = table.Column(type: "uniqueidentifier", nullable: true), + topic_id = table.Column(type: "varchar(250)", nullable: true), + subscription_id = table.Column(type: "varchar(250)", nullable: true), + action = table.Column(type: "varchar(250)", nullable: true), + string_data = table.Column(type: "ntext", nullable: true), + object_data = table.Column(type: "ntext", nullable: true), + exception = table.Column(type: "ntext", nullable: true), + subscriptions = table.Column(type: "ntext", nullable: true), + data_type_full_name = table.Column(type: "varchar(250)", nullable: true), + note = table.Column(type: "varchar(250)", nullable: true), + state = table.Column(type: "varchar(50)", nullable: false), + tenant_id = table.Column(type: "int", nullable: false), + created_date_time = table.Column(type: "datetime", nullable: false), + last_modified = table.Column(type: "datetime", nullable: true), + created_by = table.Column(type: "varchar(250)", nullable: true), + modified_by = table.Column(type: "varchar(250)", nullable: true), + priority = table.Column(type: "int", nullable: false), + status = table.Column(type: "varchar(50)", nullable: false), + is_deleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_queue_log", x => x.id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "mix_queue_log"); + } + } +} diff --git a/src/platform/mix.database/Migrations/QueueLog/SqlServer/SqlServerQueueLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/QueueLog/SqlServer/SqlServerQueueLogDbContextModelSnapshot.cs new file mode 100644 index 000000000..37973dc5d --- /dev/null +++ b/src/platform/mix.database/Migrations/QueueLog/SqlServer/SqlServerQueueLogDbContextModelSnapshot.cs @@ -0,0 +1,121 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Database.Entities.QueueLog; + +#nullable disable + +namespace Mix.Database.Migrations.QueueLog.SqlServer +{ + [DbContext(typeof(SqlServerQueueLogDbContext))] + partial class SqlServerQueueLogDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Mix.Database.Entities.QueueLog.QueueLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasColumnName("id") + .HasDefaultValueSql("newid()"); + + b.Property("Action") + .HasColumnType("varchar(250)") + .HasColumnName("action"); + + b.Property("CreatedBy") + .HasColumnType("varchar(250)") + .HasColumnName("created_by"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime") + .HasColumnName("created_date_time"); + + b.Property("DataTypeFullName") + .HasColumnType("varchar(250)") + .HasColumnName("data_type_full_name"); + + b.Property("Exception") + .HasColumnType("ntext") + .HasColumnName("exception"); + + b.Property("IsDeleted") + .HasColumnType("bit") + .HasColumnName("is_deleted"); + + b.Property("LastModified") + .HasColumnType("datetime") + .HasColumnName("last_modified"); + + b.Property("ModifiedBy") + .HasColumnType("varchar(250)") + .HasColumnName("modified_by"); + + b.Property("Note") + .HasColumnType("varchar(250)") + .HasColumnName("note"); + + b.Property("ObjectData") + .HasColumnType("ntext") + .HasColumnName("object_data"); + + b.Property("Priority") + .HasColumnType("int") + .HasColumnName("priority"); + + b.Property("QueueMessageId") + .HasColumnType("uniqueidentifier") + .HasColumnName("queue_message_id"); + + b.Property("State") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("state") + .HasAnnotation("MySql:CharSet", "utf8"); + + b.Property("Status") + .IsRequired() + .HasColumnType("varchar(50)") + .HasColumnName("status") + .HasAnnotation("MySql:CharSet", "utf8"); + + b.Property("StringData") + .HasColumnType("ntext") + .HasColumnName("string_data"); + + b.Property("SubscriptionId") + .HasColumnType("varchar(250)") + .HasColumnName("subscription_id"); + + b.Property("Subscriptions") + .HasColumnType("ntext") + .HasColumnName("subscriptions"); + + b.Property("TenantId") + .HasColumnType("int") + .HasColumnName("tenant_id"); + + b.Property("TopicId") + .HasColumnType("varchar(250)") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_queue_log"); + + b.ToTable("mix_queue_log", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250123101902_Init.Designer.cs b/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250304083407_Init.Designer.cs similarity index 96% rename from src/platform/mix.database/Migrations/QueueLog/Sqlite/20250123101902_Init.Designer.cs rename to src/platform/mix.database/Migrations/QueueLog/Sqlite/20250304083407_Init.Designer.cs index 11ea22ca3..1011aefe2 100644 --- a/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250123101902_Init.Designer.cs +++ b/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250304083407_Init.Designer.cs @@ -11,14 +11,14 @@ namespace Mix.Database.Migrations.QueueLog.Sqlite { [DbContext(typeof(SqliteQueueLogDbContext))] - [Migration("20250123101902_Init")] + [Migration("20250304083407_Init")] partial class Init { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.2"); modelBuilder.Entity("Mix.Database.Entities.QueueLog.QueueLog", b => { @@ -111,7 +111,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_queue_log"); - b.ToTable("queue_log", (string)null); + b.ToTable("mix_queue_log", (string)null); }); #pragma warning restore 612, 618 } diff --git a/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250123101902_Init.cs b/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250304083407_Init.cs similarity index 97% rename from src/platform/mix.database/Migrations/QueueLog/Sqlite/20250123101902_Init.cs rename to src/platform/mix.database/Migrations/QueueLog/Sqlite/20250304083407_Init.cs index d6d54bd64..ec7f78cf7 100644 --- a/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250123101902_Init.cs +++ b/src/platform/mix.database/Migrations/QueueLog/Sqlite/20250304083407_Init.cs @@ -12,7 +12,7 @@ public partial class Init : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "queue_log", + name: "mix_queue_log", columns: table => new { id = table.Column(type: "TEXT", nullable: false, defaultValueSql: "newid()"), @@ -46,7 +46,7 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "queue_log"); + name: "mix_queue_log"); } } } diff --git a/src/platform/mix.database/Migrations/QueueLog/Sqlite/SqlITEQueueLogDbContextModelSnapshot.cs b/src/platform/mix.database/Migrations/QueueLog/Sqlite/SqlITEQueueLogDbContextModelSnapshot.cs index 83eb4182f..a784f5f27 100644 --- a/src/platform/mix.database/Migrations/QueueLog/Sqlite/SqlITEQueueLogDbContextModelSnapshot.cs +++ b/src/platform/mix.database/Migrations/QueueLog/Sqlite/SqlITEQueueLogDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class SqliteQueueLogDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.0"); + modelBuilder.HasAnnotation("ProductVersion", "9.0.2"); modelBuilder.Entity("Mix.Database.Entities.QueueLog.QueueLog", b => { @@ -108,7 +108,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id") .HasName("pk_queue_log"); - b.ToTable("queue_log", (string)null); + b.ToTable("mix_queue_log", (string)null); }); #pragma warning restore 612, 618 } diff --git a/src/platform/mix.database/Migrations/Settings/MySql/20250130063308_Init.cs b/src/platform/mix.database/Migrations/Settings/MySql/20250130063308_Init.cs index b85f8dfd8..53992b216 100644 --- a/src/platform/mix.database/Migrations/Settings/MySql/20250130063308_Init.cs +++ b/src/platform/mix.database/Migrations/Settings/MySql/20250130063308_Init.cs @@ -80,16 +80,16 @@ protected override void Up(MigrationBuilder migrationBuilder) values: new object[] { 14, null, "portal", "MixHeart", "Mix Heart", "Mix Heart", 1, "mix_heart", "{\"MixHeart\":{\"CacheConnection\":null,\"IsCache\":true,\"CacheMode\":\"HYBRID\",\"CacheFolder\":\"wwwroot/mixcontent/cache\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":\"https://localhost:5010\",\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":\"https://localhost:5010\",\"Messenger\":\"https://localhost:5010\",\"Scheduler\":null,\"Grpc\":null,\"MixMq\":\"https://localhost:5010\",\"LocalGateway\":\"https://localhost:7142\",\"Storage\":\"\",\"UniversalPortal\":\"https://universal.p4ps.net\",\"UniversalPortalDev\":\"https://mix-portal-angular.vercel.app\",\"LocalPortalDev\":\"http://localhost:4200\",\"DataVerse\":\"https://mix-portal-angular-phongcaos-projects.vercel.app\"}}", false }); + values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":null,\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":null,\"Messenger\":null,\"Scheduler\":null,\"Grpc\":null,\"MixMq\":null,\"LocalGateway\":null,\"Storage\":null,\"UniversalPortal\":\"https://universal.p4ps.net\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 17, null, "portal", null, "Database", "Database", 1, "database", "{\"ConnectionStrings\":{\"MixAccountConnection\":null,\"MixAuditLogConnection\":null,\"MixQueueLogConnection\":null,\"MixCmsConnection\":null,\"MixQuartzConnection\":null,\"MixDbConnection\":null},\"ClearDbPool\":false,\"DatabaseProvider\":\"PostgreSQL\"}", false }); + values: new object[] { 17, null, "portal", null, "Database", "Database", 1, "database", "{\"ConnectionStrings\":{\"MixAccountConnection\":null,\"MixAuditLogConnection\":null,\"MixQueueLogConnection\":null,\"MixCmsConnection\":null,\"MixQuartzConnection\":null,\"MixDbConnection\":null},\"ClearDbPool\":false,\"DatabaseProvider\":\"SQLITE\"}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, values: new object[] { 18, null, "portal", "MessageQueueSettings", "Queue", "Queue", 1, "queue", "{\"MessageQueueSettings\":{\"Provider\":\"MIX\",\"GoogleQueueSetting\":{\"CredentialFile\":\"\",\"ProjectId\":\"\"},\"AzureServiceBus\":{\"ConnectionStrings\":\"\",\"QueueName\":\"\"},\"Mix\":{\"ProjectId\":\"\"},\"RabitMqQueueSetting\":{\"HostName\":\"localhost\",\"UserName\":null,\"Password\":null,\"VHost\":null,\"Port\":5672}}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[{\"Priority\":0,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":5010}],\"UpstreamPathTemplate\":\"/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Portal_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/pos/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7119}],\"UpstreamPathTemplate\":\"/pos/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Pos_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":1,\"DownstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7016}],\"UpstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":5,\"DownstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":6,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/integration/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Integration_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":9,\"DownstreamPathTemplate\":\"/tms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/tms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"TMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":7,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7186}],\"UpstreamPathTemplate\":\"/invoice/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Invoice_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":8,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7015}],\"UpstreamPathTemplate\":\"/one/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"One_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/bms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/bms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":120,\"Region\":\"BMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}}],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); + values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); } /// diff --git a/src/platform/mix.database/Migrations/Settings/Postgres/20250130061042_Init.cs b/src/platform/mix.database/Migrations/Settings/Postgres/20250130061042_Init.cs index 22e800e79..e93a78cbf 100644 --- a/src/platform/mix.database/Migrations/Settings/Postgres/20250130061042_Init.cs +++ b/src/platform/mix.database/Migrations/Settings/Postgres/20250130061042_Init.cs @@ -71,7 +71,7 @@ protected override void Up(MigrationBuilder migrationBuilder) values: new object[] { 14, null, "portal", "MixHeart", "Mix Heart", "Mix Heart", 1, "mix_heart", "{\"MixHeart\":{\"CacheConnection\":null,\"IsCache\":true,\"CacheMode\":\"HYBRID\",\"CacheFolder\":\"wwwroot/mixcontent/cache\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":\"https://localhost:5010\",\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":\"https://localhost:5010\",\"Messenger\":\"https://localhost:5010\",\"Scheduler\":null,\"Grpc\":null,\"MixMq\":\"https://localhost:5010\",\"LocalGateway\":\"https://localhost:7142\",\"Storage\":\"\",\"UniversalPortal\":\"https://universal.p4ps.net\",\"UniversalPortalDev\":\"https://mix-portal-angular.vercel.app\",\"LocalPortalDev\":\"http://localhost:4200\",\"DataVerse\":\"https://mix-portal-angular-phongcaos-projects.vercel.app\"}}", false }); + values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":null,\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":null,\"Messenger\":null,\"Scheduler\":null,\"Grpc\":null,\"MixMq\":null,\"LocalGateway\":null,\"Storage\":null,\"UniversalPortal\":\"https://universal.p4ps.net\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, values: new object[] { 17, null, "portal", null, "Database", "Database", 1, "database", "{\"ConnectionStrings\":{\"MixAccountConnection\":null,\"MixAuditLogConnection\":null,\"MixQueueLogConnection\":null,\"MixCmsConnection\":null,\"MixQuartzConnection\":null,\"MixDbConnection\":null},\"ClearDbPool\":false,\"DatabaseProvider\":\"SQLITE\"}", false }); @@ -80,7 +80,7 @@ protected override void Up(MigrationBuilder migrationBuilder) values: new object[] { 18, null, "portal", "MessageQueueSettings", "Queue", "Queue", 1, "queue", "{\"MessageQueueSettings\":{\"Provider\":\"MIX\",\"GoogleQueueSetting\":{\"CredentialFile\":\"\",\"ProjectId\":\"\"},\"AzureServiceBus\":{\"ConnectionStrings\":\"\",\"QueueName\":\"\"},\"Mix\":{\"ProjectId\":\"\"},\"RabitMqQueueSetting\":{\"HostName\":\"localhost\",\"UserName\":null,\"Password\":null,\"VHost\":null,\"Port\":5672}}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[{\"Priority\":0,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":5010}],\"UpstreamPathTemplate\":\"/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Portal_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/pos/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7119}],\"UpstreamPathTemplate\":\"/pos/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Pos_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":1,\"DownstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7016}],\"UpstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":5,\"DownstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":6,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/integration/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Integration_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":9,\"DownstreamPathTemplate\":\"/tms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/tms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"TMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":7,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7186}],\"UpstreamPathTemplate\":\"/invoice/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Invoice_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":8,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7015}],\"UpstreamPathTemplate\":\"/one/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"One_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/bms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/bms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":120,\"Region\":\"BMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}}],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); + values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); } /// diff --git a/src/platform/mix.database/Migrations/Settings/SqlServer/20250130063406_Init.cs b/src/platform/mix.database/Migrations/Settings/SqlServer/20250130063406_Init.cs index 8fb9daa7b..bbb0c4326 100644 --- a/src/platform/mix.database/Migrations/Settings/SqlServer/20250130063406_Init.cs +++ b/src/platform/mix.database/Migrations/Settings/SqlServer/20250130063406_Init.cs @@ -69,7 +69,7 @@ protected override void Up(MigrationBuilder migrationBuilder) values: new object[] { 14, null, "portal", "MixHeart", "Mix Heart", "Mix Heart", 1, "mix_heart", "{\"MixHeart\":{\"CacheConnection\":null,\"IsCache\":true,\"CacheMode\":\"HYBRID\",\"CacheFolder\":\"wwwroot/mixcontent/cache\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":\"https://localhost:5010\",\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":\"https://localhost:5010\",\"Messenger\":\"https://localhost:5010\",\"Scheduler\":null,\"Grpc\":null,\"MixMq\":\"https://localhost:5010\",\"LocalGateway\":\"https://localhost:7142\",\"Storage\":\"\",\"UniversalPortal\":\"https://universal.p4ps.net\",\"UniversalPortalDev\":\"https://mix-portal-angular.vercel.app\",\"LocalPortalDev\":\"http://localhost:4200\",\"DataVerse\":\"https://mix-portal-angular-phongcaos-projects.vercel.app\"}}", false }); + values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":null,\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":null,\"Messenger\":null,\"Scheduler\":null,\"Grpc\":null,\"MixMq\":null,\"LocalGateway\":null,\"Storage\":null,\"UniversalPortal\":\"https://universal.p4ps.net\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, values: new object[] { 17, null, "portal", null, "Database", "Database", 1, "database", "{\"ConnectionStrings\":{\"MixAccountConnection\":null,\"MixAuditLogConnection\":null,\"MixQueueLogConnection\":null,\"MixCmsConnection\":null,\"MixQuartzConnection\":null,\"MixDbConnection\":null},\"ClearDbPool\":false,\"DatabaseProvider\":\"SQLITE\"}", false }); @@ -78,7 +78,7 @@ protected override void Up(MigrationBuilder migrationBuilder) values: new object[] { 18, null, "portal", "MessageQueueSettings", "Queue", "Queue", 1, "queue", "{\"MessageQueueSettings\":{\"Provider\":\"MIX\",\"GoogleQueueSetting\":{\"CredentialFile\":\"\",\"ProjectId\":\"\"},\"AzureServiceBus\":{\"ConnectionStrings\":\"\",\"QueueName\":\"\"},\"Mix\":{\"ProjectId\":\"\"},\"RabitMqQueueSetting\":{\"HostName\":\"localhost\",\"UserName\":null,\"Password\":null,\"VHost\":null,\"Port\":5672}}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[{\"Priority\":0,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":5010}],\"UpstreamPathTemplate\":\"/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Portal_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/pos/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7119}],\"UpstreamPathTemplate\":\"/pos/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Pos_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":1,\"DownstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7016}],\"UpstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":5,\"DownstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":6,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/integration/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Integration_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":9,\"DownstreamPathTemplate\":\"/tms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/tms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"TMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":7,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7186}],\"UpstreamPathTemplate\":\"/invoice/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Invoice_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":8,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7015}],\"UpstreamPathTemplate\":\"/one/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"One_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/bms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/bms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":120,\"Region\":\"BMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}}],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); + values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); } /// diff --git a/src/platform/mix.database/Migrations/Settings/Sqlite/20250130060350_Init.cs b/src/platform/mix.database/Migrations/Settings/Sqlite/20250130060350_Init.cs index 1d36f9cda..a3faa88b0 100644 --- a/src/platform/mix.database/Migrations/Settings/Sqlite/20250130060350_Init.cs +++ b/src/platform/mix.database/Migrations/Settings/Sqlite/20250130060350_Init.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -70,7 +70,7 @@ protected override void Up(MigrationBuilder migrationBuilder) values: new object[] { 14, null, "portal", "MixHeart", "Mix Heart", "Mix Heart", 1, "mix_heart", "{\"MixHeart\":{\"CacheConnection\":null,\"IsCache\":true,\"CacheMode\":\"HYBRID\",\"CacheFolder\":\"wwwroot/mixcontent/cache\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":\"https://localhost:5010\",\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":\"https://localhost:5010\",\"Messenger\":\"https://localhost:5010\",\"Scheduler\":null,\"Grpc\":null,\"MixMq\":\"https://localhost:5010\",\"LocalGateway\":\"https://localhost:7142\",\"Storage\":\"\",\"UniversalPortal\":\"https://universal.p4ps.net\",\"UniversalPortalDev\":\"https://mix-portal-angular.vercel.app\",\"LocalPortalDev\":\"http://localhost:4200\",\"DataVerse\":\"https://mix-portal-angular-phongcaos-projects.vercel.app\"}}", false }); + values: new object[] { 16, null, "portal", "Endpoints", "Endpoints", "Endpoints", 1, "endpoints", "{\"Endpoints\":{\"Account\":null,\"Common\":null,\"Portal\":null,\"Theme\":null,\"Mixcore\":null,\"Messenger\":null,\"Scheduler\":null,\"Grpc\":null,\"MixMq\":\"https://localhost:5010\",\"LocalGateway\":null,\"Storage\":null,\"UniversalPortal\":\"https://universal.p4ps.net\"}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, values: new object[] { 17, null, "portal", null, "Database", "Database", 1, "database", "{\"ConnectionStrings\":{\"MixAccountConnection\":null,\"MixAuditLogConnection\":null,\"MixQueueLogConnection\":null,\"MixCmsConnection\":null,\"MixQuartzConnection\":null,\"MixDbConnection\":null},\"ClearDbPool\":false,\"DatabaseProvider\":\"SQLITE\"}", false }); @@ -79,7 +79,7 @@ protected override void Up(MigrationBuilder migrationBuilder) values: new object[] { 18, null, "portal", "MessageQueueSettings", "Queue", "Queue", 1, "queue", "{\"MessageQueueSettings\":{\"Provider\":\"MIX\",\"GoogleQueueSetting\":{\"CredentialFile\":\"\",\"ProjectId\":\"\"},\"AzureServiceBus\":{\"ConnectionStrings\":\"\",\"QueueName\":\"\"},\"Mix\":{\"ProjectId\":\"\"},\"RabitMqQueueSetting\":{\"HostName\":\"localhost\",\"UserName\":null,\"Password\":null,\"VHost\":null,\"Port\":5672}}}", false }); migrationBuilder.InsertData("mix_global_setting", columns: new[] { "id", "last_modified", "service_name", "section_name", "display_name", "description", "tenant_id", "system_name", "settings", "is_encrypt" }, - values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[{\"Priority\":0,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":5010}],\"UpstreamPathTemplate\":\"/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Portal_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/pos/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7119}],\"UpstreamPathTemplate\":\"/pos/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Pos_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":1,\"DownstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7016}],\"UpstreamPathTemplate\":\"/api/v2/rest/auth/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":5,\"DownstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/api/p4ps/rms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":10},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Auth_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":20}},{\"Priority\":6,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/integration/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Integration_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":9,\"DownstreamPathTemplate\":\"/tms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7034}],\"UpstreamPathTemplate\":\"/tms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"TMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":7,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7186}],\"UpstreamPathTemplate\":\"/invoice/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"Invoice_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":8,\"DownstreamPathTemplate\":\"/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7015}],\"UpstreamPathTemplate\":\"/one/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":0,\"Region\":\"One_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}},{\"Priority\":2,\"DownstreamPathTemplate\":\"/bms/{everything}\",\"DownstreamScheme\":\"https\",\"DownstreamHostAndPorts\":[{\"Host\":\"localhost\",\"Port\":7080}],\"UpstreamPathTemplate\":\"/bms/{everything}\",\"UpstreamHttpMethod\":[\"Get\",\"Post\",\"Put\",\"Patch\",\"Delete\"],\"RateLimitOptions\":{\"ClientWhitelist\":[],\"EnableRateLimiting\":true,\"Period\":\"1s\",\"PeriodTimespan\":1,\"Limit\":1000},\"FileCacheOptions\":{\"TtlSeconds\":120,\"Region\":\"BMS_VN\"},\"HttpHandlerOptions\":{\"AllowAutoRedirect\":true,\"UseCookieContainer\":true,\"UseTracing\":true,\"MaxConnectionsPerServer\":1000}}],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); + values: new object[] { 19, null, "gateway", null, "Ocelot", "Ocelot", 1, "ocelot", "{\"Routes\":[],\"GlobalConfiguration\":{\"BaseUrl\":\"\"}}", false }); } /// diff --git a/src/platform/mix.database/Services/MixGlobalSettings/DatabaseService.cs b/src/platform/mix.database/Services/MixGlobalSettings/DatabaseService.cs index 2081ca7fe..5b985f6da 100644 --- a/src/platform/mix.database/Services/MixGlobalSettings/DatabaseService.cs +++ b/src/platform/mix.database/Services/MixGlobalSettings/DatabaseService.cs @@ -18,7 +18,7 @@ namespace Mix.Database.Services.MixGlobalSettings { public class DatabaseService : GlobalSettingServiceBase { - public MixDatabaseProvider DatabaseProvider => AppSettings.DatabaseProvider; + public MixDatabaseProvider DatabaseProvider => AppSettings.DatabaseProvider; protected IHttpContextAccessor HttpContextAccessor; public DatabaseService(IHttpContextAccessor httpContextAccessor, IConfiguration configuration, MixGlobalSetting settings) diff --git a/src/platform/mix.database/Services/MixGlobalSettings/MixEndpointService.cs b/src/platform/mix.database/Services/MixGlobalSettings/MixEndpointService.cs index c00347025..18ccb0a0a 100644 --- a/src/platform/mix.database/Services/MixGlobalSettings/MixEndpointService.cs +++ b/src/platform/mix.database/Services/MixGlobalSettings/MixEndpointService.cs @@ -18,6 +18,15 @@ public void SetDefaultDomain(string domain) MixMq ??= domain; Mixcore ??= domain; } + + public void InitDomain(string domain) + { + Account ??= domain; + Mixcore ??= domain; + Messenger ??= domain; + MixMq ??= domain; + SaveSettings(); + } protected override void LoadAppSettings() { diff --git a/src/platform/mix.database/Services/MixGlobalSettings/PortalConfigService.cs b/src/platform/mix.database/Services/MixGlobalSettings/PortalConfigService.cs index 070946d6e..90718009d 100644 --- a/src/platform/mix.database/Services/MixGlobalSettings/PortalConfigService.cs +++ b/src/platform/mix.database/Services/MixGlobalSettings/PortalConfigService.cs @@ -2,23 +2,17 @@ using Mix.Database.Entities.MixDb.EntityConfigurations; using Mix.Database.Entities.Settings; using Mix.Heart.Helpers; +using Mix.Shared.Models.Configurations; using Mix.Shared.Services; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; namespace Mix.Database.Services.MixGlobalSettings { - public class PortalConfigService : GlobalSettingServiceBase + public class PortalConfigService : GlobalSettingServiceBase { public PortalConfigService(IConfiguration configuration, MixGlobalSetting settings) : base(configuration, settings) { } - protected override void LoadAppSettings() - { - var content = _settings.IsEncrypt ? AesEncryptionHelper.DecryptString(_settings.Settings, _aesKey) - : _settings.Settings; - RawSettings = JObject.Parse(content); - AppSettings = RawSettings[_sectionName].ToObject(); - } } } diff --git a/src/platform/mix.database/mix.database.csproj b/src/platform/mix.database/mix.database.csproj index 5657b644b..d0a7ce1a0 100644 --- a/src/platform/mix.database/mix.database.csproj +++ b/src/platform/mix.database/mix.database.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/platform/mix.identity/mix.identity.csproj b/src/platform/mix.identity/mix.identity.csproj index f511dcbb1..506be0345 100644 --- a/src/platform/mix.identity/mix.identity.csproj +++ b/src/platform/mix.identity/mix.identity.csproj @@ -21,15 +21,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/platform/mix.library/Base/MixControllerBase.cs b/src/platform/mix.library/Base/MixControllerBase.cs index 6cbb4e206..f29034ef9 100644 --- a/src/platform/mix.library/Base/MixControllerBase.cs +++ b/src/platform/mix.library/Base/MixControllerBase.cs @@ -56,21 +56,21 @@ protected MixControllerBase( private void LoadCulture() { - if (RouteData.Values["culture"]?.ToString()?.ToLower() is not null) - { - Culture = RouteData.Values["culture"]?.ToString()?.ToLower(); - } - if (RouteData.Values["seoName"] is not null) - { - string seoName = RouteData.Values["seoName"]?.ToString()?.ToLower(); - string culture = seoName.Split('/')[0]; - if (TenantService.AllCultures.Any(m => m.Specificulture == culture)) - { - RouteData.Values["culture"] = culture; - RouteData.Values["seoName"] = seoName.Replace(culture, string.Empty); - Culture = culture; - } - } + //if (RouteData.Values["culture"]?.ToString()?.ToLower() is not null) + //{ + // Culture = RouteData.Values["culture"]?.ToString()?.ToLower(); + //} + //if (RouteData.Values["seoName"] is not null) + //{ + // string seoName = RouteData.Values["seoName"]?.ToString()?.ToLower(); + // string culture = seoName.Split('/')[0]; + // if (TenantService.AllCultures.Any(m => m.Specificulture == culture)) + // { + // RouteData.Values["culture"] = culture; + // RouteData.Values["seoName"] = seoName.Replace(culture, string.Empty); + // Culture = culture; + // } + //} //if (!_globalConfigService.Instance.CheckValidCulture(Culture)) //{ // Culture = GlobalSettingsService.Instance.DefaultCulture; diff --git a/src/platform/mix.library/Base/MixRestApiControllerBase.cs b/src/platform/mix.library/Base/MixRestApiControllerBase.cs index 026539373..b92fee45a 100644 --- a/src/platform/mix.library/Base/MixRestApiControllerBase.cs +++ b/src/platform/mix.library/Base/MixRestApiControllerBase.cs @@ -116,10 +116,6 @@ protected virtual Task SaveManyHandler(List data, CancellationToken cance cancellationToken.ThrowIfCancellationRequested(); return RestApiService.SaveManyHandler(data, cancellationToken); } - private async Task RemoveCacheHandler(MixCacheService cacheService, TPrimaryKey id) - { - await cacheService.RemoveCacheAsync(id, typeof(TEntity).FullName); - } #endregion diff --git a/src/platform/mix.library/Interfaces/IMixIdentityService.cs b/src/platform/mix.library/Interfaces/IMixIdentityService.cs index 0d98fbf9e..0d9e36697 100644 --- a/src/platform/mix.library/Interfaces/IMixIdentityService.cs +++ b/src/platform/mix.library/Interfaces/IMixIdentityService.cs @@ -4,6 +4,7 @@ using Mix.Database.Entities.Account; using Mix.Identity.Domain.Models; using Mix.Identity.ViewModels; +using Mix.Shared.Models; using Mix.Shared.Models.Configurations; using System.Security.Claims; @@ -14,15 +15,15 @@ public interface IMixIdentityService public List Roles { get; set; } public bool CheckEndpointPermission(ClaimsPrincipal user, PathString path, string method); public Claim CreateClaim(string type, string value); - public Task ExternalLogin(RegisterExternalBindingModel model, CancellationToken cancellationToken = default); + public Task> ExternalLogin(RegisterExternalBindingModel model, CancellationToken cancellationToken = default); public Task GenerateAccessTokenAsync(MixUser user, bool isRemember, JObject additionalData = default, CancellationToken cancellationToken = default); public Task GenerateTokenAsync(MixUser user, JObject info, DateTime expires, string refreshToken, MixAuthenticationConfigurations appConfigs); public Task GetAuthData(MixUser user, bool rememberMe, int tenantId, JObject additionalData = default, CancellationToken cancellationToken = default); public string GetClaim(ClaimsPrincipal User, string claimType); - public Task GetTokenAsync(GetTokenModel model, CancellationToken cancellationToken = default); - public Task LoginAsync(LoginRequestModel model, CancellationToken cancellationToken = default); + public Task> GetTokenAsync(GetTokenModel model, CancellationToken cancellationToken = default); + public Task> LoginAsync(LoginRequestModel model, CancellationToken cancellationToken = default); + public Task> RenewTokenAsync(RenewTokenDto refreshTokenDto, CancellationToken cancellationToken = default); public Task RegisterAsync(RegisterRequestModel model, int tenantId, UnitOfWorkInfo _cmsUOW, JObject additionalData = default, CancellationToken cancellationToken = default); - public Task RenewTokenAsync(RenewTokenDto refreshTokenDto, CancellationToken cancellationToken = default); public Task VerifyExternalAccessTokenAsync(MixExternalLoginProviders provider, string accessToken, MixAuthenticationConfigurations appConfigs); } } \ No newline at end of file diff --git a/src/platform/mix.library/Publishers/MixBackgroundTaskPublisher.cs b/src/platform/mix.library/Publishers/MixBackgroundTaskPublisher.cs index 11163c2df..6570c2fb3 100644 --- a/src/platform/mix.library/Publishers/MixBackgroundTaskPublisher.cs +++ b/src/platform/mix.library/Publishers/MixBackgroundTaskPublisher.cs @@ -16,7 +16,7 @@ public MixBackgroundTaskPublisher( IConfiguration configuration, MixEndpointService mixEndpointService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) { } diff --git a/src/platform/mix.library/Publishers/MixDbCommandPublisher.cs b/src/platform/mix.library/Publishers/MixDbCommandPublisher.cs index f452d5fb5..de508be53 100644 --- a/src/platform/mix.library/Publishers/MixDbCommandPublisher.cs +++ b/src/platform/mix.library/Publishers/MixDbCommandPublisher.cs @@ -17,7 +17,7 @@ public MixDbCommandPublisher( IConfiguration configuration, MixEndpointService mixEndpointService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) { } diff --git a/src/platform/mix.library/Publishers/MixPublisher.cs b/src/platform/mix.library/Publishers/MixPublisher.cs index 792716d6c..e079acda8 100644 --- a/src/platform/mix.library/Publishers/MixPublisher.cs +++ b/src/platform/mix.library/Publishers/MixPublisher.cs @@ -19,7 +19,7 @@ public MixPublisher( IConfiguration configuration, IWebHostEnvironment environment, MixEndpointService mixEndpointService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(topicId, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) { } diff --git a/src/platform/mix.library/Publishers/MixViewModelChangedPublisher.cs b/src/platform/mix.library/Publishers/MixViewModelChangedPublisher.cs index b66bd446b..2ae9e9dea 100644 --- a/src/platform/mix.library/Publishers/MixViewModelChangedPublisher.cs +++ b/src/platform/mix.library/Publishers/MixViewModelChangedPublisher.cs @@ -16,7 +16,7 @@ public MixViewModelChangedPublisher( IConfiguration configuration, MixEndpointService mixEndpointService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) { } diff --git a/src/platform/mix.library/Services/MixIdentityService.cs b/src/platform/mix.library/Services/MixIdentityService.cs index 4e54f3b85..ad358ccad 100644 --- a/src/platform/mix.library/Services/MixIdentityService.cs +++ b/src/platform/mix.library/Services/MixIdentityService.cs @@ -20,7 +20,9 @@ using Mix.Mq.Lib.Models; using Mix.RepoDb.Repositories; using Mix.Service.Commands; +using Mix.Shared.Models; using Mix.Shared.Models.Configurations; +using MySqlX.XDevAPI.Common; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; @@ -131,7 +133,7 @@ public virtual async Task GetUserAsync(Guid userId, Cancellati return null; } - public virtual async Task LoginAsync(LoginRequestModel model, CancellationToken cancellationToken = default) + public virtual async Task> LoginAsync(LoginRequestModel model, CancellationToken cancellationToken = default) { try { @@ -155,23 +157,24 @@ public virtual async Task LoginAsync(LoginRequestModel model if (user == null) { - throw new MixException(MixErrorStatus.Badrequest, "Login failed"); + return new ApiResponseModel(false, null, "Login failed"); } var result = await SignInManager.PasswordSignInAsync(user, model.Password, isPersistent: model.RememberMe, lockoutOnFailure: true).ConfigureAwait(false); if (result.IsLockedOut) { - throw new MixException(MixErrorStatus.Badrequest, "This account has been locked out, please try again later."); + return new ApiResponseModel(false, null, "This account has been locked out, please try again later."); } if (result.Succeeded) { - return await GetAuthData(user, model.RememberMe, CurrentTenant.Id, default, cancellationToken); + return new ApiResponseModel(true, + await GetAuthData(user, true, CurrentTenant.Id, default, cancellationToken)); } else { - throw new MixException(MixErrorStatus.Badrequest, "Login failed"); + return new ApiResponseModel(false, null, "Login failed"); } } catch (MixException) @@ -211,7 +214,7 @@ public virtual async Task GetAuthData(MixUser user, bool rem //return default; } - public virtual async Task GetTokenAsync(GetTokenModel model, CancellationToken cancellationToken = default) + public virtual async Task> GetTokenAsync(GetTokenModel model, CancellationToken cancellationToken = default) { MixUser user = null; if (!string.IsNullOrEmpty(model.Email)) @@ -225,7 +228,8 @@ public virtual async Task GetTokenAsync(GetTokenModel model, if (user != null) { - return await GetAuthData(user, true, CurrentTenant.Id, default, cancellationToken); + return new ApiResponseModel(true, + await GetAuthData(user, true, CurrentTenant.Id, default, cancellationToken)); } return default; } @@ -374,7 +378,7 @@ public virtual async Task GenerateAccessTokenAsync( } } - public virtual async Task ExternalLogin(RegisterExternalBindingModel model, CancellationToken cancellationToken = default) + public virtual async Task> ExternalLogin(RegisterExternalBindingModel model, CancellationToken cancellationToken = default) { try { @@ -400,10 +404,10 @@ public virtual async Task ExternalLogin(RegisterExternalBind // return local token if already register if (user != null) { - return await GetAuthData(user, true, CurrentTenant.Id, model.Data, cancellationToken); + return new ApiResponseModel(true, await GetAuthData(user, true, CurrentTenant.Id, model.Data, cancellationToken)); } - throw new MixException(MixErrorStatus.Badrequest, "Invalid Account"); + return new ApiResponseModel(false, null, "Invalid account"); } throw new MixException(MixErrorStatus.Badrequest); } @@ -417,12 +421,12 @@ public virtual async Task ExternalLogin(RegisterExternalBind } } - public async Task RenewTokenAsync(RenewTokenDto refreshTokenDto, CancellationToken cancellationToken = default) + public async Task> RenewTokenAsync(RenewTokenDto refreshTokenDto, CancellationToken cancellationToken = default) { var result = new TokenResponseModel(); if (refreshTokenDto.RefreshToken == default(Guid)) { - throw new MixException(MixErrorStatus.Badrequest, "Invalid Token"); + return new ApiResponseModel(false, result, "Invalid Token"); } string key = $"tokens:{refreshTokenDto.RefreshToken}"; var oldToken = await CacheService.GetAsync(key, cancellationToken); @@ -437,21 +441,23 @@ public async Task RenewTokenAsync(RenewTokenDto refreshToken { var user = await UserManager.FindByNameAsync(oldToken.UserName); await SignInManager.SignInAsync(user, true).ConfigureAwait(false); - return await GetAuthData(user, true, CurrentTenant.Id, default, cancellationToken); + return new ApiResponseModel(true, await GetAuthData(user, true, CurrentTenant.Id, default, cancellationToken)); + } else - { + { + return new ApiResponseModel(false, result, "Invalid Token"); throw new MixException(MixErrorStatus.Badrequest, "Invalid Token"); } } else { - throw new MixException(MixErrorStatus.Badrequest, "Token expired"); + return new ApiResponseModel(false, result, "Token Expired"); } } else { - throw new MixException(MixErrorStatus.Badrequest, "Token expired"); + return new ApiResponseModel(false, result, "Token Expired"); } } diff --git a/src/platform/mix.library/Startup/AddConfigurations.cs b/src/platform/mix.library/Startup/AddConfigurations.cs index 981705fd8..9bec2346c 100644 --- a/src/platform/mix.library/Startup/AddConfigurations.cs +++ b/src/platform/mix.library/Startup/AddConfigurations.cs @@ -37,31 +37,52 @@ public static IHostApplicationBuilder AddConfigurations(this IHostApplicationBui AddJsonStream(new MemoryStream(jsonString)); } builder.Configuration.Build(); - - builder.Services.TryAddSingleton( + if (settings.Any(m => m.SystemName == "endpoints")) + { + builder.Services.TryAddSingleton( m => new MixEndpointService( builder.Configuration, settings.First(m => m.SystemName == "endpoints"))); - builder.Services.TryAddSingleton( + } + if (settings.Any(m => m.SystemName == "portal")) + { + builder.Services.TryAddSingleton( m => new PortalConfigService( builder.Configuration, settings.First(m => m.SystemName == "portal"))); - builder.Services.TryAddSingleton( - m => new AuthConfigService( - builder.Configuration, - settings.First(m => m.SystemName == "authentication"))); - builder.Services.TryAddSingleton(m => new GlobalSettingsService( + } + if (settings.Any(m => m.SystemName == "authentication")) + { + builder.Services.TryAddSingleton( + m => new AuthConfigService( + builder.Configuration, + settings.First(m => m.SystemName == "authentication"))); + } + if (settings.Any(m => m.SystemName == "global")) + { + builder.Services.TryAddSingleton(m => new GlobalSettingsService( builder.Configuration, settings.First(m => m.SystemName == "global"))); - builder.Services.TryAddSingleton(m => new DatabaseService( + } + if (settings.Any(m => m.SystemName == "database")) + { + builder.Services.TryAddSingleton(m => new DatabaseService( builder.Services.GetService(), builder.Configuration, settings.First(m => m.SystemName == "database"))); - - builder.Services.TryAddSingleton(m => new SmtpConfigService(builder.Configuration, settings.First(m => m.SystemName == "smtp"))); - builder.Services.TryAddSingleton(m => new MixHeartConfigService(builder.Configuration, settings.First(m => m.SystemName == "mix_heart"))); - builder.Services.TryAddSingleton(m => new IPSecurityConfigService(builder.Configuration, settings.First(m => m.SystemName == "ip"))); - + } + if (settings.Any(m => m.SystemName == "smtp")) + { + builder.Services.TryAddSingleton(m => new SmtpConfigService(builder.Configuration, settings.First(m => m.SystemName == "smtp"))); + } + if (settings.Any(m => m.SystemName == "mix_heart")) + { + builder.Services.TryAddSingleton(m => new MixHeartConfigService(builder.Configuration, settings.First(m => m.SystemName == "mix_heart"))); + } + if (settings.Any(m => m.SystemName == "ip")) + { + builder.Services.TryAddSingleton(m => new IPSecurityConfigService(builder.Configuration, settings.First(m => m.SystemName == "ip"))); + } builder.Services.TryAddSingleton(); return builder; } diff --git a/src/platform/mix.library/Startup/AuthServiceCollectionExtensions.cs b/src/platform/mix.library/Startup/AuthServiceCollectionExtensions.cs index 0e4dfb396..42c99475d 100644 --- a/src/platform/mix.library/Startup/AuthServiceCollectionExtensions.cs +++ b/src/platform/mix.library/Startup/AuthServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -99,6 +100,7 @@ public static IServiceCollection AddMixIdentityConfigurations(this I .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { + //options.ForwardDefaultSelector = ForwardReferenceToken("introspection"); options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = @@ -117,18 +119,7 @@ public static IServiceCollection AddMixIdentityConfigurations(this I .AddMicrosoftIdentityWebApiIf( !string.IsNullOrEmpty(authConfigurations.AzureAd?.ClientId), configuration); - //services.ConfigureApplicationCookie(options => - //{ - // options.Cookie.Name = authConfigurations.Issuer; - // options.Cookie.HttpOnly = true; - // options.Cookie.MaxAge = TimeSpan.FromMinutes(authConfigurations.AccessTokenExpiration); - // options.ExpireTimeSpan = TimeSpan.FromMinutes(authConfigurations.AccessTokenExpiration); - // options.LoginPath = accessDeniedPath; - // options.LogoutPath = "/"; - // options.AccessDeniedPath = accessDeniedPath; - // options.SlidingExpiration = true; - //}); - // Firebase service must be singleton (only one firebase default instance) + services.AddRequiredScopeAuthorization(); services.TryAddSingleton(); services.TryAddSingleton(); @@ -162,5 +153,51 @@ public static SymmetricSecurityKey Create(string secret) return new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret)); } } + + /// + /// Provides a forwarding func for JWT vs reference tokens (based on existence of dot in token) + /// + /// Scheme name of the introspection handler + /// + public static Func ForwardReferenceToken(string introspectionScheme = "introspection") + { + string Select(HttpContext context) + { + var (scheme, credential) = GetSchemeAndCredential(context); + + if (scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase) && + !credential.Contains(".")) + { + return introspectionScheme; + } + + return null; + } + + return Select; + } + + /// + /// Extracts scheme and credential from Authorization header (if present) + /// + /// + /// + public static (string, string) GetSchemeAndCredential(HttpContext context) + { + var header = context.Request.Headers["Authorization"].FirstOrDefault(); + + if (string.IsNullOrEmpty(header)) + { + return ("", ""); + } + + var parts = header.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length != 2) + { + return ("", ""); + } + + return (parts[0], parts[1]); + } } } \ No newline at end of file diff --git a/src/platform/mix.library/Startup/MixCommonService.cs b/src/platform/mix.library/Startup/MixCommonService.cs index 71c8cec86..089d7ac4e 100644 --- a/src/platform/mix.library/Startup/MixCommonService.cs +++ b/src/platform/mix.library/Startup/MixCommonService.cs @@ -25,7 +25,8 @@ public static IHostApplicationBuilder AddMixCommonServices(this IHostApplication builder.Services.TryAddScoped(); builder.Services.TryAddScoped(); - + builder.Services.AddMixCache(builder.Configuration); + builder.Services.TryAddSingleton(); return builder; } } diff --git a/src/platform/mix.library/Startup/Swagger.cs b/src/platform/mix.library/Startup/Swagger.cs index 368bea827..4f8f0af09 100644 --- a/src/platform/mix.library/Startup/Swagger.cs +++ b/src/platform/mix.library/Startup/Swagger.cs @@ -57,8 +57,8 @@ public static IApplicationBuilder UseMixSwaggerApps(this IApplicationBuilder app string routeTemplate = "swagger/{documentName}/swagger.json"; string endPoint = $"/swagger/{version}/swagger.json"; - if (isDevelop) - { + //if (isDevelop) + //{ var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; app.UseSwagger(opt => @@ -76,7 +76,7 @@ public static IApplicationBuilder UseMixSwaggerApps(this IApplicationBuilder app c.EnableFilter(); c.EnableDeepLinking(); }); - } + //} return app; } diff --git a/src/platform/mix.library/Subscribers/MixBackgroundTaskSubscriber.cs b/src/platform/mix.library/Subscribers/MixBackgroundTaskSubscriber.cs index 4a1edf952..0e34add9d 100644 --- a/src/platform/mix.library/Subscribers/MixBackgroundTaskSubscriber.cs +++ b/src/platform/mix.library/Subscribers/MixBackgroundTaskSubscriber.cs @@ -37,7 +37,7 @@ public MixBackgroundTaskSubscriber( MixDbEventService mixDbEventService, IMemoryQueueService queueService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, nameof(MixBackgroundTaskSubscriber), 20, serviceProvider, configuration, queueService, logger, rabbitMQObjectPolicy) { PortalHub = portalHub; diff --git a/src/platform/mix.library/Subscribers/MixDbCommandSubscriber.cs b/src/platform/mix.library/Subscribers/MixDbCommandSubscriber.cs index 7b023ae5c..dbdd200eb 100644 --- a/src/platform/mix.library/Subscribers/MixDbCommandSubscriber.cs +++ b/src/platform/mix.library/Subscribers/MixDbCommandSubscriber.cs @@ -30,7 +30,7 @@ public MixDbCommandSubscriber( IConfiguration configuration, IMemoryQueueService queueService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, nameof(MixDbCommandSubscriber), 20, serviceProvider, configuration, queueService, logger, rabbitMQObjectPolicy) { _allowActions = [.. Enum.GetNames(typeof(MixDbCommandQueueAction))]; diff --git a/src/platform/mix.library/Subscribers/MixViewModelChangedSubscriber.cs b/src/platform/mix.library/Subscribers/MixViewModelChangedSubscriber.cs index 4adc33f46..2e6913976 100644 --- a/src/platform/mix.library/Subscribers/MixViewModelChangedSubscriber.cs +++ b/src/platform/mix.library/Subscribers/MixViewModelChangedSubscriber.cs @@ -21,7 +21,7 @@ public MixViewModelChangedSubscriber( IMixTenantService mixTenantService, IMemoryQueueService queueService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, nameof(MixDbCommandSubscriber), 20, serviceProvider, configuration, queueService, logger, rabbitMQObjectPolicy) { _mixTenantService = mixTenantService; diff --git a/src/platform/mix.library/mix.library.csproj b/src/platform/mix.library/mix.library.csproj index e8274c293..4e6da157a 100644 --- a/src/platform/mix.library/mix.library.csproj +++ b/src/platform/mix.library/mix.library.csproj @@ -79,17 +79,18 @@ - + - - - - + + + + - - - - + + + + + diff --git a/src/platform/mix.log/Publishers/MixLogPublisher.cs b/src/platform/mix.log/Publishers/MixLogPublisher.cs index 9d0edf34c..2b6ad77a8 100644 --- a/src/platform/mix.log/Publishers/MixLogPublisher.cs +++ b/src/platform/mix.log/Publishers/MixLogPublisher.cs @@ -6,21 +6,20 @@ using Mix.Mq.Lib.Models; using Mix.Queue.Engines; using Mix.Queue.Engines.MixQueue; +using Mix.Queue.Engines.RabbitMQ; using Mix.Queue.Interfaces; namespace Mix.Log.Lib.Publishers { public class MixLogPublisher : PublisherBase { - private const string TopicId = MixQueueTopics.MixLog; - public MixLogPublisher( IMemoryQueueService queueService, IConfiguration configuration, MixEndpointService mixEndpointService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) - : base(TopicId, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) + RabbitModelPooledObjectPolicy? rabbitMQObjectPolicy = null) + : base(MixQueueTopics.MixLog, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) { } } diff --git a/src/platform/mix.log/Services/AuditLogService.cs b/src/platform/mix.log/Services/AuditLogService.cs index 9f0ea50da..052f9303b 100644 --- a/src/platform/mix.log/Services/AuditLogService.cs +++ b/src/platform/mix.log/Services/AuditLogService.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; using Mix.Constant.Constants; using Mix.Database.Entities.AuditLog; using Mix.Database.Services.MixGlobalSettings; @@ -9,10 +10,12 @@ using Mix.Log.Lib.Models; using Mix.Mq.Lib.Models; using Mix.Queue.Interfaces; +using Mix.Service.Models; using Mix.Service.Services; using Mix.SignalR.Enums; using Mix.SignalR.Interfaces; using Mix.SignalR.Models; +using Mix.Shared.Extensions; namespace Mix.Log.Lib.Services { @@ -22,9 +25,25 @@ public class AuditLogService : IAuditLogService private readonly ILogStreamHubClientService _logStreamHub; private readonly IMemoryQueueService _queueService; private AuditLogDbContext _dbContext; - public int TenantId { get; set; } - public AuditLogService(IMemoryQueueService queueService, ILogStreamHubClientService logStreamHub, DatabaseService databaseService) + protected ISession? Session; + private MixTenantSystemModel _currentTenant; + public MixTenantSystemModel CurrentTenant { + get + { + if (_currentTenant == null) + { + _currentTenant = Session?.Get(MixRequestQueryKeywords.Tenant) ?? new MixTenantSystemModel() + { + Id = 1 + }; + } + return _currentTenant; + } + } + public AuditLogService(IHttpContextAccessor httpContextAccessor, IMemoryQueueService queueService, ILogStreamHubClientService logStreamHub, DatabaseService databaseService) + { + Session = httpContextAccessor?.HttpContext?.Session; _queueService = queueService; _logStreamHub = logStreamHub; _databaseService = databaseService; @@ -59,7 +78,7 @@ public void QueueRequest(AuditLogDataModel request) { request.CreatedAt = DateTime.UtcNow; var cmd = new LogAuditLogCommand(request); - _queueService.PushMemoryQueue(TenantId, MixQueueTopics.MixLog, MixQueueActions.AuditLog, cmd); + _queueService.PushMemoryQueue(CurrentTenant.Id, MixQueueTopics.MixLog, MixQueueActions.AuditLog, cmd); } #region Helpers diff --git a/src/platform/mix.log/Subscribers/MixLogSubscriber.cs b/src/platform/mix.log/Subscribers/MixLogSubscriber.cs index acd155804..82aaab82d 100644 --- a/src/platform/mix.log/Subscribers/MixLogSubscriber.cs +++ b/src/platform/mix.log/Subscribers/MixLogSubscriber.cs @@ -37,7 +37,7 @@ public MixLogSubscriber( IMixQueueLog queueMessageLogService, IAuditLogService auditLogService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, nameof(MixLogSubscriber), 20, serviceProvider, configuration, queueService, logger, rabbitMQObjectPolicy) { _queueMessageLogService = queueMessageLogService; diff --git a/src/platform/mix.mixdb/Interfaces/IMixDbDataService.cs b/src/platform/mix.mixdb/Interfaces/IMixDbDataService.cs index 53e540aaa..95a66f344 100644 --- a/src/platform/mix.mixdb/Interfaces/IMixDbDataService.cs +++ b/src/platform/mix.mixdb/Interfaces/IMixDbDataService.cs @@ -44,6 +44,6 @@ public interface IMixDbDataService : IDisposable object? ParseObjectValueToDbType(MixDataType? dataType, JToken value); void SetDbConnection(UnitOfWorkInfo uow); Task ExtractIdAsync(string tableName, JObject obj); - + Task GetByIdAsync(string tableName, object objId, string? selectColumns, CancellationToken cancellationToken) where T : class; } } diff --git a/src/platform/mix.mixdb/Publishers/MixRepoDbPublisher.cs b/src/platform/mix.mixdb/Publishers/MixRepoDbPublisher.cs index 3cb90cba6..4f25115b9 100644 --- a/src/platform/mix.mixdb/Publishers/MixRepoDbPublisher.cs +++ b/src/platform/mix.mixdb/Publishers/MixRepoDbPublisher.cs @@ -16,7 +16,7 @@ public MixRepoDbPublisher( IConfiguration configuration, MixEndpointService mixEndpointService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(MixQueueTopics.MixRepoDb, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) { diff --git a/src/platform/mix.mixdb/Services/MixdbStructureService.cs b/src/platform/mix.mixdb/Services/MixdbStructureService.cs index d18de394b..e462a3d16 100644 --- a/src/platform/mix.mixdb/Services/MixdbStructureService.cs +++ b/src/platform/mix.mixdb/Services/MixdbStructureService.cs @@ -231,22 +231,15 @@ public async Task MigrateSystemDatabases(CancellationToken cancellationToken = d #region Helpers protected async Task GetMixDatabase(string tableName) { + string name = $"{typeof(MixDbDatabaseViewModel).FullName}_{tableName}"; var result = await _memoryCache.TryGetValueAsync( - tableName, - async cache => + name, + cache => { cache.SlidingExpiration = TimeSpan.FromSeconds(20); - var db = await MixDbDatabaseViewModel.GetRepository(_cmsUow, CacheService) - .GetSingleAsync(m => m.SystemName == tableName); - if (db != null) - { - db.DatabaseProvider = db.MixDatabaseContext != null - ? db.MixDatabaseContext.DatabaseProvider - : _databaseService.DatabaseProvider; - } - return db; + return MixDbDatabaseViewModel.GetRepository(_cmsUow, CacheService).GetSingleAsync(m => m.SystemName == tableName); } - ); + ); if (result == null) { throw new MixException(MixErrorStatus.Badrequest, "Invalid table name"); diff --git a/src/platform/mix.mixdb/Services/RepodbDataService.cs b/src/platform/mix.mixdb/Services/RepodbDataService.cs index 729068e07..d2a6b9676 100644 --- a/src/platform/mix.mixdb/Services/RepodbDataService.cs +++ b/src/platform/mix.mixdb/Services/RepodbDataService.cs @@ -30,6 +30,7 @@ using Mix.Database.Services.MixGlobalSettings; using Microsoft.Extensions.Configuration; using Mix.Lib.Extensions; +using MySqlX.XDevAPI.Common; namespace Mix.Mixdb.Services { @@ -99,6 +100,14 @@ public void Init(string connectionString) await LoadMixDb(tableName); return await GetSingleByAsync(tableName, new MixQueryField(_fieldNameService.Id, objId, MixCompareOperator.Equal), selectColumns, cancellationToken); } + + public async Task GetByIdAsync(string tableName, object objId, string? selectColumns, CancellationToken cancellationToken) + where T : class + { + await LoadMixDb(tableName); + var obj = await GetSingleByAsync(tableName, new MixQueryField(_fieldNameService.Id, objId, MixCompareOperator.Equal), selectColumns, cancellationToken); + return obj?.ToObject(); + } public async Task GetSingleByAsync(string tableName, MixQueryField query, string? selectColumns, CancellationToken cancellationToken) { @@ -336,6 +345,7 @@ private async Task LoadOneToMany(string tableName, JObject item, MixDatabaseRela { while (!cancellationToken.IsCancellationRequested) { + req.Queries ??= new(); var parentId = _fieldNameService.NamingConvention == MixDatabaseNamingConvention.SnakeCase ? $"{rel.SourceDatabaseName}_id" : $"{rel.SourceDatabaseName}Id"; @@ -928,23 +938,29 @@ private async Task LoadMixDb(string tableName) { if (_mixDb != null && _mixDb.SystemName == tableName) { + _fieldNameService = new FieldNameService(_mixDb.NamingConvention); return; } string name = $"{typeof(MixDbDatabaseViewModel).FullName}_{tableName}"; _mixDb = await GetMixDb(tableName); + if (_mixDb == null) + { + throw new MixException(MixErrorStatus.Badrequest, "Invalid table name"); + } _fieldNameService = new FieldNameService(_mixDb.NamingConvention); } - private async Task GetMixDb(string tableName) + private async Task GetMixDb(string tableName) { + string name = $"{typeof(MixDbDatabaseViewModel).FullName}_{tableName}"; return await _memoryCache.TryGetValueAsync( - tableName, + name, cache => { cache.SlidingExpiration = TimeSpan.FromSeconds(20); return MixDbDatabaseViewModel.GetRepository(_uow, _cacheSrv).GetSingleAsync(m => m.SystemName == tableName); } - ) ?? throw new NullReferenceException(tableName); + ); } #endregion diff --git a/src/platform/mix.mixdb/Services/ScylladbDataService.cs b/src/platform/mix.mixdb/Services/ScylladbDataService.cs index 6db179633..994515e5c 100644 --- a/src/platform/mix.mixdb/Services/ScylladbDataService.cs +++ b/src/platform/mix.mixdb/Services/ScylladbDataService.cs @@ -695,16 +695,17 @@ private async Task LoadMixDb(string tableName) _fieldNameService = new FieldNameService(_mixDb.NamingConvention); } - private async Task GetMixDb(string tableName) + private async Task GetMixDb(string tableName) { + string name = $"{typeof(MixDbDatabaseViewModel).FullName}_{tableName}"; return await _memoryCache.TryGetValueAsync( - tableName, + name, cache => { cache.SlidingExpiration = TimeSpan.FromSeconds(20); return MixDbDatabaseViewModel.GetRepository(_cmsUow, _cacheSrv).GetSingleAsync(m => m.SystemName == tableName); } - ) ?? throw new NullReferenceException(tableName); + ); } public async Task ExtractIdAsync(string tableName, JObject obj) @@ -725,6 +726,12 @@ public async Task ExtractIdAsync(string tableName, JObject obj) throw new NotImplementedException(); } + public async Task GetByIdAsync(string tableName, object objId, string? selectColumns, CancellationToken cancellationToken) where T : class + { + var obj = await GetSingleByAsync(tableName, new MixQueryField(_fieldNameService.Id, objId, MixCompareOperator.Equal), selectColumns, cancellationToken); + return obj?.ToObject(); + } + #endregion } diff --git a/src/platform/mix.mixdb/Subscribers/MixRepoDbSubscriber.cs b/src/platform/mix.mixdb/Subscribers/MixRepoDbSubscriber.cs index 8b16011c8..711033674 100644 --- a/src/platform/mix.mixdb/Subscribers/MixRepoDbSubscriber.cs +++ b/src/platform/mix.mixdb/Subscribers/MixRepoDbSubscriber.cs @@ -27,7 +27,7 @@ public MixRepoDbSubscriber( IPortalHubClientService portalHub, IMemoryQueueService queueService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(MixQueueTopics.MixRepoDb, nameof(MixRepoDbSubscriber), 20, serviceProvider, configuration, queueService, logger, rabbitMQObjectPolicy) { PortalHub = portalHub; diff --git a/src/platform/mix.mixdb/mix.mixdb.csproj b/src/platform/mix.mixdb/mix.mixdb.csproj index 06c591f01..7ff500374 100644 --- a/src/platform/mix.mixdb/mix.mixdb.csproj +++ b/src/platform/mix.mixdb/mix.mixdb.csproj @@ -8,12 +8,12 @@ - - + + all - - + + diff --git a/src/platform/mix.quartz/Jobs/MixJobBase.cs b/src/platform/mix.quartz/Jobs/MixJobBase.cs index 33fcd9f22..dc92ba383 100644 --- a/src/platform/mix.quartz/Jobs/MixJobBase.cs +++ b/src/platform/mix.quartz/Jobs/MixJobBase.cs @@ -27,10 +27,16 @@ public async Task Execute(IJobExecutionContext context) } catch (Exception ex) { - Console.WriteLine(ex); + await ExceptionHandler(ex); } } + public virtual Task ExceptionHandler(Exception ex) + { + Console.WriteLine(ex); + return Task.CompletedTask; + } + public string JobName { get; set; } public string Group { get; set; } public Type JobType { get; set; } diff --git a/src/platform/mix.quartz/mix.quartz.csproj b/src/platform/mix.quartz/mix.quartz.csproj index ef36d5ef7..cffdd75cb 100644 --- a/src/platform/mix.quartz/mix.quartz.csproj +++ b/src/platform/mix.quartz/mix.quartz.csproj @@ -6,12 +6,12 @@ - - - - - - + + + + + + diff --git a/src/platform/mix.queue/Engines/PublisherBase.cs b/src/platform/mix.queue/Engines/PublisherBase.cs index 2d8fe9295..11945ff78 100644 --- a/src/platform/mix.queue/Engines/PublisherBase.cs +++ b/src/platform/mix.queue/Engines/PublisherBase.cs @@ -5,6 +5,7 @@ using Mix.Database.Services.MixGlobalSettings; using Mix.Heart.Exceptions; using Mix.Mq.Lib.Models; +using Mix.Queue.Engines.RabbitMQ; using Mix.Queue.Interfaces; using Mix.Queue.Models.QueueSetting; using RabbitMQ.Client; @@ -27,7 +28,7 @@ public abstract class PublisherBase : BackgroundService protected readonly IConfiguration Configuration; protected readonly MixEndpointService MixEndpointService; protected readonly ILogger ILogger; - protected readonly IPooledObjectPolicy? RabbitMqObjectPolicy; + protected readonly IPooledObjectPolicy? RabbitMqObjectPolicy; protected PublisherBase( string topicId, @@ -35,7 +36,7 @@ protected PublisherBase( IConfiguration configuration, MixEndpointService mixEndpointService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) { _topicId = topicId; ILogger = logger; @@ -51,7 +52,7 @@ protected List> CreatePublisher( try { var queuePublishers = new List>(); - var providerSetting = Configuration["MessageQueueSetting:Provider"]; + var providerSetting = Configuration[$"{MixAppSettingsSection.MessageQueueSettings}:Provider"]; if (string.IsNullOrEmpty(providerSetting)) { return default; @@ -62,7 +63,7 @@ protected List> CreatePublisher( switch (Provider) { case MixQueueProvider.AZURE: - var azureSettingPath = Configuration.GetSection("MessageQueueSetting:AzureServiceBus"); + var azureSettingPath = Configuration.GetSection($"{MixAppSettingsSection.MessageQueueSettings}:AzureServiceBus"); var azureSetting = new AzureQueueSetting(); azureSettingPath.Bind(azureSetting); @@ -71,7 +72,7 @@ protected List> CreatePublisher( Provider, azureSetting, topicId, MixEndpointService)); break; case MixQueueProvider.GOOGLE: - var googleSettingPath = Configuration.GetSection("MessageQueueSetting:GoogleQueueSetting"); + var googleSettingPath = Configuration.GetSection($"{MixAppSettingsSection.MessageQueueSettings}:GoogleQueueSetting"); var googleSetting = new GoogleQueueSetting(); googleSettingPath.Bind(googleSetting); googleSetting.CredentialFile = googleSetting.CredentialFile; @@ -89,7 +90,7 @@ protected List> CreatePublisher( case MixQueueProvider.MIX: if (MixEndpointService.MixMq != null) { - var mixSettingPath = Configuration.GetSection("MessageQueueSetting:Mix"); + var mixSettingPath = Configuration.GetSection($"{MixAppSettingsSection.MessageQueueSettings}:Mix"); var mixSetting = new MixQueueSetting(); mixSettingPath.Bind(mixSetting); queuePublishers.Add( diff --git a/src/platform/mix.queue/Engines/QueueEngineFactory.cs b/src/platform/mix.queue/Engines/QueueEngineFactory.cs index 74ba67e1d..e9b95e1b5 100644 --- a/src/platform/mix.queue/Engines/QueueEngineFactory.cs +++ b/src/platform/mix.queue/Engines/QueueEngineFactory.cs @@ -40,7 +40,7 @@ public static IQueuePublisher CreatePublisher( return publisher; } - public static IQueuePublisher CreateRabbitMqPublisher(IPooledObjectPolicy objectPolicy, string topicId) + public static IQueuePublisher CreateRabbitMqPublisher(IPooledObjectPolicy objectPolicy, string topicId) where T : MessageQueueModel { return new RabbitMQPublisher(objectPolicy, topicId); @@ -74,10 +74,12 @@ public static IQueueSubscriber CreateSubscriber( subscriber.SubscriptionId = subscriptionId; return subscriber; } - public static IQueueSubscriber CreateRabbitMQSubscriber(IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId, Func handler) + public static IQueueSubscriber CreateRabbitMQSubscriber(IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId, Func handler) where T : MessageQueueModel { - return new RabbitMQSubscriber(objectPolicy, topicId, subscriptionId, handler); + var subscriber = new RabbitMQSubscriber(objectPolicy, topicId, subscriptionId, handler); + subscriber.InitializeQueueAsync(objectPolicy, topicId, subscriptionId).Wait(); + return subscriber; } #endregion diff --git a/src/platform/mix.queue/Engines/RabitMq/RabbitMQPublisher.cs b/src/platform/mix.queue/Engines/RabitMq/RabbitMQPublisher.cs index 143dbfe5b..0ea97e947 100644 --- a/src/platform/mix.queue/Engines/RabitMq/RabbitMQPublisher.cs +++ b/src/platform/mix.queue/Engines/RabitMq/RabbitMQPublisher.cs @@ -14,31 +14,27 @@ public class RabbitMQPublisher : IQueuePublisher where T : MessageQueueModel { private readonly string _topicId; - private readonly DefaultObjectPool _objectPool; + private readonly DefaultObjectPool _objectPool; - public RabbitMQPublisher(IPooledObjectPolicy objectPolicy, string topicId) + public RabbitMQPublisher(IPooledObjectPolicy objectPolicy, string topicId) { _topicId = topicId; - _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); + _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); } - public Task SendMessage(T message) + public async Task SendMessage(T message) { var channel = _objectPool.Get(); try { var sendBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)); - var properties = channel.CreateBasicProperties(); - properties.Persistent = true; - - channel.BasicPublish( + await channel.BasicPublishAsync( exchange: _topicId, routingKey: $"{_topicId}", - basicProperties: properties, + true, + basicProperties: new BasicProperties() { Persistent = true }, body: sendBytes); - - return Task.CompletedTask; } catch { diff --git a/src/platform/mix.queue/Engines/RabitMq/RabbitMQSubscriber.cs b/src/platform/mix.queue/Engines/RabitMq/RabbitMQSubscriber.cs index fb7613d89..1191ead33 100644 --- a/src/platform/mix.queue/Engines/RabitMq/RabbitMQSubscriber.cs +++ b/src/platform/mix.queue/Engines/RabitMq/RabbitMQSubscriber.cs @@ -22,39 +22,38 @@ internal class RabbitMQSubscriber : IQueueSubscriber { public string SubscriptionId { get; set; } private readonly Func _messageHandler; - private EventingBasicConsumer _consumer; + private AsyncEventingBasicConsumer _consumer; private string _topicId; - private DefaultObjectPool _objectPool; - private IModel _channel; + private DefaultObjectPool _objectPool; + private IChannel _channel; public RabbitMQSubscriber( - IPooledObjectPolicy objectPolicy, + IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId, Func messageHandler) { _messageHandler = messageHandler; - InitializeQueue(objectPolicy, topicId, subscriptionId); } - private void InitializeQueue(IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId) + public async Task InitializeQueueAsync(IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId) { SubscriptionId = subscriptionId; _topicId = topicId; - _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); + _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); _channel = _objectPool.Get(); - _channel.ExchangeDeclare(exchange: topicId, type: ExchangeType.Topic); - var queueResult = _channel.QueueDeclare(queue: subscriptionId, + await _channel.ExchangeDeclareAsync(exchange: topicId, type: ExchangeType.Topic); + var queueResult = await _channel.QueueDeclareAsync(queue: subscriptionId, durable: true, exclusive: false, autoDelete: false, arguments: null); - _channel.QueueBind(queue: queueResult.QueueName, + await _channel.QueueBindAsync(queue: queueResult.QueueName, exchange: _topicId, routingKey: _topicId); - _channel.BasicQos(0, 1, false); - _consumer = new EventingBasicConsumer(_channel); - _consumer.Received += (ch, ea) => + await _channel.BasicQosAsync(0, 1, false); + _consumer = new AsyncEventingBasicConsumer(_channel); + _consumer.ReceivedAsync += async (ch, ea) => { // received message try @@ -64,17 +63,17 @@ private void InitializeQueue(IPooledObjectPolicy objectPolicy, string to { var msg = JObject.Parse(body).ToObject(); _messageHandler(msg); - _channel.BasicAck(ea.DeliveryTag, false); + await _channel.BasicAckAsync(ea.DeliveryTag, false); } } catch (Exception ex) { Console.Error.WriteLine($"Cannot process message {subscriptionId}"); Console.Error.WriteLine(ex); - _channel.BasicNack(ea.DeliveryTag, false, false); + await _channel.BasicNackAsync(ea.DeliveryTag, false, false); } }; - _channel.BasicConsume(queueResult.QueueName, false, _consumer); + await _channel.BasicConsumeAsync(queueResult.QueueName, false, _consumer); } /// diff --git a/src/platform/mix.queue/Engines/RabitMq/RabbitModelPooledObjectPolicy.cs b/src/platform/mix.queue/Engines/RabitMq/RabbitModelPooledObjectPolicy.cs index 700f5b235..c4417bd00 100644 --- a/src/platform/mix.queue/Engines/RabitMq/RabbitModelPooledObjectPolicy.cs +++ b/src/platform/mix.queue/Engines/RabitMq/RabbitModelPooledObjectPolicy.cs @@ -5,7 +5,7 @@ namespace Mix.Queue.Engines.RabbitMQ { - public class RabbitModelPooledObjectPolicy : IPooledObjectPolicy + public class RabbitModelPooledObjectPolicy : IPooledObjectPolicy { private readonly RabitMqQueueSetting _options; @@ -39,15 +39,15 @@ private IConnection GetConnection() { factory.VirtualHost = _options.VHost; } - return factory.CreateConnection(); + return factory.CreateConnectionAsync().Result; } - public IModel Create() + public IChannel Create() { - return _connection.CreateModel(); + return _connection.CreateChannelAsync().Result; } - public bool Return(IModel obj) + public bool Return(IChannel obj) { if (obj.IsOpen) { diff --git a/src/platform/mix.queue/Engines/RabitMq/RabitMQSubscriber.cs b/src/platform/mix.queue/Engines/RabitMq/RabitMQSubscriber.cs index 9aa21f31e..e51521612 100644 --- a/src/platform/mix.queue/Engines/RabitMq/RabitMQSubscriber.cs +++ b/src/platform/mix.queue/Engines/RabitMq/RabitMQSubscriber.cs @@ -22,39 +22,39 @@ internal class RabitMQSubscriber : IQueueSubscriber { public string SubscriptionId { get; set; } private readonly Func _messageHandler; - private EventingBasicConsumer _consumer; + private AsyncEventingBasicConsumer _consumer; private string _topicId; - private DefaultObjectPool _objectPool; - private IModel _channel; + private DefaultObjectPool _objectPool; + private IChannel _channel; public RabitMQSubscriber( - IPooledObjectPolicy objectPolicy, + IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId, Func messageHandler) { _messageHandler = messageHandler; - InitializeQueue(objectPolicy, topicId, subscriptionId); + InitializeQueueAsync(objectPolicy, topicId, subscriptionId); } - private void InitializeQueue(IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId) + private async Task InitializeQueueAsync(IPooledObjectPolicy objectPolicy, string topicId, string subscriptionId) { SubscriptionId = subscriptionId; _topicId = topicId; - _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); + _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); _channel = _objectPool.Get(); - _channel.ExchangeDeclare(exchange: topicId, type: ExchangeType.Topic); - var queueResult = _channel.QueueDeclare(queue: subscriptionId, + await _channel.ExchangeDeclareAsync(exchange: topicId, type: ExchangeType.Topic); + var queueResult = await _channel.QueueDeclareAsync(queue: subscriptionId, durable: true, exclusive: false, autoDelete: false, arguments: null); - _channel.QueueBind(queue: queueResult.QueueName, + await _channel.QueueBindAsync(queue: queueResult.QueueName, exchange: _topicId, routingKey: _topicId); - _channel.BasicQos(0, 1, false); - _consumer = new EventingBasicConsumer(_channel); - _consumer.Received += (ch, ea) => + await _channel.BasicQosAsync(0, 1, false); + _consumer = new AsyncEventingBasicConsumer(_channel); + _consumer.ReceivedAsync += async (ch, ea) => { // received message try @@ -64,17 +64,17 @@ private void InitializeQueue(IPooledObjectPolicy objectPolicy, string to { var msg = JObject.Parse(body).ToObject(); _messageHandler(msg); - _channel.BasicAck(ea.DeliveryTag, false); + await _channel.BasicAckAsync(ea.DeliveryTag, false); } } catch (Exception ex) { Console.Error.WriteLine($"Cannot process message {subscriptionId}"); Console.Error.WriteLine(ex); - _channel.BasicNack(ea.DeliveryTag, false, false); + await _channel.BasicNackAsync(ea.DeliveryTag, false, false); } }; - _channel.BasicConsume(queueResult.QueueName, false, _consumer); + await _channel.BasicConsumeAsync(queueResult.QueueName, false, _consumer); } /// diff --git a/src/platform/mix.queue/Engines/RabitMq/RabitMqPublisher.cs b/src/platform/mix.queue/Engines/RabitMq/RabitMqPublisher.cs index 1aba166bd..14be18be3 100644 --- a/src/platform/mix.queue/Engines/RabitMq/RabitMqPublisher.cs +++ b/src/platform/mix.queue/Engines/RabitMq/RabitMqPublisher.cs @@ -16,37 +16,37 @@ namespace Mix.Queue.Engines.RabitMQ public class RabitMQPublisher : IQueuePublisher where T : MessageQueueModel { - private DefaultObjectPool _objectPool; + private DefaultObjectPool _objectPool; private readonly string _topicId; - public RabitMQPublisher(IPooledObjectPolicy objectPolicy, string topicId) + public RabitMQPublisher(IPooledObjectPolicy objectPolicy, string topicId) { _topicId = topicId; InitializeQueue(objectPolicy); } - private void InitializeQueue(IPooledObjectPolicy objectPolicy) + private void InitializeQueue(IPooledObjectPolicy objectPolicy) { - _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); + _objectPool = new DefaultObjectPool(objectPolicy, Environment.ProcessorCount * 2); } - public Task SendMessage(T message) + public async Task SendMessage(T message) { var channel = _objectPool.Get(); try { var sendBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)); - var properties = channel.CreateBasicProperties(); + var properties = new BasicProperties(); properties.Persistent = true; - channel.BasicPublish( + await channel.BasicPublishAsync( exchange: _topicId, routingKey: $"{_topicId}", + true, basicProperties: properties, body: sendBytes); - return Task.CompletedTask; } catch { diff --git a/src/platform/mix.queue/Engines/SubscriberBase.cs b/src/platform/mix.queue/Engines/SubscriberBase.cs index 6c917cc50..a2d327a13 100644 --- a/src/platform/mix.queue/Engines/SubscriberBase.cs +++ b/src/platform/mix.queue/Engines/SubscriberBase.cs @@ -34,7 +34,7 @@ public abstract class SubscriberBase : BackgroundService protected IQueueSubscriber _subscriber; protected IServiceScope ServiceScope { get; set; } - private readonly IPooledObjectPolicy? _rabbitMQObjectPolicy; + private readonly IPooledObjectPolicy? _rabbitMQObjectPolicy; protected SubscriberBase( string topicId, @@ -44,7 +44,7 @@ protected SubscriberBase( IConfiguration configuration, IMemoryQueueService queueService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) { _timeout = timeout; _configuration = configuration; diff --git a/src/platform/mix.queue/Extensions/QueueServiceCollectionExtension.cs b/src/platform/mix.queue/Extensions/QueueServiceCollectionExtension.cs index 595ef6eea..7579e2635 100644 --- a/src/platform/mix.queue/Extensions/QueueServiceCollectionExtension.cs +++ b/src/platform/mix.queue/Extensions/QueueServiceCollectionExtension.cs @@ -70,7 +70,7 @@ public static IServiceCollection AddRabbit(this IHostApplicationBuilder builder) builder.Services.Configure(rabbitConfig); builder.Services.AddSingleton(); - builder.Services.AddSingleton, RabbitModelPooledObjectPolicy>(); + builder.Services.AddSingleton(); return builder.Services; } diff --git a/src/platform/mix.queue/mix.queue.csproj b/src/platform/mix.queue/mix.queue.csproj index 43b70a379..efadc2146 100644 --- a/src/platform/mix.queue/mix.queue.csproj +++ b/src/platform/mix.queue/mix.queue.csproj @@ -16,20 +16,20 @@ - - - - - - - - - + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/src/platform/mix.repodb/Repositories/RepoDbRepository.cs b/src/platform/mix.repodb/Repositories/RepoDbRepository.cs index 43998b269..6b0e6ac57 100644 --- a/src/platform/mix.repodb/Repositories/RepoDbRepository.cs +++ b/src/platform/mix.repodb/Repositories/RepoDbRepository.cs @@ -15,13 +15,16 @@ using Newtonsoft.Json.Linq; using Npgsql; using RepoDb; +using RepoDb.DbHelpers; +using RepoDb.DbSettings; using RepoDb.Enumerations; using RepoDb.Interfaces; +using RepoDb.StatementBuilders; using System.Data; namespace Mix.RepoDb.Repositories { - public class RepoDbRepository: IDisposable + public class RepoDbRepository : IDisposable { #region Properties private bool _isRoot; @@ -139,10 +142,17 @@ private void InitializeRepoDb() switch (_databaseProvider) { case MixDatabaseProvider.SQLSERVER: + var dbSetting = new SqlServerDbSetting(); GlobalConfiguration.Setup().UseSqlServer(); + DbSettingMapper + .Add(dbSetting, true); + DbHelperMapper + .Add(new SqlServerDbHelper(), true); + StatementBuilderMapper + .Add(new SqlServerStatementBuilder(dbSetting), true); break; case MixDatabaseProvider.MySQL: - GlobalConfiguration.Setup().UseMySql(); + GlobalConfiguration.Setup().UseMySql().UseMySqlConnector(); break; case MixDatabaseProvider.PostgreSQL: GlobalConfiguration.Setup().UsePostgreSql(); @@ -200,11 +210,11 @@ public async Task> GetPagingAsync( { throw new MixException(MixErrorStatus.Badrequest, $"{nameof(pagingRequest.SortByColumns)} must have value"); } - + List sortByColumns = pagingRequest.SortByColumns .Select(m => new OrderField(m.FieldName, m.Direction == SortDirection.Asc ? Order.Ascending : Order.Descending)) .ToList(); - builder = _databaseProvider == MixDatabaseProvider.PostgreSQL ? new OptimizedPostgresSqlStatementBuilder(true) : _connection.GetStatementBuilder(); + builder = GetStatementBuilder(_databaseProvider)?? _connection.GetStatementBuilder(); int pageSize = pagingRequest.PageSize ?? 100; @@ -224,11 +234,22 @@ public async Task> GetPagingAsync( PageSize = pagingRequest.PageSize, Total = count, TotalPage = (int)Math.Ceiling((double)count / pageSize), - SortByColumns= pagingRequest.SortByColumns + SortByColumns = pagingRequest.SortByColumns } }; } + private IStatementBuilder? GetStatementBuilder(MixDatabaseProvider databaseProvider) + { + return _databaseProvider switch + { + MixDatabaseProvider.PostgreSQL => new OptimizedPostgresSqlStatementBuilder(true), + MixDatabaseProvider.MySQL => StatementBuilderMapper.Get(), + MixDatabaseProvider.SQLSERVER => StatementBuilderMapper.Get(), + MixDatabaseProvider.SQLITE => StatementBuilderMapper.Get() + }; + } + public async Task?> GetListByAsync( string tableName, List queryFields, diff --git a/src/platform/mix.repodb/mix.repodb.csproj b/src/platform/mix.repodb/mix.repodb.csproj index 0d3ce639e..f7cdb806a 100644 --- a/src/platform/mix.repodb/mix.repodb.csproj +++ b/src/platform/mix.repodb/mix.repodb.csproj @@ -20,6 +20,7 @@ + diff --git a/src/platform/mix.service/Services/BaseHubClientService.cs b/src/platform/mix.service/Services/BaseHubClientService.cs index 05027c9dc..261a46ee3 100644 --- a/src/platform/mix.service/Services/BaseHubClientService.cs +++ b/src/platform/mix.service/Services/BaseHubClientService.cs @@ -159,6 +159,7 @@ private void Init() }) .WithAutomaticReconnect() .Build(); + Connection.Closed += async (error) => { await Task.Delay(new Random().Next(0, 5) * 1000); diff --git a/src/platform/mix.shared/Models/ApiResponseModel.cs b/src/platform/mix.shared/Models/ApiResponseModel.cs new file mode 100644 index 000000000..d9600db06 --- /dev/null +++ b/src/platform/mix.shared/Models/ApiResponseModel.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Shared.Models +{ + public class ApiResponseModel + { + public bool IsSuccess { get; set; } + public T? Result { get; set; } + public string[]? Errors { get; set; } + public Exception? Exception { get; set; } + + public ApiResponseModel(bool isSuccess, T? result = default, params string[] messages) + { + IsSuccess = isSuccess; + Result = result; + Errors = messages; + } + } +} diff --git a/src/platform/mix.shared/Models/Configurations/PortalConfigurationModel.cs b/src/platform/mix.shared/Models/Configurations/PortalConfigurationModel.cs new file mode 100644 index 000000000..6ebaadbd4 --- /dev/null +++ b/src/platform/mix.shared/Models/Configurations/PortalConfigurationModel.cs @@ -0,0 +1,24 @@ +using Mix.Heart.Enums; + +namespace Mix.Shared.Models.Configurations +{ + public class PortalConfigurationModel + { + public int PrimaryColorHue { get; set; } + public int PrimaryColorSaturation { get; set; } + public int PrimaryColorBrightness { get; set; } + public string BgColor { get; set; } + public string TextColor { get; set; } + public string PrimaryColor { get; set; } + public string BgColorHover { get; set; } + public string BorderColor { get; set; } + public string BorderColorHover { get; set; } + public string LinkColor { get; set; } + public string LinkColorHover { get; set; } + public string LinkColorActive { get; set; } + public string TextColorHover { get; set; } + public string FontFamily { get; set; } + public string FontSizeH1 { get; set; } + public string FontSize { get; set; } + } +} diff --git a/src/platform/mix.shared/Models/HttpRequestModel.cs b/src/platform/mix.shared/Models/HttpRequestModel.cs index 0ad5c07f7..7dd54261c 100644 --- a/src/platform/mix.shared/Models/HttpRequestModel.cs +++ b/src/platform/mix.shared/Models/HttpRequestModel.cs @@ -6,6 +6,9 @@ public class HttpRequestModel { public string Method { get; set; } public string RequestUrl { get; set; } - public JObject Body { get; set; } + public JObject? Body { get; set; } + public Dictionary? QueryParams { get; set; } + public string? BearerToken { get; set; } + public List>? Headers { get; set; } } } diff --git a/src/platform/mix.shared/Models/SearchMixDbRequestModel.cs b/src/platform/mix.shared/Models/SearchMixDbRequestModel.cs index e0438e267..c757cf8d1 100644 --- a/src/platform/mix.shared/Models/SearchMixDbRequestModel.cs +++ b/src/platform/mix.shared/Models/SearchMixDbRequestModel.cs @@ -32,7 +32,7 @@ public SearchMixDbRequestModel( string? requestedBy = default) { TableName = tableName; - Queries = queries.ToList(); + Queries = queries?.ToList(); Paging = paging; RequestedBy = requestedBy; } @@ -40,7 +40,7 @@ public SearchMixDbRequestModel( public SearchMixDbRequestModel Clone() { var result = ReflectionHelper.CloneObject(this); - result.Queries = Queries.ToList(); + result.Queries = Queries?.ToList(); return result; } @@ -48,7 +48,7 @@ public SearchMixDbRequestModel Clone() public MixConjunction Conjunction { get; set; } = MixConjunction.And; public string TableName { get; set; } public string? RequestedBy { get; set; } - public List Queries { get; set; } + public List? Queries { get; set; } public PagingRequestModel Paging { get; set; } public List RelatedDataRequests { get; set; } } diff --git a/src/platform/mix.shared/Services/HttpService.cs b/src/platform/mix.shared/Services/HttpService.cs index 9c6ffcf4f..0b32556ab 100644 --- a/src/platform/mix.shared/Services/HttpService.cs +++ b/src/platform/mix.shared/Services/HttpService.cs @@ -35,9 +35,14 @@ public async Task SendHttpRequestModel( switch (method) { case "GET": - return await GetAsync(request.RequestUrl, cancellationToken: cancellationToken); + return await GetAsync( + request.RequestUrl, + queryParams: request.QueryParams, + bearerToken: request.BearerToken, + requestHeaders: request.Headers, + cancellationToken: cancellationToken); case "POST": - return await PostAsync(request.RequestUrl, request.Body, cancellationToken: cancellationToken); + return await PostAsync(request.RequestUrl, request.Body,bearerToken: request.BearerToken, requestHeaders: request.Headers, cancellationToken: cancellationToken); } return default; } diff --git a/src/platform/mix.shared/mix.shared.csproj b/src/platform/mix.shared/mix.shared.csproj index e4ba86375..1d93c637f 100644 --- a/src/platform/mix.shared/mix.shared.csproj +++ b/src/platform/mix.shared/mix.shared.csproj @@ -14,16 +14,16 @@ - - - - - + + + + + - + diff --git a/src/platform/mix.signalr.hub/mix.signalr.hub.csproj b/src/platform/mix.signalr.hub/mix.signalr.hub.csproj index d30f06ffa..6446bdbd1 100644 --- a/src/platform/mix.signalr.hub/mix.signalr.hub.csproj +++ b/src/platform/mix.signalr.hub/mix.signalr.hub.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/platform/mix.signalr/mix.signalr.csproj b/src/platform/mix.signalr/mix.signalr.csproj index 70327de01..7b131e077 100644 --- a/src/platform/mix.signalr/mix.signalr.csproj +++ b/src/platform/mix.signalr/mix.signalr.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/src/platform/mix.storage.lib/Engines/AzureStorage/AzureStorageUploader.cs b/src/platform/mix.storage.lib/Engines/AzureStorage/AzureStorageUploader.cs index 07e567320..0b9590f21 100644 --- a/src/platform/mix.storage.lib/Engines/AzureStorage/AzureStorageUploader.cs +++ b/src/platform/mix.storage.lib/Engines/AzureStorage/AzureStorageUploader.cs @@ -23,7 +23,7 @@ public AzureStorageUploader( : base(httpContext, configuration, cmsUow) { _settings = new(); - Configuration.Bind("StorageSetting:AzureStorageSetting", _settings); + Configuration.Bind("StorageSettings:AzureStorageSetting", _settings); _blobClient = new BlobContainerClient(_settings.AzureWebJobStorage, _settings.ContainerName); if (string.IsNullOrEmpty(_settings.CdnUrl)) { diff --git a/src/platform/mix.storage.lib/Services/MixStorageService.cs b/src/platform/mix.storage.lib/Services/MixStorageService.cs index e7e6c8fc3..b3d7cfc30 100644 --- a/src/platform/mix.storage.lib/Services/MixStorageService.cs +++ b/src/platform/mix.storage.lib/Services/MixStorageService.cs @@ -33,7 +33,7 @@ public MixStorageService( private IMixUploader CreateUploader(IHttpContextAccessor httpContext, UnitOfWorkInfo cmsUow) { - _configuration.Bind("StorageSetting", Settings); + _configuration.Bind("StorageSettings", Settings); return Settings.Provider switch { diff --git a/src/platform/mix.storage.lib/Subscribers/StorageBackgroundTaskSubscriber.cs b/src/platform/mix.storage.lib/Subscribers/StorageBackgroundTaskSubscriber.cs index e45c6881e..8bab1f726 100644 --- a/src/platform/mix.storage.lib/Subscribers/StorageBackgroundTaskSubscriber.cs +++ b/src/platform/mix.storage.lib/Subscribers/StorageBackgroundTaskSubscriber.cs @@ -36,7 +36,7 @@ public StorageBackgroundTaskSubscriber( IPortalHubClientService portalHub, IMemoryQueueService queueService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(TopicId, nameof(StorageBackgroundTaskSubscriber), 20, serviceProvider, configuration, queueService, logger, rabbitMQObjectPolicy) { AuditLogService = auditLogService; diff --git a/src/platform/mix.storage.lib/mix.storage.lib.csproj b/src/platform/mix.storage.lib/mix.storage.lib.csproj index 9e540734e..edf6175c6 100644 --- a/src/platform/mix.storage.lib/mix.storage.lib.csproj +++ b/src/platform/mix.storage.lib/mix.storage.lib.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230528150942_InitEcommerce.Designer.cs b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230528150942_InitEcommerce.Designer.cs index 88991cbf3..469f35045 100644 --- a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230528150942_InitEcommerce.Designer.cs +++ b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230528150942_InitEcommerce.Designer.cs @@ -91,7 +91,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("longtext"); b.Property("Total") @@ -173,7 +173,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("longtext"); b.Property("Total") diff --git a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230607113542_AddOrderTempId.Designer.cs b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230607113542_AddOrderTempId.Designer.cs index 382674d4f..7eaa81973 100644 --- a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230607113542_AddOrderTempId.Designer.cs +++ b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/20230607113542_AddOrderTempId.Designer.cs @@ -94,7 +94,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("TempId") .HasColumnType("char(36)"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("longtext"); b.Property("Total") @@ -176,7 +176,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("longtext"); b.Property("Total") diff --git a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/EcommerceDbContextModelSnapshot.cs b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/EcommerceDbContextModelSnapshot.cs index 85ed1cddc..ae3cc0fad 100644 --- a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/EcommerceDbContextModelSnapshot.cs +++ b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/EcommerceDb/EcommerceDbContextModelSnapshot.cs @@ -91,7 +91,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TempId") .HasColumnType("char(36)"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("longtext"); b.Property("Total") @@ -173,7 +173,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("longtext"); b.Property("Total") diff --git a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/20230527064302_InitOnepay.Designer.cs b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/20230527064302_InitOnepay.Designer.cs index dbab22dd3..d3781386e 100644 --- a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/20230527064302_InitOnepay.Designer.cs +++ b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/20230527064302_InitOnepay.Designer.cs @@ -63,7 +63,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("varchar(250)"); b.Property("vpc_AccessCode") diff --git a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/OnepayDbContextModelSnapshot.cs b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/OnepayDbContextModelSnapshot.cs index ee1359804..98c31ee8e 100644 --- a/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/OnepayDbContextModelSnapshot.cs +++ b/src/services/core/ecommerces/mix.services.ecommerce.lib/Migrations/OnepayDb/OnepayDbContextModelSnapshot.cs @@ -60,7 +60,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) MySqlPropertyBuilderExtensions.HasCharSet(b.Property("Status"), "utf8"); - b.Property("Title") + b.Property("DisplayName") .HasColumnType("varchar(250)"); b.Property("vpc_AccessCode") diff --git a/src/services/core/graphql/mix.services.graphql.lib/mix.services.graphql.lib.csproj b/src/services/core/graphql/mix.services.graphql.lib/mix.services.graphql.lib.csproj index 3487298dc..60d63bcdd 100644 --- a/src/services/core/graphql/mix.services.graphql.lib/mix.services.graphql.lib.csproj +++ b/src/services/core/graphql/mix.services.graphql.lib/mix.services.graphql.lib.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/src/services/core/graphql/mix.services.graphql/mix.services.graphql.csproj b/src/services/core/graphql/mix.services.graphql/mix.services.graphql.csproj index 1d1902a0c..6232f3048 100644 --- a/src/services/core/graphql/mix.services.graphql/mix.services.graphql.csproj +++ b/src/services/core/graphql/mix.services.graphql/mix.services.graphql.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/services/core/mix-auth-service/mix.auth.api/Controllers/MixAccountController.cs b/src/services/core/mix-auth-service/mix.auth.api/Controllers/MixAccountController.cs index 67225def3..5d2f3aa31 100644 --- a/src/services/core/mix-auth-service/mix.auth.api/Controllers/MixAccountController.cs +++ b/src/services/core/mix-auth-service/mix.auth.api/Controllers/MixAccountController.cs @@ -238,7 +238,7 @@ public async Task Login([FromBody] LoginDto requestDto) string decryptMsg = AesEncryptionHelper.DecryptString(requestDto.Message, Configuration.AesKey()); var model = JsonConvert.DeserializeObject(decryptMsg); var loginResult = await _idService.LoginAsync(model); - return Ok(loginResult); + return loginResult.IsSuccess ? Ok(loginResult.Result) : BadRequest(loginResult.Errors); } [Route("external-login")] @@ -249,7 +249,7 @@ public async Task ExternalLogin([FromBody] LoginDto requestDto) string decryptMsg = AesEncryptionHelper.DecryptString(requestDto.Message, Configuration.AesKey()); var model = JsonConvert.DeserializeObject(decryptMsg); var loginResult = await _idService.ExternalLogin(model); - return Ok(loginResult); + return loginResult.IsSuccess ? Ok(loginResult.Result) : BadRequest(loginResult.Errors); } [Route("login-unsecure")] @@ -258,7 +258,7 @@ public async Task ExternalLogin([FromBody] LoginDto requestDto) public async Task LoginUnSecure([FromBody] LoginRequestModel model) { var loginResult = await _idService.LoginAsync(model); - return Ok(loginResult); + return loginResult.IsSuccess ? Ok(loginResult.Result) : BadRequest(loginResult.Errors); } [AllowAnonymous] @@ -266,7 +266,7 @@ public async Task LoginUnSecure([FromBody] LoginRequestModel model public async Task ExternalLoginUnSecure([FromBody] RegisterExternalBindingModel model) { var loginResult = await _idService.ExternalLogin(model); - return Ok(loginResult); + return loginResult.IsSuccess ? Ok(loginResult.Result) : BadRequest(loginResult.Errors); } [Route("get-external-login-providers")] @@ -282,7 +282,7 @@ public async Task GetExternalLoginProviders() public async Task RenewToken([FromBody] RenewTokenDto refreshTokenDto) { var token = await _idService.RenewTokenAsync(refreshTokenDto); - return Ok(token); + return token.IsSuccess ? Ok(token.Result): BadRequest(token.Errors); } [MixAuthorize] diff --git a/src/services/core/mix-auth-service/mix.auth.api/Domain/Subscribers/MixAuthBackgroundTaskSubscriber.cs b/src/services/core/mix-auth-service/mix.auth.api/Domain/Subscribers/MixAuthBackgroundTaskSubscriber.cs index 773916623..e23ace136 100644 --- a/src/services/core/mix-auth-service/mix.auth.api/Domain/Subscribers/MixAuthBackgroundTaskSubscriber.cs +++ b/src/services/core/mix-auth-service/mix.auth.api/Domain/Subscribers/MixAuthBackgroundTaskSubscriber.cs @@ -25,7 +25,7 @@ public MixAuthBackgroundTaskSubscriber( IPortalHubClientService portalHub, IMemoryQueueService queueService, ILogger logger, - IPooledObjectPolicy? rabbitMQObjectPolicy = null) + IPooledObjectPolicy? rabbitMQObjectPolicy = null) : base(topicId, nameof(MixAuthBackgroundTaskSubscriber), 20, serviceProvider, configuration, queueService, logger, rabbitMQObjectPolicy) { PortalHub = portalHub; diff --git a/src/services/core/mix-auth-service/mix.auth.api/mix.auth.api.csproj b/src/services/core/mix-auth-service/mix.auth.api/mix.auth.api.csproj index 25fa306f1..c4014a8e5 100644 --- a/src/services/core/mix-auth-service/mix.auth.api/mix.auth.api.csproj +++ b/src/services/core/mix-auth-service/mix.auth.api/mix.auth.api.csproj @@ -14,7 +14,7 @@ Mix.Auth.Api - + @@ -47,7 +47,7 @@ - + diff --git a/src/services/core/mix-message-queue/mix.mq.server/mix.mq.server.csproj b/src/services/core/mix-message-queue/mix.mq.server/mix.mq.server.csproj index 265add976..f74729ec8 100644 --- a/src/services/core/mix-message-queue/mix.mq.server/mix.mq.server.csproj +++ b/src/services/core/mix-message-queue/mix.mq.server/mix.mq.server.csproj @@ -18,11 +18,11 @@ - - - - - + + + + + diff --git a/src/services/mix.automation/mix.automation.api/Controllers/MixDomainController.cs b/src/services/mix.automation/mix.automation.api/Controllers/MixDomainController.cs new file mode 100644 index 000000000..790494a98 --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/Controllers/MixDomainController.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Mvc; +using Mix.Auth.Constants; +using Mix.Automation.Lib.Entities; +using Mix.Automation.Lib.ViewModels; +using Mix.Heart.Services; +using Mix.Heart.UnitOfWork; +using Mix.Lib.Attributes; +using Mix.Lib.Base; +using Mix.Lib.Interfaces; +using Mix.Lib.Services; +using Mix.Mq.Lib.Models; +using Mix.Queue.Interfaces; +using Mix.SignalR.Interfaces; + +namespace Mix.Automation.Api.Controllers +{ + [Route("api/v2/rest/mix-portal/mix-workflow-trigger")] + [ApiController] + [MixAuthorize(MixRoles.Owner)] + public class WorkflowTriggerController + : MixRestfulApiControllerBase + { + public WorkflowTriggerController( + IHttpContextAccessor httpContextAccessor, + IConfiguration configuration, + MixCacheService cacheService, + MixIdentityService mixIdentityService, UnitOfWorkInfo uow, IMemoryQueueService queueService, + IPortalHubClientService portalHub, + IMixTenantService mixTenantService) + : base(httpContextAccessor, configuration, + cacheService, mixIdentityService, uow, queueService, portalHub, mixTenantService) + { + + } + + #region Overrides + + + #endregion + } +} diff --git a/src/services/mix.automation/mix.automation.api/Program.cs b/src/services/mix.automation/mix.automation.api/Program.cs new file mode 100644 index 000000000..7371e0123 --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/Program.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection.Extensions; +using Mix.Automation.Lib.Extensions; +using Mix.Lib.Interfaces; +using Mix.Lib.Services; + +var builder = WebApplication.CreateBuilder(args); + +builder.AddServiceDefaults(); + +// Add services to the container. +builder.AddConfigurations(); + +builder.Services.AddControllers(); +builder.AddMixCommonServices(); + +builder.AddMixAutomationServices(); +builder.AddMixCors(); +var app = builder.Build(); + +app.MapDefaultEndpoints(); + +// Configure the HTTP request pipeline. + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/src/services/mix.automation/mix.automation.api/Properties/launchSettings.json b/src/services/mix.automation/mix.automation.api/Properties/launchSettings.json new file mode 100644 index 000000000..0c86084eb --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:18853", + "sslPort": 44390 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "http://localhost:5227", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "https://localhost:7287;http://localhost:5227", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/services/mix.automation/mix.automation.api/StartupService.cs b/src/services/mix.automation/mix.automation.api/StartupService.cs new file mode 100644 index 000000000..1b412ce58 --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/StartupService.cs @@ -0,0 +1,20 @@ +using Mix.Automation.Lib.Extensions; +using Mix.Shared.Interfaces; +namespace mix.automation.api +{ + public class StartupService : IStartupService + { + public void AddServices(IHostApplicationBuilder builder) + { + builder.AddMixAutomationServices(); + } + + public void UseApps(IApplicationBuilder app, IConfiguration configuration, bool isDevelop) + { + } + + public void UseEndpoints(IEndpointRouteBuilder endpoints, IConfiguration configuration, bool isDevelop) + { + } + } +} diff --git a/src/services/mix.automation/mix.automation.api/appsettings.Development.json b/src/services/mix.automation/mix.automation.api/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/services/mix.automation/mix.automation.api/appsettings.json b/src/services/mix.automation/mix.automation.api/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/services/mix.automation/mix.automation.api/mix.automation.api.csproj b/src/services/mix.automation/mix.automation.api/mix.automation.api.csproj new file mode 100644 index 000000000..da3b1d3dc --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/mix.automation.api.csproj @@ -0,0 +1,21 @@ + + + + net9.0 + enable + enable + Mix.Automation.Api + + + + + + + + + + Client + + + + diff --git a/src/services/mix.automation/mix.automation.api/mix.automation.api.http b/src/services/mix.automation/mix.automation.api/mix.automation.api.http new file mode 100644 index 000000000..c17b2bac2 --- /dev/null +++ b/src/services/mix.automation/mix.automation.api/mix.automation.api.http @@ -0,0 +1,6 @@ +@mix.automation.api_HostAddress = http://localhost:5227 + +GET {{mix.automation.api_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowActionConfiguration.cs b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowActionConfiguration.cs new file mode 100644 index 000000000..fbdfbdc5c --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowActionConfiguration.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Automation.Lib.Enums; +using Mix.Constant.Constants; +using Mix.Database.EntityConfigurations.Base; +using Mix.Database.Services.MixGlobalSettings; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Entities.EntityConfigurations +{ + public class WorkflowActionConfiguration : SimpleEntityBaseConfiguration + { + public WorkflowActionConfiguration(DatabaseService databaseService) : base(databaseService) + { + } + + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + builder.ToTable(MixAutomationConstants.DatabaseNames.WorkflowAction); + + builder.Property(e => e.Type) + .HasColumnName("type") + .HasConversion(new EnumToStringConverter()); + + builder.Property(e => e.Body) + .HasConversion( + v => v != default ? v.ToString(Newtonsoft.Json.Formatting.None) : default, + v => !string.IsNullOrEmpty(v) ? JObject.Parse(v) : default) + .IsRequired(false) + .HasColumnName("body") + .HasColumnType(Config.Text); + + builder.Property(e => e.Request) + .HasConversion( + v => v != default ? v.ToString(Newtonsoft.Json.Formatting.None) : default, + v => !string.IsNullOrEmpty(v) ? JObject.Parse(v) : default) + .IsRequired(false) + .HasColumnName("request") + .HasColumnType(Config.Text); + + builder.Property(e => e.Type) + .HasColumnName("type"); + + builder.Property(e => e.Index) + .HasColumnName("index"); + + builder.Property(e => e.WorkflowId) + .HasColumnName("mix_workflow_id"); + + builder.Property(e => e.Title) + .HasColumnName("title"); + + builder.Property(e => e.Description) + .HasColumnName("description"); + + builder.Property(e => e.CreatedDateTime) + .HasColumnName("created_date_time") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.LastModified) + .HasColumnName("last_modified") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.CreatedBy) + .HasColumnName("created_by") + .HasColumnType($"{Config.String}{Config.MediumLength}"); + + builder.Property(e => e.Priority) + .HasColumnName("priority") + .HasColumnType(Config.Integer); + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowActionDataConfiguration.cs b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowActionDataConfiguration.cs new file mode 100644 index 000000000..5c23b93dc --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowActionDataConfiguration.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Mix.Automation.Lib.Enums; +using Mix.Constant.Constants; +using Mix.Database.EntityConfigurations.Base; +using Mix.Database.Services.MixGlobalSettings; +using Mix.Heart.Entities; +using Mix.Heart.Enums; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Entities.EntityConfigurations +{ + public class WorkflowActionDataConfiguration : SimpleEntityBaseConfiguration + { + public WorkflowActionDataConfiguration(DatabaseService databaseService) : base(databaseService) + { + } + + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + builder.ToTable(MixAutomationConstants.DatabaseNames.WorkflowActionData); + + builder.Property(e => e.Index) + .HasColumnName("index"); + + builder.Property(e => e.ActionType) + .HasColumnName("action_type") + .HasConversion(new EnumToStringConverter()); + + builder.Property(e => e.ActionStatus) + .HasColumnName("action_status") + .HasConversion(new EnumToStringConverter()); + + builder.Property(e => e.IsSuccess) + .HasColumnName("is_success"); + + builder.Property(e => e.Body) + .HasConversion( + v => v.ToString(Newtonsoft.Json.Formatting.None), + v => !string.IsNullOrEmpty(v) ? JObject.Parse(v) : default) + .IsRequired(false) + .HasColumnName("body") + .HasColumnType(Config.Text); + + builder.Property(e => e.Request) + .HasConversion( + v => v != default ? v.ToString(Newtonsoft.Json.Formatting.None) : default, + v => !string.IsNullOrEmpty(v) ? JObject.Parse(v) : default) + .IsRequired(false) + .HasColumnName("request") + .HasColumnType(Config.Text); + + builder.Property(e => e.Response) + .HasConversion( + v => v != default ? v.ToString(Newtonsoft.Json.Formatting.None) : default, + v => !string.IsNullOrEmpty(v) ? JObject.Parse(v) : default) + .IsRequired(false) + .HasColumnName("response") + .HasColumnType(Config.Text); + + builder.Property(e => e.Exception) + .HasConversion( + v => v != default ? v.ToString(Newtonsoft.Json.Formatting.None) : default, + v => !string.IsNullOrEmpty(v) ? JObject.Parse(v) : default) + .IsRequired(false) + .HasColumnName("exception") + .HasColumnType(Config.Text); + + builder.Property(e => e.Duration) + .HasColumnName("duration"); + + builder.Property(e => e.TriggerId) + .HasColumnName("mix_workflow_trigger_id"); + + builder.Property(e => e.ActionId) + .HasColumnName("mix_workflow_action_id"); + + builder.Property(e => e.CreatedDateTime) + .HasColumnName("created_date_time") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.LastModified) + .HasColumnName("last_modified") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.CreatedBy) + .HasColumnName("created_by") + .HasColumnType($"{Config.String}{Config.MediumLength}"); + + builder.Property(e => e.Priority) + .HasColumnName("priority") + .HasColumnType(Config.Integer); + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowConfiguration.cs b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowConfiguration.cs new file mode 100644 index 000000000..6fd721e93 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowConfiguration.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mix.Constant.Constants; +using Mix.Database.EntityConfigurations.Base; +using Mix.Database.Services.MixGlobalSettings; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Entities.EntityConfigurations +{ + public class WorkflowConfiguration : SimpleEntityBaseConfiguration + { + public WorkflowConfiguration(DatabaseService databaseService) : base(databaseService) + { + } + + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + builder.ToTable(MixAutomationConstants.DatabaseNames.Workflow); + builder.Property(e => e.Title) + .HasColumnName("title"); + builder.Property(e => e.Description) + .HasColumnName("description"); + + builder.Property(e => e.CreatedDateTime) + .HasColumnName("created_date_time") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.LastModified) + .HasColumnName("last_modified") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.CreatedBy) + .HasColumnName("created_by") + .HasColumnType($"{Config.String}{Config.MediumLength}"); + + builder.Property(e => e.Priority) + .HasColumnName("priority") + .HasColumnType(Config.Integer); + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowTriggerConfiguration.cs b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowTriggerConfiguration.cs new file mode 100644 index 000000000..141997fe0 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/EntityConfigurations/WorkflowTriggerConfiguration.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Mix.Constant.Constants; +using Mix.Database.EntityConfigurations.Base; +using Mix.Database.Services.MixGlobalSettings; +using Newtonsoft.Json.Linq; +using RepoDb.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Entities.EntityConfigurations +{ + public class WorkflowTriggerConfiguration : SimpleEntityBaseConfiguration + { + public WorkflowTriggerConfiguration(DatabaseService databaseService) : base(databaseService) + { + } + + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + builder.ToTable(MixAutomationConstants.DatabaseNames.WorkflowTrigger); + + builder.Property(e => e.IsSuccess) + .HasColumnName("is_success"); + + builder.Property(e => e.Input) + .HasConversion( + v => v != default ? v.ToString(Newtonsoft.Json.Formatting.None) : default, + v => !string.IsNullOrEmpty(v) ? JObject.Parse(v) : default) + .IsRequired(false) + .HasColumnName("input") + .HasColumnType(Config.Text); + + builder.Property(e => e.WorkflowId) + .HasColumnName("mix_workflow_id"); + + builder.Property(e => e.CreatedDateTime) + .HasColumnName("created_date_time") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.LastModified) + .HasColumnName("last_modified") + .HasColumnType(Config.DateTime); + + builder.Property(e => e.CreatedBy) + .HasColumnName("created_by") + .HasColumnType($"{Config.String}{Config.MediumLength}"); + + builder.Property(e => e.Priority) + .HasColumnName("priority") + .HasColumnType(Config.Integer); + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/Workflow.cs b/src/services/mix.automation/mix.automation.lib/Entities/Workflow.cs new file mode 100644 index 000000000..020ad5ca3 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/Workflow.cs @@ -0,0 +1,11 @@ +using Mix.Heart.Entities; +using Mix.Heart.Enums; + +namespace Mix.Automation.Lib.Entities +{ + public class Workflow: WorkflowEntityBase + { + public string? Title { get; set; } + public string? Description { get; set; } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/WorkflowAction.cs b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowAction.cs new file mode 100644 index 000000000..61cab883d --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowAction.cs @@ -0,0 +1,18 @@ +using Mix.Automation.Lib.Enums; +using Mix.Heart.Entities; +using Mix.Heart.Enums; +using Newtonsoft.Json.Linq; + +namespace Mix.Automation.Lib.Entities +{ + public class WorkflowAction: WorkflowEntityBase + { + public ActionType Type { get; set; } + public string? Title { get; set; } + public string? Description { get; set; } + public int? Index { get; set; } + public int? WorkflowId { get; set; } + public JObject? Request { get; set; } + public JObject? Body { get; set; } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/WorkflowActionData.cs b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowActionData.cs new file mode 100644 index 000000000..1cbdc8fc3 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowActionData.cs @@ -0,0 +1,22 @@ +using Mix.Automation.Lib.Enums; +using Mix.Heart.Entities; +using Mix.Heart.Enums; +using Newtonsoft.Json.Linq; + +namespace Mix.Automation.Lib.Entities +{ + public class WorkflowActionData : WorkflowEntityBase + { + public bool? IsSuccess { get; set; } + public ActionStatus? ActionStatus { get; set; } + public ActionType ActionType { get; set; } + public double Duration { get; set; } + public int TriggerId { get; set; } + public int ActionId { get; set; } + public int Index { get; set; } + public JObject? Request { get; set; } + public JObject? Body { get; set; } + public JObject? Response { get; set; } + public JObject? Exception { get; set; } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/WorkflowDbContext.cs b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowDbContext.cs new file mode 100644 index 000000000..e8842ab3b --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowDbContext.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using Mix.Constant.Constants; +using Mix.Database.Base; +using Mix.Database.Services.MixGlobalSettings; +using Mix.Heart.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Entities +{ + public sealed class WorkflowDbContext : BaseDbContext + { + public WorkflowDbContext(DatabaseService databaseService) + : base(databaseService, MixConstants.CONST_MIXDB_CONNECTION) + { + _dbContextType = GetType(); + } + public DbSet Workflow { get; set; } + public DbSet WorkflowAction { get; set; } + public DbSet WorkflowTrigger { get; set; } + public DbSet WorkflowActionData { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/WorkflowEntityBase.cs b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowEntityBase.cs new file mode 100644 index 000000000..b561adbf1 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowEntityBase.cs @@ -0,0 +1,17 @@ +using Mix.Heart.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Entities +{ + public abstract class WorkflowEntityBase : SimpleEntityBase + { + public DateTime CreatedDateTime { get; set; } + public DateTime? LastModified { get; set; } + public string? CreatedBy { get; set; } + public int Priority { get; set; } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Entities/WorkflowTrigger.cs b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowTrigger.cs new file mode 100644 index 000000000..48b25bf64 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Entities/WorkflowTrigger.cs @@ -0,0 +1,13 @@ +using Mix.Heart.Entities; +using Mix.Heart.Enums; +using Newtonsoft.Json.Linq; + +namespace Mix.Automation.Lib.Entities +{ + public class WorkflowTrigger : WorkflowEntityBase + { + public bool IsSuccess { get; set; } + public int WorkflowId { get; set; } + public JObject? Input { get; set; } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Enums/ActionStatus.cs b/src/services/mix.automation/mix.automation.lib/Enums/ActionStatus.cs new file mode 100644 index 000000000..829cac538 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Enums/ActionStatus.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Enums +{ + public enum ActionStatus + { + Started, + Succeeded, + Failed, + Canceled + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Enums/ActionType.cs b/src/services/mix.automation/mix.automation.lib/Enums/ActionType.cs new file mode 100644 index 000000000..e57f77539 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Enums/ActionType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Enums +{ + public enum ActionType + { + Request, + ParseMarkdownJson, + PortalNotification + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Enums/MixAutomationAction.cs b/src/services/mix.automation/mix.automation.lib/Enums/MixAutomationAction.cs new file mode 100644 index 000000000..e82f1c136 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Enums/MixAutomationAction.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Enums +{ + internal enum MixAutomationAction + { + CreateTrigger, + ExecuteTrigger + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Extensions/ServiceExtensions.cs b/src/services/mix.automation/mix.automation.lib/Extensions/ServiceExtensions.cs new file mode 100644 index 000000000..75db4a2a7 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Extensions/ServiceExtensions.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Mix.Automation.Lib.Entities; +using Mix.Automation.Lib.MessageQueue; +using Mix.Automation.Lib.Services; +using Mix.Automation.Lib.Subscribers; +using Mix.Database.Base; +using Mix.Database.Services.MixGlobalSettings; +using Mix.Heart.UnitOfWork; +using Mix.Lib.Middlewares; +using Mix.Mq.Lib.Models; +using Mix.Queue.Interfaces; +using Mix.Queue.Services; +using Mix.Service.Services; +using Mix.Shared.Services; +using Mix.SignalR.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Extensions +{ + public static class ServiceExtensions + { + public static IHostApplicationBuilder AddMixAutomationServices(this IHostApplicationBuilder builder) + { + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); + builder.Services.AddDbContext(); + builder.Services.TryAddScoped>(); + builder.Services.TryAddSingleton(); + builder.Services.AddMixCache(builder.Configuration); + builder.Services.TryAddSingleton(); + builder.Services.TryAddScoped(); + builder.Services.AddHostedService(); + builder.Services.AddHostedService(); + builder.Services.TryAddSingleton, MemoryQueueService>(); + UnitOfWorkMiddleware.AddUnitOfWork>(); + return builder; + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/MessageQueue/MixAutomationPublisher.cs b/src/services/mix.automation/mix.automation.lib/MessageQueue/MixAutomationPublisher.cs new file mode 100644 index 000000000..f80e83433 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/MessageQueue/MixAutomationPublisher.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ObjectPool; +using Mix.Database.Services.MixGlobalSettings; +using Mix.Mq.Lib.Models; +using Mix.Queue.Engines; +using Mix.Queue.Interfaces; +using RabbitMQ.Client; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.MessageQueue +{ + public sealed class MixAutomationPublisher : PublisherBase + { + public MixAutomationPublisher(IMemoryQueueService queueService, IConfiguration configuration, MixEndpointService mixEndpointService, ILogger logger, IPooledObjectPolicy? rabbitMQObjectPolicy = null) + : base(MixAutomationConstants.Topics.MixAutomation, queueService, configuration, mixEndpointService, logger, rabbitMQObjectPolicy) + { + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/MessageQueue/MixAutomationSubscriber.cs b/src/services/mix.automation/mix.automation.lib/MessageQueue/MixAutomationSubscriber.cs new file mode 100644 index 000000000..00c564e8a --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/MessageQueue/MixAutomationSubscriber.cs @@ -0,0 +1,87 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ObjectPool; +using Mix.Automation.Lib.Entities; +using Mix.Automation.Lib.Enums; +using Mix.Automation.Lib.Models; +using Mix.Automation.Lib.Services; +using Mix.Automation.Lib.ViewModels; +using Mix.Constant.Enums; +using Mix.Database.Services.MixGlobalSettings; +using Mix.Heart.Services; +using Mix.Heart.UnitOfWork; +using Mix.Mq.Lib.Models; +using Mix.Queue.Engines; +using Mix.Queue.Interfaces; +using RabbitMQ.Client; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Subscribers +{ + public sealed class MixAutomationSubscriber : SubscriberBase + { + private readonly string[] _allowActions; + public MixAutomationSubscriber( + IServiceProvider servicesProvider, + IConfiguration configuration, + IMemoryQueueService queueService, + ILogger logger, + DatabaseService databaseService, + MixCacheService cacheService, + IPooledObjectPolicy? rabbitMQObjectPolicy = null) + : base( + MixAutomationConstants.Topics.MixAutomation, + nameof(MixAutomationSubscriber), + 2000, + servicesProvider, configuration, queueService, logger, rabbitMQObjectPolicy) + { + _allowActions = GetAllowActions(); + } + public override Task StartAsync(CancellationToken cancellationToken) + { + return base.StartAsync(cancellationToken); + } + public override async Task Handler(MessageQueueModel model, CancellationToken cancellationToken) + { + if (!_allowActions.Any(m => m == model.Action)) + { + return; + } + _ = Enum.TryParse(model.Action, out MixAutomationAction action); + using (ServiceScope = ServicesProvider.CreateScope()) + { + var srv = GetRequiredService(); + switch (action) + { + case MixAutomationAction.CreateTrigger: + var dto = model.ParseData(); + if (srv != null) + { + await srv.CreateTrigger(dto); + } + break; + case MixAutomationAction.ExecuteTrigger: + var exeModel = model.ParseData(); + if (srv != null) + { + await srv.TriggerWorkflow(exeModel, cancellationToken); + } + break; + default: + break; + } + } + } + + private string[] GetAllowActions() + { + return [.. Enum.GetNames(typeof(MixAutomationAction))]; + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/MixAutomationConstants.cs b/src/services/mix.automation/mix.automation.lib/MixAutomationConstants.cs new file mode 100644 index 000000000..a6b8a1c96 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/MixAutomationConstants.cs @@ -0,0 +1,25 @@ +using Quartz; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib +{ + public class MixAutomationConstants + { + public static class DatabaseNames + { + public const string Workflow = "mix_workflow"; + public const string WorkflowAction = "mix_workflow_action"; + public const string WorkflowTrigger = "mix_workflow_trigger"; + public const string WorkflowActionData = "mix_workflow_action_data"; + } + + public static class Topics + { + public const string MixAutomation = "MixAutomation"; + } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Models/CreateWorkflowTriggerModel.cs b/src/services/mix.automation/mix.automation.lib/Models/CreateWorkflowTriggerModel.cs new file mode 100644 index 000000000..5b4cce6f1 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Models/CreateWorkflowTriggerModel.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Models +{ + public sealed class CreateWorkflowTriggerModel + { + public int WorkflowId { get; set; } + public JObject? Input { get; set; } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Models/ExecuteWorkflowTriggerModel.cs b/src/services/mix.automation/mix.automation.lib/Models/ExecuteWorkflowTriggerModel.cs new file mode 100644 index 000000000..dae7a3911 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Models/ExecuteWorkflowTriggerModel.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Models +{ + public sealed class ExecuteWorkflowTriggerModel + { + public int TriggerId { get; set; } + } +} diff --git a/src/services/mix.automation/mix.automation.lib/Services/WorkflowService.cs b/src/services/mix.automation/mix.automation.lib/Services/WorkflowService.cs new file mode 100644 index 000000000..a0e6b7dbb --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/Services/WorkflowService.cs @@ -0,0 +1,262 @@ +using FirebaseAdmin.Auth.Multitenancy; +using Microsoft.CodeAnalysis.CodeActions; +using Mix.Automation.Lib.Entities; +using Mix.Automation.Lib.Enums; +using Mix.Automation.Lib.Models; +using Mix.Automation.Lib.ViewModels; +using Mix.Database.Services.MixGlobalSettings; +using Mix.Heart.Extensions; +using Mix.Heart.Helpers; +using Mix.Heart.Services; +using Mix.Heart.UnitOfWork; +using Mix.Mq.Lib.Models; +using Mix.Queue.Interfaces; +using Mix.Shared.Models; +using Mix.Shared.Services; +using Mix.SignalR.Hubs; +using Mix.SignalR.Interfaces; +using Mix.SignalR.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.Services +{ + public sealed class WorkflowService + { + protected readonly IMemoryQueueService _queueService; + private readonly IPortalHubClientService _portalHub; + private DatabaseService _databaseService; + private MixCacheService _cacheService; + private readonly HttpService _httpService; + private UnitOfWorkInfo _uow; + + public WorkflowService(UnitOfWorkInfo uow, MixCacheService cacheService, DatabaseService databaseService, HttpService httpService, IPortalHubClientService portalHub, IMemoryQueueService queueService) + { + _uow = uow; + _cacheService = cacheService; + _databaseService = databaseService; + _httpService = httpService; + _portalHub = portalHub; + _queueService = queueService; + } + + public async Task CreateTrigger(CreateWorkflowTriggerModel dto, CancellationToken cancellationToken = default) + { + var wf = await WorkflowViewModel.GetRepository(_uow, _cacheService).GetSingleAsync(m => m.Id == dto.WorkflowId); + if (wf != null) + { + var trigger = new WorkflowTriggerViewModel() + { + WorkflowId = dto.WorkflowId, + Input = dto.Input, + Actions = new List() + }; + + JArray responses = new JArray(); + if (wf != null) + { + foreach (var action in wf.Actions.OrderBy(m => m.Index)) + { + trigger.Actions.Add(new WorkflowActionDataViewModel() + { + ActionId = action.Id, + ActionType = action.Type, + Index = action.Index, + Request = action.Request, + Body = action.Body + }); + } + trigger.SetUowInfo(_uow, _cacheService); + await trigger.SaveAsync(cancellationToken); + await _uow.CompleteAsync(cancellationToken); + + + var msg = new MessageQueueModel(1) + { + Success = true, + TopicId = MixAutomationConstants.Topics.MixAutomation, + Action = MixAutomationAction.ExecuteTrigger.ToString(), + Data = ReflectionHelper.ParseObject(new ExecuteWorkflowTriggerModel() + { + TriggerId = trigger.Id + }).ToString(Formatting.None) + }; + + _queueService.PushMemoryQueue(msg); + } + } + } + + public async Task TriggerWorkflow(ExecuteWorkflowTriggerModel dto, CancellationToken cancellationToken = default) + { + var trigger = await WorkflowTriggerViewModel.GetRepository(_uow, _cacheService).GetSingleAsync(m => m.Id == dto.TriggerId); + if (trigger != null) + { + JArray responses = new JArray(); + foreach (var action in trigger.Actions.OrderBy(m => m.Index)) + { + if (action.Index == 0) + { + if (trigger.Input != null) + { + action.Request = InjectParameters(action.Request, trigger.Input); + action.Body = InjectParameters(action.Body, trigger.Input); + } + } + else + { + action.Request = InjectParameters(action.Request, responses); + action.Body = InjectParameters(action.Body, responses); + } + await ExecuteAction(action, responses, cancellationToken); + responses.Add(action.Response ?? new JObject()); + if (!action.IsSuccess) + { + break; + } + } + trigger.SetUowInfo(_uow, _cacheService); + await trigger.SaveAsync(cancellationToken); + await _uow.CompleteAsync(cancellationToken); + } + } + + private async Task ExecuteAction(WorkflowActionDataViewModel action, JArray responses, CancellationToken cancellationToken) + { + JObject? resp = default; + DateTime start = DateTime.UtcNow; + action.ActionStatus = ActionStatus.Started; + action.CreatedDateTime = DateTime.UtcNow; + action.LastModified = DateTime.UtcNow; + try + { + await NotifyWorkflowActionStatus(action, cancellationToken); + switch (action.ActionType) + { + case ActionType.Request: + if (action.Request != null) + { + var request = action.Request.ToObject(); + if (request == null || string.IsNullOrEmpty(request.RequestUrl)) + { + action.IsSuccess = false; + action.ActionStatus = ActionStatus.Failed; + action.Response = new JObject(new JProperty("error", "RequestUrl cannot be nul")); + break; + } + + request!.Body = action.Body; + action.Request = action.Request; + action.Body = action.Body; + action.Response = await _httpService.SendHttpRequestModel(request); + } + break; + case ActionType.ParseMarkdownJson: + string markdownJsonPattern = "\\`{3}(\\w+)?\\n([^\\`]+)\\n\\`{3}"; + action.Body = InjectParameters(action.Body, responses); + var markdownContent = action.Body!.Value("content"); + if (!string.IsNullOrEmpty(markdownContent) && Regex.IsMatch(markdownContent, markdownJsonPattern)) + { + action.Response = JObject.Parse(Regex.Match(markdownContent, markdownJsonPattern).Groups[2].Value.Replace("\\\"", "\"")); + } + break; + case ActionType.PortalNotification: + var portalMsg = InjectParameters(action.Body, responses); + action.Body = new JObject(new JProperty("input", portalMsg)); + await _portalHub.SendMessageAsync(portalMsg.ToObject()); + action.Response = new JObject(); + break; + default: + break; + } + + action.IsSuccess = true; + action.ActionStatus = ActionStatus.Succeeded; + action.Duration = (DateTime.UtcNow - start).TotalMilliseconds; + await NotifyWorkflowActionStatus(action, cancellationToken); + } + catch (Exception ex) + { + action.IsSuccess = false; + action.Exception = JObject.FromObject(ex); + action.ActionStatus = ActionStatus.Failed; + action.Duration = (DateTime.UtcNow - start).TotalMilliseconds; + await NotifyWorkflowActionStatus(action, cancellationToken); + } + } + + private async Task NotifyWorkflowActionStatus(WorkflowActionDataViewModel action, CancellationToken cancellationToken) + { + await _portalHub.SendMessageAsync(new SignalRMessageModel() + { + Action = SignalR.Enums.MessageAction.NewMessage, + Data = action, + Title = "Action Status", + Message = action.ActionStatus.ToString() + }); + } + + #region Private Methods + + private JObject? InjectParameters(JObject? body, JToken responses) + { + string pattern = "(\\[{2})((\\d|\\.|\\w)+)(\\]{2})"; + string? strBody = body?.ToString(Formatting.None); + if (string.IsNullOrEmpty(strBody) || !Regex.IsMatch(strBody, pattern)) + { + return body; + } + else + { + foreach (Match match in Regex.Matches(strBody, pattern)) + { + strBody = strBody.Replace(match.Value, ExtractInput(match.Groups[2].Value, responses).Escape()); + } + return JObject.Parse(strBody); + } + } + + private T? ExtractInput(string inputPath, JToken jObject) + { + if (jObject is null) + { + return default; + } + + if (string.IsNullOrEmpty(inputPath)) + { + return jObject.ToObject(); + } + + var keys = inputPath.Split('.'); + JToken? token = jObject; + foreach (var key in keys) + { + if (int.TryParse(key, out var index)) + { + if (token.Type == JTokenType.Array) + { + token = JArray.FromObject(token)[index]; + } + else + { + token = token[key]; + } + } + else + { + token = token[key]; + } + } + return token != null ? token.ToObject() : default; + } + + #endregion + } +} diff --git a/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowActionDataViewModel.cs b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowActionDataViewModel.cs new file mode 100644 index 000000000..cfdc77ac8 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowActionDataViewModel.cs @@ -0,0 +1,64 @@ +using Mix.Automation.Lib.Entities; +using Mix.Automation.Lib.Enums; +using Mix.Heart.UnitOfWork; +using Mix.Heart.ViewModel; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.ViewModels +{ + public sealed class WorkflowActionDataViewModel : WorkflowViewModelBase + { + #region Contructors + + public WorkflowActionDataViewModel() + { + } + + public WorkflowActionDataViewModel(WorkflowDbContext context) : base(context) + { + } + + public WorkflowActionDataViewModel(UnitOfWorkInfo unitOfWorkInfo) : base(unitOfWorkInfo) + { + } + + public WorkflowActionDataViewModel(WorkflowActionData entity, UnitOfWorkInfo uowInfo) : base(entity, uowInfo) + { + } + + #endregion + + #region Properties + public bool IsSuccess { get; set; } + public ActionType ActionType { get; set; } + public ActionStatus? ActionStatus { get; set; } + public double Duration { get; set; } + public int TriggerId { get; set; } + public int ActionId { get; set; } + public int Index { get; set; } + public JObject? Request { get; set; } + public JObject? Body { get; set; } + public JObject? Response { get; set; } + public JObject? Exception { get; set; } + + public WorkflowActionViewModel Action { get; set; } + + #endregion + + #region Overrides + + public override async Task ExpandView(CancellationToken cancellationToken = default) + { + Action = await WorkflowActionViewModel.GetRepository(UowInfo, CacheService) + .GetSingleAsync(m => m.Id == ActionId); + } + + #endregion + + } +} diff --git a/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowActionViewModel.cs b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowActionViewModel.cs new file mode 100644 index 000000000..a645b2c16 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowActionViewModel.cs @@ -0,0 +1,47 @@ +using Mix.Automation.Lib.Entities; +using Mix.Automation.Lib.Enums; +using Mix.Heart.UnitOfWork; +using Mix.Heart.ViewModel; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.ViewModels +{ + public sealed class WorkflowActionViewModel : WorkflowViewModelBase + { + #region Contructors + + public WorkflowActionViewModel() + { + } + + public WorkflowActionViewModel(WorkflowDbContext context) : base(context) + { + } + + public WorkflowActionViewModel(UnitOfWorkInfo unitOfWorkInfo) : base(unitOfWorkInfo) + { + } + + public WorkflowActionViewModel(WorkflowAction entity, UnitOfWorkInfo uowInfo) : base(entity, uowInfo) + { + } + + #endregion + + #region Properties + public ActionType Type { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public string InputPath { get; set; } + public int Index { get; set; } + public int WorkflowId { get; set; } + public JObject? Request { get; set; } + public JObject? Body { get; set; } + #endregion + } +} diff --git a/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowTriggerViewModel.cs b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowTriggerViewModel.cs new file mode 100644 index 000000000..2f08ac31b --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowTriggerViewModel.cs @@ -0,0 +1,61 @@ +using Mix.Automation.Lib.Entities; +using Mix.Heart.UnitOfWork; +using Mix.Heart.ViewModel; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.ViewModels +{ + public sealed class WorkflowTriggerViewModel : WorkflowViewModelBase + { + #region Contructors + + public WorkflowTriggerViewModel() + { + } + + public WorkflowTriggerViewModel(WorkflowDbContext context) : base(context) + { + } + + public WorkflowTriggerViewModel(UnitOfWorkInfo unitOfWorkInfo) : base(unitOfWorkInfo) + { + } + + public WorkflowTriggerViewModel(WorkflowTrigger entity, UnitOfWorkInfo uowInfo) : base(entity, uowInfo) + { + } + + #endregion + + #region Properties + public bool IsSuccess { get; set; } + public int WorkflowId { get; set; } + public JObject? Input { get; set; } + + public List Actions { get; set; } + #endregion + + #region Overrides + + public override async Task ExpandView(CancellationToken cancellationToken = default) + { + Actions = await WorkflowActionDataViewModel.GetRepository(UowInfo, CacheService). + GetSortedListAsync(m => m.TriggerId == Id, nameof(WorkflowAction.Index), Heart.Enums.SortDirection.Asc, cancellationToken); + } + protected override async Task SaveEntityRelationshipAsync(WorkflowTrigger parentEntity, CancellationToken cancellationToken = default) + { + foreach (var action in Actions) + { + action.TriggerId = parentEntity.Id; + action.SetUowInfo(UowInfo, CacheService); + await action.SaveAsync(cancellationToken); + } + } + #endregion + } +} diff --git a/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowViewModel.cs b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowViewModel.cs new file mode 100644 index 000000000..28d4f4b65 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowViewModel.cs @@ -0,0 +1,52 @@ +using Mix.Automation.Lib.Entities; +using Mix.Heart.UnitOfWork; +using Mix.Heart.ViewModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.ViewModels +{ + public sealed class WorkflowViewModel : WorkflowViewModelBase + { + #region Contructors + + public WorkflowViewModel() + { + } + + public WorkflowViewModel(WorkflowDbContext context) : base(context) + { + } + + public WorkflowViewModel(UnitOfWorkInfo unitOfWorkInfo) : base(unitOfWorkInfo) + { + } + + public WorkflowViewModel(Workflow entity, UnitOfWorkInfo uowInfo) : base(entity, uowInfo) + { + } + + #endregion + + #region Properties + + public string Title { get; set; } + public string Description { get; set; } + + public List Actions { get; set; } + + #endregion + + #region Overrides + + public override async Task ExpandView(CancellationToken cancellationToken = default) + { + Actions = await WorkflowActionViewModel.GetRepository(UowInfo, CacheService).GetAllAsync(m => m.WorkflowId == Id, cancellationToken); + } + + #endregion + } +} diff --git a/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowViewModelBase.cs b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowViewModelBase.cs new file mode 100644 index 000000000..fdf09c9f8 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/ViewModels/WorkflowViewModelBase.cs @@ -0,0 +1,64 @@ +using DocumentFormat.OpenXml.Wordprocessing; +using Microsoft.EntityFrameworkCore; +using Mix.Automation.Lib.Entities; +using Mix.Heart.Entities; +using Mix.Heart.Enums; +using Mix.Heart.UnitOfWork; +using Mix.Heart.ViewModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mix.Automation.Lib.ViewModels +{ + public abstract class WorkflowViewModelBase : SimpleViewModelBase + where TPrimaryKey : IComparable + where TEntity : class, IEntity + where TDbContext : DbContext + where TView : WorkflowViewModelBase + { + + #region Contructors + + public WorkflowViewModelBase() + { + } + + public WorkflowViewModelBase(TDbContext context) : base(context) + { + } + + public WorkflowViewModelBase(UnitOfWorkInfo unitOfWorkInfo) : base(unitOfWorkInfo) + { + } + + public WorkflowViewModelBase(TEntity entity, UnitOfWorkInfo uowInfo) : base(entity, uowInfo) + { + } + + #endregion + + #region Properties + public DateTime CreatedDateTime { get; set; } + public DateTime? LastModified { get; set; } + public string CreatedBy { get; set; } + public string ModifiedBy { get; set; } + public int Priority { get; set; } + + #endregion + + #region Overrides + + public override void InitDefaultValues(string? language = null, int? cultureId = null) + { + if (CreatedDateTime == default) + { + CreatedDateTime = DateTime.UtcNow; + LastModified = DateTime.UtcNow; + } + } + #endregion + } +} diff --git a/src/services/mix.automation/mix.automation.lib/mix.automation.lib.csproj b/src/services/mix.automation/mix.automation.lib/mix.automation.lib.csproj new file mode 100644 index 000000000..9c8f2b539 --- /dev/null +++ b/src/services/mix.automation/mix.automation.lib/mix.automation.lib.csproj @@ -0,0 +1,14 @@ + + + + net9.0 + enable + enable + Mix.Automation.Lib + + + + + + + diff --git a/src/test/mix.xunittest/mix.xunittest.csproj b/src/test/mix.xunittest/mix.xunittest.csproj index 5d4683af3..b223c909c 100644 --- a/src/test/mix.xunittest/mix.xunittest.csproj +++ b/src/test/mix.xunittest/mix.xunittest.csproj @@ -36,14 +36,14 @@ - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd13..000000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - -