diff --git a/.gitignore b/.gitignore index 6cd3154..65410ec 100644 --- a/.gitignore +++ b/.gitignore @@ -226,7 +226,6 @@ ClientBin/ *.dbmdl *.dbproj.schemaview *.jfm -*.pfx *.publishsettings orleans.codegen.cs diff --git a/Dockerfile.EventHub.HttpApi.Host b/Dockerfile.EventHub.HttpApi.Host deleted file mode 100644 index 0c354d8..0000000 --- a/Dockerfile.EventHub.HttpApi.Host +++ /dev/null @@ -1,23 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base -WORKDIR /app -EXPOSE 80 -EXPOSE 443 - -FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build -WORKDIR /src -COPY ["src/EventHub.HttpApi.Host/EventHub.HttpApi.Host.csproj", "src/EventHub.HttpApi.Host/"] -RUN dotnet restore "src/EventHub.HttpApi.Host/EventHub.HttpApi.Host.csproj" -COPY . . -WORKDIR "/src/src/EventHub.HttpApi.Host" - -RUN dotnet dev-certs https -v -ep localhost.pfx -p e8202f07-66e5-4619-be07-72ba76fde97f - -RUN dotnet build "EventHub.HttpApi.Host.csproj" -c Release -o /app - -FROM build AS publish -RUN dotnet publish "EventHub.HttpApi.Host.csproj" -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "EventHub.HttpApi.Host.dll"] \ No newline at end of file diff --git a/Dockerfile.EventHub.IdentityServer b/Dockerfile.EventHub.IdentityServer deleted file mode 100644 index ecde15b..0000000 --- a/Dockerfile.EventHub.IdentityServer +++ /dev/null @@ -1,26 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base -WORKDIR /app -EXPOSE 80 -EXPOSE 443 - -FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build -WORKDIR /src -COPY ["src/EventHub.IdentityServer/EventHub.IdentityServer.csproj", "src/EventHub.IdentityServer/"] -RUN dotnet restore "src/EventHub.IdentityServer/EventHub.IdentityServer.csproj" -COPY . . -WORKDIR "/src/src/EventHub.IdentityServer" - -RUN dotnet dev-certs https -ep aspnetapp.pfx -p mypassword123 -RUN dotnet dev-certs https --trust -RUN dotnet dev-certs https -ep $env:USERPROFILE\.aspnet\https\aspnetapp.pfx -p mypassword123 -RUN dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p mypassword123 - -RUN dotnet build "EventHub.IdentityServer.csproj" -c Release -o /app - -FROM build AS publish -RUN dotnet publish "EventHub.IdentityServer.csproj" -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "EventHub.IdentityServer.dll"] \ No newline at end of file diff --git a/Dockerfile.EventHub.Web b/Dockerfile.EventHub.Web deleted file mode 100644 index 48f8448..0000000 --- a/Dockerfile.EventHub.Web +++ /dev/null @@ -1,23 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base -WORKDIR /app -EXPOSE 80 -EXPOSE 443 - -FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build -WORKDIR /src -COPY ["src/EventHub.Web/EventHub.Web.csproj", "src/EventHub.Web/"] -RUN dotnet restore "src/EventHub.Web/EventHub.Web.csproj" -COPY . . -WORKDIR "/src/src/EventHub.Web" - -RUN dotnet dev-certs https -v -ep localhost.pfx -p e8202f07-66e5-4619-be07-72ba76fde97f - -RUN dotnet build "EventHub.Web.csproj" -c Release -o /app - -FROM build AS publish -RUN dotnet publish "EventHub.Web.csproj" -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "EventHub.Web.dll"] \ No newline at end of file diff --git a/azure-pipelines.eventhub.httpapi.host.yml b/azure-pipelines.eventhub.httpapi.host.yml deleted file mode 100644 index e8f71eb..0000000 --- a/azure-pipelines.eventhub.httpapi.host.yml +++ /dev/null @@ -1,62 +0,0 @@ -trigger: -- dev - -variables: - - dockerRegistryServiceConnection: 'b46a670b-6940-4de5-b6b1-b06f9dd1c849' - imageRepository: 'voloeventhubhttpapihost' - containerRegistry: 'voloeventhubhttpapihost.azurecr.io' - dockerfilePath: '**/Dockerfile.EventHub.HttpApi.Host' - tag: '$(Build.BuildNumber)' - - # Agent VM image name - vmImageName: 'ubuntu-latest' - - -stages: -- stage: Build - displayName: Build stage - jobs: - - job: Build - displayName: Build - pool: - vmImage: $(vmImageName) - steps: - - task: Docker@2 - displayName: Build and push an image to container registry - inputs: - command: buildAndPush - repository: $(imageRepository) - dockerfile: $(dockerfilePath) - containerRegistry: $(dockerRegistryServiceConnection) - tags: | - $(tag) - - - upload: manifests - artifact: manifests - -- stage: Deploy - displayName: Deploy stage - dependsOn: Build - - jobs: - - deployment: Deploy - displayName: Deploy-HttpApi-Host - pool: - vmImage: $(vmImageName) - environment: 'volosofteventhub-6504.default' - strategy: - runOnce: - deploy: - steps: - - - task: KubernetesManifest@0 - displayName: Deploy to Kubernetes cluster - inputs: - action: deploy - manifests: | - $(Pipeline.Workspace)/manifests/deployment.eventhub.httpapi.host.yml - $(Pipeline.Workspace)/manifests/service.eventhub.httpapi.host.yml - containers: | - $(containerRegistry)/$(imageRepository):$(tag) - diff --git a/azure-pipelines.eventhub.httpapi.host2.yml b/azure-pipelines.eventhub.httpapi.host2.yml deleted file mode 100644 index 1804248..0000000 --- a/azure-pipelines.eventhub.httpapi.host2.yml +++ /dev/null @@ -1,61 +0,0 @@ -trigger: -- dev - -variables: - dockerRegistryServiceConnection: '8a2bf690-716a-490c-8a88-d93fab12cf9d' - imageRepository: 'eventhubhttpapihost' - containerRegistry: 'eventhubregistry.azurecr.io' - dockerfilePath: '**/Dockerfile.EventHub.HttpApi.Host' - tag: '$(Build.BuildNumber)' - - # Agent VM image namedocker images - vmImageName: 'ubuntu-latest' - - -stages: -- stage: Build - displayName: Build stage - jobs: - - job: Build - displayName: Build - pool: - vmImage: $(vmImageName) - steps: - - task: Docker@2 - displayName: Build and push an image to container registry - inputs: - command: buildAndPush - repository: $(imageRepository) - dockerfile: $(dockerfilePath) - containerRegistry: $(dockerRegistryServiceConnection) - tags: | - $(tag) - - - upload: manifests - artifact: manifests - -- stage: Deploy - displayName: Deploy stage - dependsOn: Build - - jobs: - - deployment: Deploy - displayName: Deploy-HttpApi-Host - pool: - vmImage: $(vmImageName) - environment: 'volosofteventhub-6504.default' - strategy: - runOnce: - deploy: - steps: - - - task: KubernetesManifest@0 - displayName: Deploy to Kubernetes cluster - inputs: - action: deploy - manifests: | - $(Pipeline.Workspace)/manifests/deployment.eventhub.httpapi.host.yml - $(Pipeline.Workspace)/manifests/service.eventhub.httpapi.host.yml - containers: | - $(containerRegistry)/$(imageRepository):$(tag) - diff --git a/azure-pipelines.eventhub.identityserver.yml b/azure-pipelines.eventhub.identityserver.yml deleted file mode 100644 index f88f3b6..0000000 --- a/azure-pipelines.eventhub.identityserver.yml +++ /dev/null @@ -1,62 +0,0 @@ -trigger: -- dev - -variables: - - # Container registry service connection established during pipeline creation - dockerRegistryServiceConnection: '18b12bb9-87a3-4539-a769-7a1e371886cf' - imageRepository: 'voloeventhubidentityserver' - containerRegistry: 'voloeventhubidentityserver.azurecr.io' - dockerfilePath: '**/Dockerfile.EventHub.IdentityServer' - tag: '$(Build.BuildNumber)' - - # Agent VM image name - vmImageName: 'ubuntu-latest' - - -stages: -- stage: Build - displayName: Build stage - jobs: - - job: Build - displayName: Build - pool: - vmImage: $(vmImageName) - steps: - - task: Docker@2 - displayName: Build and push an image to container registry - inputs: - command: buildAndPush - repository: $(imageRepository) - dockerfile: $(dockerfilePath) - containerRegistry: $(dockerRegistryServiceConnection) - tags: | - $(tag) - - - publish: manifests - artifact: manifests - -- stage: Deploy - displayName: Deploy stage - dependsOn: Build - - jobs: - - deployment: Deploy - displayName: Deploy - pool: - vmImage: $(vmImageName) - environment: 'volosofteventhub-1359.default' - strategy: - runOnce: - deploy: - steps: - - task: KubernetesManifest@0 - displayName: Deploy to Kubernetes cluster - inputs: - action: deploy - manifests: | - $(Pipeline.Workspace)/manifests/deployment.eventhub.identityserver.yml - $(Pipeline.Workspace)/manifests/service.eventhub.identityserver.yml - containers: | - $(containerRegistry)/$(imageRepository):$(tag) - diff --git a/azure-pipelines.eventhub.web.yml b/azure-pipelines.eventhub.web.yml deleted file mode 100644 index 3bf77dc..0000000 --- a/azure-pipelines.eventhub.web.yml +++ /dev/null @@ -1,67 +0,0 @@ -trigger: -- dev - - -variables: - - # Container registry service connection established during pipeline creation - dockerRegistryServiceConnection: '855289e7-1034-4e42-90b4-38c072ecefcb' - imageRepository: 'voloeventhub' - containerRegistry: 'voloeventhub.azurecr.io' - dockerfilePath: '**/Dockerfile.EventHub.Web' - tag: '$(Build.BuildNumber)' - - - # Agent VM image name - vmImageName: 'ubuntu-latest' - - -stages: -- stage: Build - displayName: Build stage - jobs: - - job: Build - displayName: Build - pool: - vmImage: $(vmImageName) - steps: - - task: Docker@2 - displayName: Build and push an image to container registry - inputs: - command: buildAndPush - repository: $(imageRepository) - dockerfile: $(dockerfilePath) - containerRegistry: $(dockerRegistryServiceConnection) - tags: | - $(tag) - - - upload: manifests - artifact: manifests - -- stage: Deploy - displayName: Deploy stage - dependsOn: Build - - jobs: - - deployment: Deploy - displayName: Deploy-EventHub-Web - pool: - vmImage: $(vmImageName) - environment: 'volosofteventhub-3839.default' - strategy: - runOnce: - deploy: - steps: - - - - task: KubernetesManifest@0 - displayName: Deploy to Kubernetes cluster - inputs: - action: deploy - manifests: | - $(Pipeline.Workspace)/manifests/deployment.eventhub.web.yml - $(Pipeline.Workspace)/manifests/service.eventhub.web.yml - - containers: | - $(containerRegistry)/$(imageRepository):$(tag) - diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index daa3903..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,61 +0,0 @@ -trigger: -- dev - -variables: - dockerRegistryServiceConnection: 'efbb038e-4f76-4833-a538-fbd8ecb7c545' - imageRepository: 'eventhubhttpapihost' - containerRegistry: 'eventhubregistry.azurecr.io' - dockerfilePath: '**/Dockerfile.EventHub.HttpApi.Host' - tag: '$(Build.BuildNumber)' - - # Agent VM image namedocker images - vmImageName: 'ubuntu-latest' - - -stages: -- stage: Build - displayName: Build stage - jobs: - - job: Build - displayName: Build - pool: - vmImage: $(vmImageName) - steps: - - task: Docker@2 - displayName: Build and push an image to container registry - inputs: - command: buildAndPush - repository: $(imageRepository) - dockerfile: $(dockerfilePath) - containerRegistry: $(dockerRegistryServiceConnection) - tags: | - $(tag) - - - upload: manifests - artifact: manifests - -- stage: Deploy - displayName: Deploy stage - dependsOn: Build - - jobs: - - deployment: Deploy - displayName: Deploy-HttpApi-Host - pool: - vmImage: $(vmImageName) - environment: 'volosofteventhub-2042' - strategy: - runOnce: - deploy: - steps: - - - task: KubernetesManifest@0 - displayName: Deploy to Kubernetes cluster - inputs: - action: deploy - manifests: | - $(Pipeline.Workspace)/manifests/deployment.eventhub.httpapi.host.yml - $(Pipeline.Workspace)/manifests/service.eventhub.httpapi.host.yml - containers: | - $(containerRegistry)/$(imageRepository):$(tag) - diff --git a/etc/azure/deploy-to-k8s.yml b/etc/azure/deploy-to-k8s.yml new file mode 100644 index 0000000..98c3a1f --- /dev/null +++ b/etc/azure/deploy-to-k8s.yml @@ -0,0 +1,25 @@ +pool: + vmImage: 'windows-latest' + +steps: +- task: PowerShell@2 + inputs: + filePath: 'etc/k8s/scripts/build-images.ps1' + +- task: PowerShell@2 + inputs: + filePath: 'etc/azure/scripts/push-images.ps1' +- task: HelmDeploy@0 + inputs: + connectionType: 'Azure Resource Manager' + azureSubscription: 'volosoft(fe02ef9f-9e1c-4e40-b924-505981688dd8)' + azureResourceGroup: 'volo' + kubernetesCluster: 'volo' + useClusterAdmin: true + namespace: 'eventhub' + command: 'upgrade' + chartType: 'Name' + chartName: 'eventhub' + releaseName: 'eh-az' + valueFile: 'etc/k8s/helm-chart/eventhub/values.azure.yaml' + arguments: '--namespace eventhub --create-namespace' \ No newline at end of file diff --git a/etc/azure/scripts/cluster-issuer.yaml b/etc/azure/scripts/cluster-issuer.yaml new file mode 100644 index 0000000..c3445a0 --- /dev/null +++ b/etc/azure/scripts/cluster-issuer.yaml @@ -0,0 +1,18 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: info@volosoft.com + privateKeySecretRef: + name: letsencrypt + solvers: + - http01: + ingress: + class: nginx + podTemplate: + spec: + nodeSelector: + "kubernetes.io/os": linux \ No newline at end of file diff --git a/etc/azure/scripts/configure-dns.ps1 b/etc/azure/scripts/configure-dns.ps1 new file mode 100644 index 0000000..b02d007 --- /dev/null +++ b/etc/azure/scripts/configure-dns.ps1 @@ -0,0 +1,2 @@ +kubectl apply -f .\corednsms.yaml +kubectl delete pod --namespace kube-system -l k8s-app=kube-dns \ No newline at end of file diff --git a/etc/azure/scripts/corednsms.yaml b/etc/azure/scripts/corednsms.yaml new file mode 100644 index 0000000..940e126 --- /dev/null +++ b/etc/azure/scripts/corednsms.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: coredns-custom + namespace: kube-system +data: + Corefile.override: | + rewrite name substring account.openeventhub.com eh-az-account \ No newline at end of file diff --git a/etc/azure/scripts/install-ingress.ps1 b/etc/azure/scripts/install-ingress.ps1 new file mode 100644 index 0000000..153ed91 --- /dev/null +++ b/etc/azure/scripts/install-ingress.ps1 @@ -0,0 +1,69 @@ +$REGISTRY_NAME="volocr.azurecr.io" +$ACR_URL="volocr.azurecr.io" +$CONTROLLER_REGISTRY="k8s.gcr.io" +$CONTROLLER_IMAGE="ingress-nginx/controller" +$CONTROLLER_TAG="v0.48.1" +$PATCH_REGISTRY="docker.io" +$PATCH_IMAGE="jettech/kube-webhook-certgen" +$PATCH_TAG="v1.5.1" +$DEFAULTBACKEND_REGISTRY="k8s.gcr.io" +$DEFAULTBACKEND_IMAGE="defaultbackend-amd64" +$DEFAULTBACKEND_TAG="1.5" +$CERT_MANAGER_REGISTRY="quay.io" +$CERT_MANAGER_TAG="v1.3.1" +$CERT_MANAGER_IMAGE_CONTROLLER="jetstack/cert-manager-controller" +$CERT_MANAGER_IMAGE_WEBHOOK="jetstack/cert-manager-webhook" +$CERT_MANAGER_IMAGE_CAINJECTOR="jetstack/cert-manager-cainjector" + +az acr import --name $REGISTRY_NAME --source ${CONTROLLER_REGISTRY}/${CONTROLLER_IMAGE}:${CONTROLLER_TAG} --image ${CONTROLLER_IMAGE}:${CONTROLLER_TAG} +az acr import --name $REGISTRY_NAME --source ${PATCH_REGISTRY}/${PATCH_IMAGE}:${PATCH_TAG} --image ${PATCH_IMAGE}:${PATCH_TAG} +az acr import --name $REGISTRY_NAME --source ${DEFAULTBACKEND_REGISTRY}/${DEFAULTBACKEND_IMAGE}:${DEFAULTBACKEND_TAG} --image ${DEFAULTBACKEND_IMAGE}:${DEFAULTBACKEND_TAG} +az acr import --name $REGISTRY_NAME --source ${CERT_MANAGER_REGISTRY}/${CERT_MANAGER_IMAGE_CONTROLLER}:${CERT_MANAGER_TAG} --image ${CERT_MANAGER_IMAGE_CONTROLLER}:${CERT_MANAGER_TAG} +az acr import --name $REGISTRY_NAME --source ${CERT_MANAGER_REGISTRY}/${CERT_MANAGER_IMAGE_WEBHOOK}:${CERT_MANAGER_TAG} --image ${CERT_MANAGER_IMAGE_WEBHOOK}:${CERT_MANAGER_TAG} +az acr import --name $REGISTRY_NAME --source ${CERT_MANAGER_REGISTRY}/${CERT_MANAGER_IMAGE_CAINJECTOR}:${CERT_MANAGER_TAG} --image ${CERT_MANAGER_IMAGE_CAINJECTOR}:${CERT_MANAGER_TAG} + +# Create a namespace for your ingress resources +kubectl create namespace ingress-basic + +# Add the ingress-nginx repository +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx + +# Use Helm to deploy an NGINX ingress controller +helm install nginx-ingress ingress-nginx/ingress-nginx ` + --namespace ingress-basic ` + --set controller.replicaCount=1 ` + --set controller.nodeSelector."kubernetes\.io/os"=linux ` + --set controller.image.registry=$ACR_URL ` + --set controller.image.image=$CONTROLLER_IMAGE ` + --set controller.image.tag=$CONTROLLER_TAG ` + --set controller.image.digest="" ` + --set controller.admissionWebhooks.patch.nodeSelector."kubernetes\.io/os"=linux ` + --set controller.admissionWebhooks.patch.image.registry=$ACR_URL ` + --set controller.admissionWebhooks.patch.image.image=$PATCH_IMAGE ` + --set controller.admissionWebhooks.patch.image.tag=$PATCH_TAG ` + --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux ` + --set defaultBackend.image.registry=$ACR_URL ` + --set defaultBackend.image.image=$DEFAULTBACKEND_IMAGE ` + --set defaultBackend.image.tag=$DEFAULTBACKEND_TAG + +# Label the ingress-basic namespace to disable resource validation +kubectl label namespace ingress-basic cert-manager.io/disable-validation=true + +# Add the Jetstack Helm repository +helm repo add jetstack https://charts.jetstack.io + +# Update your local Helm chart repository cache +helm repo update + +# Install the cert-manager Helm chart +helm install cert-manager jetstack/cert-manager ` + --namespace ingress-basic ` + --version ${CERT_MANAGER_TAG} ` + --set installCRDs=true ` + --set nodeSelector."kubernetes\.io/os"=linux ` + --set image.repository=${ACR_URL}/${CERT_MANAGER_IMAGE_CONTROLLER} ` + --set image.tag=${CERT_MANAGER_TAG} ` + --set webhook.image.repository=${ACR_URL}/${CERT_MANAGER_IMAGE_WEBHOOK} ` + --set webhook.image.tag=${CERT_MANAGER_TAG} ` + --set cainjector.image.repository=${ACR_URL}/${CERT_MANAGER_IMAGE_CAINJECTOR} ` + --set cainjector.image.tag=${CERT_MANAGER_TAG} \ No newline at end of file diff --git a/etc/azure/scripts/push-images.ps1 b/etc/azure/scripts/push-images.ps1 new file mode 100644 index 0000000..a3e504d --- /dev/null +++ b/etc/azure/scripts/push-images.ps1 @@ -0,0 +1,22 @@ +az acr login --name volocr + +docker tag eventhub.dbmigrator:latest volocr.azurecr.io/eventhub.dbmigrator:latest +docker push volocr.azurecr.io/eventhub.dbmigrator:latest + +docker tag eventhub.www:latest volocr.azurecr.io/eventhub.www:latest +docker push volocr.azurecr.io/eventhub.www:latest + +docker tag eventhub.api:latest volocr.azurecr.io/eventhub.api:latest +docker push volocr.azurecr.io/eventhub.api:latest + +docker tag eventhub.admin:latest volocr.azurecr.io/eventhub.admin:latest +docker push volocr.azurecr.io/eventhub.admin:latest + +docker tag eventhub.admin-api:latest volocr.azurecr.io/eventhub.admin-api:latest +docker push volocr.azurecr.io/eventhub.admin-api:latest + +docker tag eventhub.account:latest volocr.azurecr.io/eventhub.account:latest +docker push volocr.azurecr.io/eventhub.account:latest + +docker tag eventhub.background-services:latest volocr.azurecr.io/eventhub.background-services:latest +docker push volocr.azurecr.io/eventhub.background-services:latest \ No newline at end of file diff --git a/etc/dev-cert/localhost.pfx b/etc/dev-cert/localhost.pfx new file mode 100644 index 0000000..abc91fe Binary files /dev/null and b/etc/dev-cert/localhost.pfx differ diff --git a/etc/k8s/README.md b/etc/k8s/README.md new file mode 100644 index 0000000..7810dae --- /dev/null +++ b/etc/k8s/README.md @@ -0,0 +1,21 @@ + ### Pre-requirements + +* Docker Desktop with Kubernetes enabled +* Install [NGINX ingress](https://kubernetes.github.io/ingress-nginx/deploy/) for k8s + +### How to run? + +* Add entries to the hosts file (in Windows: `C:\Windows\System32\drivers\etc\hosts`): + +```` +127.0.0.1 eh-st-account +127.0.0.1 eh-st-www +127.0.0.1 eh-st-api +127.0.0.1 eh-st-admin +127.0.0.1 eh-st-admin-api +```` + +* Run `build-images.ps1` in the `scripts` directory. +* Run `helm install eh-st eventhub` in the `helm-chart` directory. +* Browse https://eh-st-www and https://eh-st-admin +* Username: `admin`, password: `1q2w3E*`. diff --git a/etc/k8s/helm-chart/deploy-azure.ps1 b/etc/k8s/helm-chart/deploy-azure.ps1 new file mode 100644 index 0000000..96bc82d --- /dev/null +++ b/etc/k8s/helm-chart/deploy-azure.ps1 @@ -0,0 +1 @@ +helm upgrade --install eh-az eventhub -f .\eventhub\values.azure.yaml --namespace eventhub --create-namespace \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/Chart.yaml b/etc/k8s/helm-chart/eventhub/Chart.yaml new file mode 100644 index 0000000..ba07319 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: eventhub +appVersion: "1.0" +description: EventHub solution +version: 1.0.0 +type: application diff --git a/etc/k8s/helm-chart/eventhub/charts/account/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/account/Chart.yaml new file mode 100644 index 0000000..ad4fd58 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/account/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: account +appVersion: "1.0" +description: EventHub Account Application +version: 1.0.0 +type: application \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/account/templates/account-ingress.yaml b/etc/k8s/helm-chart/eventhub/charts/account/templates/account-ingress.yaml new file mode 100644 index 0000000..bc7a7b7 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/account/templates/account-ingress.yaml @@ -0,0 +1,28 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }}-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "{{ .Values.global.nginxProxyBufferSize }}" + nginx.ingress.kubernetes.io/proxy-buffers-number: "{{ .Values.global.nginxProxyBuffersNumber }}" + cert-manager.io/cluster-issuer: letsencrypt + nginx.ingress.kubernetes.io/configuration-snippet: | + more_set_input_headers "from-ingress: true"; +spec: + tls: + - hosts: + - {{ .Values.global.accountDomain }} + secretName: {{ .Release.Name }}-{{ .Chart.Name }}-tls + rules: + - host: "{{ .Values.global.accountDomain }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-{{ .Chart.Name }} + port: + number: 80 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/account/templates/account-service.yaml b/etc/k8s/helm-chart/eventhub/charts/account/templates/account-service.yaml new file mode 100644 index 0000000..ee157c0 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/account/templates/account-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + name: {{ .Release.Name }}-{{ .Chart.Name }} + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + ports: + - name: "80" + port: 80 + - name: "443" + port: 443 + selector: + app: {{ .Release.Name }}-{{ .Chart.Name }} diff --git a/etc/k8s/helm-chart/eventhub/charts/account/templates/account.yaml b/etc/k8s/helm-chart/eventhub/charts/account/templates/account.yaml new file mode 100644 index 0000000..0fc80aa --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/account/templates/account.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + spec: + containers: + - image: {{ .Values.containerImage }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + name: {{ .Release.Name }}-{{ .Chart.Name }} + ports: + - name: http + containerPort: 80 + - name: https + containerPort: 443 + env: +{{ include "eventhub.global.env" . | indent 8 }} + - name: "ConnectionStrings__Default" + value: {{ .Values.global.defaultConnString }} + diff --git a/etc/k8s/helm-chart/eventhub/charts/account/values.yaml b/etc/k8s/helm-chart/eventhub/charts/account/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/admin-api/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/admin-api/Chart.yaml new file mode 100644 index 0000000..4b82d4b --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin-api/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: admin-api +appVersion: "1.0" +description: EventHub Admin API Application +version: 1.0.0 +type: application \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api-ingress.yaml b/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api-ingress.yaml new file mode 100644 index 0000000..e59ac77 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api-ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }}-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "{{ .Values.global.nginxProxyBufferSize }}" + nginx.ingress.kubernetes.io/proxy-buffers-number: "{{ .Values.global.nginxProxyBuffersNumber }}" + cert-manager.io/cluster-issuer: letsencrypt +spec: + tls: + - hosts: + - {{ .Values.global.adminApiDomain }} + secretName: {{ .Release.Name }}-{{ .Chart.Name }}-tls + rules: + - host: "{{ .Values.global.adminApiDomain }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-{{ .Chart.Name }} + port: + number: 80 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api-service.yaml b/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api-service.yaml new file mode 100644 index 0000000..ee157c0 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + name: {{ .Release.Name }}-{{ .Chart.Name }} + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + ports: + - name: "80" + port: 80 + - name: "443" + port: 443 + selector: + app: {{ .Release.Name }}-{{ .Chart.Name }} diff --git a/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api.yaml b/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api.yaml new file mode 100644 index 0000000..0fc80aa --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin-api/templates/admin-api.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + spec: + containers: + - image: {{ .Values.containerImage }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + name: {{ .Release.Name }}-{{ .Chart.Name }} + ports: + - name: http + containerPort: 80 + - name: https + containerPort: 443 + env: +{{ include "eventhub.global.env" . | indent 8 }} + - name: "ConnectionStrings__Default" + value: {{ .Values.global.defaultConnString }} + diff --git a/etc/k8s/helm-chart/eventhub/charts/admin-api/values.yaml b/etc/k8s/helm-chart/eventhub/charts/admin-api/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/admin/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/admin/Chart.yaml new file mode 100644 index 0000000..6432aff --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: admin +appVersion: "1.0" +description: EventHub Admin Application +version: 1.0.0 +type: application \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin-ingress.yaml b/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin-ingress.yaml new file mode 100644 index 0000000..c2d1bc8 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin-ingress.yaml @@ -0,0 +1,28 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }}-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "{{ .Values.global.nginxProxyBufferSize }}" + nginx.ingress.kubernetes.io/proxy-buffers-number: "{{ .Values.global.nginxProxyBuffersNumber }}" + cert-manager.io/cluster-issuer: letsencrypt + nginx.ingress.kubernetes.io/configuration-snippet: | + more_set_headers "blazor-environment: {{ .Values.global.dotnetEnvironment }}"; +spec: + tls: + - hosts: + - {{ .Values.global.adminDomain }} + secretName: {{ .Release.Name }}-{{ .Chart.Name }}-tls + rules: + - host: "{{ .Values.global.adminDomain }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-{{ .Chart.Name }} + port: + number: 80 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin-service.yaml b/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin-service.yaml new file mode 100644 index 0000000..ee157c0 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + name: {{ .Release.Name }}-{{ .Chart.Name }} + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + ports: + - name: "80" + port: 80 + - name: "443" + port: 443 + selector: + app: {{ .Release.Name }}-{{ .Chart.Name }} diff --git a/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin.yaml b/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin.yaml new file mode 100644 index 0000000..ca27707 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/admin/templates/admin.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + spec: + containers: + - image: {{ .Values.containerImage }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + name: {{ .Release.Name }}-{{ .Chart.Name }} + ports: + - name: http + containerPort: 80 + - name: https + containerPort: 443 + env: +{{ include "eventhub.global.env" . | indent 8 }} diff --git a/etc/k8s/helm-chart/eventhub/charts/admin/values.yaml b/etc/k8s/helm-chart/eventhub/charts/admin/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/api/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/api/Chart.yaml new file mode 100644 index 0000000..fa75f5b --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/api/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: api +appVersion: "1.0" +description: EventHub API Application +version: 1.0.0 +type: application \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/api/templates/api-ingress.yaml b/etc/k8s/helm-chart/eventhub/charts/api/templates/api-ingress.yaml new file mode 100644 index 0000000..2a678ad --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/api/templates/api-ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }}-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "{{ .Values.global.nginxProxyBufferSize }}" + nginx.ingress.kubernetes.io/proxy-buffers-number: "{{ .Values.global.nginxProxyBuffersNumber }}" + cert-manager.io/cluster-issuer: letsencrypt +spec: + tls: + - hosts: + - {{ .Values.global.apiDomain }} + secretName: {{ .Release.Name }}-{{ .Chart.Name }}-tls + rules: + - host: "{{ .Values.global.apiDomain }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-{{ .Chart.Name }} + port: + number: 80 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/api/templates/api-service.yaml b/etc/k8s/helm-chart/eventhub/charts/api/templates/api-service.yaml new file mode 100644 index 0000000..ee157c0 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/api/templates/api-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + name: {{ .Release.Name }}-{{ .Chart.Name }} + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + ports: + - name: "80" + port: 80 + - name: "443" + port: 443 + selector: + app: {{ .Release.Name }}-{{ .Chart.Name }} diff --git a/etc/k8s/helm-chart/eventhub/charts/api/templates/api.yaml b/etc/k8s/helm-chart/eventhub/charts/api/templates/api.yaml new file mode 100644 index 0000000..0fc80aa --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/api/templates/api.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + spec: + containers: + - image: {{ .Values.containerImage }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + name: {{ .Release.Name }}-{{ .Chart.Name }} + ports: + - name: http + containerPort: 80 + - name: https + containerPort: 443 + env: +{{ include "eventhub.global.env" . | indent 8 }} + - name: "ConnectionStrings__Default" + value: {{ .Values.global.defaultConnString }} + diff --git a/etc/k8s/helm-chart/eventhub/charts/api/values.yaml b/etc/k8s/helm-chart/eventhub/charts/api/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/background-services/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/background-services/Chart.yaml new file mode 100644 index 0000000..ce84bfe --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/background-services/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: background-services +appVersion: "1.0" +description: EventHub Background Services Application +version: 1.0.0 +type: application \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/background-services/templates/background-services.yaml b/etc/k8s/helm-chart/eventhub/charts/background-services/templates/background-services.yaml new file mode 100644 index 0000000..6e7d8ee --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/background-services/templates/background-services.yaml @@ -0,0 +1,18 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + activeDeadlineSeconds: 180 + template: + spec: + containers: + - name: {{ .Release.Name }}-{{ .Chart.Name }} + image: {{ .Values.containerImage }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + env: +{{ include "eventhub.global.env" . | indent 8 }} + - name: "ConnectionStrings__Default" + value: {{ .Values.global.defaultConnString }} + restartPolicy: Never + backoffLimit: 10 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/background-services/values.yaml b/etc/k8s/helm-chart/eventhub/charts/background-services/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/dbmigrator/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/dbmigrator/Chart.yaml new file mode 100644 index 0000000..fa640cd --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/dbmigrator/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: dbmigrator +appVersion: "1.0" +description: EventHub Migrator Application +version: 1.0.0 +type: application \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/dbmigrator/templates/migrator.yaml b/etc/k8s/helm-chart/eventhub/charts/dbmigrator/templates/migrator.yaml new file mode 100644 index 0000000..03b7b64 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/dbmigrator/templates/migrator.yaml @@ -0,0 +1,24 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + activeDeadlineSeconds: 180 + template: + spec: + containers: + - name: {{ .Release.Name }}-{{ .Chart.Name }} + image: {{ .Values.containerImage }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + env: +{{ include "eventhub.global.env" . | indent 8 }} + - name: "ConnectionStrings__Default" + value: {{ .Values.global.defaultConnString }} + - name: "IdentityServer__Clients__EventHub_Web__RootUrl" + value: {{ .Values.global.wwwUrl }} + - name: "IdentityServer__Clients__EventHub_Blazor__RootUrl" + value: {{ .Values.global.adminUrl }} + - name: "IdentityServer__Clients__EventHub_Swagger__RootUrl" + value: {{ .Values.global.apiUrl }} + restartPolicy: Never + backoffLimit: 10 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/dbmigrator/values.yaml b/etc/k8s/helm-chart/eventhub/charts/dbmigrator/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/postgresql/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/postgresql/Chart.yaml new file mode 100644 index 0000000..ce984f6 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/postgresql/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: postgresql +appVersion: "1.0" +description: Runs PostgreSQL Instance +version: 1.0.0 +type: application diff --git a/etc/k8s/helm-chart/eventhub/charts/postgresql/templates/postgresql-service.yaml b/etc/k8s/helm-chart/eventhub/charts/postgresql/templates/postgresql-service.yaml new file mode 100644 index 0000000..6c08d9d --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/postgresql/templates/postgresql-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + name: {{ .Release.Name }}-{{ .Chart.Name }} + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + type: ClusterIP + ports: + - name: postgresql + port: 5432 + selector: + app: {{ .Release.Name }}-{{ .Chart.Name }} + diff --git a/etc/k8s/helm-chart/eventhub/charts/postgresql/templates/postgresql.yaml b/etc/k8s/helm-chart/eventhub/charts/postgresql/templates/postgresql.yaml new file mode 100644 index 0000000..3d37d3d --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/postgresql/templates/postgresql.yaml @@ -0,0 +1,43 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + serviceName: {{ .Release.Name }}-{{ .Chart.Name }} + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + spec: + containers: + - image: postgres + name: {{ .Release.Name }}-{{ .Chart.Name }} +{{- if eq .Release.Name "eh-az" }} + volumeMounts: + - mountPath: "/var/lib/postgresql/data" + name: {{ .Release.Name }}-{{ .Chart.Name }}-database-volume + subPath: postgresql-data +{{- end }} + ports: + - name: postgresql + containerPort: 5432 + env: + - name: POSTGRES_USER + value: "root" + - name: POSTGRES_PASSWORD + value: "root" +{{- if eq .Release.Name "eh-az" }} + volumeClaimTemplates: + - metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }}-database-volume + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: "managed-premium-retain" + resources: + requests: + storage: 64Gi +{{- end }} \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/postgresql/values.yaml b/etc/k8s/helm-chart/eventhub/charts/postgresql/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/redis/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/redis/Chart.yaml new file mode 100644 index 0000000..11ec26c --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/redis/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: redis +appVersion: "1.0" +description: Runs Redis instance +version: 1.0.0 +type: application diff --git a/etc/k8s/helm-chart/eventhub/charts/redis/templates/redis-service.yaml b/etc/k8s/helm-chart/eventhub/charts/redis/templates/redis-service.yaml new file mode 100644 index 0000000..23800f9 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/redis/templates/redis-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + name: {{ .Release.Name }}-{{ .Chart.Name }} + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + type: ClusterIP + ports: + - name: redis + port: 6379 + selector: + app: {{ .Release.Name }}-{{ .Chart.Name }} \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/redis/templates/redis.yaml b/etc/k8s/helm-chart/eventhub/charts/redis/templates/redis.yaml new file mode 100644 index 0000000..2ba176d --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/redis/templates/redis.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + spec: + containers: + - image: redis:6.0.10-alpine + name: {{ .Release.Name }}-{{ .Chart.Name }} + ports: + - name: redis + containerPort: 6379 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/redis/values.yaml b/etc/k8s/helm-chart/eventhub/charts/redis/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/charts/www/Chart.yaml b/etc/k8s/helm-chart/eventhub/charts/www/Chart.yaml new file mode 100644 index 0000000..319d808 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/www/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: www +appVersion: "1.0" +description: EventHub WWW Application +version: 1.0.0 +type: application \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/www/templates/www-ingress.yaml b/etc/k8s/helm-chart/eventhub/charts/www/templates/www-ingress.yaml new file mode 100644 index 0000000..1fc3832 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/www/templates/www-ingress.yaml @@ -0,0 +1,36 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }}-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "{{ .Values.global.nginxProxyBufferSize }}" + nginx.ingress.kubernetes.io/proxy-buffers-number: "{{ .Values.global.nginxProxyBuffersNumber }}" +{{- if eq .Release.Name "eh-az" }} + nginx.ingress.kubernetes.io/from-to-www-redirect: "true" +{{- end }} + cert-manager.io/cluster-issuer: letsencrypt +spec: + tls: + - hosts: + - {{ .Values.global.wwwDomain }} +{{- if eq .Release.Name "eh-az" }} + - {{ print "www." .Values.global.wwwDomain }} +{{- end }} + secretName: {{ .Release.Name }}-{{ .Chart.Name }}-tls + rules: +{{- if eq .Release.Name "eh-az" }} + - host: "{{ print "www." .Values.global.wwwDomain }}" +{{- else }} + - host: "{{ .Values.global.wwwDomain }}" +{{- end }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-{{ .Chart.Name }} + port: + number: 80 \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/charts/www/templates/www-service.yaml b/etc/k8s/helm-chart/eventhub/charts/www/templates/www-service.yaml new file mode 100644 index 0000000..ee157c0 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/www/templates/www-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + name: {{ .Release.Name }}-{{ .Chart.Name }} + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + ports: + - name: "80" + port: 80 + - name: "443" + port: 443 + selector: + app: {{ .Release.Name }}-{{ .Chart.Name }} diff --git a/etc/k8s/helm-chart/eventhub/charts/www/templates/www.yaml b/etc/k8s/helm-chart/eventhub/charts/www/templates/www.yaml new file mode 100644 index 0000000..ca27707 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/charts/www/templates/www.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Chart.Name }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Chart.Name }} + spec: + containers: + - image: {{ .Values.containerImage }} + imagePullPolicy: {{ .Values.global.imagePullPolicy }} + name: {{ .Release.Name }}-{{ .Chart.Name }} + ports: + - name: http + containerPort: 80 + - name: https + containerPort: 443 + env: +{{ include "eventhub.global.env" . | indent 8 }} diff --git a/etc/k8s/helm-chart/eventhub/charts/www/values.yaml b/etc/k8s/helm-chart/eventhub/charts/www/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/etc/k8s/helm-chart/eventhub/templates/_helpers.tpl b/etc/k8s/helm-chart/eventhub/templates/_helpers.tpl new file mode 100644 index 0000000..cfa6a9c --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/templates/_helpers.tpl @@ -0,0 +1,24 @@ +{{- define "eventhub.global.env" -}} +- name: "DOTNET_ENVIRONMENT" + value: "{{ .Values.global.dotnetEnvironment }}" +- name: "AppUrls__Account" + value: "{{ .Values.global.accountUrl }}" +- name: "AppUrls__Www" + value: "{{ .Values.global.wwwUrl }}" +- name: "AppUrls__Api" + value: "{{ .Values.global.apiUrl }}" +- name: "AppUrls__ApiInternal" + value: "{{ .Values.global.apiUrlInternal }}" +- name: "AppUrls__Admin" + value: "{{ .Values.global.adminUrl }}" +- name: "AppUrls__AdminApi" + value: "{{ .Values.global.adminApiUrl }}" +- name: "Redis__Configuration" + value: "{{ .Values.global.redisConfiguration }}" +- name: "AuthServer__Authority" + value: "{{ .Values.global.internalAuthServerAuthority }}" +- name: "AuthServer__RequireHttpsMetadata" + value: "{{ .Values.global.internalAuthServerRequireHttpsMetadata }}" +- name: "StringEncryption__DefaultPassPhrase" + value: "{{ .Values.global.stringEncryptionDefaultPassPhrase }}" +{{- end }} \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/templates/managed-premium-retain.yaml b/etc/k8s/helm-chart/eventhub/templates/managed-premium-retain.yaml new file mode 100644 index 0000000..3ad6d00 --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/templates/managed-premium-retain.yaml @@ -0,0 +1,11 @@ +{{- if eq .Release.Name "eh-az" }} +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + name: managed-premium-retain +provisioner: kubernetes.io/azure-disk +reclaimPolicy: Retain +parameters: + storageaccounttype: Premium_LRS + kind: Managed +{{- end }} \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/values.azure.yaml b/etc/k8s/helm-chart/eventhub/values.azure.yaml new file mode 100644 index 0000000..c98ca3c --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/values.azure.yaml @@ -0,0 +1,35 @@ +global: + dotnetEnvironment: 'Production' + accountDomain: account.openeventhub.com + accountUrl: https://account.openeventhub.com + apiDomain: api.openeventhub.com + apiUrl: https://api.openeventhub.com + apiUrlInternal: http://eh-az-api + wwwDomain: openeventhub.com + wwwUrl: "https://www.openeventhub.com" + adminDomain: admin.openeventhub.com + adminUrl: https://admin.openeventhub.com + adminApiDomain: admin-api.openeventhub.com + adminApiUrl: https://admin-api.openeventhub.com + nginxProxyBufferSize: "32k" + nginxProxyBuffersNumber: "8" + defaultConnString: "Host=eh-az-postgresql;Port=5432;Database=EventHub;Username=root;Password=root" + redisConfiguration: "eh-az-redis" + internalAuthServerAuthority: "http://account.openeventhub.com" + internalAuthServerRequireHttpsMetadata: "false" + stringEncryptionDefaultPassPhrase: "TxVIZFPxK33czbbv" + imagePullPolicy: Always +account: + containerImage: "volocr.azurecr.io/eventhub.account" +api: + containerImage: "volocr.azurecr.io/eventhub.api" +www: + containerImage: "volocr.azurecr.io/eventhub.www" +admin-api: + containerImage: "volocr.azurecr.io/eventhub.admin-api" +admin: + containerImage: "volocr.azurecr.io/eventhub.admin" +dbmigrator: + containerImage: "volocr.azurecr.io/eventhub.dbmigrator" +background-services: + containerImage: "volocr.azurecr.io/eventhub.background-services" \ No newline at end of file diff --git a/etc/k8s/helm-chart/eventhub/values.yaml b/etc/k8s/helm-chart/eventhub/values.yaml new file mode 100644 index 0000000..0fd8d6a --- /dev/null +++ b/etc/k8s/helm-chart/eventhub/values.yaml @@ -0,0 +1,35 @@ +global: + dotnetEnvironment: 'Staging' + accountDomain: eh-st-account + accountUrl: https://eh-st-account + apiDomain: eh-st-api + apiUrl: https://eh-st-api + apiUrlInternal: http://eh-st-api + wwwDomain: eh-st-www + wwwUrl: "https://eh-st-www" + adminDomain: eh-st-admin + adminUrl: https://eh-st-admin + adminApiDomain: eh-st-admin-api + adminApiUrl: https://eh-st-admin-api + nginxProxyBufferSize: "32k" + nginxProxyBuffersNumber: "8" + defaultConnString: "Host=eh-st-postgresql;Port=5432;Database=EventHub;Username=root;Password=root" + redisConfiguration: "eh-st-redis" + internalAuthServerAuthority: "http://eh-st-account" + internalAuthServerRequireHttpsMetadata: "false" + stringEncryptionDefaultPassPhrase: "TxVIZFPxK33czbbv" + imagePullPolicy: Never +account: + containerImage: "eventhub.account" +api: + containerImage: "eventhub.api" +www: + containerImage: "eventhub.www" +admin-api: + containerImage: "eventhub.admin-api" +admin: + containerImage: "eventhub.admin" +dbmigrator: + containerImage: "eventhub.dbmigrator" +background-services: + containerImage: "eventhub.background-services" \ No newline at end of file diff --git a/etc/k8s/scripts/build-images.ps1 b/etc/k8s/scripts/build-images.ps1 new file mode 100644 index 0000000..a8f6bcb --- /dev/null +++ b/etc/k8s/scripts/build-images.ps1 @@ -0,0 +1,63 @@ +$currentFolder = $PSScriptRoot +$slnFolder = Join-Path $currentFolder "../../../" +$dbmigratorFolder = Join-Path $slnFolder "src/EventHub.DbMigrator" +$webFolder = Join-Path $slnFolder "src/EventHub.Web" +$apiFolder = Join-Path $slnFolder "src/EventHub.HttpApi.Host" +$adminFolder = Join-Path $slnFolder "src/EventHub.Admin.Web" +$adminApiFolder = Join-Path $slnFolder "src/EventHub.Admin.HttpApi.Host" +$identityServerFolder = Join-Path $slnFolder "src/EventHub.IdentityServer" +$backgroundServicesFolder = Join-Path $slnFolder "src/EventHub.BackgroundServices" + +### DB MIGRATOR + +Write-Host "*** BUILDING DB MIGRATOR ****************" -ForegroundColor Green +Set-Location $dbmigratorFolder +dotnet publish -c Release +docker build -t eventhub.dbmigrator . + +### WEB (WWW) + +Write-Host "*** BUILDING WEB (WWW) ****************" -ForegroundColor Green +Set-Location $webFolder +dotnet publish -c Release +docker build -t eventhub.www . + +### API + +Write-Host "*** BUILDING API ****************" -ForegroundColor Green +Set-Location $apiFolder +dotnet publish -c Release +docker build -t eventhub.api . + +### ADMIN (BLAZOR) + +Write-Host "*** BUILDING ADMIN (BLAZOR) ****************" -ForegroundColor Green +Set-Location $adminFolder +dotnet publish -c Release +docker build -t eventhub.admin . + +### ADMIN API + +Write-Host "*** BUILDING ADMIN API ****************" -ForegroundColor Green +Set-Location $adminApiFolder +dotnet publish -c Release +docker build -t eventhub.admin-api . + +### IDENTITY SERVER (ACCOUNT) + +Write-Host "*** BUILDING IDENTITY SERVER (ACCOUNT) ****************" -ForegroundColor Green +Set-Location $identityServerFolder +dotnet publish -c Release +docker build -t eventhub.account . + +### BACKGROUND SERVICES + +Write-Host "*** BUILDING BACKGROUND SERVICES ****************" -ForegroundColor Green +Set-Location $backgroundServicesFolder +dotnet publish -c Release +docker build -t eventhub.background-services . + +### ALL COMPLETED + +Write-Host "ALL COMPLETED" -ForegroundColor Green +Set-Location $currentFolder \ No newline at end of file diff --git a/etc/k8s/scripts/minikube-load-images.ps1 b/etc/k8s/scripts/minikube-load-images.ps1 new file mode 100644 index 0000000..e63234c --- /dev/null +++ b/etc/k8s/scripts/minikube-load-images.ps1 @@ -0,0 +1,7 @@ +minikube image load eventhub.dbmigrator:latest +minikube image load eventhub.www:latest +minikube image load eventhub.api:latest +minikube image load eventhub.admin:latest +minikube image load eventhub.admin-api:latest +minikube image load eventhub.account:latest +minikube image load eventhub.background-services:latest \ No newline at end of file diff --git a/etc/notes/ui-design.md b/etc/notes/ui-design.md deleted file mode 100644 index 516413e..0000000 --- a/etc/notes/ui-design.md +++ /dev/null @@ -1,32 +0,0 @@ -# UI Design Notes - -## Implementing the Design - -### Overall / Layout - -* Home & Events are separated, so we should create a separation in the solution - -### Home Page - -* Search area: - * Language selection - * Location (country / city) search / with online option - * When (date range selection) - -### Events Page - -* Search area: - * Language selection - * Location (country / city) search / with online option - * When (date range selection) - -## Features - -### Should Implement for the Design - -* ... - -### Plan for Future - -* Event & Organization Categories -* Event & Organization Tags (use the tagging system of CMS Kit) \ No newline at end of file diff --git a/localhost.conf b/localhost.conf deleted file mode 100644 index 6a46c26..0000000 --- a/localhost.conf +++ /dev/null @@ -1,23 +0,0 @@ -[req] -default_bits = 2048 -default_keyfile = localhost.key -distinguished_name = req_distinguished_name -req_extensions = req_ext -x509_extensions = v3_ca - -[req_distinguished_name] -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_default = localhost -commonName_max = 64 - -[req_ext] -subjectAltName = @alt_names - -[v3_ca] -subjectAltName = @alt_names -basicConstraints = critical, CA:false -keyUsage = keyCertSign, cRLSign, digitalSignature,keyEncipherment - -[alt_names] -DNS.1 = localhost -DNS.2 = 127.0.0.1 \ No newline at end of file diff --git a/manifests/deployment.eventhub.httpapi.host.yml b/manifests/deployment.eventhub.httpapi.host.yml deleted file mode 100644 index 8f063ed..0000000 --- a/manifests/deployment.eventhub.httpapi.host.yml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: volosoft-eventhub-httpapi-host-deployment -spec: - selector: - matchLabels: - app: volosoft-eventhub-httpapi-host-pod - template: - metadata: - labels: - app: volosoft-eventhub-httpapi-host-pod - spec: - containers: - - name: volosoft-eventhub-httpapi-host-container - image: voloeventhubhttpapihost.azurecr.io/voloeventhubhttpapihost - imagePullPolicy: Always - resources: - limits: - memory: "128Mi" - cpu: "500m" - ports: - - containerPort: 80 \ No newline at end of file diff --git a/manifests/deployment.eventhub.identityserver.yml b/manifests/deployment.eventhub.identityserver.yml deleted file mode 100644 index 3150c4c..0000000 --- a/manifests/deployment.eventhub.identityserver.yml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: volosoft-eventhub-identityserver-deployment -spec: - selector: - matchLabels: - app: volosoft-eventhub-identityserver-pod - template: - metadata: - labels: - app: volosoft-eventhub-identityserver-pod - spec: - containers: - - name: volosoft-eventhub-identityserver-container - image: voloeventhubidentityserver.azurecr.io/voloeventhubidentityserver - imagePullPolicy: Always - resources: - limits: - memory: "128Mi" - cpu: "500m" - ports: - - containerPort: 80 \ No newline at end of file diff --git a/manifests/deployment.eventhub.web.yml b/manifests/deployment.eventhub.web.yml deleted file mode 100644 index a64eae8..0000000 --- a/manifests/deployment.eventhub.web.yml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: volosoft-eventhub-web-deployment -spec: - selector: - matchLabels: - app: volosoft-eventhub-web-pod - template: - metadata: - labels: - app: volosoft-eventhub-web-pod - spec: - containers: - - name: volosoft-eventhub-web-container - image: voloeventhub.azurecr.io/voloeventhub - imagePullPolicy: Always - resources: - limits: - memory: "128Mi" - cpu: "500m" - ports: - - containerPort: 80 diff --git a/manifests/deployment.yml b/manifests/deployment.yml deleted file mode 100644 index eaf7dae..0000000 --- a/manifests/deployment.yml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion : apps/v1 -kind: Deployment -metadata: - name: eventhubhttpapihost -spec: - replicas: 1 - selector: - matchLabels: - app: eventhubhttpapihost - template: - metadata: - labels: - app: eventhubhttpapihost - spec: - containers: - - name: eventhubhttpapihost - image: eventhubregistry.azurecr.io/eventhubhttpapihost - ports: - - containerPort: 80 \ No newline at end of file diff --git a/manifests/service.eventhub.httpapi.host.yml b/manifests/service.eventhub.httpapi.host.yml deleted file mode 100644 index 3602aab..0000000 --- a/manifests/service.eventhub.httpapi.host.yml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: volosoft-eventhub-httpapi-host-service -spec: - selector: - app: volosoft-eventhub-httpapi-host-pod - ports: - - port: 5002 - targetPort: 80 - type: LoadBalancer diff --git a/manifests/service.eventhub.identityserver.yml b/manifests/service.eventhub.identityserver.yml deleted file mode 100644 index 060bb03..0000000 --- a/manifests/service.eventhub.identityserver.yml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: volosoft-eventhub-identityserver-service -spec: - selector: - app: volosoft-eventhub-identityserver-pod - ports: - - name: http - port: 5001 - protocol: TCP - targetPort: 80 - - name: https - port: 5000 - targetPort: 443 - protocol: TCP - type: LoadBalancer \ No newline at end of file diff --git a/manifests/service.eventhub.web.yml b/manifests/service.eventhub.web.yml deleted file mode 100644 index f688b01..0000000 --- a/manifests/service.eventhub.web.yml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: volosoft-eventhub-web-service -spec: - selector: - app: volosoft-eventhub-web-pod - ports: - - port: 8080 - targetPort: 80 - type: LoadBalancer \ No newline at end of file diff --git a/manifests/service.yml b/manifests/service.yml deleted file mode 100644 index 2d19ec4..0000000 --- a/manifests/service.yml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: eventhubhttpapihost -spec: - type: LoadBalancer - ports: - - port: 80 - selector: - app: eventhubhttpapihost \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/EventHubAdminRemoteServiceConsts.cs b/src/EventHub.Admin.Application.Contracts/EventHubAdminRemoteServiceConsts.cs new file mode 100644 index 0000000..8a9b68b --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/EventHubAdminRemoteServiceConsts.cs @@ -0,0 +1,7 @@ +namespace EventHub.Admin +{ + public static class EventHubAdminRemoteServiceConsts + { + public const string RemoteServiceName = "EventHubAdmin"; + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/IOrganizationAppService.cs b/src/EventHub.Admin.Application.Contracts/Organizations/IOrganizationAppService.cs new file mode 100644 index 0000000..b0850b8 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/IOrganizationAppService.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace EventHub.Admin.Organizations +{ + public interface IOrganizationAppService : IApplicationService + { + Task> GetListAsync(OrganizationListFilterDto input); + + Task GetAsync(Guid id); + + Task GetByNameAsync(string name); + + Task UpdateAsync(Guid id, UpdateOrganizationDto input); + + Task DeleteAsync(Guid id); + } +} diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/IOrganizationMembershipAppService.cs b/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/IOrganizationMembershipAppService.cs new file mode 100644 index 0000000..b471473 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/IOrganizationMembershipAppService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace EventHub.Admin.Organizations.Memberships +{ + public interface IOrganizationMembershipAppService : IApplicationService + { + Task> GetListAsync(OrganizationMemberListFilterDto input); + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberDto.cs b/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberDto.cs new file mode 100644 index 0000000..e4e3550 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberDto.cs @@ -0,0 +1,18 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Organizations.Memberships +{ + public class OrganizationMemberDto : EntityDto + { + public string OrganizationName { get; set; } + + public string UserName { get; set; } + + public string Email { get; set; } + + public string Name { get; set; } + + public string Surname { get; set; } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberListFilterDto.cs b/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberListFilterDto.cs new file mode 100644 index 0000000..8c6cce2 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/Memberships/OrganizationMemberListFilterDto.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Organizations.Memberships +{ + public class OrganizationMemberListFilterDto : PagedResultRequestDto + { + public Guid? OrganizationId { get; set; } + + public string UserName { get; set; } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationInListDto.cs b/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationInListDto.cs new file mode 100644 index 0000000..e5ddbff --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationInListDto.cs @@ -0,0 +1,14 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Organizations +{ + public class OrganizationInListDto : EntityDto + { + public string Name { get; set; } + + public string DisplayName { get; set; } + + public int MemberCount { get; set; } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationListFilterDto.cs b/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationListFilterDto.cs new file mode 100644 index 0000000..43c2a65 --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationListFilterDto.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Organizations +{ + public class OrganizationListFilterDto : PagedAndSortedResultRequestDto + { + public string Name { get; set; } + + public string DisplayName { get; set; } + + public int? MinMemberCount { get; set; } + + public int? MaxMemberCount { get; set; } + } +} diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationProfileDto.cs b/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationProfileDto.cs new file mode 100644 index 0000000..86b469c --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/OrganizationProfileDto.cs @@ -0,0 +1,32 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Organizations +{ + public class OrganizationProfileDto : EntityDto + { + public string Name { get; set; } + + public string DisplayName { get; set; } + + public byte[] ProfilePictureContent { get; set; } + + public string OwnerUserName { get; set; } + + public string OwnerEmail { get; set; } + + public string Description { get; set; } + + public string Website { get; set; } + + public string TwitterUsername { get; set; } + + public string GitHubUsername { get; set; } + + public string FacebookUsername { get; set; } + + public string InstagramUsername { get; set; } + + public string MediumUsername { get; set; } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/Organizations/UpdateOrganizationDto.cs b/src/EventHub.Admin.Application.Contracts/Organizations/UpdateOrganizationDto.cs new file mode 100644 index 0000000..c90c59b --- /dev/null +++ b/src/EventHub.Admin.Application.Contracts/Organizations/UpdateOrganizationDto.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using EventHub.Organizations; +using JetBrains.Annotations; + +namespace EventHub.Admin.Organizations +{ + public class UpdateOrganizationDto + { + [Required] + [StringLength(OrganizationConsts.MaxDisplayNameLength, MinimumLength = OrganizationConsts.MinDisplayNameLength)] + public string DisplayName { get; set; } + + [Required] + [StringLength(OrganizationConsts.MaxDescriptionNameLength, MinimumLength = OrganizationConsts.MinDescriptionNameLength)] + public string Description { get; set; } + + [CanBeNull] + public byte[] ProfilePictureContent { get; set; } + + [CanBeNull] + [StringLength(OrganizationConsts.MaxWebsiteLength)] + public string Website { get; set; } + + [CanBeNull] + [StringLength(OrganizationConsts.MaxTwitterUsernameLength)] + public string TwitterUsername { get; set; } + + [CanBeNull] + [StringLength(OrganizationConsts.MaxGitHubUsernameLength)] + public string GitHubUsername { get; set; } + + [CanBeNull] + [StringLength(OrganizationConsts.MaxFacebookUsernameLength)] + public string FacebookUsername { get; set; } + + [CanBeNull] + [StringLength(OrganizationConsts.MaxInstagramUsernameLength)] + public string InstagramUsername { get; set; } + + [CanBeNull] + [StringLength(OrganizationConsts.MaxMediumUsernameLength)] + public string MediumUsername { get; set; } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs index e0f7571..b9790b9 100644 --- a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs +++ b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissionDefinitionProvider.cs @@ -8,10 +8,13 @@ namespace EventHub.Admin.Permissions { public override void Define(IPermissionDefinitionContext context) { - var myGroup = context.AddGroup(EventHubPermissions.GroupName); + var eventHubGroup = context.AddGroup(EventHubPermissions.GroupName); - //Define your own permissions here. Example: - //myGroup.AddPermission(EventHubPermissions.MyPermission1, L("Permission:MyPermission1")); + var organizationsPermission = eventHubGroup.AddPermission(EventHubPermissions.Organizations.Default, L("Permission:OrganizationManagement")); + organizationsPermission.AddChild(EventHubPermissions.Organizations.Create, L("Permission:Create")); + organizationsPermission.AddChild(EventHubPermissions.Organizations.Update, L("Permission:Edit")); + organizationsPermission.AddChild(EventHubPermissions.Organizations.Delete, L("Permission:Delete")); + organizationsPermission.AddChild(EventHubPermissions.Organizations.Memberships.Default, L("Permission:MembershipManagement")); } private static LocalizableString L(string name) diff --git a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs index b7fbe16..a1003bc 100644 --- a/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs +++ b/src/EventHub.Admin.Application.Contracts/Permissions/EventHubPermissions.cs @@ -4,7 +4,17 @@ { public const string GroupName = "EventHub"; - //Add your own permission names. Example: - //public const string MyPermission1 = GroupName + ".MyPermission1"; + public static class Organizations + { + public const string Default = GroupName + ".Organizations"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + + public static class Memberships + { + public const string Default = Organizations.Default + ".Memberships"; + } + } } } \ No newline at end of file diff --git a/src/EventHub.Admin.Application/EventHubAdminAppService.cs b/src/EventHub.Admin.Application/EventHubAdminAppService.cs new file mode 100644 index 0000000..5fd1056 --- /dev/null +++ b/src/EventHub.Admin.Application/EventHubAdminAppService.cs @@ -0,0 +1,13 @@ +using EventHub.Localization; +using Volo.Abp.Application.Services; + +namespace EventHub.Admin +{ + public class EventHubAdminAppService : ApplicationService + { + protected EventHubAdminAppService() + { + LocalizationResource = typeof(EventHubResource); + } + } +} diff --git a/src/EventHub.Admin.Application/EventHubAdminApplicationModule.cs b/src/EventHub.Admin.Application/EventHubAdminApplicationModule.cs index b046665..64fc7dc 100644 --- a/src/EventHub.Admin.Application/EventHubAdminApplicationModule.cs +++ b/src/EventHub.Admin.Application/EventHubAdminApplicationModule.cs @@ -1,4 +1,5 @@ using Volo.Abp.Account; +using Volo.Abp.AutoMapper; using Volo.Abp.Identity; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement; @@ -10,9 +11,17 @@ namespace EventHub.Admin typeof(AbpAccountApplicationModule), typeof(EventHubAdminApplicationContractsModule), typeof(AbpIdentityApplicationModule), - typeof(AbpPermissionManagementApplicationModule) + typeof(AbpPermissionManagementApplicationModule), + typeof(AbpAutoMapperModule) )] public class EventHubAdminApplicationModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.AddProfile(validate: true); + }); + } } } \ No newline at end of file diff --git a/src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs b/src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs new file mode 100644 index 0000000..b11a97c --- /dev/null +++ b/src/EventHub.Admin.Application/EventHubApplicationAutoMapperProfile.cs @@ -0,0 +1,24 @@ +using AutoMapper; +using EventHub.Admin.Organizations; +using EventHub.Admin.Organizations.Memberships; +using EventHub.Organizations; +using EventHub.Organizations.Memberships; +using Volo.Abp.AutoMapper; + +namespace EventHub.Admin +{ + public class EventHubAdminApplicationAutoMapperProfile : Profile + { + public EventHubAdminApplicationAutoMapperProfile() + { + CreateMap(); + + CreateMap() + .Ignore(x => x.OwnerUserName) + .Ignore(x => x.OwnerEmail) + .Ignore(x => x.ProfilePictureContent); + + CreateMap(); + } + } +} diff --git a/src/EventHub.Admin.Application/Organizations/Memberships/OrganizationMembershipAppService.cs b/src/EventHub.Admin.Application/Organizations/Memberships/OrganizationMembershipAppService.cs new file mode 100644 index 0000000..b97d381 --- /dev/null +++ b/src/EventHub.Admin.Application/Organizations/Memberships/OrganizationMembershipAppService.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using EventHub.Admin.Permissions; +using EventHub.Organizations.Memberships; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; + + +namespace EventHub.Admin.Organizations.Memberships +{ + [Authorize(EventHubPermissions.Organizations.Memberships.Default)] + public class OrganizationMembershipAppService : ApplicationService, IOrganizationMembershipAppService + { + private readonly IOrganizationMembershipRepository _organizationMembershipRepository; + private readonly IRepository _userRepository; + + public OrganizationMembershipAppService( + IOrganizationMembershipRepository organizationMembershipRepository, + IRepository userRepository) + { + _organizationMembershipRepository = organizationMembershipRepository; + _userRepository = userRepository; + } + + public async Task> GetListAsync(OrganizationMemberListFilterDto input) + { + IdentityUser user = null; + if (!input.UserName.IsNullOrWhiteSpace()) + { + input.UserName = input.UserName.ToLower(); + user = await _userRepository.SingleOrDefaultAsync(x => x.UserName.ToLower() == input.UserName); + if (user is null) + { + throw new BusinessException(EventHubErrorCodes.UserNotFound) + .WithData("UserName", input.UserName); + } + } + + var members = await _organizationMembershipRepository.GetMemberListAsync(input.OrganizationId, user?.Id, input.SkipCount, input.MaxResultCount); + var totalCount = await _organizationMembershipRepository.GetCountAsync(input.OrganizationId, user?.Id); + + return new PagedResultDto( + totalCount, + ObjectMapper.Map, List>(members) + ); + } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Application/Organizations/OrganizationAppService.cs b/src/EventHub.Admin.Application/Organizations/OrganizationAppService.cs new file mode 100644 index 0000000..df3794c --- /dev/null +++ b/src/EventHub.Admin.Application/Organizations/OrganizationAppService.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using EventHub.Admin.Permissions; +using EventHub.Organizations; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.BlobStoring; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; + +namespace EventHub.Admin.Organizations +{ + [Authorize(EventHubPermissions.Organizations.Default)] + public class OrganizationAppService : EventHubAdminAppService, IOrganizationAppService + { + private readonly IRepository _organizationRepository; + private readonly IRepository _identityUserRepository; + private readonly IBlobContainer _organizationBlobContainer; + + public OrganizationAppService( + IRepository organizationRepository, + IRepository identityUserRepository, + IBlobContainer organizationBlobContainer) + { + _organizationRepository = organizationRepository; + _identityUserRepository = identityUserRepository; + _organizationBlobContainer = organizationBlobContainer; + } + + public async Task> GetListAsync(OrganizationListFilterDto input) + { + var query = await _organizationRepository.GetQueryableAsync(); + + if (!input.Name.IsNullOrWhiteSpace()) + { + input.Name = input.Name.ToLower(); + query = query.Where(o => o.Name.ToLower().Contains(input.Name)); + } + + if (!input.DisplayName.IsNullOrWhiteSpace()) + { + input.DisplayName = input.DisplayName.ToLower(); + query = query.Where(o => o.DisplayName.ToLower().Contains(input.DisplayName)); + } + + if (input.MinMemberCount != null) + { + query = query.Where(o => o.MemberCount >= input.MinMemberCount); + } + + if (input.MaxMemberCount != null) + { + query = query.Where(o => o.MemberCount <= input.MaxMemberCount); + } + + var totalCount = await AsyncExecuter.CountAsync(query); + + query = query.OrderBy(string.IsNullOrWhiteSpace(input.Sorting) + ? OrganizationConsts.DefaultSorting + : input.Sorting); + + query = query.PageBy(input); + + var organizationListDto = ObjectMapper + .Map, List>(await AsyncExecuter.ToListAsync(query)); + + return new PagedResultDto(totalCount, organizationListDto); + } + + public async Task GetAsync(Guid id) + { + var organization = await _organizationRepository.GetAsync(id); + + return await CreateOrganizationProfileDto(organization); + } + + public async Task GetByNameAsync(string name) + { + var organization = await _organizationRepository.FindAsync(x => x.Name.ToLower() == name.ToLower()); + if (organization is null) + { + throw new BusinessException(EventHubErrorCodes.OrganizationNotFound) + .WithData("OrganizationName", name); + } + + return await CreateOrganizationProfileDto(organization); + } + + [Authorize(EventHubPermissions.Organizations.Update)] + public async Task UpdateAsync(Guid id, UpdateOrganizationDto input) + { + var organization = await _organizationRepository.GetAsync(id); + + organization.SetDisplayName(input.DisplayName); + organization.SetDescription(input.Description); + + organization.Website = input.Website; + organization.TwitterUsername = input.TwitterUsername; + organization.GitHubUsername = input.GitHubUsername; + organization.FacebookUsername = input.FacebookUsername; + organization.InstagramUsername = input.InstagramUsername; + organization.MediumUsername = input.MediumUsername; + + if (input.ProfilePictureContent?.Length > 0) + { + await SaveCoverImageAsync(organization.Id, input.ProfilePictureContent); + } + else + { + await DeleteCoverImageAsync(organization.Id); + } + + await _organizationRepository.UpdateAsync(organization); + + return ObjectMapper.Map(organization); + } + + [Authorize(EventHubPermissions.Organizations.Delete)] + public async Task DeleteAsync(Guid id) + { + await _organizationRepository.DeleteAsync(id); + } + + private async Task CreateOrganizationProfileDto(Organization organization) + { + var dto = ObjectMapper.Map(organization); + + var user = await _identityUserRepository.GetAsync(organization.OwnerUserId); + dto.OwnerUserName = user.UserName; + dto.OwnerEmail = user.Email; + + dto.ProfilePictureContent = await GetCoverImageAsync(organization.Id); + return dto; + } + + private async Task GetCoverImageAsync(Guid id) + { + var blobName = id.ToString(); + + return await _organizationBlobContainer.GetAllBytesOrNullAsync(blobName); + } + + private async Task SaveCoverImageAsync(Guid id, byte[] coverImageContent) + { + var blobName = id.ToString(); + + await _organizationBlobContainer.SaveAsync(blobName, coverImageContent, overrideExisting: true); + } + + private async Task DeleteCoverImageAsync(Guid id) + { + var blobName = id.ToString(); + + await _organizationBlobContainer.DeleteAsync(blobName); + } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs b/src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs new file mode 100644 index 0000000..f10d811 --- /dev/null +++ b/src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using EventHub.Admin.Organizations.Memberships; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Admin.Controllers.Organizations.Memberships +{ + [RemoteService(Name = EventHubAdminRemoteServiceConsts.RemoteServiceName)] + [Controller] + [Area("eventhub-admin")] + [ControllerName("OrganizationMembership")] + [Route("/api/eventhub/admin/organization-membership")] + public class OrganizationMembershipController : AbpController, IOrganizationMembershipAppService + { + private readonly IOrganizationMembershipAppService _organizationMembershipAppService; + + public OrganizationMembershipController(IOrganizationMembershipAppService organizationMembershipAppService) + { + _organizationMembershipAppService = organizationMembershipAppService; + } + + [HttpGet] + public async Task> GetListAsync(OrganizationMemberListFilterDto input) + { + return await _organizationMembershipAppService.GetListAsync(input); + } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/OrganizationController.cs b/src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/OrganizationController.cs new file mode 100644 index 0000000..82fd290 --- /dev/null +++ b/src/EventHub.Admin.HttpApi.Host/Controllers/Organizations/OrganizationController.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using EventHub.Admin.Organizations; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Admin.Controllers.Organizations +{ + [RemoteService(Name = EventHubAdminRemoteServiceConsts.RemoteServiceName)] + [Controller] + [Area("eventhub-admin")] + [ControllerName("Organization")] + [Route("/api/eventhub/admin/organization")] + public class OrganizationController : AbpController, IOrganizationAppService + { + private readonly IOrganizationAppService _organizationAppService; + + public OrganizationController(IOrganizationAppService organizationAppService) + { + _organizationAppService = organizationAppService; + } + + [HttpGet] + public Task> GetListAsync(OrganizationListFilterDto input) + { + return _organizationAppService.GetListAsync(input); + } + + [HttpGet] + [Route("{id}")] + public Task GetAsync(Guid id) + { + return _organizationAppService.GetAsync(id); + } + + [HttpGet] + [Route("by-name/{name}")] + public Task GetByNameAsync(string name) + { + return _organizationAppService.GetByNameAsync(name); + } + + [HttpPut] + public Task UpdateAsync(Guid id, UpdateOrganizationDto input) + { + return _organizationAppService.UpdateAsync(id, input); + } + + [HttpDelete] + [Route("{id}")] + public Task DeleteAsync(Guid id) + { + return _organizationAppService.DeleteAsync(id); + } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.HttpApi.Host/Dockerfile b/src/EventHub.Admin.HttpApi.Host/Dockerfile new file mode 100644 index 0000000..7dda575 --- /dev/null +++ b/src/EventHub.Admin.HttpApi.Host/Dockerfile @@ -0,0 +1,4 @@ + FROM mcr.microsoft.com/dotnet/aspnet:5.0 + COPY bin/Release/net5.0/publish/ app/ + WORKDIR /app + ENTRYPOINT ["dotnet", "EventHub.Admin.HttpApi.Host.dll"] \ No newline at end of file diff --git a/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs b/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs index 38ff481..17946a0 100644 --- a/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs +++ b/src/EventHub.Admin.HttpApi.Host/EventHubAdminHttpApiHostModule.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using EventHub.Admin.Utils; using EventHub.EntityFrameworkCore; +using EventHub.Web; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors; @@ -54,6 +56,7 @@ namespace EventHub.Admin ConfigureVirtualFileSystem(context); ConfigureRedis(context, configuration); ConfigureCors(context, configuration); + ConfigureCookies(context); ConfigureSwaggerServices(context, configuration); ConfigureBackgroundJobs(); } @@ -109,7 +112,7 @@ namespace EventHub.Admin private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddAbpSwaggerGenWithOAuth( - configuration["AuthServer:Authority"], + configuration[EventHubUrlOptions.GetAccountConfigKey()], new Dictionary { {"EventHubAdmin", "EventHub Admin API"} @@ -148,10 +151,7 @@ namespace EventHub.Admin { builder .WithOrigins( - configuration["App:CorsOrigins"] - .Split(",", StringSplitOptions.RemoveEmptyEntries) - .Select(o => o.RemovePostFix("/")) - .ToArray() + configuration[EventHubUrlOptions.GetAdminConfigKey()] ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() @@ -162,6 +162,11 @@ namespace EventHub.Admin }); } + private void ConfigureCookies(ServiceConfigurationContext context) + { + context.Services.AddSameSiteCookiePolicy(); + } + public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); @@ -194,8 +199,9 @@ namespace EventHub.Admin app.UseErrorPage(); } + app.UseCookiePolicy(); app.UseCorrelationId(); - app.UseVirtualFiles(); + app.UseStaticFiles(); app.UseRouting(); app.UseCors(DefaultCorsPolicyName); app.UseAuthentication(); diff --git a/src/EventHub.Admin.HttpApi.Host/Program.cs b/src/EventHub.Admin.HttpApi.Host/Program.cs index fdb4ed3..076a07e 100644 --- a/src/EventHub.Admin.HttpApi.Host/Program.cs +++ b/src/EventHub.Admin.HttpApi.Host/Program.cs @@ -20,9 +20,7 @@ namespace EventHub.Admin .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.Async(c => c.File("Logs/logs.txt")) -#if DEBUG .WriteTo.Async(c => c.Console()) -#endif .CreateLogger(); try diff --git a/src/EventHub.Admin.HttpApi.Host/Utils/SameSiteCookiesServiceCollectionExtensions.cs b/src/EventHub.Admin.HttpApi.Host/Utils/SameSiteCookiesServiceCollectionExtensions.cs new file mode 100644 index 0000000..0895c29 --- /dev/null +++ b/src/EventHub.Admin.HttpApi.Host/Utils/SameSiteCookiesServiceCollectionExtensions.cs @@ -0,0 +1,71 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace EventHub.Admin.Utils +{ + public static class SameSiteCookiesServiceCollectionExtensions + { + public static IServiceCollection AddSameSiteCookiePolicy(this IServiceCollection services) + { + services.Configure(options => + { + options.MinimumSameSitePolicy = SameSiteMode.Unspecified; + options.OnAppendCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + options.OnDeleteCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + }); + + return services; + } + + private static void CheckSameSite(HttpContext httpContext, CookieOptions options) + { + if (options.SameSite == SameSiteMode.None) + { + var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); + if (!httpContext.Request.IsHttps || DisallowsSameSiteNone(userAgent)) + { + // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1) + options.SameSite = SameSiteMode.Unspecified; + } + } + } + + private static bool DisallowsSameSiteNone(string userAgent) + { + // Cover all iOS based browsers here. This includes: + // - Safari on iOS 12 for iPhone, iPod Touch, iPad + // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad + // - Chrome on iOS 12 for iPhone, iPod Touch, iPad + // All of which are broken by SameSite=None, because they use the iOS networking stack + if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12")) + { + return true; + } + + // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes: + // - Safari on Mac OS X. + // This does not include: + // - Chrome on Mac OS X + // Because they do not use the Mac OS networking stack. + if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && + userAgent.Contains("Version/") && userAgent.Contains("Safari")) + { + return true; + } + + // Cover Chrome 50-69, because some versions are broken by SameSite=None, + // and none in this range require it. + // Note: this covers some pre-Chromium Edge versions, + // but pre-Chromium Edge does not require SameSite=None. + if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.HttpApi.Host/appsettings.Development.json b/src/EventHub.Admin.HttpApi.Host/appsettings.Development.json deleted file mode 100644 index 2c63c08..0000000 --- a/src/EventHub.Admin.HttpApi.Host/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/src/EventHub.Admin.HttpApi.Host/appsettings.json b/src/EventHub.Admin.HttpApi.Host/appsettings.json index 7c2ab2d..aca9ac1 100644 --- a/src/EventHub.Admin.HttpApi.Host/appsettings.json +++ b/src/EventHub.Admin.HttpApi.Host/appsettings.json @@ -1,20 +1,14 @@ { - "App": { - "CorsOrigins": "https://*.openeventhub.com,https://localhost:44307,https://localhost:44308" - }, "ConnectionStrings": { "Default": "Host=localhost;Database=EventHub;Username=root;Password=root;Port=5432" }, "Redis": { - "Configuration": "127.0.0.1" + "Configuration": "localhost" }, "AuthServer": { "Authority": "https://localhost:44313", "RequireHttpsMetadata": "true", "SwaggerClientId": "EventHub_Swagger", "SwaggerClientSecret": "1q2w3e*" - }, - "StringEncryption": { - "DefaultPassPhrase": "TxVIZFPxK33czbbv" } } diff --git a/src/EventHub.Admin.Web/Dockerfile b/src/EventHub.Admin.Web/Dockerfile new file mode 100644 index 0000000..cdfeb3c --- /dev/null +++ b/src/EventHub.Admin.Web/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx:latest +COPY ./bin/Release/net5.0/publish/wwwroot/ /usr/share/nginx/html/ +COPY ./nginx.conf /etc/nginx/conf.d/default.conf \ No newline at end of file diff --git a/src/EventHub.Admin.Web/EventHub.Admin.Web.csproj b/src/EventHub.Admin.Web/EventHub.Admin.Web.csproj index 51d7080..aaa7547 100644 --- a/src/EventHub.Admin.Web/EventHub.Admin.Web.csproj +++ b/src/EventHub.Admin.Web/EventHub.Admin.Web.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs b/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs index 3cdc492..53cffe5 100644 --- a/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs +++ b/src/EventHub.Admin.Web/EventHubBlazorAutoMapperProfile.cs @@ -1,4 +1,5 @@ using AutoMapper; +using EventHub.Admin.Organizations; namespace EventHub.Admin.Web { @@ -6,7 +7,7 @@ namespace EventHub.Admin.Web { public EventHubBlazorAutoMapperProfile() { - //Define your AutoMapper configuration here for the Blazor project. + CreateMap(); } } -} +} \ No newline at end of file diff --git a/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs b/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs index 90fd929..1aa0481 100644 --- a/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs +++ b/src/EventHub.Admin.Web/Menus/EventHubMenuContributor.cs @@ -1,9 +1,12 @@ using System; using System.Threading.Tasks; +using EventHub.Admin.Permissions; using EventHub.Localization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Volo.Abp.Account.Localization; +using Volo.Abp.Authorization.Permissions; using Volo.Abp.UI.Navigation; using Volo.Abp.Users; @@ -30,7 +33,7 @@ namespace EventHub.Admin.Web.Menus } } - private Task ConfigureMainMenuAsync(MenuConfigurationContext context) + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) { var l = context.GetLocalizer(); @@ -44,7 +47,7 @@ namespace EventHub.Admin.Web.Menus ) ); - return Task.CompletedTask; + await AddOrganizationMenu(context, l); } private Task ConfigureUserMenuAsync(MenuConfigurationContext context) @@ -67,5 +70,22 @@ namespace EventHub.Admin.Web.Menus return Task.CompletedTask; } + + private Task AddOrganizationMenu(MenuConfigurationContext context, IStringLocalizer l) + { + var organizationMenu = new ApplicationMenuItem(EventHubMenus.OrganizationManagement.GroupName, + l["Menu:OrganizationManagement"], icon: "fa fa-sitemap"); + context.Menu.Items.Insert(2, organizationMenu); + + organizationMenu.AddItem( + new ApplicationMenuItem(EventHubMenus.OrganizationManagement.Organizations, l["Menu:Organizations"], + url: "/organizations").RequirePermissions(EventHubPermissions.Organizations.Default)); + organizationMenu.AddItem( + new ApplicationMenuItem(EventHubMenus.OrganizationManagement.OrganizationMemberships, + l["Menu:OrganizationMemberships"], url: "/organization-memberships") + .RequirePermissions(EventHubPermissions.Organizations.Memberships.Default)); + + return Task.CompletedTask; + } } -} +} \ No newline at end of file diff --git a/src/EventHub.Admin.Web/Menus/EventHubMenus.cs b/src/EventHub.Admin.Web/Menus/EventHubMenus.cs index e24ac1c..66562a1 100644 --- a/src/EventHub.Admin.Web/Menus/EventHubMenus.cs +++ b/src/EventHub.Admin.Web/Menus/EventHubMenus.cs @@ -5,7 +5,11 @@ private const string Prefix = "EventHub"; public const string Home = Prefix + ".Home"; - //Add your menu items here... - + public static class OrganizationManagement + { + public const string GroupName = Prefix + ".OrganizationManagement"; + public const string Organizations = GroupName + ".Organizations"; + public const string OrganizationMemberships = GroupName + ".OrganizationMemberships"; + } } } \ No newline at end of file diff --git a/src/EventHub.Admin.Web/Pages/OrganizationManagement.razor b/src/EventHub.Admin.Web/Pages/OrganizationManagement.razor new file mode 100644 index 0000000..6a81b63 --- /dev/null +++ b/src/EventHub.Admin.Web/Pages/OrganizationManagement.razor @@ -0,0 +1,268 @@ +@page "/organizations" +@using Volo.Abp.BlazoriseUI.Components +@using EventHub.Admin.Permissions +@using Microsoft.AspNetCore.Authorization +@using EventHub.Admin.Organizations +@using EventHub.Organizations +@using Volo.Abp.AspNetCore.Components.Notifications +@inherits EventHubComponentBase +@inject NavigationManager NavigationManager +@attribute [Authorize(EventHubPermissions.Organizations.Default)] +@inject IOrganizationAppService OrganizationAppService +@inject IUiNotificationService UiNotificationService + + + + @* ************************* PAGE HEADER ************************* *@ +

@L["Organizations"]

+
+ + @* ************************* SEARCH ************************* *@ + + +
+
+ + + @L["Name"] + + + + + + + @L["DisplayName"] + + + + + + + @L["MinMemberCount"] + + + + + + + @L["MaxMemberCount"] + + + +
+
+ + @* ************************* DATA GRID ************************* *@ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +@* ************************* EDIT MODAL ************************* *@ + + + +
+ + @L["UpdateOrganization"] + + + + + + @L["OrganizationInfo"] + @L["ProfileImage"] + + + + + + + @L["DisplayName"] * + + + + + + + + + + + @L["Description"] * + + + + + + + + + + + @L["Website"] + + + + + + + + + + + @L["TwitterUsername"] + + + + + + + + + + + @L["GitHubUsername"] + + + + + + + + + + + @L["FacebookUsername"] + + + + + + + + + + + + @L["InstagramUsername"] + + + + + + + + + + + @L["MediumUsername"] + + + + + + + + + + + + @if (!IsLoadingProfileImage) + { + @if (!ProfileImageUrl.IsNullOrEmpty()) + { +
+ profile-image +
+ } + } + else + { +
+
+ @L["Loading"]... +
+
+ } + +
+ + @L["ChooseProfileImage"] + + + + + @if (!ProfileImageUrl.IsNullOrWhiteSpace()) + { + + } + else + { + + } + + +
+
+
+
+
+ + + + +
+
+
\ No newline at end of file diff --git a/src/EventHub.Admin.Web/Pages/OrganizationManagement.razor.cs b/src/EventHub.Admin.Web/Pages/OrganizationManagement.razor.cs new file mode 100644 index 0000000..d441bae --- /dev/null +++ b/src/EventHub.Admin.Web/Pages/OrganizationManagement.razor.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Blazorise; +using Blazorise.DataGrid; +using EventHub.Admin.Organizations; +using Microsoft.AspNetCore.Components.Web; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Web.Pages +{ + public partial class OrganizationManagement + { + private IReadOnlyList OrganizationList { get; set; } + private OrganizationListFilterDto Filter { get; set; } + private int CurrentPage { get; set; } + private string CurrentSorting { get; set; } + private int TotalCount { get; set; } + private int PageSize { get; } + + private OrganizationProfileDto Organization { get; set; } + private string ProfileImageUrl { get; set; } + + private Guid EditingOrganizationId { get; set; } + private UpdateOrganizationDto EditingOrganization { get; set; } + private Modal EditOrganizationModal { get; set; } + private IFileEntry FileEntry { get; set; } + private bool IsLoadingProfileImage { get; set; } + private string SelectedTabInEditModal { get; set; } + + public OrganizationManagement() + { + Filter = new OrganizationListFilterDto(); + PageSize = LimitedResultRequestDto.DefaultMaxResultCount; + + OrganizationList = new List(); + EditingOrganization = new UpdateOrganizationDto(); + IsLoadingProfileImage = false; + SelectedTabInEditModal = TabContentInEditModal.OrganizationProfile.ToString(); + } + + protected override async Task OnInitializedAsync() + { + await GetOrganizationsAsync(); + } + + private async Task OnDataGridReadAsync(DataGridReadDataEventArgs e) + { + CurrentSorting = e.Columns + .Where(c => c.Direction != SortDirection.None) + .Select(c => c.Field + (c.Direction == SortDirection.Descending ? " DESC" : "")) + .JoinAsString(","); + CurrentPage = e.Page - 1; + await GetOrganizationsAsync(); + await InvokeAsync(StateHasChanged); + } + + private async Task GetOrganizationsAsync() + { + Filter.MaxResultCount = PageSize; + Filter.SkipCount = CurrentPage * PageSize; + Filter.Sorting = CurrentSorting; + + var result = await OrganizationAppService.GetListAsync(Filter); + OrganizationList = result.Items; + TotalCount = (int) result.TotalCount; + } + + private async Task OpenEditOrganizationModal(OrganizationInListDto input) + { + EditingOrganizationId = input.Id; + Organization = await OrganizationAppService.GetAsync(EditingOrganizationId); + FillProfileImageUrl(Organization.ProfilePictureContent); + + EditingOrganization = ObjectMapper.Map(Organization); + EditOrganizationModal.Show(); + } + + private async Task UpdateOrganizationAsync() + { + await OrganizationAppService.UpdateAsync(EditingOrganizationId, EditingOrganization); + await GetOrganizationsAsync(); + EditOrganizationModal.Hide(); + } + + private void OnDeleteCoverImageButtonClicked() + { + EditingOrganization.ProfilePictureContent = null; + FileEntry = new FileEntry(); + ProfileImageUrl = null; + IsLoadingProfileImage = false; + } + + private async Task DeleteOrganizationAsync(OrganizationInListDto organization) + { + await OrganizationAppService.DeleteAsync(organization.Id); + await GetOrganizationsAsync(); + } + + private async Task OnProfileImageFileChanged(FileChangedEventArgs e) + { + FileEntry = e.Files.FirstOrDefault(); + if (FileEntry is null) + { + return; + } + + IsLoadingProfileImage = true; + + using (var stream = new MemoryStream()) + { + await FileEntry.WriteToStreamAsync(stream); + + stream.Seek(0, SeekOrigin.Begin); + EditingOrganization.ProfilePictureContent = stream.ToArray(); + FillProfileImageUrl(EditingOrganization.ProfilePictureContent); + await InvokeAsync(StateHasChanged); + } + } + + private void FillProfileImageUrl(byte[] content) + { + if (content != null) + { + var imageBase64Data = Convert.ToBase64String(content); + var imageDataUrl = $"data:image/png;base64,{imageBase64Data}"; + ProfileImageUrl = imageDataUrl; + } + } + + private void OnProgressedForProfileImage(FileProgressedEventArgs e) + { + if (e.Percentage == 100D) + { + IsLoadingProfileImage = false; + } + } + + private void OnSelectedTabChangedInEditModal(string name) + { + SelectedTabInEditModal = name; + } + + private void OnEditModalClosing(CancelEventArgs e) + { + IsLoadingProfileImage = false; + SelectedTabInEditModal = TabContentInEditModal.OrganizationProfile.ToString(); + } + + private async Task OnKeyPress(KeyboardEventArgs e) + { + if (e.Code == "Enter" || e.Code == "NumpadEnter") + { + await GetOrganizationsAsync(); + } + } + } + + public enum TabContentInEditModal : byte + { + OrganizationProfile = 0, + ProfileImage + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor b/src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor new file mode 100644 index 0000000..79f6f18 --- /dev/null +++ b/src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor @@ -0,0 +1,78 @@ +@page "/organization-memberships/{OrganizationId:guid?}" +@using Volo.Abp.BlazoriseUI.Components +@using EventHub.Admin.Organizations.Memberships +@using EventHub.Admin.Permissions +@using Microsoft.AspNetCore.Authorization +@inherits EventHubComponentBase +@attribute [Authorize(EventHubPermissions.Organizations.Memberships.Default)] +@inject IOrganizationMembershipAppService OrganizationMembershipAppService + + + + @* ************************* HEADER ************************* *@ +

+ @L["OrganizationMemberships"] + @if (OrganizationId.HasValue && !OrganizationName.IsNullOrWhiteSpace()) + { + (@OrganizationName) + } +

+
+ + @* ************************* SEARCH ************************* *@ + +
+ + + + @L["OrganizationName"] + + + + + + + @L["UserName"] + + + + +
+ + @* ************************* DATA GRID ************************* *@ + + + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor.cs b/src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor.cs new file mode 100644 index 0000000..8d9a017 --- /dev/null +++ b/src/EventHub.Admin.Web/Pages/OrganizationMembershipManagement.razor.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Blazorise.DataGrid; +using EventHub.Admin.Organizations; +using EventHub.Admin.Organizations.Memberships; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; +using Volo.Abp.Application.Dtos; + +namespace EventHub.Admin.Web.Pages +{ + public partial class OrganizationMembershipManagement + { + [Parameter] + public Guid? OrganizationId { get; set; } + private string OrganizationName { get; set; } + + [Inject] + protected IOrganizationAppService OrganizationAppService { get; set; } + + + private IReadOnlyList OrganizationMemberList { get; set; } + private OrganizationMemberListFilterDto Filter { get; set; } + private int CurrentPage { get; set; } + private int TotalCount { get; set; } + private int PageSize { get; } + + public OrganizationMembershipManagement() + { + Filter = new OrganizationMemberListFilterDto(); + PageSize = LimitedResultRequestDto.DefaultMaxResultCount; + + OrganizationMemberList = new List(); + } + + protected override async Task OnInitializedAsync() + { + await GetOrganizationMembershipsAsync(); + + if (OrganizationId.HasValue) + { + OrganizationName = (await OrganizationAppService.GetAsync(OrganizationId.Value)).Name; + } + } + + private async Task OnDataGridReadAsync(DataGridReadDataEventArgs e) + { + CurrentPage = e.Page - 1; + await GetOrganizationMembershipsAsync(); + await InvokeAsync(StateHasChanged); + } + + private async Task GetOrganizationMembershipsAsync() + { + Filter.MaxResultCount = PageSize; + Filter.SkipCount = CurrentPage * PageSize; + + if (OrganizationId.HasValue) + { + Filter.OrganizationId = OrganizationId!.Value; + } + else + { + Filter.OrganizationId = null; + } + + if (!Filter.UserName.IsNullOrWhiteSpace()) + { + Filter.UserName = Filter.UserName.Trim(); + } + + var result = await OrganizationMembershipAppService.GetListAsync(Filter); + OrganizationMemberList = result.Items; + TotalCount = (int) result.TotalCount; + } + + private async Task OnKeyPress(KeyboardEventArgs e) + { + if (e.Code == "Enter" || e.Code == "NumpadEnter") + { + if (OrganizationName.IsNullOrWhiteSpace()) + { + OrganizationId = null; + OrganizationName = null; + } + else + { + var organization = await OrganizationAppService.GetByNameAsync(OrganizationName.Trim()); + OrganizationId = organization.Id; + OrganizationName = organization.Name; + } + + await GetOrganizationMembershipsAsync(); + } + } + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Web/nginx.conf b/src/EventHub.Admin.Web/nginx.conf new file mode 100644 index 0000000..4369baa --- /dev/null +++ b/src/EventHub.Admin.Web/nginx.conf @@ -0,0 +1,7 @@ +server { + listen 80; + location / { + root /usr/share/nginx/html; + try_files $uri /index.html; + } +} \ No newline at end of file diff --git a/src/EventHub.Admin.Web/wwwroot/appsettings.Development.json b/src/EventHub.Admin.Web/wwwroot/appsettings.Development.json deleted file mode 100644 index 0db3279..0000000 --- a/src/EventHub.Admin.Web/wwwroot/appsettings.Development.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} diff --git a/src/EventHub.Admin.Web/wwwroot/appsettings.Production.json b/src/EventHub.Admin.Web/wwwroot/appsettings.Production.json new file mode 100644 index 0000000..4be599b --- /dev/null +++ b/src/EventHub.Admin.Web/wwwroot/appsettings.Production.json @@ -0,0 +1,13 @@ +{ + "App": { + "SelfUrl": "https://admin.openeventhub.com" + }, + "AuthServer": { + "Authority": "https://account.openeventhub.com" + }, + "RemoteServices": { + "Default": { + "BaseUrl": "https://admin-api.openeventhub.com" + } + } +} diff --git a/src/EventHub.Admin.Web/wwwroot/appsettings.Staging.json b/src/EventHub.Admin.Web/wwwroot/appsettings.Staging.json new file mode 100644 index 0000000..52d847d --- /dev/null +++ b/src/EventHub.Admin.Web/wwwroot/appsettings.Staging.json @@ -0,0 +1,13 @@ +{ + "App": { + "SelfUrl": "https://eh-st-admin" + }, + "AuthServer": { + "Authority": "https://eh-st-account" + }, + "RemoteServices": { + "Default": { + "BaseUrl": "https://eh-st-admin-api" + } + } +} diff --git a/src/EventHub.Application.Contracts/EventHubRemoteServiceConsts.cs b/src/EventHub.Application.Contracts/EventHubRemoteServiceConsts.cs new file mode 100644 index 0000000..b46795f --- /dev/null +++ b/src/EventHub.Application.Contracts/EventHubRemoteServiceConsts.cs @@ -0,0 +1,7 @@ +namespace EventHub +{ + public static class EventHubRemoteServiceConsts + { + public const string RemoteServiceName = "EventHub"; + } +} \ No newline at end of file diff --git a/src/EventHub.Application/Organizations/Memberships/OrganizationMembershipAppService.cs b/src/EventHub.Application/Organizations/Memberships/OrganizationMembershipAppService.cs index 744d74d..d6a43ea 100644 --- a/src/EventHub.Application/Organizations/Memberships/OrganizationMembershipAppService.cs +++ b/src/EventHub.Application/Organizations/Memberships/OrganizationMembershipAppService.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using EventHub.Users; using Microsoft.AspNetCore.Authorization; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Repositories; diff --git a/src/EventHub.BackgroundServices/Dockerfile b/src/EventHub.BackgroundServices/Dockerfile new file mode 100644 index 0000000..9cf1f9e --- /dev/null +++ b/src/EventHub.BackgroundServices/Dockerfile @@ -0,0 +1,4 @@ + FROM mcr.microsoft.com/dotnet/aspnet:5.0 + COPY bin/Release/net5.0/ app/ + WORKDIR /app + ENTRYPOINT ["dotnet", "EventHub.BackgroundServices.dll"] \ No newline at end of file diff --git a/src/EventHub.DbMigrator/Dockerfile b/src/EventHub.DbMigrator/Dockerfile new file mode 100644 index 0000000..175494f --- /dev/null +++ b/src/EventHub.DbMigrator/Dockerfile @@ -0,0 +1,4 @@ + FROM mcr.microsoft.com/dotnet/aspnet:5.0 + COPY bin/Release/net5.0/ app/ + WORKDIR /app + ENTRYPOINT ["dotnet", "EventHub.DbMigrator.dll"] \ No newline at end of file diff --git a/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs b/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs index 8370a52..efa5dc2 100644 --- a/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs +++ b/src/EventHub.Domain.Shared/EventHubDomainSharedModule.cs @@ -1,4 +1,6 @@ using EventHub.Localization; +using EventHub.Web; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AuditLogging; using Volo.Abp.BackgroundJobs; using Volo.Abp.Identity; @@ -33,6 +35,8 @@ namespace EventHub public override void ConfigureServices(ServiceConfigurationContext context) { + var configuration = context.Services.GetConfiguration(); + Configure(options => { options.FileSets.AddEmbedded(); @@ -52,6 +56,8 @@ namespace EventHub { options.MapCodeNamespace("EventHub", typeof(EventHubResource)); }); + + Configure(configuration.GetSection("AppUrls")); } } } diff --git a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs index 148225b..f1650b6 100644 --- a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs +++ b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs @@ -14,5 +14,7 @@ public const string CantChangeEventTiming = "EventHub:CantChangeEventTiming"; public const string SessionTimeShouldBeInTheEventTime = "EventHub:SessionTimeShouldBeInTheEventTime"; public const string SessionTimeConflictsWithAnExistingSession = "EventHub:SessionTimeConflictsWithAnExistingSession"; + public const string UserNotFound = "EventHub:UserNotFound"; + public const string OrganizationNotFound = "EventHub:OrganizationNotFound"; } } diff --git a/src/EventHub.Domain.Shared/Localization/EventHub/en.json b/src/EventHub.Domain.Shared/Localization/EventHub/en.json index 0aca53c..c40bfd5 100644 --- a/src/EventHub.Domain.Shared/Localization/EventHub/en.json +++ b/src/EventHub.Domain.Shared/Localization/EventHub/en.json @@ -84,5 +84,38 @@ "EventTimingChangeSuccessMessage": "The time of the event has been changed successfully!", "PastEvent": "Past Event", "EventHub:SessionEndTimeCantBeEarlierThanStartTime": "Session end time can not be earlier than the start time." + "Menu:OrganizationManagement": "Organization Management", + "Menu:OrganizationMemberships": "Memberships", + "Permission:OrganizationManagement": "Organization Management", + "Permission:MembershipManagement": "Membership Management", + "Permission:Create": "Create", + "Permission:Edit": "Edit", + "Permission:Delete": "Delete", + "UserName": "User name", + "Email": "Email", + "Name": "Name", + "Surname": "Surname", + "DisplayName": "Display Name", + "MinMemberCount": "Min Member Count", + "MaxMemberCount": "Max Member Count", + "MemberCount": "Member Count", + "UpdateOrganization": "Update Organization", + "Description": "Description", + "Website": "Website", + "TwitterUsername": "Twitter Username", + "GitHubUsername": "GitHub Username", + "FacebookUsername": "Facebook Username", + "InstagramUsername": "Instagram Username", + "MediumUsername": "Medium Username", + "ChooseProfileImage": "Choose profile image:", + "OrganizationMemberships": "Organization Memberships", + "EventHub:UserNotFound": "There is no user named \"{UserName}\"", + "EventHub:OrganizationNotFound": "There is no organization named \"{OrganizationName}\"", + "DeleteCoverImage": "Delete Cover Image", + "DeleteConfirmationMessage": "Are you sure you want to delete this item?", + "Loading": "Loading", + "OrganizationInfo": "Organization Info", + "ProfileImage": "Profile Image", + "OrganizationName": "Organization Name" } } diff --git a/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs b/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs index 7b9fedc..712dcf6 100644 --- a/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs +++ b/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs @@ -2,6 +2,8 @@ { public static class OrganizationConsts { + public const string DefaultSorting = "CreationTime desc"; + public const int MinNameLength = 2; public const int MaxNameLength = 32; @@ -24,6 +26,5 @@ public const int MaxInstagramUsernameLength = 24; public const int MaxMediumUsernameLength = 24; - } } diff --git a/src/EventHub.Domain.Shared/Web/EventHubExternalUrls.cs b/src/EventHub.Domain.Shared/Web/EventHubExternalUrls.cs deleted file mode 100644 index e6d8c7a..0000000 --- a/src/EventHub.Domain.Shared/Web/EventHubExternalUrls.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace EventHub.Web -{ - public static class EventHubExternalUrls - { -#if DEBUG - public const string EhAccount = "https://localhost:44313"; - public const string EhApi = "https://localhost:44362"; - public const string EhAdmin = "https://localhost:44307"; - public const string EhWww = "https://localhost:44308"; -#else - // TODO: Change these production links - public const string EhAccount = "https://localhost:44313"; - public const string EhApi = "https://localhost:44362"; - public const string EhAdmin = "https://localhost:44307"; - public const string EhWww = "https://localhost:44308"; -#endif - } -} \ No newline at end of file diff --git a/src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs b/src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs new file mode 100644 index 0000000..5d758a1 --- /dev/null +++ b/src/EventHub.Domain.Shared/Web/EventHubUrlOptions.cs @@ -0,0 +1,49 @@ +namespace EventHub.Web +{ + public class EventHubUrlOptions + { + private const string ConfigurationName = "AppUrls"; + + public string Account { get; set; } = "https://localhost:44313"; + public string Www { get; set; } = "https://localhost:44308"; + public string Api { get; set; } = "https://localhost:44362"; + public string ApiInternal { get; set; } = "https://localhost:44362"; + public string Admin { get; set; } = "https://localhost:44307"; + public string AdminApi { get; set; } = "https://localhost:44305"; + + public static string GetAccountConfigKey() + { + return GetConfigKey(nameof(Account)); + } + + public static string GetWwwConfigKey() + { + return GetConfigKey(nameof(Www)); + } + + public static string GetApiInternalConfigKey() + { + return GetConfigKey(nameof(ApiInternal)); + } + + public static string GetApiConfigKey() + { + return GetConfigKey(nameof(Api)); + } + + public static string GetAdminConfigKey() + { + return GetConfigKey(nameof(Admin)); + } + + public static string GetAdminApiConfigKey() + { + return GetConfigKey(nameof(AdminApi)); + } + + private static string GetConfigKey(string appName) + { + return $"{ConfigurationName}:{appName}"; + } + } +} \ No newline at end of file diff --git a/src/EventHub.Domain/Emailing/EventHubEmailTemplateRenderingEngine.cs b/src/EventHub.Domain/Emailing/EventHubEmailTemplateRenderingEngine.cs index 623a3f5..9021183 100644 --- a/src/EventHub.Domain/Emailing/EventHubEmailTemplateRenderingEngine.cs +++ b/src/EventHub.Domain/Emailing/EventHubEmailTemplateRenderingEngine.cs @@ -5,6 +5,7 @@ using EventHub.Web; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.TextTemplating; using Volo.Abp.TextTemplating.Scriban; @@ -18,15 +19,19 @@ namespace EventHub.Emailing )] public class EventHubEmailTemplateRenderingEngine : ScribanTemplateRenderingEngine { + private readonly EventHubUrlOptions _options; + public EventHubEmailTemplateRenderingEngine( ITemplateDefinitionManager templateDefinitionManager, ITemplateContentProvider templateContentProvider, - IStringLocalizerFactory stringLocalizerFactory + IStringLocalizerFactory stringLocalizerFactory, + IOptions options ) : base( templateDefinitionManager, templateContentProvider, stringLocalizerFactory) { + _options = options.Value; } public override async Task RenderAsync( @@ -41,7 +46,7 @@ namespace EventHub.Emailing } globalContext["current_year"] = DateTime.Today.Year; - globalContext["app_url"] = EventHubExternalUrls.EhWww; + globalContext["app_url"] = _options.Www; return await base.RenderAsync(templateName, model, cultureName, globalContext); } diff --git a/src/EventHub.Domain/EventHubDomainModule.cs b/src/EventHub.Domain/EventHubDomainModule.cs index 000b842..8c17cf5 100644 --- a/src/EventHub.Domain/EventHubDomainModule.cs +++ b/src/EventHub.Domain/EventHubDomainModule.cs @@ -34,7 +34,6 @@ namespace EventHub { options.FileSets.AddEmbedded(); }); - #if DEBUG context.Services.Replace(ServiceDescriptor.Singleton()); #endif diff --git a/src/EventHub.Domain/Organizations/Memberships/IOrganizationMembershipRepository.cs b/src/EventHub.Domain/Organizations/Memberships/IOrganizationMembershipRepository.cs new file mode 100644 index 0000000..07f542a --- /dev/null +++ b/src/EventHub.Domain/Organizations/Memberships/IOrganizationMembershipRepository.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; + +namespace EventHub.Organizations.Memberships +{ + public interface IOrganizationMembershipRepository : IRepository + { + Task> GetMemberListAsync( + Guid? organizationId, + Guid? userId, + int skipCount, + int maxResultCount, + CancellationToken cancellationToken = default); + + Task GetCountAsync( + Guid? organizationId, + Guid? userId, + CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/EventHub.Domain/Organizations/Memberships/OrganizationMemberWithDetails.cs b/src/EventHub.Domain/Organizations/Memberships/OrganizationMemberWithDetails.cs new file mode 100644 index 0000000..df907f3 --- /dev/null +++ b/src/EventHub.Domain/Organizations/Memberships/OrganizationMemberWithDetails.cs @@ -0,0 +1,18 @@ +using System; +using Volo.Abp.Domain.Entities; + +namespace EventHub.Organizations.Memberships +{ + public class OrganizationMemberWithDetails : Entity + { + public string OrganizationName { get; set; } + + public string UserName { get; set; } + + public string Email { get; set; } + + public string Name { get; set; } + + public string Surname { get; set; } + } +} \ No newline at end of file diff --git a/src/EventHub.Domain/Organizations/Memberships/OrganizationMembershipChangedEventHandler.cs b/src/EventHub.Domain/Organizations/Memberships/OrganizationMembershipChangedEventHandler.cs new file mode 100644 index 0000000..4625fd1 --- /dev/null +++ b/src/EventHub.Domain/Organizations/Memberships/OrganizationMembershipChangedEventHandler.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using EventHub.Organizations; +using EventHub.Organizations.Memberships; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.EventBus; + +namespace EventHub.Domain.Organizations.Memberships +{ + public class OrganizationMembershipChangedEventHandler : ILocalEventHandler>, + ILocalEventHandler>, ITransientDependency + { + private readonly IRepository _organizationRepository; + + public OrganizationMembershipChangedEventHandler(IRepository organizationRepository) + { + _organizationRepository = organizationRepository; + } + + public async Task HandleEventAsync(EntityCreatedEventData eventData) + { + var organization = await _organizationRepository.GetAsync(eventData.Entity.OrganizationId); + + organization.MemberCount++; + + await _organizationRepository.UpdateAsync(organization); + } + + public async Task HandleEventAsync(EntityDeletedEventData eventData) + { + var organization = await _organizationRepository.GetAsync(eventData.Entity.OrganizationId); + + organization.MemberCount--; + + await _organizationRepository.UpdateAsync(organization); + } + } +} diff --git a/src/EventHub.Domain/Organizations/Organization.cs b/src/EventHub.Domain/Organizations/Organization.cs index 88db74b..557f988 100644 --- a/src/EventHub.Domain/Organizations/Organization.cs +++ b/src/EventHub.Domain/Organizations/Organization.cs @@ -26,6 +26,8 @@ namespace EventHub.Organizations public string MediumUsername { get; set; } + public int MemberCount { get; internal set; } + private Organization() { } diff --git a/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Organizations/Memberships/OrganizationMembershipRepository.cs b/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Organizations/Memberships/OrganizationMembershipRepository.cs new file mode 100644 index 0000000..fa1b3bb --- /dev/null +++ b/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/Organizations/Memberships/OrganizationMembershipRepository.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using EventHub.Organizations; +using EventHub.Organizations.Memberships; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Identity; + +namespace EventHub.EntityFrameworkCore.Organizations.Memberships +{ + public class OrganizationMembershipRepository : EfCoreRepository, + IOrganizationMembershipRepository + { + public OrganizationMembershipRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public async Task> GetMemberListAsync( + Guid? organizationId, + Guid? userId, + int skipCount, + int maxResultCount, + CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + + var organizationMembershipsDbSet = dbContext.Set(); + var identityUserDbSet = dbContext.Set(); + var organizationDbSet = dbContext.Set(); + + var query = organizationMembershipsDbSet + .Join(organizationDbSet, t => t.OrganizationId, organization => organization.Id, + (organizationMember, organization) => new {organizationMember, organization}) + .Join(identityUserDbSet, organizationWithMember => organizationWithMember.organizationMember.UserId, + user => user.Id, (organizationWithMember, user) => new {organizationWithMember, user}) + .WhereIf(userId.HasValue, t => t.user.Id == userId) + .WhereIf(organizationId.HasValue, + t => t.organizationWithMember.organizationMember.OrganizationId == organizationId) + .OrderByDescending(o => o.organizationWithMember.organizationMember.CreationTime) + .Select(t => new OrganizationMemberWithDetails + { + OrganizationName = t.organizationWithMember.organization.Name, + UserName = t.user.UserName, + Email = t.user.Email, + Name = t.user.Name, + Surname = t.user.Surname + }); + + return await query + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async Task GetCountAsync( + Guid? organizationId, + Guid? userId, + CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + + var organizationMembershipsDbSet = dbContext.Set(); + var identityUserDbSet = dbContext.Set(); + + return await organizationMembershipsDbSet + .Join(identityUserDbSet, organizationMember => organizationMember.UserId, user => user.Id, + (organizationMember, user) => new {organizationMember, user}) + .WhereIf(userId.HasValue, t => t.user.Id == userId) + .WhereIf(organizationId.HasValue, t => t.organizationMember.OrganizationId == organizationId) + .CountAsync(GetCancellationToken(cancellationToken)); + } + } +} \ No newline at end of file diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.Designer.cs b/src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.Designer.cs new file mode 100644 index 0000000..183e466 --- /dev/null +++ b/src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.Designer.cs @@ -0,0 +1,2628 @@ +// +using System; +using EventHub.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +namespace EventHub.Migrations +{ + [DbContext(typeof(EventHubDbContext))] + [Migration("20210727070707_Added_MemberCount_To_Organization")] + partial class Added_MemberCount_To_Organization + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.8") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("EventHub.Countries.Country", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("EhCountries"); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("City") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CountryId") + .HasColumnType("uuid"); + + b.Property("CountryName") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsEmailSentToMembers") + .HasColumnType("boolean"); + + b.Property("IsOnline") + .HasColumnType("boolean"); + + b.Property("IsRemindingEmailSent") + .HasColumnType("boolean"); + + b.Property("IsTimingChangeEmailSent") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Language") + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OnlineLink") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TimingChangeCount") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(69) + .HasColumnType("character varying(69)"); + + b.Property("UrlCode") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.HasIndex("IsEmailSentToMembers"); + + b.HasIndex("StartTime"); + + b.HasIndex("UrlCode"); + + b.HasIndex("IsRemindingEmailSent", "StartTime"); + + b.HasIndex("OrganizationId", "StartTime"); + + b.ToTable("EhEvents"); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("EventId", "UserId"); + + b.ToTable("EhEventRegistrations"); + }); + + modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "UserId"); + + b.ToTable("EhOrganizationMemberships"); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FacebookUsername") + .HasColumnType("text"); + + b.Property("GitHubUsername") + .HasColumnType("text"); + + b.Property("InstagramUsername") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MediumUsername") + .HasColumnType("text"); + + b.Property("MemberCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("OwnerUserId") + .HasColumnType("uuid"); + + b.Property("TwitterUsername") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.HasIndex("Name"); + + b.HasIndex("OwnerUserId"); + + b.ToTable("EhOrganizations"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("text"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("integer") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasColumnType("text"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("smallint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uuid"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EntityChangeId") + .HasColumnType("uuid"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("LastTryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("NextTryTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContainerId") + .HasColumnType("uuid"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("bytea"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SourceTenantId") + .HasColumnType("uuid"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("TargetTenantId") + .HasColumnType("uuid"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique(); + + b.ToTable("AbpLinkUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("boolean") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SecurityStamp"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("character varying(196)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("character varying(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowedAccessTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiResourceClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiResourceId", "Key", "Value"); + + b.ToTable("IdentityServerApiResourceProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Scope"); + + b.ToTable("IdentityServerApiResourceScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Expiration") + .HasColumnType("timestamp without time zone"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiResourceSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiScopeId", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiScopeId", "Key", "Value"); + + b.ToTable("IdentityServerApiScopeProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AbsoluteRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenType") + .HasColumnType("integer"); + + b.Property("AllowAccessTokensViaBrowser") + .HasColumnType("boolean"); + + b.Property("AllowOfflineAccess") + .HasColumnType("boolean"); + + b.Property("AllowPlainTextPkce") + .HasColumnType("boolean"); + + b.Property("AllowRememberConsent") + .HasColumnType("boolean"); + + b.Property("AllowedIdentityTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AlwaysIncludeUserClaimsInIdToken") + .HasColumnType("boolean"); + + b.Property("AlwaysSendClientClaims") + .HasColumnType("boolean"); + + b.Property("AuthorizationCodeLifetime") + .HasColumnType("integer"); + + b.Property("BackChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime") + .HasColumnType("integer"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DeviceCodeLifetime") + .HasColumnType("integer"); + + b.Property("EnableLocalLogin") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IdentityTokenLifetime") + .HasColumnType("integer"); + + b.Property("IncludeJwtId") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("integer"); + + b.Property("RefreshTokenUsage") + .HasColumnType("integer"); + + b.Property("RequireClientSecret") + .HasColumnType("boolean"); + + b.Property("RequireConsent") + .HasColumnType("boolean"); + + b.Property("RequirePkce") + .HasColumnType("boolean"); + + b.Property("RequireRequestObject") + .HasColumnType("boolean"); + + b.Property("SlidingRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("UpdateAccessTokenClaimsOnRefresh") + .HasColumnType("boolean"); + + b.Property("UserCodeType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UserSsoLifetime") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Origin") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("GrantType") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Provider") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "Key", "Value"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("RedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Expiration") + .HasColumnType("timestamp without time zone"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Devices.DeviceFlowCodes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UserCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.HasIndex("UserCode"); + + b.ToTable("IdentityServerDeviceFlowCodes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsumedTime") + .HasColumnType("timestamp without time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityResourceClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("IdentityResourceId", "Key", "Value"); + + b.ToTable("IdentityServerIdentityResourceProperties"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.HasOne("EventHub.Countries.Country", null) + .WithMany() + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.HasOne("EventHub.Events.Event", null) + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b => + { + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("OwnerUserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null) + .WithMany() + .HasForeignKey("ContainerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Properties") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("UserClaims") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("Properties") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("Properties") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Navigation("Properties"); + + b.Navigation("Scopes"); + + b.Navigation("Secrets"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Navigation("AllowedCorsOrigins"); + + b.Navigation("AllowedGrantTypes"); + + b.Navigation("AllowedScopes"); + + b.Navigation("Claims"); + + b.Navigation("ClientSecrets"); + + b.Navigation("IdentityProviderRestrictions"); + + b.Navigation("PostLogoutRedirectUris"); + + b.Navigation("Properties"); + + b.Navigation("RedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.cs b/src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.cs new file mode 100644 index 0000000..d926cb7 --- /dev/null +++ b/src/EventHub.EntityFrameworkCore/Migrations/20210727070707_Added_MemberCount_To_Organization.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace EventHub.Migrations +{ + public partial class Added_MemberCount_To_Organization : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MemberCount", + table: "EhOrganizations", + type: "integer", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "MemberCount", + table: "EhOrganizations"); + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs b/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs index 7feb89a..a80b1fc 100644 --- a/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs +++ b/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs @@ -384,6 +384,9 @@ namespace EventHub.Migrations b.Property("MediumUsername") .HasColumnType("text"); + b.Property("MemberCount") + .HasColumnType("integer"); + b.Property("Name") .IsRequired() .HasMaxLength(32) diff --git a/src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs b/src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs new file mode 100644 index 0000000..b1f6e34 --- /dev/null +++ b/src/EventHub.HttpApi.Host/Controllers/Events/EventController.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using EventHub.Events; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Controllers.Events +{ + [RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] + [Area("eventhub")] + [ControllerName("Event")] + [Route("api/eventhub/event")] + public class EventController : AbpController, IEventAppService + { + private readonly IEventAppService _eventAppService; + + public EventController(IEventAppService eventAppService) + { + _eventAppService = eventAppService; + } + + [HttpPost] + public async Task CreateAsync(CreateEventDto input) + { + return await _eventAppService.CreateAsync(input); + } + + [HttpGet] + public async Task> GetListAsync(EventListFilterDto input) + { + return await _eventAppService.GetListAsync(input); + } + + [HttpGet] + [Route("by-url-code/{urlCode}")] + public async Task GetByUrlCodeAsync(string urlCode) + { + return await _eventAppService.GetByUrlCodeAsync(urlCode); + } + + [HttpGet] + [Route("location/{id}")] + public async Task GetLocationAsync(Guid id) + { + return await _eventAppService.GetLocationAsync(id); + } + + [HttpGet] + [Route("lookup/countries")] + public async Task> GetCountriesLookupAsync() + { + return await _eventAppService.GetCountriesLookupAsync(); + } + + [HttpGet] + [Route("is-event-owner/{id}")] + public async Task IsEventOwnerAsync(Guid id) + { + return await _eventAppService.IsEventOwnerAsync(id); + } + + [HttpPut] + [Route("{id}")] + public async Task UpdateAsync(Guid id, UpdateEventDto input) + { + await _eventAppService.UpdateAsync(id, input); + } + + [HttpGet] + [Route("cover-image/{id}")] + public async Task GetCoverImageAsync(Guid id) + { + return await _eventAppService.GetCoverImageAsync(id); + } + } +} \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/Controllers/Events/Registrations/EventRegistrationController.cs b/src/EventHub.HttpApi.Host/Controllers/Events/Registrations/EventRegistrationController.cs new file mode 100644 index 0000000..8eb8341 --- /dev/null +++ b/src/EventHub.HttpApi.Host/Controllers/Events/Registrations/EventRegistrationController.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using EventHub.Events.Registrations; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Controllers.Events.Registrations +{ + [RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] + [Area("eventhub")] + [ControllerName("EventRegistration")] + [Route("api/eventhub/event-registration")] + public class EventRegistrationController : AbpController, IEventRegistrationAppService + { + private readonly IEventRegistrationAppService _eventRegistrationAppService; + + public EventRegistrationController(IEventRegistrationAppService eventRegistrationAppService) + { + _eventRegistrationAppService = eventRegistrationAppService; + } + + [HttpGet] + [Route("register/{eventId}")] + public async Task RegisterAsync(Guid eventId) + { + await _eventRegistrationAppService.RegisterAsync(eventId); + } + + [HttpGet] + [Route("un-register/{eventId}")] + public async Task UnregisterAsync(Guid eventId) + { + await _eventRegistrationAppService.UnregisterAsync(eventId); + } + + [HttpGet] + [Route("is-registered/{eventId}")] + public async Task IsRegisteredAsync(Guid eventId) + { + return await _eventRegistrationAppService.IsRegisteredAsync(eventId); + } + + [HttpGet] + public async Task> GetAttendeesAsync(Guid eventId) + { + return await _eventRegistrationAppService.GetAttendeesAsync(eventId); + } + + [HttpGet] + [Route("is-past-event/{eventId}")] + public async Task IsPastEventAsync(Guid eventId) + { + return await _eventRegistrationAppService.IsPastEventAsync(eventId); + } + } +} \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs b/src/EventHub.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs new file mode 100644 index 0000000..ac1d710 --- /dev/null +++ b/src/EventHub.HttpApi.Host/Controllers/Organizations/Memberships/OrganizationMembershipController.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading.Tasks; +using EventHub.Organizations.Memberships; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Controllers.Organizations.Memberships +{ + [RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] + [Area("eventhub")] + [ControllerName("OrganizationMembership")] + [Route("api/eventhub/organization-membership")] + public class OrganizationMembershipController : AbpController, IOrganizationMembershipAppService + { + private readonly IOrganizationMembershipAppService _organizationMembershipAppService; + + public OrganizationMembershipController(IOrganizationMembershipAppService organizationMembershipAppService) + { + _organizationMembershipAppService = organizationMembershipAppService; + } + + [HttpPut] + [Route("join/{organizationId}")] + public async Task JoinAsync(Guid organizationId) + { + await _organizationMembershipAppService.JoinAsync(organizationId); + } + + [HttpPut] + [Route("leave/{organizationId}")] + public async Task LeaveAsync(Guid organizationId) + { + await _organizationMembershipAppService.LeaveAsync(organizationId); + + } + + [HttpGet] + [Route("is-joined/{organizationId}")] + public async Task IsJoinedAsync(Guid organizationId) + { + return await _organizationMembershipAppService.IsJoinedAsync(organizationId); + } + + [HttpGet] + public async Task> GetMembersAsync(OrganizationMemberListFilterDto input) + { + return await _organizationMembershipAppService.GetMembersAsync(input); + } + } +} \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/Controllers/Organizations/OrganizationController.cs b/src/EventHub.HttpApi.Host/Controllers/Organizations/OrganizationController.cs new file mode 100644 index 0000000..3f090b5 --- /dev/null +++ b/src/EventHub.HttpApi.Host/Controllers/Organizations/OrganizationController.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading.Tasks; +using EventHub.Organizations; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Controllers.Organizations +{ + [RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] + [Area("eventhub")] + [ControllerName("Organization")] + [Route("api/eventhub/organization")] + public class OrganizationController : AbpController, IOrganizationAppService + { + private readonly IOrganizationAppService _organizationAppService; + + public OrganizationController(IOrganizationAppService organizationAppService) + { + _organizationAppService = organizationAppService; + } + + [HttpPost] + public async Task CreateAsync(CreateOrganizationDto input) + { + await _organizationAppService.CreateAsync(input); + } + + [HttpGet] + public async Task> GetListAsync(OrganizationListFilterDto input) + { + return await _organizationAppService.GetListAsync(input); + } + + [HttpGet] + [Route("{name}")] + public async Task GetProfileAsync(string name) + { + return await _organizationAppService.GetProfileAsync(name); + } + + [HttpGet] + [Route("by-user-id/{userId}")] + public async Task> GetOrganizationsByUserIdAsync(Guid userId) + { + return await _organizationAppService.GetOrganizationsByUserIdAsync(userId); + } + + [HttpGet] + [Route("is-organization-owner/{organizationId}")] + public async Task IsOrganizationOwnerAsync(Guid organizationId) + { + return await _organizationAppService.IsOrganizationOwnerAsync(organizationId); + } + + [HttpPut] + [Route("{id}")] + public async Task UpdateAsync(Guid id, UpdateOrganizationDto input) + { + await _organizationAppService.UpdateAsync(id, input); + } + } +} \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/Controllers/Users/UserController.cs b/src/EventHub.HttpApi.Host/Controllers/Users/UserController.cs new file mode 100644 index 0000000..5364c88 --- /dev/null +++ b/src/EventHub.HttpApi.Host/Controllers/Users/UserController.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using EventHub.Members; +using EventHub.Users; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; + +namespace EventHub.Controllers.Users +{ + [RemoteService(Name = EventHubRemoteServiceConsts.RemoteServiceName)] + [Area("eventhub")] + [ControllerName("User")] + [Route("/api/eventhub/user")] + public class UserController : AbpController, IUserAppService + { + private readonly IUserAppService _userAppService; + + public UserController(IUserAppService userAppService) + { + _userAppService = userAppService; + } + + [HttpGet] + [Route("{username}")] + public async Task FindByUserNameAsync(string username) + { + return await _userAppService.FindByUserNameAsync(username); + } + } +} \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/Dockerfile b/src/EventHub.HttpApi.Host/Dockerfile new file mode 100644 index 0000000..b5bd5d9 --- /dev/null +++ b/src/EventHub.HttpApi.Host/Dockerfile @@ -0,0 +1,4 @@ + FROM mcr.microsoft.com/dotnet/aspnet:5.0 + COPY bin/Release/net5.0/publish/ app/ + WORKDIR /app + ENTRYPOINT ["dotnet", "EventHub.HttpApi.Host.dll"] \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs index 65b752f..3769b07 100644 --- a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs +++ b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs @@ -11,6 +11,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using EventHub.EntityFrameworkCore; +using EventHub.Utils; +using EventHub.Web; using Microsoft.AspNetCore.Localization; using StackExchange.Redis; using Microsoft.OpenApi.Models; @@ -26,6 +28,7 @@ using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Swashbuckle; +using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.VirtualFileSystem; namespace EventHub @@ -49,13 +52,13 @@ namespace EventHub var configuration = context.Services.GetConfiguration(); var hostingEnvironment = context.Services.GetHostingEnvironment(); - ConfigureConventionalControllers(); ConfigureAuthentication(context, configuration); ConfigureLocalization(); ConfigureCache(configuration); ConfigureVirtualFileSystem(context); ConfigureRedis(context, configuration); ConfigureCors(context, configuration); + ConfigureCookies(context); ConfigureSwaggerServices(context, configuration); ConfigureBackgroundJobs(); } @@ -97,14 +100,6 @@ namespace EventHub } } - private void ConfigureConventionalControllers() - { - Configure(options => - { - options.ConventionalControllers.Create(typeof(EventHubApplicationModule).Assembly); - }); - } - private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) @@ -119,7 +114,7 @@ namespace EventHub private static void ConfigureSwaggerServices(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddAbpSwaggerGenWithOAuth( - configuration["AuthServer:Authority"], + configuration[EventHubUrlOptions.GetAccountConfigKey()], new Dictionary { {"EventHub", "EventHub API"} @@ -158,10 +153,7 @@ namespace EventHub { builder .WithOrigins( - configuration["App:CorsOrigins"] - .Split(",", StringSplitOptions.RemoveEmptyEntries) - .Select(o => o.RemovePostFix("/")) - .ToArray() + configuration[EventHubUrlOptions.GetWwwConfigKey()] ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() @@ -172,6 +164,11 @@ namespace EventHub }); } + private void ConfigureCookies(ServiceConfigurationContext context) + { + context.Services.AddSameSiteCookiePolicy(); + } + public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); @@ -181,6 +178,12 @@ namespace EventHub { app.UseDeveloperExceptionPage(); } + + app.Use((context, next) => + { + context.Request.Scheme = "https"; + return next(); + }); var supportedCultures = new[] { @@ -204,8 +207,9 @@ namespace EventHub app.UseErrorPage(); } + app.UseCookiePolicy(); app.UseCorrelationId(); - app.UseVirtualFiles(); + app.UseStaticFiles(); app.UseRouting(); app.UseCors(DefaultCorsPolicyName); app.UseAuthentication(); diff --git a/src/EventHub.HttpApi.Host/Program.cs b/src/EventHub.HttpApi.Host/Program.cs index 08c00d8..1bf31ea 100644 --- a/src/EventHub.HttpApi.Host/Program.cs +++ b/src/EventHub.HttpApi.Host/Program.cs @@ -20,9 +20,7 @@ namespace EventHub .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.Async(c => c.File("Logs/logs.txt")) -#if DEBUG .WriteTo.Async(c => c.Console()) -#endif .CreateLogger(); try diff --git a/src/EventHub.HttpApi.Host/Utils/SameSiteCookiesServiceCollectionExtensions.cs b/src/EventHub.HttpApi.Host/Utils/SameSiteCookiesServiceCollectionExtensions.cs new file mode 100644 index 0000000..2b35fe2 --- /dev/null +++ b/src/EventHub.HttpApi.Host/Utils/SameSiteCookiesServiceCollectionExtensions.cs @@ -0,0 +1,71 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace EventHub.Utils +{ + public static class SameSiteCookiesServiceCollectionExtensions + { + public static IServiceCollection AddSameSiteCookiePolicy(this IServiceCollection services) + { + services.Configure(options => + { + options.MinimumSameSitePolicy = SameSiteMode.Unspecified; + options.OnAppendCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + options.OnDeleteCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + }); + + return services; + } + + private static void CheckSameSite(HttpContext httpContext, CookieOptions options) + { + if (options.SameSite == SameSiteMode.None) + { + var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); + if (!httpContext.Request.IsHttps || DisallowsSameSiteNone(userAgent)) + { + // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1) + options.SameSite = SameSiteMode.Unspecified; + } + } + } + + private static bool DisallowsSameSiteNone(string userAgent) + { + // Cover all iOS based browsers here. This includes: + // - Safari on iOS 12 for iPhone, iPod Touch, iPad + // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad + // - Chrome on iOS 12 for iPhone, iPod Touch, iPad + // All of which are broken by SameSite=None, because they use the iOS networking stack + if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12")) + { + return true; + } + + // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes: + // - Safari on Mac OS X. + // This does not include: + // - Chrome on Mac OS X + // Because they do not use the Mac OS networking stack. + if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && + userAgent.Contains("Version/") && userAgent.Contains("Safari")) + { + return true; + } + + // Cover Chrome 50-69, because some versions are broken by SameSite=None, + // and none in this range require it. + // Note: this covers some pre-Chromium Edge versions, + // but pre-Chromium Edge does not require SameSite=None. + if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/EventHub.HttpApi.Host/appsettings.Development.json b/src/EventHub.HttpApi.Host/appsettings.Development.json deleted file mode 100644 index 2c63c08..0000000 --- a/src/EventHub.HttpApi.Host/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/src/EventHub.HttpApi.Host/appsettings.json b/src/EventHub.HttpApi.Host/appsettings.json index 7c2ab2d..aca9ac1 100644 --- a/src/EventHub.HttpApi.Host/appsettings.json +++ b/src/EventHub.HttpApi.Host/appsettings.json @@ -1,20 +1,14 @@ { - "App": { - "CorsOrigins": "https://*.openeventhub.com,https://localhost:44307,https://localhost:44308" - }, "ConnectionStrings": { "Default": "Host=localhost;Database=EventHub;Username=root;Password=root;Port=5432" }, "Redis": { - "Configuration": "127.0.0.1" + "Configuration": "localhost" }, "AuthServer": { "Authority": "https://localhost:44313", "RequireHttpsMetadata": "true", "SwaggerClientId": "EventHub_Swagger", "SwaggerClientSecret": "1q2w3e*" - }, - "StringEncryption": { - "DefaultPassPhrase": "TxVIZFPxK33czbbv" } } diff --git a/src/EventHub.IdentityServer/Dockerfile b/src/EventHub.IdentityServer/Dockerfile new file mode 100644 index 0000000..20a0003 --- /dev/null +++ b/src/EventHub.IdentityServer/Dockerfile @@ -0,0 +1,4 @@ + FROM mcr.microsoft.com/dotnet/aspnet:5.0 + COPY bin/Release/net5.0/publish/ app/ + WORKDIR /app + ENTRYPOINT ["dotnet", "EventHub.IdentityServer.dll"] \ No newline at end of file diff --git a/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj b/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj index 8d54cd5..dce5b30 100644 --- a/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj +++ b/src/EventHub.IdentityServer/EventHub.IdentityServer.csproj @@ -20,6 +20,10 @@ + + + Always + diff --git a/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs b/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs index 08898db..55d63bc 100644 --- a/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs +++ b/src/EventHub.IdentityServer/EventHubIdentityServerModule.cs @@ -1,6 +1,5 @@ -using System; using System.IO; -using System.Linq; +using System.Security.Cryptography.X509Certificates; using Localization.Resources.AbpUi; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Cors; @@ -9,16 +8,19 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using EventHub.EntityFrameworkCore; using EventHub.Localization; +using EventHub.Utils; using EventHub.Web; using EventHub.Web.Theme; using EventHub.Web.Theme.Bundling; using IdentityServer4.Configuration; +using IdentityServer4.Extensions; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using StackExchange.Redis; using Volo.Abp; using Volo.Abp.Account; using Volo.Abp.Account.Web; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; -using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Auditing; @@ -26,6 +28,7 @@ using Volo.Abp.Autofac; using Volo.Abp.BackgroundJobs; using Volo.Abp.Caching; using Volo.Abp.Caching.StackExchangeRedis; +using Volo.Abp.IdentityServer; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.UI.Navigation.Urls; @@ -46,6 +49,40 @@ namespace EventHub { private const string DefaultCorsPolicyName = "Default"; + public override void PreConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + + if (!hostingEnvironment.IsDevelopment()) + { + var configuration = context.Services.GetConfiguration(); + + PreConfigure(options => + { + options.AddDeveloperSigningCredential = false; + }); + + PreConfigure(builder => + { + builder.AddSigningCredential(GetSigningCertificate(hostingEnvironment, configuration)); + }); + } + } + + private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration) + { + var fileName = "account.openeventhub.pfx"; + var passPhrase = "a8202f07-66e5-4619-be07-72ba76fde97f"; + var file = Path.Combine(hostingEnv.ContentRootPath, fileName); + + if (!File.Exists(file)) + { + throw new FileNotFoundException($"Signing Certificate couldn't found: {file}"); + } + + return new X509Certificate2(file, passPhrase); + } + public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); @@ -79,17 +116,11 @@ namespace EventHub options.ApplicationName = "AuthServer"; }); - Configure(options => - { - options.Applications["MVC"].RootUrl = EventHubExternalUrls.EhAccount; - }); - Configure(options => { - options.IssuerUri = EventHubExternalUrls.EhAccount; + options.IssuerUri = configuration[EventHubUrlOptions.GetAccountConfigKey()]; }); - if (hostingEnvironment.IsDevelopment()) { Configure(options => @@ -102,8 +133,15 @@ namespace EventHub Configure(options => { - options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; - options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); + options.Applications["MVC"].RootUrl = configuration[EventHubUrlOptions.GetAccountConfigKey()]; + options.RedirectAllowedUrls.AddRange( + new[] + { + configuration[EventHubUrlOptions.GetWwwConfigKey()], + configuration[EventHubUrlOptions.GetAdminConfigKey()], + configuration[EventHubUrlOptions.GetApiConfigKey()], + configuration[EventHubUrlOptions.GetAdminApiConfigKey()] + }); }); Configure(options => @@ -127,10 +165,10 @@ namespace EventHub { builder .WithOrigins( - configuration["App:CorsOrigins"] - .Split(",", StringSplitOptions.RemoveEmptyEntries) - .Select(o => o.RemovePostFix("/")) - .ToArray() + configuration[EventHubUrlOptions.GetWwwConfigKey()], + configuration[EventHubUrlOptions.GetApiConfigKey()], + configuration[EventHubUrlOptions.GetAdminConfigKey()], + configuration[EventHubUrlOptions.GetAdminApiConfigKey()] ) .WithAbpExposedHeaders() .SetIsOriginAllowedToAllowWildcardSubdomains() @@ -139,13 +177,26 @@ namespace EventHub .AllowCredentials(); }); }); + + context.Services.AddSameSiteCookiePolicy(); } public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); var env = context.GetEnvironment(); + var configuration = context.ServiceProvider.GetRequiredService(); + app.Use(async (ctx, next) => + { + if (ctx.Request.Headers.ContainsKey("from-ingress")) + { + ctx.SetIdentityServerOrigin(configuration[EventHubUrlOptions.GetAccountConfigKey()]); + } + + await next(); + }); + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); @@ -157,9 +208,10 @@ namespace EventHub { app.UseErrorPage(); } - + + app.UseCookiePolicy(); app.UseCorrelationId(); - app.UseVirtualFiles(); + app.UseStaticFiles(); app.UseRouting(); app.UseCors(DefaultCorsPolicyName); app.UseAuthentication(); diff --git a/src/EventHub.IdentityServer/Program.cs b/src/EventHub.IdentityServer/Program.cs index ef4acd5..c65563b 100644 --- a/src/EventHub.IdentityServer/Program.cs +++ b/src/EventHub.IdentityServer/Program.cs @@ -20,9 +20,7 @@ namespace EventHub .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.Async(c => c.File("Logs/logs.txt")) -#if DEBUG .WriteTo.Async(c => c.Console()) -#endif .CreateLogger(); try diff --git a/src/EventHub.IdentityServer/Utils/SameSiteCookiesServiceCollectionExtensions.cs b/src/EventHub.IdentityServer/Utils/SameSiteCookiesServiceCollectionExtensions.cs new file mode 100644 index 0000000..2b35fe2 --- /dev/null +++ b/src/EventHub.IdentityServer/Utils/SameSiteCookiesServiceCollectionExtensions.cs @@ -0,0 +1,71 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace EventHub.Utils +{ + public static class SameSiteCookiesServiceCollectionExtensions + { + public static IServiceCollection AddSameSiteCookiePolicy(this IServiceCollection services) + { + services.Configure(options => + { + options.MinimumSameSitePolicy = SameSiteMode.Unspecified; + options.OnAppendCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + options.OnDeleteCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + }); + + return services; + } + + private static void CheckSameSite(HttpContext httpContext, CookieOptions options) + { + if (options.SameSite == SameSiteMode.None) + { + var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); + if (!httpContext.Request.IsHttps || DisallowsSameSiteNone(userAgent)) + { + // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1) + options.SameSite = SameSiteMode.Unspecified; + } + } + } + + private static bool DisallowsSameSiteNone(string userAgent) + { + // Cover all iOS based browsers here. This includes: + // - Safari on iOS 12 for iPhone, iPod Touch, iPad + // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad + // - Chrome on iOS 12 for iPhone, iPod Touch, iPad + // All of which are broken by SameSite=None, because they use the iOS networking stack + if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12")) + { + return true; + } + + // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes: + // - Safari on Mac OS X. + // This does not include: + // - Chrome on Mac OS X + // Because they do not use the Mac OS networking stack. + if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && + userAgent.Contains("Version/") && userAgent.Contains("Safari")) + { + return true; + } + + // Cover Chrome 50-69, because some versions are broken by SameSite=None, + // and none in this range require it. + // Note: this covers some pre-Chromium Edge versions, + // but pre-Chromium Edge does not require SameSite=None. + if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/EventHub.IdentityServer/account.openeventhub.pfx b/src/EventHub.IdentityServer/account.openeventhub.pfx new file mode 100644 index 0000000..e82db7f Binary files /dev/null and b/src/EventHub.IdentityServer/account.openeventhub.pfx differ diff --git a/src/EventHub.IdentityServer/appsettings.Development.json b/src/EventHub.IdentityServer/appsettings.Development.json deleted file mode 100644 index 2c63c08..0000000 --- a/src/EventHub.IdentityServer/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/src/EventHub.IdentityServer/appsettings.json b/src/EventHub.IdentityServer/appsettings.json index c9e8b26..85f4ef3 100644 --- a/src/EventHub.IdentityServer/appsettings.json +++ b/src/EventHub.IdentityServer/appsettings.json @@ -1,27 +1,8 @@ { - "App": { - "SelfUrl": "https://localhost:44313", - "CorsOrigins": "https://*.EventHub.com,http://localhost:4200,https://localhost:44307,https://localhost:44362", - "RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307" - }, "ConnectionStrings": { "Default": "Host=localhost;Database=EventHub;Username=root;Password=root;Port=5432" }, "Redis": { - "Configuration": "127.0.0.1" - }, - "StringEncryption": { - "DefaultPassPhrase": "TxVIZFPxK33czbbv" - }, - "Settings": { - "Abp.Mailing.Smtp.Host": "127.0.0.1", - "Abp.Mailing.Smtp.Port": "25", - "Abp.Mailing.Smtp.UserName": "", - "Abp.Mailing.Smtp.Password": "", - "Abp.Mailing.Smtp.Domain": "", - "Abp.Mailing.Smtp.EnableSsl": "false", - "Abp.Mailing.Smtp.UseDefaultCredentials": "true", - "Abp.Mailing.DefaultFromAddress": "noreply@abp.io", - "Abp.Mailing.DefaultFromDisplayName": "ABP application" + "Configuration": "localhost" } } diff --git a/src/EventHub.Web.Theme/Themes/EventHub/Components/Footer/Default.cshtml b/src/EventHub.Web.Theme/Themes/EventHub/Components/Footer/Default.cshtml index 149877e..a528740 100644 --- a/src/EventHub.Web.Theme/Themes/EventHub/Components/Footer/Default.cshtml +++ b/src/EventHub.Web.Theme/Themes/EventHub/Components/Footer/Default.cshtml @@ -1,7 +1,9 @@ @using EventHub.Web @using Microsoft.AspNetCore.Http.Extensions +@using Microsoft.Extensions.Options @using Volo.Abp.Users @inject ICurrentUser CurrentUser +@inject IOptions UrlOptions