├── microservices
├── apps
│ ├── usi-platform-service
│ │ ├── README.adoc
│ │ ├── src
│ │ │ ├── usi-platform.service.ts
│ │ │ ├── auth
│ │ │ │ ├── auth.module.ts
│ │ │ │ └── auth.controller.ts
│ │ │ ├── query-config
│ │ │ │ └── query-config.module.ts
│ │ │ ├── main.ts
│ │ │ ├── usi-platform.module.ts
│ │ │ └── usi-platform.controller.ts
│ │ └── tsconfig.app.json
│ ├── infopin-service
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── src
│ │ │ ├── defect
│ │ │ │ ├── test
│ │ │ │ │ ├── fulda-high-water.jpg
│ │ │ │ │ └── test-data.ts
│ │ │ │ └── defect.module.ts
│ │ │ ├── report
│ │ │ │ ├── test
│ │ │ │ │ └── fulda-high-water.jpg
│ │ │ │ └── report.module.ts
│ │ │ ├── climate-project
│ │ │ │ ├── test
│ │ │ │ │ └── fulda-high-water.jpg
│ │ │ │ └── climate-project.module.ts
│ │ │ ├── main.ts
│ │ │ ├── multer-config.ts
│ │ │ ├── infopin-service.module.ts
│ │ │ └── utility
│ │ │ │ └── RoleUtil.ts
│ │ ├── tsconfig.app.json
│ │ └── README.adoc
│ ├── report-service
│ │ ├── src
│ │ │ ├── dto
│ │ │ │ └── mail.dto.ts
│ │ │ ├── mail
│ │ │ │ ├── mail.module.ts
│ │ │ │ └── mail.service.ts
│ │ │ ├── query
│ │ │ │ ├── query.module.ts
│ │ │ │ └── query.service.ts
│ │ │ ├── config
│ │ │ │ └── config.module.ts
│ │ │ ├── main.ts
│ │ │ ├── scheduler.service.ts
│ │ │ └── report.module.ts
│ │ └── tsconfig.app.json
│ ├── ngsi-service
│ │ ├── src
│ │ │ ├── auth
│ │ │ │ ├── token-data.ts
│ │ │ │ └── test
│ │ │ │ │ └── jest-e2e.json
│ │ │ ├── query
│ │ │ │ └── query.module.ts
│ │ │ ├── data
│ │ │ │ ├── data.module.ts
│ │ │ │ └── test
│ │ │ │ │ └── jest-e2e.json
│ │ │ ├── scheduler.service.ts
│ │ │ ├── report
│ │ │ │ ├── report.module.ts
│ │ │ │ └── test
│ │ │ │ │ └── test-data.ts
│ │ │ ├── main.ts
│ │ │ ├── ngsi.controller.ts
│ │ │ ├── fiware-wizard
│ │ │ │ └── fiware-wizard.module.ts
│ │ │ └── ngsi.module.ts
│ │ └── tsconfig.app.json
│ ├── mail-service
│ │ ├── src
│ │ │ ├── dto
│ │ │ │ └── mail.dto.ts
│ │ │ ├── mail.module.ts
│ │ │ └── main.ts
│ │ └── tsconfig.app.json
│ ├── api-service
│ │ ├── src
│ │ │ ├── query
│ │ │ │ └── query.module.ts
│ │ │ ├── auth
│ │ │ │ └── auth.module.ts
│ │ │ ├── data
│ │ │ │ └── data.module.ts
│ │ │ ├── organisation-schedule.service.ts
│ │ │ ├── report
│ │ │ │ └── report.module.ts
│ │ │ ├── schedule.service.ts
│ │ │ └── main.ts
│ │ └── tsconfig.app.json
│ ├── orchideo-connect-service
│ │ ├── src
│ │ │ ├── query
│ │ │ │ └── query.module.ts
│ │ │ ├── auth
│ │ │ │ └── auth.module.ts
│ │ │ ├── data
│ │ │ │ └── data.module.ts
│ │ │ ├── organisation-schedule.service.ts
│ │ │ ├── report
│ │ │ │ └── report.module.ts
│ │ │ ├── schedule.service.ts
│ │ │ ├── system-user
│ │ │ │ └── test
│ │ │ │ │ └── test-data.ts
│ │ │ └── main.ts
│ │ └── tsconfig.app.json
│ ├── dashboard-service
│ │ ├── src
│ │ │ ├── data-model
│ │ │ │ └── fiware-models
│ │ │ │ │ ├── fiware-models.enum.ts
│ │ │ │ │ ├── interesting-place.model.ts
│ │ │ │ │ ├── parking-info.model.ts
│ │ │ │ │ └── swimming-info.model.ts
│ │ │ ├── logging
│ │ │ │ ├── logger.module.ts
│ │ │ │ ├── logger.controller.ts
│ │ │ │ └── test
│ │ │ │ │ └── test-util.ts
│ │ │ ├── logo
│ │ │ │ ├── logo.module.ts
│ │ │ │ └── test
│ │ │ │ │ └── test-data.ts
│ │ │ ├── query
│ │ │ │ └── query.module.ts
│ │ │ ├── widget-to-panel
│ │ │ │ └── widget-to-panel.module.ts
│ │ │ ├── tab
│ │ │ │ └── tab.module.ts
│ │ │ ├── dashboard
│ │ │ │ └── populate
│ │ │ │ │ └── fiware.types.ts
│ │ │ ├── data-source
│ │ │ │ └── data-source.module.ts
│ │ │ ├── query-config
│ │ │ │ └── query-config.module.ts
│ │ │ ├── main.ts
│ │ │ ├── corporate-info
│ │ │ │ └── corporate-info.module.ts
│ │ │ ├── validators
│ │ │ │ └── widget-validator.pipe.ts
│ │ │ ├── panel
│ │ │ │ ├── panel.module.ts
│ │ │ │ └── test
│ │ │ │ │ └── test-data.ts
│ │ │ ├── general-settings
│ │ │ │ ├── test
│ │ │ │ │ └── test-data.ts
│ │ │ │ └── general-settings.module.ts
│ │ │ ├── tenant
│ │ │ │ └── tenant.module.ts
│ │ │ ├── widget-to-tenant
│ │ │ │ └── widget-to-tenant.module.ts
│ │ │ ├── dashboard-to-tenant
│ │ │ │ └── dashboard-to-tenant.module.ts
│ │ │ └── auth-data
│ │ │ │ └── auth-data.module.ts
│ │ ├── tsconfig.app.json
│ │ └── README.adoc
│ ├── data-translation-service
│ │ ├── tsconfig.app.json
│ │ └── src
│ │ │ ├── populate
│ │ │ └── fiware.types.ts
│ │ │ ├── scheduler.service.ts
│ │ │ ├── main.ts
│ │ │ └── data-translation.module.ts
│ ├── static-data-service
│ │ ├── src
│ │ │ ├── transformation
│ │ │ │ └── transformation.module.ts
│ │ │ ├── data
│ │ │ │ └── data.module.ts
│ │ │ ├── schedule.service.ts
│ │ │ ├── query
│ │ │ │ └── query.module.ts
│ │ │ ├── main.ts
│ │ │ └── static-data.module.ts
│ │ └── tsconfig.app.json
│ └── test
│ │ ├── jest-e2e.json
│ │ ├── jwt-token-util.ts
│ │ └── index.e2e-spec.ts
├── .dockerignore
├── libs
│ ├── auth-helper
│ │ ├── src
│ │ │ ├── index.ts
│ │ │ └── PublicDecorator.ts
│ │ └── tsconfig.lib.json
│ └── postgres-db
│ │ ├── src
│ │ ├── index.ts
│ │ ├── schemas
│ │ │ ├── index.ts
│ │ │ ├── data-model.schema.ts
│ │ │ ├── tenant.schema.ts
│ │ │ ├── tenant.system-user.schema.ts
│ │ │ ├── data-source.schema.ts
│ │ │ ├── logo.schema.ts
│ │ │ ├── general-settings.schema.ts
│ │ │ ├── dashboard.schema.ts
│ │ │ ├── defect.schema.ts
│ │ │ ├── sensor-report.schema.ts
│ │ │ ├── widget-to-tenant.schema.ts
│ │ │ ├── dashboard.grouping-element.schema.ts
│ │ │ ├── query.schema.ts
│ │ │ ├── dashboard.widget-to-panel.schema.ts
│ │ │ └── dashboard-to-tenant.schema.ts
│ │ └── postgres-db.module.ts
│ │ └── tsconfig.lib.json
├── tsconfig.build.json
├── .gitignore
├── tsconfig.json
├── Dockerfile.api-service
├── Dockerfile.mail-service
├── Dockerfile.ngsi-service
├── Dockerfile.report-service
├── Dockerfile.infopin-service
├── Dockerfile.dashboard-service
├── Dockerfile.data-translation-service
├── Dockerfile.static-data-service
├── Dockerfile.usi-platform-service
└── Dockerfile.orchideo-connect-service
├── k8s
└── helm
│ ├── charts
│ ├── frontend
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── api-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── mail-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── ngsi-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── report-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── dashboard-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── infopin-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── static-data-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── data-translation-service
│ │ ├── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ │ ├── Chart.yaml
│ │ └── values.yaml
│ ├── orchideo-connect-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── usi-platform-service
│ │ └── templates
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ └── deployment.yaml
│ ├── migrations
│ │ ├── values.yaml
│ │ ├── templates
│ │ │ └── job.yaml
│ │ └── Chart.yaml
│ └── common
│ │ ├── templates
│ │ └── _service.yaml
│ │ └── Chart.yaml
│ ├── templates
│ ├── secrets
│ │ ├── postgresql-secrets.yaml
│ │ ├── frontend-secrets.yaml
│ │ ├── usi-platform-service-secrets.yaml
│ │ ├── keycloak-secrets.yaml
│ │ └── postgresql-connection-secrets.yaml
│ └── configmaps
│ │ ├── keycloak-config.yaml
│ │ ├── trusted-ca-configmap.yaml
│ │ ├── postgresql-init-configmap.yaml
│ │ ├── static-data-configmap.yaml
│ │ ├── report-service-configmap.yaml
│ │ ├── infopin-service-configmap.yaml
│ │ ├── ngsi-service-configmap.yaml
│ │ ├── mail-service-configmap.yaml
│ │ ├── usi-platform-service-configmap.yaml
│ │ ├── orchideo-connect-service-configmap.yaml
│ │ ├── api-service-configmap.yaml
│ │ ├── postgresql-connection-configmap.yaml
│ │ └── dashboard-service-configmap.yaml
│ ├── ca-issuer-clusterissuer.yaml
│ ├── postgresql
│ └── initDb
│ │ └── init.sql
│ ├── .helmignore
│ └── values-infrastructure.yaml
├── frontend
├── app
│ ├── favicon.ico
│ ├── scrollbar.css
│ ├── (dashboard)
│ │ ├── not-found
│ │ │ └── page.tsx
│ │ ├── [tenant]
│ │ │ └── admin
│ │ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── layout.tsx
│ ├── custom-hooks
│ │ ├── isMobileView.ts
│ │ └── useAutoScaleFont.ts
│ ├── globals.css
│ └── MarkerCluster.css
├── postcss.config.js
├── .dockerignore
├── public
│ ├── login_background.jpeg
│ └── vercel.svg
├── types
│ ├── index.ts
│ ├── wizard.ts
│ ├── table.ts
│ ├── pagination.ts
│ ├── snackbar.ts
│ ├── queryData.ts
│ └── groupingElement.ts
├── ui
│ ├── HorizontalDivider.tsx
│ ├── VerticalDivider.tsx
│ ├── IFrameComponent.tsx
│ ├── WizardLabel.tsx
│ ├── FullSizedLink.tsx
│ ├── Table
│ │ └── TableWrapper.tsx
│ ├── PageHeadline.tsx
│ ├── Tooltip.tsx
│ ├── WizardSelectBox.tsx
│ ├── WizardFileUpload.tsx
│ ├── NoDataWarning.tsx
│ ├── Buttons
│ │ ├── GenericButton.tsx
│ │ ├── CollapseButton.tsx
│ │ ├── DeleteButton.tsx
│ │ └── RedirectPageButton.tsx
│ ├── BlockContainer.tsx
│ ├── HeaderLogo.tsx
│ ├── MultipleColorPickers.tsx
│ ├── VisibilityDisplay.tsx
│ └── Icons
│ │ └── DashboardIcon.tsx
├── cypress.config.ts
├── cypress
│ └── support
│ │ ├── index.d.ts
│ │ └── e2e.ts
├── utils
│ ├── fontUtil.ts
│ ├── tabTypeHelper.ts
│ ├── gridHelper.ts
│ ├── dragDropHelper.ts
│ ├── tenantHelper.ts
│ └── apiHelper.ts
├── .gitignore
├── next.config.js
├── providers
│ ├── TanStackQueryProvider.tsx
│ ├── LoginProvider.tsx
│ ├── CorporateIdentityProvider.tsx
│ └── AuthWrapper.tsx
├── api
│ ├── dataSource-service.ts
│ ├── wizard-service-usi-platform.ts
│ └── mail-service.ts
├── tsconfig.json
├── assets
│ ├── icons
│ │ ├── ForestFire.tsx
│ │ ├── Heat.tsx
│ │ ├── HumidityNormal.tsx
│ │ ├── Waves.tsx
│ │ ├── Snowflake.tsx
│ │ └── index.tsx
│ └── smartCityLogo.svg
├── components
│ └── Map
│ │ └── SetViewToBounds.tsx
├── env.example
└── Dockerfile.frontend
├── database
├── migrations
│ └── generated
│ │ ├── 0026_ancient_chat_0.23.0.sql
│ │ ├── 0004_lively_tenebrous_0.8.8_to_0.8.10.sql
│ │ ├── 0020_cute_peter_parker_0.19.11.sql
│ │ ├── 0021_noisy_frightful_four_0.20.0.sql
│ │ ├── 0025_fair_crusher_hogan_0.22.3.sql
│ │ ├── 0028_bouncy_frightful_four_0.24.0.sql
│ │ ├── 0023_long_energizer_0.22.0.sql
│ │ ├── 0022_abnormal_dormammu_0.21.1.sql
│ │ ├── 0017_thankful_yellowjacket_0.17.5.sql
│ │ ├── 0014_thick_warstar_0.15.1.sql
│ │ ├── 0024_needy_justice_0.22.1.sql
│ │ ├── 0015_sudden_nightmare_0.16.0.sql
│ │ ├── 0010_slippery_santa_claus_0.11.2.sql
│ │ ├── 0003_light_saracen_0.8.3_to_0.8.8.sql
│ │ ├── 0027_fine_terror_0.23.1.sql
│ │ ├── 0008_wonderful_omega_red_0.10.1.sql
│ │ ├── 0019_needy_energizer_0.18.3.sql
│ │ ├── 0005_moaning_betty_ross_0.9.0.sql
│ │ ├── 0002_powerful_tarantula_0.8.0_to_0.8.3.sql
│ │ ├── 0012_peaceful_galactus_0.12.0b.sql
│ │ ├── 0016_nasty_the_renegades_0.17.0.sql
│ │ ├── 0006_short_peter_quill_0.9.1.sql
│ │ ├── 0018_past_deadpool_0.18.0.sql
│ │ └── 0011_bitter_loa_0.12.0a.sql
├── datamodels
│ ├── singe_entity_single_attributes.json
│ ├── singe_entity_multi_attributes.json
│ ├── LD_1e1a.json
│ └── map_format.json
└── drizzle.config.ts
├── docs
├── technical-documentation.md
└── images
│ └── SC_Dashboard_Extended.png
├── .prettierrc
├── .editorconfig
├── .husky
└── pre-commit
├── tsconfig.json
├── docker-compose.local.yml
├── Dockerfile.migrations
├── .eslintrc.js
├── .eslintrc.json
└── .gitignore
/microservices/apps/usi-platform-service/README.adoc:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/images/.gitignore:
--------------------------------------------------------------------------------
1 | *.jpeg
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/frontend/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/frontend/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/api-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/api-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/frontend/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/mail-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/mail-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/ngsi-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/ngsi-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/report-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/report-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/api-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/dashboard-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/dashboard-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/infopin-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/infopin-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/mail-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/ngsi-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/static-data-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/static-data-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/dashboard-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/data-translation-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/data-translation-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/infopin-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/orchideo-connect-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/orchideo-connect-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/report-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/usi-platform-service/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.ingress" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/usi-platform-service/templates/service.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.service" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/static-data-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/usi-platform-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/data-translation-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/k8s/helm/charts/orchideo-connect-service/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | {{- template "common.deployment" . -}}
2 |
--------------------------------------------------------------------------------
/frontend/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nanashi1526/smartcity-dashboard/HEAD/frontend/app/favicon.ico
--------------------------------------------------------------------------------
/database/migrations/generated/0026_ancient_chat_0.23.0.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE "auth_data_type" ADD VALUE IF NOT EXISTS 'usi';
2 |
--------------------------------------------------------------------------------
/microservices/.dockerignore:
--------------------------------------------------------------------------------
1 | Dockerfile
2 | .dockerignore
3 | node_modules
4 | npm-debug.log
5 | .git
6 | .gitignore
7 | dist
8 |
--------------------------------------------------------------------------------
/docs/technical-documentation.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nanashi1526/smartcity-dashboard/HEAD/docs/technical-documentation.md
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/microservices/libs/auth-helper/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth-helper.middleware';
2 | export * from './auth-helper.utility';
3 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './postgres-db.module';
2 | export * from './providers/db.provider';
3 |
--------------------------------------------------------------------------------
/database/migrations/generated/0004_lively_tenebrous_0.8.8_to_0.8.10.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "panel" ADD COLUMN IF NOT EXISTS "info_msg" text;
2 |
--------------------------------------------------------------------------------
/database/migrations/generated/0020_cute_peter_parker_0.19.11.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "general_settings" ADD COLUMN IF NOT EXISTS "disclaimer" text;
2 |
--------------------------------------------------------------------------------
/database/migrations/generated/0021_noisy_frightful_four_0.20.0.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "is_layout_vertical" boolean;
2 |
--------------------------------------------------------------------------------
/database/migrations/generated/0025_fair_crusher_hogan_0.22.3.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "chart_pie_radius" smallint;
2 |
--------------------------------------------------------------------------------
/database/migrations/generated/0028_bouncy_frightful_four_0.24.0.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "dashboard" ADD COLUMN IF NOT EXISTS "allow_share" boolean;
2 |
--------------------------------------------------------------------------------
/docs/images/SC_Dashboard_Extended.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nanashi1526/smartcity-dashboard/HEAD/docs/images/SC_Dashboard_Extended.png
--------------------------------------------------------------------------------
/frontend/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .git
4 | .gitignore
5 | .dockerignore
6 | cypress/
7 | .next/
8 | .next/cache/
9 |
--------------------------------------------------------------------------------
/frontend/public/login_background.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nanashi1526/smartcity-dashboard/HEAD/frontend/public/login_background.jpeg
--------------------------------------------------------------------------------
/database/migrations/generated/0023_long_energizer_0.22.0.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "chart_allow_image_download" boolean;
2 |
--------------------------------------------------------------------------------
/microservices/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/database/migrations/generated/0022_abnormal_dormammu_0.21.1.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "menu_hover_font_color" text;
2 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/dto/mail.dto.ts:
--------------------------------------------------------------------------------
1 | export class SendMailDto {
2 | to: string;
3 | subject: string;
4 | body: string;
5 | }
6 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/auth/token-data.ts:
--------------------------------------------------------------------------------
1 | export class TokenData {
2 | refreshToken: string;
3 | accessToken: string;
4 | expiresIn: Date;
5 | }
6 |
--------------------------------------------------------------------------------
/database/migrations/generated/0017_thankful_yellowjacket_0.17.5.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "general_settings" ADD COLUMN IF NOT EXISTS "allow_theme_switching" boolean DEFAULT false;
2 |
--------------------------------------------------------------------------------
/k8s/helm/templates/secrets/postgresql-secrets.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: postgresql-secrets
5 | data:
6 | postgres-password: cG9zdGdyZXM=
7 |
--------------------------------------------------------------------------------
/frontend/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './dashboardModels';
2 | export * from './enums';
3 | export * from './table';
4 | export * from './groupingElement';
5 | export * from './queryData';
6 |
--------------------------------------------------------------------------------
/frontend/types/wizard.ts:
--------------------------------------------------------------------------------
1 | export type DataConfigRequestType = {
2 | collection: string;
3 | source?: string;
4 | attribute?: string;
5 | apiId?: string;
6 | accessToken?: string;
7 | };
8 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/keycloak-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: keycloak-config
5 | data:
6 | {{ (.Files.Glob "keycloak-config/*").AsConfig | indent 2 }}
7 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/defect/test/fulda-high-water.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nanashi1526/smartcity-dashboard/HEAD/microservices/apps/infopin-service/src/defect/test/fulda-high-water.jpg
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/report/test/fulda-high-water.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nanashi1526/smartcity-dashboard/HEAD/microservices/apps/infopin-service/src/report/test/fulda-high-water.jpg
--------------------------------------------------------------------------------
/k8s/helm/charts/migrations/values.yaml:
--------------------------------------------------------------------------------
1 | image:
2 | repository: smartcity/migrations
3 | tag: latest
4 | pullPolicy: IfNotPresent
5 | restartPolicy: OnFailure
6 | data:
7 | POSTGRES_REJECT_UNAUTHORIZED: 'true'
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "endOfLine": "lf",
5 | "tabWidth": 2,
6 | "parser": "typescript",
7 | "useTabs": false,
8 | "bracketSameLine": false
9 | }
10 |
--------------------------------------------------------------------------------
/database/migrations/generated/0014_thick_warstar_0.15.1.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_wms_url" text;--> statement-breakpoint
2 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_wms_layer" text;
3 |
--------------------------------------------------------------------------------
/frontend/ui/HorizontalDivider.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | export default function HorizontalDivider(): ReactElement {
4 | return
;
5 | }
6 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/climate-project/test/fulda-high-water.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Nanashi1526/smartcity-dashboard/HEAD/microservices/apps/infopin-service/src/climate-project/test/fulda-high-water.jpg
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/index.ts:
--------------------------------------------------------------------------------
1 | export * from './dashboard.schema';
2 | export * from './dashboard.panel.schema';
3 | export * from './dashboard.tab.schema';
4 | export * from './dashboard.widget.schema';
5 |
--------------------------------------------------------------------------------
/frontend/ui/VerticalDivider.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | export default function VerticalDivider(): ReactElement {
4 | return ;
5 | }
6 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/src/usi-platform.service.ts:
--------------------------------------------------------------------------------
1 | /* eslint @typescript-eslint/no-explicit-any: 0 */
2 | import { Injectable } from '@nestjs/common';
3 |
4 | @Injectable()
5 | export class UsiPlaformService {}
6 |
--------------------------------------------------------------------------------
/database/migrations/generated/0024_needy_justice_0.22.1.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_combined_wms_url" text;--> statement-breakpoint
2 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_combined_wms_layer" text;
3 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/trusted-ca-configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: trusted-ca-configmap
5 | data:
6 | TRUSTED_CA: |-
7 | {{ (.Files.Get .Values.trustedCAFile) | nindent 4 }}
8 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/mail/mail.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { MailService } from './mail.service';
3 |
4 | @Module({
5 | providers: [MailService],
6 | })
7 | export class MailModule {}
8 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/query/query.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryService } from './query.service';
3 |
4 | @Module({
5 | providers: [QueryService],
6 | })
7 | export class QueryModule {}
8 |
--------------------------------------------------------------------------------
/database/migrations/generated/0015_sudden_nightmare_0.16.0.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "menu_corner_color" text;--> statement-breakpoint
2 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "menu_corner_font_color" text;
3 |
--------------------------------------------------------------------------------
/frontend/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress';
2 |
3 | export default defineConfig({
4 | e2e: {
5 | baseUrl: 'http://localhost:3000/admin',
6 | },
7 | env: {
8 | apiUrl: 'http://localhost:8081',
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/microservices/apps/mail-service/src/dto/mail.dto.ts:
--------------------------------------------------------------------------------
1 | export class SendMailDto {
2 | to: string;
3 | subject: string;
4 | body: string;
5 | }
6 |
7 | export class DefectReportDto {
8 | reporterEmail: string;
9 | defectDetails: string;
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/app/scrollbar.css:
--------------------------------------------------------------------------------
1 | .hide-scrollbar::-webkit-scrollbar {
2 | display: none; /* For WebKit browsers */
3 | }
4 |
5 | .hide-scrollbar {
6 | -ms-overflow-style: none; /* For Internet Explorer and Edge */
7 | scrollbar-width: none; /* For Firefox */
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/cypress/support/index.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace Cypress {
2 | interface Chainable {
3 | keycloakLogin(username: string, password: string): Cypress.Chainable;
4 | keycloakLogout(): Cypress.Chainable;
5 | clearCache(): Cypress.Chainable;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/microservices/apps/api-service/src/query/query.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryService } from './query.service';
3 |
4 | @Module({
5 | providers: [QueryService],
6 | exports: [QueryService],
7 | })
8 | export class QueryModule {}
9 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/query/query.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryService } from './query.service';
3 |
4 | @Module({
5 | providers: [QueryService],
6 | exports: [QueryService],
7 | })
8 | export class QueryModule {}
9 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/query/query.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryService } from './query.service';
3 |
4 | @Module({
5 | providers: [QueryService],
6 | exports: [QueryService],
7 | })
8 | export class QueryModule {}
9 |
--------------------------------------------------------------------------------
/microservices/libs/auth-helper/src/PublicDecorator.ts:
--------------------------------------------------------------------------------
1 | import { SetMetadata } from '@nestjs/common';
2 |
3 | export const IS_PUBLIC_KEY = 'isPublic';
4 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
5 | export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
6 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/data-model/fiware-models/fiware-models.enum.ts:
--------------------------------------------------------------------------------
1 | export enum FiwareDataModels {
2 | EV_CHARGING_STATION = 'EVChargingStationExtended',
3 | PARKING = 'OffStreetParking',
4 | POINT_OF_INTEREST = 'PointOfInterestExtended',
5 | SWIMMING = 'OpenAirPool',
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [*]
7 | charset = utf-8
8 | indent_style = space
9 | indent_size = 2
10 | end_of_line = lf
11 | charset = utf-8
12 | trim_trailing_whitespace = true
13 | insert_final_newline = true
--------------------------------------------------------------------------------
/k8s/helm/templates/secrets/frontend-secrets.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values.frontend.enabled }}
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | name: frontend-secrets
6 | type: Opaque
7 | data:
8 | NEXT_PUBLIC_MAPBOX_TOKEN: {{ index .Values.frontend "mapBoxToken" | b64enc | quote }}
9 | {{- end }}
10 |
--------------------------------------------------------------------------------
/microservices/apps/api-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/api-service"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/apps/mail-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/mail-service"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/ngsi-service"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/libs/auth-helper/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "../../dist/libs/auth-helper"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "outDir": "../../dist/libs/postgres-db"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/apps/api-service/src/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AuthService } from './auth.service';
3 | import { HttpModule } from '@nestjs/axios';
4 |
5 | @Module({
6 | imports: [HttpModule],
7 | providers: [AuthService],
8 | })
9 | export class AuthModule {}
10 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/dashboard-service"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AuthService } from './auth.service';
3 | import { HttpModule } from '@nestjs/axios';
4 |
5 | @Module({
6 | imports: [HttpModule],
7 | providers: [AuthService],
8 | })
9 | export class AuthModule {}
10 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/usi-platform-service"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/database/migrations/generated/0010_slippery_santa_claus_0.11.2.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "auth_data" ADD COLUMN IF NOT EXISTS "collections" text[];--> statement-breakpoint
2 | ALTER TABLE "auth_data" ADD COLUMN IF NOT EXISTS "fiware_services" text[];--> statement-breakpoint
3 | ALTER TABLE "data_source" ADD COLUMN IF NOT EXISTS "collections" text[];
4 |
--------------------------------------------------------------------------------
/k8s/helm/charts/data-translation-service/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: data-translation-service
3 | description: Smart City Dashboard Data Translation Service
4 | type: application
5 | version: 0.1.0
6 | appVersion: "1.0.1"
7 |
8 | dependencies:
9 | - name: common
10 | version: 0.1.0
11 | condition: common.enabled
12 |
--------------------------------------------------------------------------------
/microservices/apps/data-translation-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/data-translation-service"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/orchideo-connect-service"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/microservices/apps/static-data-service/src/transformation/transformation.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TransformationService } from './transformation.service';
3 |
4 | @Module({
5 | providers: [TransformationService],
6 | exports: [TransformationService],
7 | })
8 | export class TransformationModule {}
9 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/postgres-db.module.ts:
--------------------------------------------------------------------------------
1 | import { Global, Module } from '@nestjs/common';
2 | import { POSTGRES_DB, PostgresDbProvider } from './providers/db.provider';
3 |
4 | @Global()
5 | @Module({
6 | providers: [PostgresDbProvider],
7 | exports: [POSTGRES_DB],
8 | })
9 | export class PostgresDbModule {}
10 |
--------------------------------------------------------------------------------
/frontend/types/table.ts:
--------------------------------------------------------------------------------
1 | export type TableConfig = {
2 | columns: Array>;
3 | viewsPerPage: number;
4 | maxPages: number;
5 | };
6 |
7 | export type TableColumn = {
8 | name: keyof T;
9 | displayName: string;
10 | };
11 |
12 | export type GenericTableContentItem = {
13 | [P in keyof T]?: T[P];
14 | };
15 |
--------------------------------------------------------------------------------
/database/migrations/generated/0003_light_saracen_0.8.3_to_0.8.8.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "corporate_info" DROP COLUMN IF EXISTS "menu_active_font_color";--> statement-breakpoint
2 | ALTER TABLE "corporate_info" DROP COLUMN IF EXISTS "use_color_transition_header";--> statement-breakpoint
3 | ALTER TABLE "corporate_info" DROP COLUMN IF EXISTS "use_color_transition_menu";
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/config/config.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigService } from './config.service';
3 | import { ConfigController } from './config.controller';
4 |
5 | @Module({
6 | providers: [ConfigService],
7 | controllers: [ConfigController],
8 | })
9 | export class ConfigModule {}
10 |
--------------------------------------------------------------------------------
/frontend/ui/IFrameComponent.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | type IFrameComponentProps = {
4 | src: string;
5 | };
6 |
7 | export default function IFrameComponent(
8 | props: IFrameComponentProps,
9 | ): ReactElement {
10 | const { src } = props;
11 | return ;
12 | }
13 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/postgresql-init-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if not (lookup "v1" "ConfigMap" .Release.Namespace "postgresql-init-configmap") }}
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | name: postgresql-init-configmap
6 | data:
7 | init.sql: |-
8 | {{ (.Files.Get "postgresql/initDb/init.sql") | nindent 4 }}
9 | {{- end }}
10 |
--------------------------------------------------------------------------------
/k8s/helm/templates/secrets/usi-platform-service-secrets.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "usi-platform-service" "enabled" }}
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | name: usi-platform-service-secrets
6 | type: Opaque
7 | data:
8 | USI_CLIENT_SECRET: {{ index .Values "usi-platform-service" "usiClientSecret" | b64enc | quote }}
9 | {{- end }}
10 |
--------------------------------------------------------------------------------
/frontend/types/pagination.ts:
--------------------------------------------------------------------------------
1 | export type UserPagination = {
2 | page: number;
3 | limit: number;
4 | };
5 |
6 | export type PaginatedResult = {
7 | data: T[];
8 | meta: PaginationMeta;
9 | };
10 |
11 | export type PaginationMeta = {
12 | totalItems: number;
13 | totalPages: number;
14 | currentPage: number;
15 | pageSize: number;
16 | };
17 |
--------------------------------------------------------------------------------
/frontend/ui/WizardLabel.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | type WizardLabeldProps = {
4 | label: string;
5 | };
6 |
7 | export default function WizardLabel(props: WizardLabeldProps): ReactElement {
8 | const { label } = props;
9 |
10 | return (
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/infopin-service"
6 | },
7 | "include": [
8 | "src/**/*"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist",
13 | "test",
14 | "**/*spec.ts"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/report-service"
6 | },
7 | "include": [
8 | "src/**/*"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist",
13 | "test",
14 | "**/*spec.ts"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | echo ""
5 | echo "Running formatter and linter..."
6 | echo ""
7 | npx lint-staged
8 | echo ""
9 |
10 | # echo ""
11 | # echo "Running unit tests..."
12 | # echo ""
13 | # npm run test:units
14 | # echo ""
15 | # echo "Running E2E-tests..."
16 | # echo ""
17 | # npm run test:e2e
18 | # echo ""
19 |
--------------------------------------------------------------------------------
/microservices/apps/static-data-service/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false,
5 | "outDir": "../../dist/apps/static-data-service"
6 | },
7 | "include": [
8 | "src/**/*"
9 | ],
10 | "exclude": [
11 | "node_modules",
12 | "dist",
13 | "test",
14 | "**/*spec.ts"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/static-data-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "static-data-service" "enabled" }}
2 | # Env vars specific for the static-data-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: static-data-service-configmap
7 | data:
8 | NEXT_PUBLIC_FRONTEND_URL: {{ index .Values "static-data-service" "frontendUrl" }}
9 | {{- end }}
10 |
--------------------------------------------------------------------------------
/k8s/helm/templates/secrets/keycloak-secrets.yaml:
--------------------------------------------------------------------------------
1 | # Encoded config variables for keycloak instance setup
2 | {{- if .Values.keycloak.enabled }}
3 | apiVersion: v1
4 | kind: Secret
5 | metadata:
6 | name: keycloak-secrets
7 | data:
8 | admin-password: YWRtaW4=
9 | db-host: {{ printf "%v-postgresql" .Release.Name | b64enc }} ## encoded smartcity-postgresql
10 | db-password: YWRtaW4=
11 | {{- end }}
12 |
--------------------------------------------------------------------------------
/microservices/apps/api-service/src/data/data.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DataService } from './data.service';
3 | import { AuthService } from '../auth/auth.service';
4 | import { HttpModule } from '@nestjs/axios';
5 |
6 | @Module({
7 | providers: [DataService, AuthService],
8 | exports: [DataService],
9 | imports: [HttpModule],
10 | })
11 | export class DataModule {}
12 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/logging/logger.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DashboardServiceLogger } from './logger.service';
3 | import { LoggerController } from './logger.controller';
4 |
5 | @Module({
6 | providers: [DashboardServiceLogger],
7 | exports: [DashboardServiceLogger],
8 | controllers: [LoggerController],
9 | })
10 | export class LoggerModule {}
11 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/data/data.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DataService } from './data.service';
3 | import { AuthService } from '../auth/auth.service';
4 | import { HttpModule } from '@nestjs/axios';
5 |
6 | @Module({
7 | providers: [DataService, AuthService],
8 | exports: [DataService],
9 | imports: [HttpModule],
10 | })
11 | export class DataModule {}
12 |
--------------------------------------------------------------------------------
/k8s/helm/ca-issuer-clusterissuer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: ClusterIssuer
3 | metadata:
4 | name: letsencrypt-prod
5 | spec:
6 | acme:
7 | server: https://acme-v02.api.letsencrypt.org/directory
8 | email: lorcan.creedon@edag.com
9 | privateKeySecretRef:
10 | name: letsencrypt-prod
11 | solvers:
12 | - http01:
13 | ingress:
14 | class: nginx
15 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/src/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AuthService } from './auth.service';
3 | import { HttpModule } from '@nestjs/axios';
4 | import { AuthController } from './auth.controller';
5 |
6 | @Module({
7 | imports: [HttpModule],
8 | providers: [AuthService],
9 | controllers: [AuthController],
10 | })
11 | export class AuthModule {}
12 |
--------------------------------------------------------------------------------
/frontend/ui/FullSizedLink.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | import Link from 'next/link';
3 |
4 | export default function FullSizedLink({
5 | children,
6 | url,
7 | }: {
8 | children: React.ReactNode;
9 | url: string;
10 | }): ReactElement {
11 | return (
12 |
13 | {children}
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/ui/Table/TableWrapper.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | export default function TableWrapper({
4 | children,
5 | }: {
6 | children: React.ReactNode;
7 | }): ReactElement {
8 | return (
9 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/logo/logo.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { LogoService } from './logo.service';
3 | import { LogoController } from './logo.controller';
4 | import { LogoRepo } from './logo.repo';
5 |
6 | @Module({
7 | providers: [LogoService, LogoRepo],
8 | controllers: [LogoController],
9 | exports: [LogoService, LogoRepo],
10 | })
11 | export class LogoModule {}
12 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/data/data.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DataService } from './data.service';
3 | import { AuthService } from '../auth/auth.service';
4 | import { HttpModule } from '@nestjs/axios';
5 |
6 | @Module({
7 | providers: [DataService, AuthService],
8 | exports: [DataService],
9 | imports: [HttpModule],
10 | })
11 | export class DataModule {}
12 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/query/query.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryController } from './query.controller';
3 | import { QueryService } from './query.service';
4 | import { QueryRepo } from './query.repo';
5 |
6 | @Module({
7 | controllers: [QueryController],
8 | providers: [QueryService, QueryRepo],
9 | exports: [QueryService],
10 | })
11 | export class QueryModule {}
12 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/src/query-config/query-config.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryConfigService } from './query-config.service';
3 | import { AuthService } from '../auth/auth.service';
4 | import { HttpModule } from '@nestjs/axios';
5 |
6 | @Module({
7 | imports: [HttpModule],
8 | providers: [QueryConfigService, AuthService],
9 | })
10 | export class QueryConfigModule {}
11 |
--------------------------------------------------------------------------------
/frontend/app/(dashboard)/not-found/page.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | export default function NotFound(): ReactElement {
4 | return (
5 |
6 |
7 |
404
8 |
Page Not Found
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/frontend/types/snackbar.ts:
--------------------------------------------------------------------------------
1 | export type SnackbarState = {
2 | id: number;
3 | open: boolean;
4 | message: string;
5 | status: 'success' | 'error' | 'warning' | 'info';
6 | timerId?: NodeJS.Timeout;
7 | };
8 |
9 | export type SnackbarContextType = {
10 | snackbars: SnackbarState[];
11 | openSnackbar: (message: string, status: SnackbarState['status']) => void; // Updated line
12 | closeSnackbar: (id: number) => void;
13 | };
14 |
--------------------------------------------------------------------------------
/microservices/apps/mail-service/src/mail.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigModule } from '@nestjs/config';
3 | import { MailController } from './mail.controller';
4 | import { MailService } from './mail.service';
5 |
6 | @Module({
7 | imports: [ConfigModule.forRoot({ envFilePath: '../.env' })],
8 | controllers: [MailController],
9 | providers: [MailService],
10 | })
11 | export class MailModule {}
12 |
--------------------------------------------------------------------------------
/database/datamodels/singe_entity_single_attributes.json:
--------------------------------------------------------------------------------
1 | {
2 | "entityId": "urn:ngsi-ld:FloodMonitoring:eui-a84041208188c97f",
3 | "attributes": [
4 | {
5 | "attrName": "measuredDistance",
6 | "values": [631.529411764706, 604.112676056338, 706.097222222222]
7 | }
8 | ],
9 | "index": [
10 | "2025-03-02T00:00:00.000+00:00",
11 | "2025-03-03T00:00:00.000+00:00",
12 | "2025-03-04T00:00:00.000+00:00"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/database/migrations/generated/0027_fine_terror_0.23.1.sql:
--------------------------------------------------------------------------------
1 | DO $$ BEGIN
2 | CREATE TYPE "chart_date_representation" AS ENUM('Default', 'Only Year', 'Only Month');
3 | EXCEPTION
4 | WHEN duplicate_object THEN null;
5 | END $$;
6 | --> statement-breakpoint
7 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "chart_date_representation" "chart_date_representation";--> statement-breakpoint
8 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "chart_has_automatic_zoom" boolean;
9 |
--------------------------------------------------------------------------------
/microservices/apps/mail-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { MailModule } from './mail.module';
3 |
4 | async function bootstrap(): Promise {
5 | const app = await NestFactory.create(MailModule);
6 |
7 | app.enableCors({
8 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
9 | methods: 'GET,POST',
10 | credentials: true,
11 | });
12 |
13 | await app.listen(8085);
14 | }
15 | bootstrap();
16 |
--------------------------------------------------------------------------------
/microservices/apps/static-data-service/src/data/data.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DataService } from './data.service';
3 | import { HttpModule } from '@nestjs/axios';
4 | import { TransformationModule } from '../transformation/transformation.module';
5 |
6 | @Module({
7 | providers: [DataService],
8 | exports: [DataService],
9 | imports: [HttpModule, TransformationModule],
10 | })
11 | export class DataModule {}
12 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/report-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "report-service" "enabled" }}
2 | # Env vars specific for the mail-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: report-service-configmap
7 | data:
8 | NEXT_PUBLIC_FRONTEND_URL: {{ index .Values "report-service" "frontendUrl" }}
9 | NEXT_PUBLIC_MAIL_SERVICE_URL: {{ index .Values "report-service" "mailServiceUrl" }}
10 | {{- end }}
11 |
--------------------------------------------------------------------------------
/k8s/helm/postgresql/initDb/init.sql:
--------------------------------------------------------------------------------
1 | -- be aware this init script is only executed once (on the first setup),
2 | -- all changes done later, needs to be applied manually
3 |
4 | -- keycloak db to separate tables from scs db
5 | create user keycloak with password 'admin';
6 | create database keycloak with owner keycloak;
7 |
8 | -- test db to separate tables from scs db
9 | create user testing with password 'admin';
10 | create database testing with owner testing;
11 |
--------------------------------------------------------------------------------
/frontend/utils/fontUtil.ts:
--------------------------------------------------------------------------------
1 | export function generateResponsiveFontSize(
2 | baseSize: number,
3 | minScale = 0.2,
4 | maxScale = 1.6,
5 | preferredScale = 0.25,
6 | ): string {
7 | const baseSizeRem = baseSize / 16;
8 | const minSize = baseSizeRem * minScale;
9 | const maxSize = baseSizeRem * maxScale;
10 | const preferredBase = baseSizeRem * 0.7;
11 |
12 | return `clamp(${minSize}rem, ${preferredScale}vw + ${preferredBase}rem, ${maxSize}rem)`;
13 | }
14 |
--------------------------------------------------------------------------------
/k8s/helm/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *.orig
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 | .vscode/
24 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/data-model.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { json, pgTable, uuid } from 'drizzle-orm/pg-core';
3 |
4 | export const dataModels = pgTable('data_model', {
5 | id: uuid('id')
6 | .primaryKey()
7 | .default(sql`gen_random_uuid()`),
8 | model: json('model'),
9 | });
10 |
11 | export type DataModel = typeof dataModels.$inferSelect;
12 | export type NewDataModel = typeof dataModels.$inferInsert;
13 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/tenant.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
3 |
4 | export const tenants = pgTable('tenant', {
5 | id: uuid('id')
6 | .primaryKey()
7 | .default(sql`gen_random_uuid()`),
8 | abbreviation: text('abbreviation').unique(),
9 | });
10 |
11 | export type Tenant = typeof tenants.$inferSelect;
12 | export type NewTenant = typeof tenants.$inferInsert;
13 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/scheduler.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { NgsiService } from './ngsi.service';
4 |
5 | @Injectable()
6 | export class ScheduleService {
7 | constructor(private readonly ngsiService: NgsiService) {}
8 |
9 | @Cron('0 * * * * *') // every minute
10 | async runSchedule(): Promise {
11 | await this.ngsiService.updateQueries();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { ReportModule } from './report.module';
3 |
4 | async function bootstrap(): Promise {
5 | const app = await NestFactory.create(ReportModule);
6 | app.enableCors({
7 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
8 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
9 | credentials: true,
10 | });
11 | await app.listen(8086);
12 | }
13 |
14 | bootstrap();
15 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/scheduler.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { ReportService } from './report.service';
4 |
5 | @Injectable()
6 | export class ScheduleService {
7 | constructor(private readonly reportService: ReportService) {}
8 |
9 | @Cron('0 * * * * *') // every minute
10 | async runSchedule(): Promise {
11 | await this.reportService.runSchedule();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { UsiPlatformModule } from './usi-platform.module';
3 |
4 | async function bootstrap(): Promise {
5 | const app = await NestFactory.create(UsiPlatformModule);
6 |
7 | app.enableCors({
8 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
9 | methods: 'GET',
10 | credentials: true,
11 | });
12 |
13 | await app.listen(8088);
14 | }
15 |
16 | bootstrap();
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "removeComments": true,
5 | "allowSyntheticDefaultImports": true,
6 | "target": "ES2021",
7 | "sourceMap": true,
8 | "incremental": true,
9 | "skipLibCheck": true,
10 | "strictNullChecks": false,
11 | "noImplicitAny": false,
12 | "strictBindCallApply": false,
13 | "forceConsistentCasingInFileNames": false,
14 | "noFallthroughCasesInSwitch": false
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/app/(dashboard)/[tenant]/admin/page.tsx:
--------------------------------------------------------------------------------
1 | import { redirect } from 'next/navigation';
2 | import { JSX } from 'react';
3 |
4 | type Props = {
5 | params: Promise<{
6 | tenant?: string;
7 | }>;
8 | };
9 |
10 | export default async function Home(props: Props): Promise {
11 | const params = await props.params;
12 | const tenant = params.tenant;
13 | const adminUrl = tenant ? `/${tenant}/admin/widgets` : '/admin/widgets';
14 | redirect(adminUrl);
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/types/queryData.ts:
--------------------------------------------------------------------------------
1 | export type AttributeData = {
2 | type: string;
3 | value: number | string;
4 | metadata: Record;
5 | };
6 |
7 | export type QueryData = {
8 | id: string;
9 | type: string;
10 | [key: string]: AttributeData | string;
11 | };
12 |
13 | export type QueryDataEntities = {
14 | type: string;
15 | value: string | number;
16 | };
17 |
18 | export type QueryDataWithAttributes = {
19 | [attribute: string]: AttributeData[];
20 | };
21 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/auth/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | },
9 | "moduleNameMapper": {
10 | "@app/postgres-db/(.*)": "/../../../libs/postgres-db/src/$1",
11 | "@app/postgres-db": "/../../../libs/postgres-db/src"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/data/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | },
9 | "moduleNameMapper": {
10 | "@app/postgres-db/(.*)": "/../../../libs/postgres-db/src/$1",
11 | "@app/postgres-db": "/../../../libs/postgres-db/src"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/microservices/apps/static-data-service/src/schedule.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { QueryService } from './query/query.service';
4 |
5 | @Injectable()
6 | export class ScheduleService {
7 | constructor(private readonly queryService: QueryService) {}
8 |
9 | @Cron('0 * * * * *') // every minute
10 | async runSchedule(): Promise {
11 | await this.queryService.updateQueries();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/data-model/fiware-models/interesting-place.model.ts:
--------------------------------------------------------------------------------
1 | export class InterestingPlaceModel {
2 | name: string;
3 | types: string[];
4 | address: {
5 | addressLocality: string;
6 | postalCode: string;
7 | streetAddress: string;
8 | };
9 | image: string;
10 | imagePreview: string;
11 | creator: string;
12 | location: {
13 | type: string;
14 | coordinates: number[];
15 | };
16 | info: string;
17 | zoomprio: string;
18 | }
19 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/widget-to-panel/widget-to-panel.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { WidgetToPanelService } from './widget-to-panel.service';
3 | import { WidgetToPanelController } from './widget-to-panel.controller';
4 | import { WidgetToPanelRepo } from './widget-to-panel.repo';
5 |
6 | @Module({
7 | providers: [WidgetToPanelService, WidgetToPanelRepo],
8 | controllers: [WidgetToPanelController],
9 | })
10 | export class WidgetToPanelModule {}
11 |
--------------------------------------------------------------------------------
/frontend/utils/tabTypeHelper.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Tab,
3 | tabComponentTypeEnum,
4 | tabComponentSubTypeEnum,
5 | TabWithCombinedWidgets,
6 | } from '@/types';
7 |
8 | export const isTabOfTypeCombinedWidget = (
9 | tab: Tab,
10 | ): tab is TabWithCombinedWidgets => {
11 | return (
12 | tab.componentType === tabComponentTypeEnum.combinedComponent ||
13 | (tab.componentType === tabComponentTypeEnum.map &&
14 | tab.componentSubType === tabComponentSubTypeEnum.combinedMap)
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/tab/tab.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TabService } from './tab.service';
3 | import { TabController } from './tab.controller';
4 | import { TabRepo } from './tab.repo';
5 | import { WidgetRepo } from '../widget/widget.repo';
6 | import { TenantRepo } from '../tenant/tenant.repo';
7 |
8 | @Module({
9 | providers: [TabService, TabRepo, WidgetRepo, TenantRepo],
10 | controllers: [TabController],
11 | })
12 | export class TabModule {}
13 |
--------------------------------------------------------------------------------
/microservices/apps/static-data-service/src/query/query.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryService } from './query.service';
3 | import { PostgresDbModule } from '@app/postgres-db';
4 | import { DataService } from '../data/data.service';
5 | import { TransformationModule } from '../transformation/transformation.module';
6 |
7 | @Module({
8 | providers: [QueryService, DataService],
9 | imports: [PostgresDbModule, TransformationModule],
10 | })
11 | export class QueryModule {}
12 |
--------------------------------------------------------------------------------
/database/migrations/generated/0008_wonderful_omega_red_0.10.1.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_allow_filter" boolean;--> statement-breakpoint
2 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_filter_attribute" text;--> statement-breakpoint
3 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_allow_legend" boolean;--> statement-breakpoint
4 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_legend_values" json;--> statement-breakpoint
5 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_legend_disclaimer" text;
6 |
--------------------------------------------------------------------------------
/frontend/utils/gridHelper.ts:
--------------------------------------------------------------------------------
1 | export function getColumnSpanSettings(width: number): string {
2 | switch (width) {
3 | case 4:
4 | return 'col-span-12 sm:col-span-12 lg:col-span-4';
5 | case 6:
6 | return 'col-span-12 sm:col-span-12 lg:col-span-6';
7 | case 8:
8 | return 'col-span-12 sm:col-span-12 lg:col-span-8';
9 | case 12:
10 | return 'col-span-12 sm:col-span-12 lg:col-span-12';
11 | default:
12 | return 'col-span-12 sm:col-span-12 lg:col-span-12';
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/infopin-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "infopin-service" "enabled" }}
2 | # Env vars specific for the infopin-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: infopin-service-configmap
7 | data:
8 | NODE_ENV: "development"
9 | NEST_JWKS_URI: {{ index .Values "infopin-service" "jwks" }}
10 | IMAGE_DIR: "../../../../apps/infopin-service/images" # for local development, stepping out of the 'dist' folder is required
11 | {{- end }}
12 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 |
29 | # vercel
30 | .vercel
31 |
32 | # typescript
33 | *.tsbuildinfo
34 | next-env.d.ts
35 |
--------------------------------------------------------------------------------
/microservices/apps/data-translation-service/src/populate/fiware.types.ts:
--------------------------------------------------------------------------------
1 | export type FiwareAttributeData = {
2 | value: string;
3 | type: string;
4 | metadata: object;
5 | };
6 |
7 | export type FiwareAttribute = {
8 | attrName: string;
9 | types: FiwareAttributeType[];
10 | };
11 |
12 | export type FiwareAttributeType = {
13 | entities: FiwareAttributeEntity[];
14 | entityType: string;
15 | };
16 |
17 | export type FiwareAttributeEntity = {
18 | entityId: string;
19 | index: string[];
20 | values: number[];
21 | };
22 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/dashboard/populate/fiware.types.ts:
--------------------------------------------------------------------------------
1 | export type FiwareAttributeData = {
2 | value: string;
3 | type: string;
4 | metadata: object;
5 | };
6 |
7 | export type FiwareAttribute = {
8 | attrName: string;
9 | types: FiwareAttributeType[];
10 | };
11 |
12 | export type FiwareAttributeType = {
13 | entities: FiwareAttributeEntity[];
14 | entityType: string;
15 | };
16 |
17 | export type FiwareAttributeEntity = {
18 | entityId: string;
19 | index: string[];
20 | values: number[];
21 | };
22 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/logging/logger.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { Public } from '@app/auth-helper/PublicDecorator';
3 | import { DashboardServiceLogger } from './logger.service';
4 |
5 | @Controller('logs')
6 | export class LoggerController {
7 | constructor(private readonly service: DashboardServiceLogger) {}
8 |
9 | @Public()
10 | @Get('/')
11 | async getAll(): Promise {
12 | console.log('getting logs');
13 | return this.service.getAll();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/microservices/apps/api-service/src/organisation-schedule.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { OrganisationService } from './organisation/organisation.service';
4 |
5 | @Injectable()
6 | export class OrganisationScheduleService {
7 | constructor(private readonly organisationService: OrganisationService) {}
8 |
9 | @Cron('0 0 0 * * *') // every day
10 | async runSchedule(): Promise {
11 | await this.organisationService.updateGroupingElements();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 |
3 | console.log('NEXT_PUBLIC_BASEPATH');
4 | console.log(process.env.NEXT_PUBLIC_BASEPATH);
5 | console.log('NEXT_PUBLIC_ASSET_PREFIX');
6 | console.log(process.env.NEXT_PUBLIC_ASSET_PREFIX);
7 |
8 | const nextConfig = {
9 | basePath: process.env.NEXT_PUBLIC_BASEPATH,
10 | assetPrefix: process.env.NEXT_PUBLIC_ASSET_PREFIX,
11 | logging: {
12 | fetches: {
13 | fullUrl: true,
14 | },
15 | },
16 | cacheMaxMemorySize: 0,
17 | };
18 |
19 | module.exports = nextConfig;
20 |
--------------------------------------------------------------------------------
/microservices/apps/data-translation-service/src/scheduler.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { DataTranslationService } from './data-translation.service';
4 |
5 | @Injectable()
6 | export class ScheduleService {
7 | constructor(
8 | private readonly dataTranslationServiceService: DataTranslationService,
9 | ) {}
10 |
11 | @Cron('0 * * * * *')
12 | async runSchedule(): Promise {
13 | await this.dataTranslationServiceService.refreshTabData();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/src/auth/auth.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { AuthService } from './auth.service';
3 |
4 | // Controller for testing purposes - get USI keycloak token
5 | @Controller('usi-auth')
6 | export class AuthController {
7 | constructor(private readonly authService: AuthService) {}
8 |
9 | @Get('token')
10 | async getToken(): Promise<{ access_token: string }> {
11 | const access_token = await this.authService.getToken();
12 | return { access_token };
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/microservices/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | pnpm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # OS
15 | .DS_Store
16 |
17 | # Tests
18 | /coverage
19 | /.nyc_output
20 |
21 | # IDEs and editors
22 | /.idea
23 | .project
24 | .classpath
25 | .c9/
26 | *.launch
27 | .settings/
28 | *.sublime-workspace
29 |
30 | # IDE - VSCode
31 | .vscode/*
32 | !.vscode/settings.json
33 | !.vscode/tasks.json
34 | !.vscode/launch.json
35 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/microservices/apps/api-service/src/report/report.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ReportService } from './report.service';
3 | import { QueryService } from '../query/query.service';
4 | import { DataService } from '../data/data.service';
5 | import { AuthService } from '../auth/auth.service';
6 | import { HttpModule } from '@nestjs/axios';
7 |
8 | @Module({
9 | providers: [ReportService, QueryService, DataService, AuthService],
10 | exports: [ReportService],
11 | imports: [HttpModule],
12 | })
13 | export class ReportModule {}
14 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/report/report.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ReportService } from './report.service';
3 | import { QueryService } from '../query/query.service';
4 | import { DataService } from '../data/data.service';
5 | import { AuthService } from '../auth/auth.service';
6 | import { HttpModule } from '@nestjs/axios';
7 |
8 | @Module({
9 | providers: [ReportService, QueryService, DataService, AuthService],
10 | exports: [ReportService],
11 | imports: [HttpModule],
12 | })
13 | export class ReportModule {}
14 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/organisation-schedule.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { OrganisationService } from './organisation/organisation.service';
4 |
5 | @Injectable()
6 | export class OrganisationScheduleService {
7 | constructor(private readonly organisationService: OrganisationService) {}
8 |
9 | @Cron('0 0 0 * * *') // every day
10 | async runSchedule(): Promise {
11 | await this.organisationService.updateGroupingElements();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/ngsi-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "ngsi-service" "enabled" }}
2 | # Env vars specific for the ngsi-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: ngsi-service-configmap
7 | data:
8 | NODE_ENV: "development"
9 | ADMIN_ROLE: "scs-admin"
10 | PASSWORD_ENCRYPT_KEY: {{ index .Values "ngsi-service" "passwordEncryptKey" }}
11 | NEST_JWKS_URI: {{ index .Values "ngsi-service" "jwks" }}
12 | NEXT_PUBLIC_FRONTEND_URL: {{ index .Values "ngsi-service" "frontendUrl" }}
13 | {{- end }}
14 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { json } from 'express';
3 | import { InfopinServiceModule } from './infopin-service.module';
4 |
5 | async function bootstrap(): Promise {
6 | const app = await NestFactory.create(InfopinServiceModule);
7 | app.enableCors({
8 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
9 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
10 | credentials: true,
11 | });
12 | app.use(json({ limit: '5mb' }));
13 | await app.listen(8084);
14 | }
15 |
16 | bootstrap();
17 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/report/report.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ReportService } from './report.service';
3 | import { QueryService } from '../query/query.service';
4 | import { DataService } from '../data/data.service';
5 | import { AuthService } from '../auth/auth.service';
6 | import { HttpModule } from '@nestjs/axios';
7 |
8 | @Module({
9 | providers: [ReportService, QueryService, DataService, AuthService],
10 | exports: [ReportService],
11 | imports: [HttpModule],
12 | })
13 | export class ReportModule {}
14 |
--------------------------------------------------------------------------------
/k8s/helm/charts/common/templates/_service.yaml:
--------------------------------------------------------------------------------
1 | {{ define "common.service" }}
2 |
3 | {{- if .Values.service.enabled }}
4 | apiVersion: v1
5 | kind: Service
6 | metadata:
7 | name: {{ include "smart-city.fullname" . }}
8 | labels:
9 | {{- include "smart-city.labels" . | nindent 4 }}
10 | spec:
11 | type: {{ .Values.service.type }}
12 | ports:
13 | - port: {{ .Values.service.port }}
14 | targetPort: http
15 | protocol: TCP
16 | name: http
17 | selector:
18 | {{- include "smart-city.selectorLabels" . | nindent 4 }}
19 | {{- end }}
20 | {{end}}
21 |
--------------------------------------------------------------------------------
/database/migrations/generated/0019_needy_energizer_0.18.3.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "slider_current_font_color" text;--> statement-breakpoint
2 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "slider_maximum_font_color" text;--> statement-breakpoint
3 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "slider_general_font_color" text;--> statement-breakpoint
4 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "slider_current_color" text;--> statement-breakpoint
5 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "slider_maximum_color" text;
6 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/data-source/data-source.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DataSourceController } from './data-source.controller';
3 | import { DataSourceRepo } from './data-source.repo';
4 | import { AuthDataRepo } from '../auth-data/auth-data.repo';
5 | import { DataSourceService } from './data-source.service';
6 |
7 | @Module({
8 | providers: [DataSourceService, DataSourceRepo, AuthDataRepo],
9 | controllers: [DataSourceController],
10 | exports: [DataSourceRepo, DataSourceService],
11 | })
12 | export class DataSourceModule {}
13 |
--------------------------------------------------------------------------------
/microservices/apps/data-translation-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { DataTranslationServiceModule } from './data-translation.module';
3 |
4 | async function bootstrap(): Promise {
5 | const app = await NestFactory.create(DataTranslationServiceModule, {
6 | logger: ['error', 'warn', 'log'],
7 | });
8 |
9 | await app.init();
10 |
11 | await new Promise(() => {
12 | // This is needed to keep the process alive even without http endpoints
13 | console.log('Service is running in background mode...');
14 | });
15 | }
16 | bootstrap();
17 |
--------------------------------------------------------------------------------
/frontend/app/custom-hooks/isMobileView.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | export const determineIsMobileView = (): boolean => {
4 | const [isMobileView, setIsMobileView] = useState(
5 | window.innerWidth <= 768,
6 | );
7 |
8 | useEffect(() => {
9 | const handleResize = (): void => setIsMobileView(window.innerWidth <= 1024);
10 |
11 | window.addEventListener('resize', handleResize);
12 | handleResize(); // Initial check
13 |
14 | return () => window.removeEventListener('resize', handleResize);
15 | }, []);
16 |
17 | return isMobileView;
18 | };
19 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/query-config/query-config.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { QueryConfigService } from './query-config.service';
3 | import { QueryConfigController } from './query-config.controller';
4 | import { QueryService } from '../query/query.service';
5 | import { QueryRepo } from '../query/query.repo';
6 | import { QueryConfigRepo } from './query-config.repo';
7 |
8 | @Module({
9 | providers: [QueryConfigService, QueryConfigRepo, QueryService, QueryRepo],
10 | controllers: [QueryConfigController],
11 | })
12 | export class QueryConfigModule {}
13 |
--------------------------------------------------------------------------------
/database/datamodels/singe_entity_multi_attributes.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": [
3 | {
4 | "attrName": "soilMoistureVwc_tiefe_10cm",
5 | "values": [105, 104, 103]
6 | },
7 | {
8 | "attrName": "soilMoistureVwc_tiefe_20cm",
9 | "values": [104, 104, 104]
10 | },
11 | {
12 | "attrName": "soilMoistureVwc_tiefe_30cm",
13 | "values": [106, 106, 105]
14 | }
15 | ],
16 | "entityId": "urn:ngsi-ld:GreenspaceRecord:DWDNeheim",
17 | "index": [
18 | "2025-03-02T00:00:00.000+00:00",
19 | "2025-03-03T00:00:00.000+00:00",
20 | "2025-03-04T00:00:00.000+00:00"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/mail-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "mail-service" "enabled" }}
2 | # Env vars specific for the mail-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: mail-service-configmap
7 | data:
8 | # Mailer Variables
9 | DEFECT_REPORT_EMAIL: "smart.city@edag.com"
10 | # SMTP server configuration
11 | MAIL_HOST: "smtp.gmail.com" # e.g. Gmail smtp server
12 | MAIL_PORT: "465"
13 | MAIL_USER: "example-email@gmail.com"
14 | MAIL_PASS: "Example Gmail App Password" # e.g. secret can be generated under App Passwords in a gmail account
15 | {{- end }}
16 |
--------------------------------------------------------------------------------
/frontend/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/usi-platform-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "usi-platform-service" "enabled" }}
2 | # Env vars specific for the dashboard-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: usi-platform-service-configmap
7 | data:
8 | NODE_ENV: "development"
9 | NEXT_PUBLIC_FRONTEND_URL: {{ index .Values "usi-platform-service" "frontendUrl" }}
10 | USI_API_URL: "https://nfk-s.urbanpulse.de/UrbanPulseManagement"
11 | USI_AUTH_URL: "https://nfk-s-auth.urbanpulse.de/auth/realms/ui/protocol/openid-connect/token"
12 | USI_CLIENT_ID: "edag"
13 |
14 | {{- end }}
15 |
--------------------------------------------------------------------------------
/frontend/ui/PageHeadline.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | type PageHeadlineProps = {
4 | headline: string;
5 | fontColor?: string;
6 | fontSize?: string;
7 | isHeadlineBold?: boolean;
8 | };
9 |
10 | export default function PageHeadline(props: PageHeadlineProps): ReactElement {
11 | const { headline, fontColor, fontSize, isHeadlineBold = true } = props;
12 |
13 | return (
14 |
18 | {headline}
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/orchideo-connect-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "orchideo-connect-service" "enabled" }}
2 | # Env vars specific for the dashboard-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: orchideo-connect-service-configmap
7 | data:
8 | NODE_ENV: "development"
9 | ORCHIDEO_CONNECT_MANDATORS_URL: "https://dev-iot-dash.swu.de/consumer-api/v1"
10 | ORCHIDEO_CONNECT_KEYCLOAK_URL: "https://dev-iot-dash.swu.de/auth/realms/ocon/protocol/openid-connect/token"
11 | NEXT_PUBLIC_FRONTEND_URL: {{ index .Values "orchideo-connect-service" "frontendUrl" }}
12 | {{- end }}
13 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/logging/test/test-util.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'node:fs';
2 | import * as path from 'node:path';
3 |
4 | export function deleteLogPath(): void {
5 | const logPath = process.env.LOG_PATH;
6 | const logDirectory = path.join(path.resolve(), logPath);
7 |
8 | if (fs.existsSync(logDirectory)) {
9 | fs.rmSync(logDirectory, { recursive: true });
10 | }
11 | }
12 |
13 | export function isLogFileExisting(): boolean {
14 | const logPath = process.env.LOG_PATH;
15 | const logDirectory = path.join(path.resolve(), logPath, '/dashboard.log');
16 |
17 | return fs.existsSync(logDirectory);
18 | }
19 |
--------------------------------------------------------------------------------
/database/migrations/generated/0005_moaning_betty_ross_0.9.0.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "auth_data" ALTER COLUMN "client_secret" SET DATA TYPE jsonb using to_jsonb(client_secret);--> statement-breakpoint
2 | ALTER TABLE "auth_data" ALTER COLUMN "app_user_password" SET DATA TYPE jsonb using to_jsonb(app_user_password);--> statement-breakpoint
3 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "menu_active_font_color" text;--> statement-breakpoint
4 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "use_color_transition_header" boolean;--> statement-breakpoint
5 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "use_color_transition_menu" boolean;
6 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/api-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "api-service" "enabled" }}
2 | # Env vars specific for the dashboard-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: api-service-configmap
7 | data:
8 | NODE_ENV: "development"
9 | API_MANDATORS_URL: "https://dev-iot-dash.swu.de/consumer-api/v1"
10 | API_KEYCLOAK_URL: "https://dev-iot-dash.swu.de/auth/realms/ocon/protocol/openid-connect/token"
11 | API_CLIENT_ID: "orchideo-connect-app"
12 | API_GRANT_TYPE: "password"
13 | NEXT_PUBLIC_FRONTEND_URL: {{ index .Values "api-service" "frontendUrl" }}
14 | {{- end }}
15 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/postgresql-connection-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if or (index .Values "dashboard-service" "enabled") (index .Values "orchideo-connect-service" "enabled") (index .Values "ngsi-service" "enabled") (index .Values "migrations" "enabled")}}
2 | # Env vars specific for establishing connection between microservice and postgresql db
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: postgresql-connection-configmap
7 | data:
8 | POSTGRES_USER: "postgres"
9 | POSTGRES_DB: "scs"
10 | POSTGRES_HOST: {{ .Release.Name }}-postgresql
11 | POSTGRES_PORT: "5432"
12 | POSTGRES_REJECT_UNAUTHORIZED: "true"
13 | {{- end }}
14 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { DashboardServiceModule } from './dashboard-service.module';
3 | import { json } from 'express';
4 |
5 | async function bootstrap(): Promise {
6 | const app = await NestFactory.create(DashboardServiceModule, {
7 | logger: ['error', 'warn', 'log'],
8 | });
9 |
10 | app.enableCors({
11 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
12 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
13 | credentials: true,
14 | });
15 | app.use(json({ limit: '5mb' }));
16 | await app.listen(8081);
17 | }
18 |
19 | bootstrap();
20 |
--------------------------------------------------------------------------------
/microservices/apps/api-service/src/schedule.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { ApiService } from './api.service';
4 | import { ReportService } from './report/report.service';
5 |
6 | @Injectable()
7 | export class ScheduleService {
8 | constructor(
9 | private readonly apiService: ApiService,
10 | private readonly reportService: ReportService,
11 | ) {}
12 |
13 | @Cron('0 * * * * *') // every minute
14 | async runSchedule(): Promise {
15 | await this.apiService.updateQueries();
16 | await this.reportService.updateReportData();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/microservices/apps/static-data-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { ScheduleService } from './schedule.service';
3 | import { StaticDataModule } from './static-data.module';
4 |
5 | async function bootstrap(): Promise {
6 | const app = await NestFactory.create(StaticDataModule);
7 |
8 | app.enableCors({
9 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
10 | methods: 'GET',
11 | credentials: true,
12 | });
13 |
14 | const schedulerService = app.get(ScheduleService);
15 |
16 | await schedulerService.runSchedule();
17 |
18 | await app.listen(8087);
19 | }
20 |
21 | bootstrap();
22 |
--------------------------------------------------------------------------------
/frontend/ui/Tooltip.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | type TooltipProps = {
4 | text: string;
5 | children: ReactElement;
6 | };
7 |
8 | const Tooltip = ({ text, children }: TooltipProps): ReactElement => {
9 | return (
10 |
11 | {children}
12 |
13 | {text}
14 |
15 |
16 | );
17 | };
18 |
19 | export default Tooltip;
20 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/tenant.system-user.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
3 | import { jsonb } from 'drizzle-orm/pg-core';
4 |
5 | export const systemUsers = pgTable('system_user', {
6 | id: uuid('id')
7 | .primaryKey()
8 | .default(sql`gen_random_uuid()`),
9 | tenantAbbreviation: text('tenant_abbreviation').notNull().unique(),
10 | username: text('username').notNull(),
11 | password: jsonb('password').notNull(),
12 | });
13 |
14 | export type SystemUser = typeof systemUsers.$inferSelect;
15 | export type NewSystemUser = typeof systemUsers.$inferInsert;
16 |
--------------------------------------------------------------------------------
/k8s/helm/templates/secrets/postgresql-connection-secrets.yaml:
--------------------------------------------------------------------------------
1 | {{- if or (index .Values "dashboard-service" "enabled") (index .Values "orchideo-connect-service" "enabled") (index .Values "ngsi-service" "enabled") (index .Values "migrations" "enabled") }}
2 | # Encoded env vars specific for establishing connection between microservice and postgres
3 | apiVersion: v1
4 | kind: Secret
5 | metadata:
6 | name: postgresql-connection-secrets
7 | data:
8 | POSTGRES_PASSWORD: cG9zdGdyZXM=
9 | PGPASSWORD: cG9zdGdyZXM= # PGPASSWORD = POSTGRES_PASSWORD - PGPASSWORD is a postgres defined value which sets the password for the psql command in Dockerfile.migrations
10 | {{- end }}
11 |
--------------------------------------------------------------------------------
/k8s/helm/values-infrastructure.yaml:
--------------------------------------------------------------------------------
1 | ## for only installing postgres & keycloak
2 | dashboard-service:
3 | enabled: false
4 |
5 | ngsi-service:
6 | enabled: false
7 |
8 | orchideo-connect-service:
9 | enabled: false
10 |
11 | mail-service:
12 | enabled: false
13 |
14 | infopin-service:
15 | enabled: false
16 |
17 | static-data-service:
18 | enabled: false
19 |
20 | report-service:
21 | enabled: false
22 |
23 | usi-platform-service:
24 | enabled: false
25 |
26 | data-translation-service:
27 | enabled: true
28 | service:
29 | enabled: false # Disable service creation since no endpoints are needed
30 |
31 | frontend:
32 | enabled: false
33 |
--------------------------------------------------------------------------------
/microservices/apps/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testMatch": ["/index.e2e-spec.ts"],
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | },
9 | "moduleNameMapper": {
10 | "@app/postgres-db/(.*)": "/../../libs/postgres-db/src/$1",
11 | "@app/postgres-db": "/../../libs/postgres-db/src",
12 | "@app/auth-helper/(.*)": "/../../libs/auth-helper/src/$1",
13 | "@app/auth-helper": "/../../libs/auth-helper/src",
14 | "apps/ngsi-service/src/(.*)": "/../../../apps/ngsi-service/src/$1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/frontend/providers/TanStackQueryProvider.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { ReactElement, useState } from 'react';
4 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5 |
6 | export default function TanStackQueryProvider({
7 | children,
8 | }: {
9 | children: React.ReactNode;
10 | }): ReactElement {
11 | const [queryClient] = useState(
12 | () =>
13 | new QueryClient({
14 | defaultOptions: {
15 | queries: {
16 | staleTime: 0,
17 | },
18 | },
19 | }),
20 | );
21 |
22 | return (
23 | {children}
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/ui/WizardSelectBox.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | type WizardSelectBoxProps = {
4 | label: string;
5 | checked: boolean;
6 | onChange: (checked: boolean) => void;
7 | };
8 |
9 | export default function WizardSelectBox(
10 | props: WizardSelectBoxProps,
11 | ): ReactElement {
12 | const { label, checked, onChange } = props;
13 |
14 | return (
15 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { NgsiModule } from './ngsi.module';
3 | import { ScheduleService } from './scheduler.service';
4 |
5 | async function bootstrap(): Promise {
6 | const app = await NestFactory.create(NgsiModule, {
7 | logger: ['error', 'warn', 'log'],
8 | });
9 |
10 | app.enableCors({
11 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
12 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
13 | credentials: true,
14 | });
15 |
16 | const schedulerService = app.get(ScheduleService);
17 | await schedulerService.runSchedule();
18 | await app.listen(8082);
19 | }
20 | bootstrap();
21 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/schedule.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { Cron } from '@nestjs/schedule';
3 | import { OrchideoConnectService } from './api.service';
4 | import { ReportService } from './report/report.service';
5 |
6 | @Injectable()
7 | export class ScheduleService {
8 | constructor(
9 | private readonly apiService: OrchideoConnectService,
10 | private readonly reportService: ReportService,
11 | ) {}
12 |
13 | @Cron('0 * * * * *') // every minute
14 | async runSchedule(): Promise {
15 | await this.apiService.updateQueries();
16 | await this.reportService.updateReportData();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/data-source.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
3 | import { authData } from './auth-data.schema';
4 |
5 | export const dataSources = pgTable('data_source', {
6 | id: uuid('id')
7 | .primaryKey()
8 | .default(sql`gen_random_uuid()`),
9 | authDataId: uuid('auth_data_id').references(() => authData.id),
10 | name: text('name').notNull(),
11 | collections: text('collections').array(),
12 | origin: text('origin').notNull(),
13 | });
14 |
15 | export type DataSource = typeof dataSources.$inferSelect;
16 | export type NewDataSource = typeof dataSources.$inferInsert;
17 |
--------------------------------------------------------------------------------
/frontend/ui/WizardFileUpload.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { ReactElement } from 'react';
3 |
4 | type WizardFileUploadProps = {
5 | setFile: (file: File | undefined) => void;
6 | };
7 |
8 | export default function WizardFileUpload(
9 | props: WizardFileUploadProps,
10 | ): ReactElement {
11 | const { setFile } = props;
12 |
13 | return (
14 |
15 | setFile(e.target.files?.[0])}
19 | accept=".jpg, .jpeg, .png"
20 | className="p-3 text-white text-base bg-transparent h-full w-full"
21 | />
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/utils/dragDropHelper.ts:
--------------------------------------------------------------------------------
1 | import { Dashboard, GroupingElement } from '@/types';
2 |
3 | export function reorderList(
4 | list: GroupingElement[],
5 | startIndex: number,
6 | endIndex: number,
7 | ): GroupingElement[] {
8 | const result = Array.from(list);
9 | const [removed] = result.splice(startIndex, 1);
10 | result.splice(endIndex, 0, removed);
11 |
12 | return result;
13 | }
14 |
15 | export function reorderDashboardList(
16 | list: Dashboard[],
17 | startIndex: number,
18 | endIndex: number,
19 | ): Dashboard[] {
20 | const result = Array.from(list);
21 | const [removed] = result.splice(startIndex, 1);
22 | result.splice(endIndex, 0, removed);
23 |
24 | return result;
25 | }
26 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/ngsi.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Param, Query } from '@nestjs/common';
2 | import { NgsiService } from './ngsi.service';
3 | import { ChartData } from 'apps/dashboard-service/src/dashboard/dashboard.service';
4 |
5 | @Controller('ngsi')
6 | export class NgsiController {
7 | constructor(private readonly ngsiService: NgsiService) {}
8 |
9 | @Get('on-demand-data/:queryId')
10 | async getOnDemandData(
11 | @Param('queryId') queryId: string,
12 | @Query('entityId') entityId: string,
13 | @Query('attribute') attribute: string,
14 | ): Promise {
15 | return await this.ngsiService.getOnDemandData(queryId, entityId, attribute);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docker-compose.local.yml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 | services:
3 | keycloak:
4 | container_name: keycloak
5 | image: quay.io/keycloak/keycloak:22.0
6 | env_file:
7 | - .env
8 | ports:
9 | - "${KEYCLOAK_PORT}:8080"
10 | # only for development: Import realm on startup
11 | volumes:
12 | - ./keycloak/import:/opt/keycloak/data/import/
13 | entrypoint: [ "/opt/keycloak/bin/kc.sh", "start-dev --import-realm" ]
14 | postgres-dev:
15 | container_name: postgres-dev
16 | image: postgres
17 | environment:
18 | POSTGRES_USER: ${POSTGRES_USER}
19 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
20 | POSTGRES_DB: ${POSTGRES_DB}
21 | ports:
22 | - '${POSTGRES_PORT}:5432'
23 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/data-model/fiware-models/parking-info.model.ts:
--------------------------------------------------------------------------------
1 | export class ParkingInfoModel {
2 | name: string;
3 | maxHeight: number;
4 | capacity: number;
5 | location: {
6 | type: string;
7 | coordinates: number[];
8 | };
9 | currentlyUsed: number;
10 | maxValue: number;
11 | type: string;
12 | address?: {
13 | id: string;
14 | street: string;
15 | streetnumber: string;
16 | zipcode: string;
17 | city: string;
18 | breitengrad: number;
19 | laengengrad: number;
20 | breitengradDisplayInternational: string;
21 | laengengradDisplayInternational: string;
22 | breitengradDisplay: string;
23 | laengengradDisplay: string;
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/api/dataSource-service.ts:
--------------------------------------------------------------------------------
1 | import { DataSource } from '@/types';
2 | import { env } from 'next-runtime-env';
3 |
4 | const NEXT_PUBLIC_BACKEND_URL = env('NEXT_PUBLIC_BACKEND_URL');
5 |
6 | export async function getDataSourcesByTenant(
7 | accessToken: string | undefined,
8 | tenant: string | undefined,
9 | ): Promise {
10 | const headers = accessToken
11 | ? { Authorization: `Bearer ${accessToken}` }
12 | : undefined;
13 | const fetched = await fetch(
14 | `${NEXT_PUBLIC_BACKEND_URL}/data-sources/tenant/${tenant}`,
15 | {
16 | headers,
17 | },
18 | )
19 | .then((res) => res.json())
20 | .catch((err) => {
21 | console.error(err);
22 | });
23 | return fetched;
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/ui/NoDataWarning.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | import DashboardIcons from '@/ui/Icons/DashboardIcon';
4 |
5 | type NoDataWarningProps = {
6 | iconColor: string;
7 | fontColor: string;
8 | };
9 |
10 | export default function NoDataWarning(props: NoDataWarningProps): ReactElement {
11 | const { iconColor, fontColor } = props;
12 |
13 | return (
14 |
17 |
18 |
19 | Keine Datenverbindung{' '}
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/defect/defect.module.ts:
--------------------------------------------------------------------------------
1 | import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
2 | import { DefectController } from './defect.controller';
3 | import { DefectService } from './defect.service';
4 | import { AuthHelperMiddleware } from '@app/auth-helper';
5 |
6 | @Module({
7 | controllers: [DefectController],
8 | providers: [DefectService],
9 | })
10 | export class DefectModule {
11 | configure(consumer: MiddlewareConsumer): void {
12 | // Apply the AuthHelperMiddleware to protect specific routes
13 | consumer.apply(AuthHelperMiddleware).forRoutes(
14 | { path: 'defects*', method: RequestMethod.ALL }, // Protect all methods in the "defects" route
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/report/report.module.ts:
--------------------------------------------------------------------------------
1 | import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
2 | import { ReportService } from './report.service';
3 | import { ReportController } from './report.controller';
4 | import { AuthHelperMiddleware } from '@app/auth-helper';
5 |
6 | @Module({
7 | providers: [ReportService],
8 | controllers: [ReportController],
9 | })
10 | export class ReportModule {
11 | configure(consumer: MiddlewareConsumer): void {
12 | // Apply the AuthHelperMiddleware to protect specific routes
13 | consumer.apply(AuthHelperMiddleware).forRoutes(
14 | { path: 'reports*', method: RequestMethod.ALL }, // Protect all methods in the "dashboards" route
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/database/migrations/generated/0002_powerful_tarantula_0.8.0_to_0.8.3.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "auth_data" ADD COLUMN IF NOT EXISTS "tenant_abbreviation" text;--> statement-breakpoint
2 | ALTER TABLE "auth_data" ADD COLUMN IF NOT EXISTS "read_roles" text[];--> statement-breakpoint
3 | ALTER TABLE "auth_data" ADD COLUMN IF NOT EXISTS "write_roles" text[];--> statement-breakpoint
4 | ALTER TABLE "auth_data" ADD COLUMN IF NOT EXISTS "visibility" "visibility";--> statement-breakpoint
5 | DO $$ BEGIN
6 | ALTER TABLE "auth_data" ADD CONSTRAINT "auth_data_tenant_abbreviation_tenant_abbreviation_fk" FOREIGN KEY ("tenant_abbreviation") REFERENCES "tenant"("abbreviation") ON DELETE no action ON UPDATE no action;
7 | EXCEPTION
8 | WHEN duplicate_object THEN null;
9 | END $$;
10 |
--------------------------------------------------------------------------------
/microservices/apps/test/jwt-token-util.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export async function generateJWTToken(
4 | clientId: string,
5 | clientSecret: string,
6 | ): Promise {
7 | let jwtToken: string;
8 | // Get JWT token
9 | const authUrl = process.env.KEYCLOAK_CLIENT_URI;
10 |
11 | const data = new URLSearchParams();
12 | data.append('client_id', clientId);
13 | data.append('client_secret', clientSecret);
14 | data.append('grant_type', 'client_credentials');
15 |
16 | try {
17 | await process.nextTick(() => {});
18 | const res = await axios.post(authUrl, data);
19 | jwtToken = res.data.access_token;
20 | } catch (error) {
21 | console.error('Error occurred:', error);
22 | }
23 |
24 | return jwtToken;
25 | }
26 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/src/usi-platform.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PostgresDbModule } from '@app/postgres-db';
3 | import { AuthService } from './auth/auth.service';
4 | import { HttpModule } from '@nestjs/axios';
5 | import { AuthModule } from './auth/auth.module';
6 | import { UsiPlaformService } from './usi-platform.service';
7 | import { UsiPlatformController } from './usi-platform.controller';
8 | import { QueryConfigService } from './query-config/query-config.service';
9 |
10 | @Module({
11 | imports: [AuthModule, HttpModule, PostgresDbModule],
12 | providers: [UsiPlaformService, AuthService, QueryConfigService],
13 | controllers: [UsiPlatformController],
14 | })
15 | export class UsiPlatformModule {}
16 |
--------------------------------------------------------------------------------
/database/datamodels/LD_1e1a.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "urn:ngsi-ld:WeatherObserved:DWD-Weather-KOPENHAGEN",
3 | "type": "WeatherObserved",
4 | "temperature": {
5 | "type": "Property",
6 | "avg": [
7 | [
8 | 13.984615384615385,
9 | "2025-05-27T15:25:40.263Z",
10 | "2025-05-27T16:25:40.263Z"
11 | ],
12 | [
13 | 14.08333333333333,
14 | "2025-05-27T16:25:40.263Z",
15 | "2025-05-27T17:25:40.263Z"
16 | ],
17 | [
18 | 13.891666666666667,
19 | "2025-05-27T17:25:40.263Z",
20 | "2025-05-27T18:25:40.263Z"
21 | ],
22 | [
23 | 13.508333333333335,
24 | "2025-05-27T18:25:40.263Z",
25 | "2025-05-27T19:25:40.263Z"
26 | ]
27 | ]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/cypress/support/e2e.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/e2e.ts is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands';
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/logo.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { pgTable, smallint, text, uuid } from 'drizzle-orm/pg-core';
3 | import { tenants } from './tenant.schema';
4 |
5 | export const logos = pgTable('logo', {
6 | id: uuid('id')
7 | .primaryKey()
8 | .default(sql`gen_random_uuid()`),
9 | tenantId: text('tenant_id').references(() => tenants.abbreviation, {
10 | onUpdate: 'cascade',
11 | }),
12 | logo: text('logo'),
13 | logoHeight: smallint('logo_height'),
14 | logoWidth: smallint('logo_width'),
15 | logoName: text('logo_name'),
16 | format: text('format'),
17 | size: text('size'),
18 | });
19 |
20 | export type Logo = typeof logos.$inferSelect;
21 | export type NewLogo = typeof logos.$inferInsert;
22 |
--------------------------------------------------------------------------------
/database/migrations/generated/0012_peaceful_galactus_0.12.0b.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS "general_settings" (
2 | "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3 | "tenant" text,
4 | "information" text,
5 | "imprint" text,
6 | "privacy" text
7 | );
8 | --> statement-breakpoint
9 | ALTER TABLE "dashboard" ADD COLUMN IF NOT EXISTS "allow_data_export" boolean;--> statement-breakpoint
10 | ALTER TABLE "widget" ADD COLUMN IF NOT EXISTS "allow_data_export" boolean;--> statement-breakpoint
11 | DO $$ BEGIN
12 | ALTER TABLE "general_settings" ADD CONSTRAINT "general_settings_tenant_tenant_abbreviation_fk" FOREIGN KEY ("tenant") REFERENCES "tenant"("abbreviation") ON DELETE no action ON UPDATE no action;
13 | EXCEPTION
14 | WHEN duplicate_object THEN null;
15 | END $$;
16 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/corporate-info/corporate-info.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { CorporateInfoService } from './corporate-info.service';
3 | import { CorporateInfoController } from './corporate-info.controller';
4 | import { CorporateInfoRepo } from './corporate-info.repo';
5 | import { CorporateInfoSidebarLogosRepo } from './corporate-info-sidebar-logos.repo';
6 | import { LogoRepo } from '../logo/logo.repo';
7 | import { TenantRepo } from '../tenant/tenant.repo';
8 |
9 | @Module({
10 | providers: [
11 | CorporateInfoService,
12 | CorporateInfoRepo,
13 | CorporateInfoSidebarLogosRepo,
14 | LogoRepo,
15 | TenantRepo,
16 | ],
17 | controllers: [CorporateInfoController],
18 | })
19 | export class CorporateInfoModule {}
20 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"],
23 | "@cypress/*": ["./cypress/*"]
24 | }
25 | },
26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "cypress/support/index.d.ts"],
27 | "exclude": ["node_modules"]
28 | }
29 |
--------------------------------------------------------------------------------
/microservices/apps/static-data-service/src/static-data.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ScheduleService } from './schedule.service';
3 | import { ScheduleModule } from '@nestjs/schedule';
4 | import { DataModule } from '../../ngsi-service/src/data/data.module';
5 | import { DataService } from './data/data.service';
6 | import { QueryModule } from './query/query.module';
7 | import { QueryService } from './query/query.service';
8 | import { TransformationModule } from './transformation/transformation.module';
9 |
10 | @Module({
11 | imports: [
12 | DataModule,
13 | ScheduleModule.forRoot(),
14 | QueryModule,
15 | TransformationModule,
16 | ],
17 | providers: [DataService, ScheduleService, QueryService],
18 | })
19 | export class StaticDataModule {}
20 |
--------------------------------------------------------------------------------
/database/migrations/generated/0016_nasty_the_renegades_0.17.0.sql:
--------------------------------------------------------------------------------
1 | DO $$ BEGIN
2 | CREATE TYPE "menu_arrow_direction_enum" AS ENUM('Oben | Oben', 'Oben | Unten', 'Links | Links', 'Links | Rechts', 'Unten | Unten', 'Unten | Oben', 'Rechts | Rechts', 'Rechts | Links');
3 | EXCEPTION
4 | WHEN duplicate_object THEN null;
5 | END $$;
6 | --> statement-breakpoint
7 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "is_panel_headline_bold" boolean;--> statement-breakpoint
8 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "is_widget_headline_bold" boolean;--> statement-breakpoint
9 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "menu_arrow_direction" "menu_arrow_direction_enum";--> statement-breakpoint
10 | ALTER TABLE "panel" ADD COLUMN IF NOT EXISTS "open_jumpoff_link_in_new_tab" boolean;
11 |
--------------------------------------------------------------------------------
/frontend/api/wizard-service-usi-platform.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { env } from 'next-runtime-env';
3 |
4 | const NEXT_PUBLIC_USI_PLATFORM_SERVICE_URL = env(
5 | 'NEXT_PUBLIC_USI_PLATFORM_SERVICE_URL',
6 | );
7 |
8 | export type UsiEventType = {
9 | name: string;
10 | sensors: string[];
11 | attributes: string[];
12 | };
13 |
14 | export async function getEventtypes(
15 | args: string | undefined,
16 | ): Promise {
17 | try {
18 | const response = await axios.get(
19 | `${NEXT_PUBLIC_USI_PLATFORM_SERVICE_URL}/usi-platform/event-types`,
20 | {
21 | params: {
22 | apiid: args,
23 | },
24 | },
25 | );
26 | return response.data;
27 | } catch (err) {
28 | console.error(err);
29 | throw err;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/database/datamodels/map_format.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "urn:ngsi-ld:VehicleCounter:VS-6",
4 | "location": {
5 | "value": {
6 | "coordinates": [7.582028, 51.205861]
7 | }
8 | },
9 | "name": {
10 | "value": "Volmestraße B54 Höhe Am Wasserturm"
11 | },
12 | "total_vehicles": {
13 | "value": 12
14 | },
15 | "auto": {
16 | "value": 12
17 | }
18 | },
19 | {
20 | "id": "urn:ngsi-ld:VehicleCounter:VS-11",
21 | "location": {
22 | "value": {
23 | "coordinates": [7.608917, 51.237694]
24 | }
25 | },
26 | "name": {
27 | "value": "Heedfelder Straße Höhe Brockhauser Weg"
28 | },
29 | "total_vehicles": {
30 | "value": 41
31 | },
32 | "auto": {
33 | "value": 39
34 | }
35 | }
36 | ]
37 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/general-settings.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core';
3 | import { tenants } from './tenant.schema';
4 |
5 | export const generalSettings = pgTable('general_settings', {
6 | id: uuid('id')
7 | .primaryKey()
8 | .default(sql`gen_random_uuid()`),
9 | tenant: text('tenant').references(() => tenants.abbreviation),
10 | information: text('information'),
11 | imprint: text('imprint'),
12 | privacy: text('privacy'),
13 | allowThemeSwitching: boolean('allow_theme_switching').default(false),
14 | disclaimer: text('disclaimer'),
15 | });
16 |
17 | export type GeneralSettings = typeof generalSettings.$inferSelect;
18 | export type NewGeneralSettings = typeof generalSettings.$inferInsert;
19 |
--------------------------------------------------------------------------------
/microservices/apps/usi-platform-service/src/usi-platform.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Query } from '@nestjs/common';
2 | import {
3 | QueryConfigService,
4 | UsiEventType,
5 | } from './query-config/query-config.service';
6 |
7 | @Controller('usi-platform')
8 | export class UsiPlatformController {
9 | constructor(private readonly queryConfigService: QueryConfigService) {}
10 |
11 | @Get('event-types')
12 | async getEventTypes(@Query('apiId') apiId?: string): Promise {
13 | return this.queryConfigService.getEventTypes(apiId);
14 | }
15 |
16 | @Get('sensors')
17 | async getSensors(
18 | @Query('eventType') eventType: string,
19 | @Query('apiId') apiId?: string,
20 | ): Promise {
21 | return this.queryConfigService.getSensors(eventType, apiId);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/system-user/test/test-data.ts:
--------------------------------------------------------------------------------
1 | import { DbType } from '@app/postgres-db';
2 | import {
3 | SystemUser,
4 | systemUsers,
5 | } from '@app/postgres-db/schemas/tenant.system-user.schema';
6 | import { v4 as uuid } from 'uuid';
7 |
8 | export function getSystemUser(): SystemUser {
9 | return {
10 | id: '',
11 | tenantAbbreviation: 'edag',
12 | username: 'testUser',
13 | password: 'admin',
14 | };
15 | }
16 |
17 | export async function createSystemUser(
18 | db: DbType,
19 | systemUser: SystemUser,
20 | ): Promise {
21 | systemUser.id = uuid();
22 | const createdSystemUser = await db
23 | .insert(systemUsers)
24 | .values(systemUser)
25 | .returning();
26 |
27 | return createdSystemUser.length > 0 ? createdSystemUser[0] : null;
28 | }
29 |
--------------------------------------------------------------------------------
/microservices/apps/api-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { ApiModule } from './api.module';
3 | import { ScheduleService } from './schedule.service';
4 | import { OrganisationScheduleService } from './organisation-schedule.service';
5 |
6 | async function bootstrap(): Promise {
7 | const app = await NestFactory.create(ApiModule);
8 |
9 | app.enableCors({
10 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
11 | methods: 'GET',
12 | credentials: true,
13 | });
14 |
15 | const schedulerService = app.get(ScheduleService);
16 | const organisationSchedulerService = app.get(OrganisationScheduleService);
17 |
18 | await schedulerService.runSchedule();
19 | await organisationSchedulerService.runSchedule();
20 |
21 | await app.listen(8083);
22 | }
23 |
24 | bootstrap();
25 |
--------------------------------------------------------------------------------
/database/migrations/generated/0006_short_peter_quill_0.9.1.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE "tab_component_sub_type" ADD VALUE IF NOT EXISTS 'Farbiger Slider';--> statement-breakpoint
2 | ALTER TYPE "tab_component_type" ADD VALUE IF NOT EXISTS 'Slider';--> statement-breakpoint
3 | ALTER TABLE "system_user" ALTER COLUMN "password" SET DATA TYPE jsonb using to_jsonb(password);;--> statement-breakpoint
4 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "chart_static_values_ticks" real[];--> statement-breakpoint
5 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "chart_static_values_logos" text[];--> statement-breakpoint
6 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "chart_static_values_texts" text[];--> statement-breakpoint
7 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "label_color" text;--> statement-breakpoint
8 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "map_widget_values" json;
9 |
--------------------------------------------------------------------------------
/frontend/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | *, *::before, *::after {
6 | box-sizing: border-box;
7 | }
8 |
9 | /* global scrollbar */
10 | ::-webkit-scrollbar {
11 | width: 0.4em;
12 | }
13 | ::-webkit-scrollbar-track {
14 | background: #FFFFFF00;
15 | }
16 | ::-webkit-scrollbar-thumb {
17 | border-radius: 10px;
18 | background: #59647D;
19 | }
20 | ::-webkit-scrollbar-thumb:hover {
21 | background: #59647D;
22 | }
23 | .child-node::before {
24 | content: '';
25 | position: absolute;
26 | top: 50%;
27 | left:-9.1%;
28 | height: 2px;
29 | background-color: #C7D2EE;
30 | width: var(--child-node-before-width, 45px); /* Fallback to 45px if the variable is not defined */
31 | transform: translateY(-50%);
32 |
33 | }
34 |
35 | body, html {
36 | height: 100%;
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/assets/icons/ForestFire.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | type SVGProps = {
3 | fontColor?: string;
4 | height?: string;
5 | };
6 |
7 | export default function ForestFire(props: SVGProps): ReactElement {
8 | const { fontColor, height = 24 } = props;
9 | return (
10 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/components/Map/SetViewToBounds.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import L from 'leaflet';
3 | import { useMap } from 'react-leaflet';
4 |
5 | export default function SetViewToBounds({
6 | markerPositions,
7 | centerPosition,
8 | }: {
9 | markerPositions: { position: [number, number]; title: string }[];
10 | centerPosition: [number, number];
11 | }): null {
12 | const map = useMap();
13 |
14 | useEffect(() => {
15 | if (markerPositions.length > 0) {
16 | const bounds = L.latLngBounds(
17 | markerPositions.map(({ position }) => position),
18 | );
19 | bounds.extend(centerPosition); // Extend bounds to include the center position
20 |
21 | map.fitBounds(bounds);
22 | }
23 | }, [map]); // this effect will only run once when the component mounts
24 |
25 | return null;
26 | }
27 |
--------------------------------------------------------------------------------
/frontend/utils/tenantHelper.ts:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { env } from 'next-runtime-env';
4 | import { useParams } from 'next/navigation';
5 | import { jwtDecode } from 'jwt-decode';
6 |
7 | export function getTenantOfPage(): string | undefined {
8 | const params = useParams();
9 | const NEXT_PUBLIC_MULTI_TENANCY = env('NEXT_PUBLIC_MULTI_TENANCY');
10 | return NEXT_PUBLIC_MULTI_TENANCY === 'true'
11 | ? (params.tenant as string)
12 | : undefined;
13 | }
14 |
15 | export function isUserMatchingTenant(
16 | bearerToken: string,
17 | tenant: string,
18 | ): boolean {
19 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
20 | const decodedToken: any = jwtDecode(bearerToken);
21 |
22 | if (decodedToken?.mandator_code !== tenant) {
23 | return false;
24 | } else {
25 | return true;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/README.adoc:
--------------------------------------------------------------------------------
1 | = Readme for the orchideo-connect-service microservice
2 |
3 | == Description
4 |
5 | Short description about the dashboard service which provides the dashboard page structure and data
6 |
7 | == Environment Variables
8 |
9 | [options="header"]
10 | |===
11 | | Variable | Description | Example Value
12 | | `NEXT_PUBLIC_FRONTEND_URL` | Adress to the frontend. Required to set appropriate CORS settings | `http:localhost:3000`
13 | | `POSTGRES_USER` | The PostgreSQL database user. | `postgres`
14 | | `POSTGRES_PASSWORD` | The password for the PostgreSQL user. | `postgres`
15 | | `POSTGRES_DB` | The name of the PostgreSQL database. | `scs`
16 | | `POSTGRES_HOST` | The host address of the PostgreSQL server. | `127.0.0.1`
17 | | `POSTGRES_PORT` | The port on which the PostgreSQL server is running. | `5432`
18 | |===
19 |
--------------------------------------------------------------------------------
/frontend/ui/Buttons/GenericButton.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | type GenericButtonProps = {
4 | label: string;
5 | handleClick: () => void;
6 | fontColor: string;
7 | };
8 |
9 | export default function GenericButton(props: GenericButtonProps): ReactElement {
10 | const { label, handleClick, fontColor } = props;
11 |
12 | const fontStyle = {
13 | color: fontColor ?? 'bg-[#FFFFFF]',
14 | };
15 |
16 | return (
17 |
18 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/microservices/apps/orchideo-connect-service/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { OrchideoConnectModule } from './api.module';
3 | import { ScheduleService } from './schedule.service';
4 | import { OrganisationScheduleService } from './organisation-schedule.service';
5 |
6 | async function bootstrap(): Promise {
7 | const app = await NestFactory.create(OrchideoConnectModule);
8 |
9 | app.enableCors({
10 | origin: process.env.NEXT_PUBLIC_FRONTEND_URL,
11 | methods: 'GET',
12 | credentials: true,
13 | });
14 |
15 | const schedulerService = app.get(ScheduleService);
16 | const organisationSchedulerService = app.get(OrganisationScheduleService);
17 |
18 | await schedulerService.runSchedule();
19 | await organisationSchedulerService.runSchedule();
20 |
21 | await app.listen(8083);
22 | }
23 |
24 | bootstrap();
25 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/query/query.service.ts:
--------------------------------------------------------------------------------
1 | import { Inject, Injectable } from '@nestjs/common';
2 | import { DbType, POSTGRES_DB } from '@app/postgres-db';
3 | import { queries, Query } from '@app/postgres-db/schemas/query.schema';
4 | import { inArray } from 'drizzle-orm';
5 | import { SensorReport } from '@app/postgres-db/schemas/sensor-report.schema';
6 |
7 | @Injectable()
8 | export class QueryService {
9 | constructor(@Inject(POSTGRES_DB) private readonly db: DbType) {}
10 |
11 | async getQueriesForReportConfigs(reports: SensorReport[]): Promise {
12 | const queryIds = reports.map((r) => r.queryId);
13 |
14 | return this.getQueriesByIds(queryIds);
15 | }
16 |
17 | async getQueriesByIds(queryIds: string[]): Promise {
18 | return this.db.select().from(queries).where(inArray(queries.id, queryIds));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Dockerfile.migrations:
--------------------------------------------------------------------------------
1 | # Base image
2 | FROM node:20.13-alpine
3 |
4 | # Set the working directory in the container
5 | WORKDIR /app
6 |
7 | # Copy package.json and package-lock.json to the container
8 | COPY package*.json ./
9 |
10 | # Install the dependencies
11 | RUN npm ci
12 |
13 | # Install PostgreSQL client for running SQL script - NOTE: disconnect from company network as sometimes an issuer cert error occurs here
14 | RUN apk add --no-cache postgresql-client
15 |
16 | # Copy the required files and directories
17 | COPY .env ./
18 | COPY database ./database
19 | COPY microservices/libs/postgres-db ./microservices/libs/postgres-db
20 | COPY init_insert.sql ./
21 |
22 | # Run the database migrations and then execute the SQL script
23 | CMD sh -c "npm run db:gen && npm run db:migrate:dev && psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB -f /app/init_insert.sql"
24 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/climate-project/climate-project.module.ts:
--------------------------------------------------------------------------------
1 | import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
2 | import { ClimateProjectService } from './climate-project.service';
3 | import { ClimateProjectController } from './climate-project.controller';
4 | import { AuthHelperMiddleware } from '@app/auth-helper';
5 |
6 | @Module({
7 | providers: [ClimateProjectService],
8 | controllers: [ClimateProjectController],
9 | exports: [ClimateProjectService],
10 | })
11 | export class ClimateProjectModule {
12 | configure(consumer: MiddlewareConsumer): void {
13 | // Apply the AuthHelperMiddleware to protect specific routes
14 | consumer.apply(AuthHelperMiddleware).forRoutes(
15 | { path: 'climate-projects*', method: RequestMethod.ALL }, // Protect all methods in the "dashboards" route
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/report.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ReportService } from './report.service';
3 | import { ScheduleModule } from '@nestjs/schedule';
4 | import { ConfigModule } from './config/config.module';
5 | import { PostgresDbModule } from '@app/postgres-db';
6 | import { QueryModule } from './query/query.module';
7 | import { MailModule } from './mail/mail.module';
8 | import { ConfigService } from './config/config.service';
9 | import { QueryService } from './query/query.service';
10 | import { MailService } from './mail/mail.service';
11 |
12 | @Module({
13 | imports: [
14 | PostgresDbModule,
15 | ScheduleModule.forRoot(),
16 | ConfigModule,
17 | QueryModule,
18 | MailModule,
19 | ],
20 | providers: [ReportService, ConfigService, QueryService, MailService],
21 | })
22 | export class ReportModule {}
23 |
--------------------------------------------------------------------------------
/frontend/ui/BlockContainer.tsx:
--------------------------------------------------------------------------------
1 | import { CSSProperties, ReactElement } from 'react';
2 |
3 | type BlockStyle = {
4 | borderWidth: string;
5 | borderRadius: string;
6 | borderColor: string;
7 | fontColor: string;
8 | backgroundColor: string;
9 | };
10 |
11 | export default function BlockContainer({
12 | children,
13 | props,
14 | }: {
15 | children: React.ReactNode;
16 | props: BlockStyle;
17 | }): ReactElement {
18 | const blockStyle: CSSProperties = {
19 | borderWidth: props.borderWidth,
20 | borderRadius: props.borderRadius,
21 | borderColor: props.borderColor,
22 | color: props.fontColor,
23 | background: props.backgroundColor,
24 | };
25 | return (
26 |
30 | {' '}
31 | {children}
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/frontend/utils/apiHelper.ts:
--------------------------------------------------------------------------------
1 | import { deleteAuthData } from '@/api/authData-service';
2 | import { deleteDashboard } from '@/api/dashboard-service';
3 | import { deleteTenant } from '@/api/tenant-service';
4 | import { deleteWidget } from '@/api/widget-service';
5 |
6 | export async function deleteGenericItemById(
7 | accessToken: string | undefined,
8 | id: string,
9 | type: string,
10 | tenant?: string,
11 | ): Promise {
12 | switch (type) {
13 | case 'dashboard':
14 | await deleteDashboard(accessToken, id, tenant);
15 | break;
16 | case 'widget':
17 | await deleteWidget(accessToken, id);
18 | break;
19 | case 'AuthData':
20 | await deleteAuthData(accessToken, id);
21 | break;
22 | case 'tenant':
23 | await deleteTenant(accessToken, id);
24 | break;
25 | default:
26 | console.error('Unknown generic type');
27 | break;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/k8s/helm/templates/configmaps/dashboard-service-configmap.yaml:
--------------------------------------------------------------------------------
1 | {{- if index .Values "dashboard-service" "enabled" }}
2 | # Env vars specific for the dashboard-service nestjs microservice
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: dashboard-service-configmap
7 | data:
8 | NODE_ENV: "development"
9 | ADMIN_ROLE: {{ index .Values "dashboard-service" "adminRole" }}
10 | SUPER_ADMIN_ROLE: {{ index .Values "dashboard-service" "superAdminRole" }}
11 | EDIT_ROLES: {{ index .Values "dashboard-service" "editRoles" | toJson }}
12 | NEST_JWKS_URI: {{ index .Values "dashboard-service" "jwks" }}
13 | NEXT_PUBLIC_FRONTEND_URL: {{ index .Values "dashboard-service" "frontendUrl" }}
14 | PASSWORD_ENCRYPT_KEY: {{ index .Values "dashboard-service" "passwordEncryptKey" }}
15 | LOG_ROTATION_CRON: {{ index .Values "dashboard-service" "logRotationCron" }}
16 | LOG_PATH: {{ index .Values "dashboard-service" "logPath" }}
17 | {{- end }}
18 |
--------------------------------------------------------------------------------
/k8s/helm/charts/migrations/templates/job.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: {{ include "smart-city.fullname" . }}
5 | spec:
6 | template:
7 | spec:
8 | containers:
9 | - name: {{ .Chart.Name }}
10 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
11 | imagePullPolicy: {{ .Values.image.pullPolicy }}
12 | envFrom:
13 | - configMapRef:
14 | name: {{ .Values.configMap.name }}
15 | - secretRef:
16 | name: {{ .Values.secretRef.name }}
17 | resources:
18 | requests:
19 | memory: {{ .Values.resources.requests.memory }}
20 | cpu: {{ .Values.resources.requests.cpu }}
21 | limits:
22 | memory: {{ .Values.resources.limits.memory }}
23 | cpu: {{ .Values.resources.limits.cpu }}
24 | restartPolicy: {{ .Values.restartPolicy }}
25 |
--------------------------------------------------------------------------------
/k8s/helm/charts/data-translation-service/values.yaml:
--------------------------------------------------------------------------------
1 | replicaCount: 1
2 |
3 | image:
4 | repository: smartcity/data-translation-service
5 | pullPolicy: IfNotPresent
6 | tag: "latest"
7 |
8 | # Remove container ports since no HTTP endpoint is needed
9 | container: {}
10 |
11 | # Remove service configuration since no network service is needed
12 | service:
13 | enabled: false
14 |
15 | # Remove ingress since no external access is needed
16 | ingress:
17 | enabled: false
18 |
19 | # Helm Required Values
20 | imagePullSecrets: []
21 | nameOverride: ""
22 | fullnameOverride: ""
23 |
24 | autoscaling:
25 | enabled: false
26 | minReplicas: 1
27 | maxReplicas: 100
28 | targetCPUUtilizationPercentage: 80
29 |
30 | resources: {}
31 |
32 | serviceAccount:
33 | create: false
34 | annotations: {}
35 | name: ""
36 |
37 | podAnnotations: {}
38 |
39 | podSecurityContext: {}
40 |
41 | securityContext: {}
42 |
43 | common:
44 | enabled: true
45 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/fiware-wizard/fiware-wizard.module.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MiddlewareConsumer,
3 | Module,
4 | NestModule,
5 | RequestMethod,
6 | } from '@nestjs/common';
7 | import { FiwareWizardService } from './fiware-wizard.service';
8 | import { FiwareWizardController } from './fiware-wizard.controller';
9 | import { HttpModule } from '@nestjs/axios';
10 | import { AuthHelperMiddleware } from '@app/auth-helper';
11 |
12 | @Module({
13 | imports: [HttpModule],
14 | providers: [FiwareWizardService],
15 | controllers: [FiwareWizardController],
16 | })
17 | export class FiwareWizardModule implements NestModule {
18 | configure(consumer: MiddlewareConsumer): void {
19 | // Apply the AuthHelperMiddleware to protect specific routes
20 | consumer.apply(AuthHelperMiddleware).forRoutes(
21 | { path: 'fiwareWizard*', method: RequestMethod.ALL }, // Protect all methods in the "fiwareWizard" route
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/dashboard.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { boolean, pgEnum, pgTable, text, uuid } from 'drizzle-orm/pg-core';
3 |
4 | export const visibilityEnum = pgEnum('visibility', [
5 | 'public',
6 | 'protected',
7 | 'invisible',
8 | ]);
9 | export const dashboards = pgTable('dashboard', {
10 | id: uuid('id')
11 | .primaryKey()
12 | .default(sql`gen_random_uuid()`),
13 | name: text('name'),
14 | allowShare: boolean('allow_share'),
15 | allowDataExport: boolean('allow_data_export'),
16 | headlineColor: text('headline_color'),
17 | icon: text('icon'),
18 | readRoles: text('read_roles').array(),
19 | type: text('type'),
20 | url: text('url'),
21 | visibility: visibilityEnum('visibility'),
22 | writeRoles: text('write_roles').array(),
23 | });
24 |
25 | export type Dashboard = typeof dashboards.$inferSelect;
26 | export type NewDashboard = typeof dashboards.$inferInsert;
27 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/validators/widget-validator.pipe.ts:
--------------------------------------------------------------------------------
1 | import {
2 | PipeTransform,
3 | Injectable,
4 | HttpException,
5 | HttpStatus,
6 | } from '@nestjs/common';
7 | import { Widget } from '@app/postgres-db/schemas';
8 |
9 | @Injectable()
10 | export class ValidateWidgetPipe implements PipeTransform {
11 | transform(widget: Widget): Partial {
12 | const errorsOccured: string[] = [];
13 | if (widget.visibility === 'protected') {
14 | if (widget.readRoles.length === 0)
15 | errorsOccured.push('Leserechte müssen ausgefüllt werden!');
16 | if (widget.writeRoles.length === 0)
17 | errorsOccured.push('Schreibberechtigungen müssen ausgefüllt sein!');
18 | }
19 | if (errorsOccured.length > 0) {
20 | throw new HttpException(
21 | `Errors in widget: ${JSON.stringify(errorsOccured)}`,
22 | HttpStatus.BAD_REQUEST,
23 | );
24 | }
25 | return widget;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/frontend/assets/icons/Heat.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | type SVGProps = {
3 | fontColor?: string;
4 | height?: string;
5 | };
6 |
7 | export default function Heat(props: SVGProps): ReactElement {
8 | const { fontColor = '#fff', height = 24 } = props;
9 | return (
10 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/panel/panel.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PanelService } from './panel.service';
3 | import { PanelController } from './panel.controller';
4 | import { PanelRepo } from './panel.repo';
5 | import { WidgetToPanelRepo } from '../widget-to-panel/widget-to-panel.repo';
6 | import { DataService as NgsiDataService } from '../../../ngsi-service/src/data/data.service';
7 | import { QueryService as NgsiQueryService } from '../../../ngsi-service/src/query/query.service';
8 | import { AuthService as NgsiAuthService } from '../../../ngsi-service/src/auth/auth.service';
9 | import { HttpModule } from '@nestjs/axios';
10 |
11 | @Module({
12 | imports: [HttpModule],
13 | providers: [
14 | PanelService,
15 | PanelRepo,
16 | WidgetToPanelRepo,
17 | NgsiAuthService,
18 | NgsiDataService,
19 | NgsiQueryService,
20 | ],
21 | controllers: [PanelController],
22 | })
23 | export class PanelModule {}
24 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/data-model/fiware-models/swimming-info.model.ts:
--------------------------------------------------------------------------------
1 | export class SwimmingInfoModel {
2 | name: string;
3 | sensors: {
4 | name: string;
5 | capacityMax: number;
6 | capacityCurrent: number;
7 | parkingInfo?: {
8 | name: string;
9 | maxHeight: number;
10 | capacity: number;
11 | location: {
12 | type: string;
13 | coordinates: number[];
14 | };
15 | currentlyUsed: number;
16 | maxValue: number;
17 | type: string;
18 | address?: {
19 | id: string;
20 | street: string;
21 | streetnumber: string;
22 | zipcode: string;
23 | city: string;
24 | breitengrad: number;
25 | laengengrad: number;
26 | breitengradDisplayInternational: string;
27 | laengengradDisplayInternational: string;
28 | breitengradDisplay: string;
29 | laengengradDisplay: string;
30 | };
31 | };
32 | }[];
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/providers/LoginProvider.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { ReactElement, useEffect } from 'react';
4 | import { usePathname } from 'next/navigation';
5 | import { useAuth } from 'react-oidc-context';
6 |
7 | export default function LoginProvider({
8 | children,
9 | }: {
10 | children: React.ReactNode;
11 | }): ReactElement {
12 | const { isAuthenticated, isLoading, signinRedirect, error } = useAuth();
13 | const pathname = usePathname();
14 |
15 | useEffect(() => {
16 | if (typeof window !== 'undefined') {
17 | if (!isAuthenticated && !isLoading) {
18 | signinRedirect({
19 | state: pathname,
20 | }).catch((err) => {
21 | console.error('Failed to redirect to login:', err);
22 | });
23 | }
24 | }
25 | }, [isAuthenticated, isLoading, pathname]);
26 |
27 | if (error) {
28 | return Authentication error: {error.message}
;
29 | }
30 |
31 | return <>{isAuthenticated ? children : null}>;
32 | }
33 |
--------------------------------------------------------------------------------
/frontend/types/groupingElement.ts:
--------------------------------------------------------------------------------
1 | import { Dashboard } from '.';
2 |
3 | export type GroupingElement = {
4 | id?: string | null;
5 | name: string | null;
6 | backgroundColor: string | null;
7 | children?: GroupingElement[];
8 | fontColor: string;
9 | gradient: boolean | null;
10 | icon: string | null;
11 | isDashboard: boolean | null;
12 | parentGroupingElementId: string | null;
13 | position: number | null;
14 | tenantAbbreviation: string | null;
15 | url: string | null;
16 | };
17 |
18 | export type GroupingElementWithDashboards = {
19 | id?: string | null;
20 | name: string | null;
21 | backgroundColor: string | null;
22 | children: GroupingElement[];
23 | dashboards: Dashboard[] | null;
24 | fontColor: string;
25 | gradient: boolean | null;
26 | icon: string | null;
27 | isDashboard: boolean | null;
28 | parentGroupingElementId: string | null;
29 | position: number | null;
30 | tenantAbbreviation: string | null;
31 | url: string | null;
32 | };
33 |
--------------------------------------------------------------------------------
/frontend/ui/Buttons/CollapseButton.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | import DashboardIcons from '../Icons/DashboardIcon';
3 |
4 | type CollapseButtonProps = {
5 | isOpen: boolean;
6 | setIsOpen: (value: boolean) => void;
7 | };
8 |
9 | export default function CollapseButton(
10 | props: CollapseButtonProps,
11 | ): ReactElement {
12 | const { isOpen, setIsOpen } = props;
13 |
14 | const handleButtonClick = (): void => {
15 | setIsOpen(!isOpen);
16 | };
17 |
18 | return (
19 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir: __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'error',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'error',
24 | 'prettier/prettier': ['error', { endOfLine: 'auto' }],
25 | },
26 | overrides: [
27 | {
28 | files: ['*.spec.ts'],
29 | rules: {
30 | '@typescript-eslint/no-explicit-any': 'off',
31 | },
32 | },
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/microservices/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "ES2021",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "strictNullChecks": false,
16 | "noImplicitAny": false,
17 | "strictBindCallApply": false,
18 | "forceConsistentCasingInFileNames": false,
19 | "noFallthroughCasesInSwitch": false,
20 | "paths": {
21 | "@app/postgres-db": [
22 | "libs/postgres-db/src"
23 | ],
24 | "@app/postgres-db/*": [
25 | "libs/postgres-db/src/*"
26 | ],
27 | "@app/auth-helper": [
28 | "libs/auth-helper/src"
29 | ],
30 | "@app/auth-helper/*": [
31 | "libs/auth-helper/src/*"
32 | ]
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "parserOptions": {
4 | "project": "tsconfig.json",
5 | "tsconfigRootDir": ".",
6 | "sourceType": "module"
7 | },
8 | "plugins": ["@typescript-eslint/eslint-plugin"],
9 | "extends": [
10 | "plugin:@typescript-eslint/recommended",
11 | "plugin:prettier/recommended"
12 | ],
13 | "root": true,
14 | "env": {
15 | "node": true,
16 | "jest": true
17 | },
18 | "ignorePatterns": [".eslintrc.json"],
19 | "rules": {
20 | "@typescript-eslint/interface-name-prefix": "off",
21 | "@typescript-eslint/explicit-function-return-type": "error",
22 | "@typescript-eslint/explicit-module-boundary-types": "off",
23 | "@typescript-eslint/no-explicit-any": "error",
24 | "prettier/prettier": ["error", { "endOfLine": "auto" }]
25 | },
26 | "overrides": [
27 | {
28 | "files": ["*.spec.ts"],
29 | "rules": {
30 | "@typescript-eslint/no-explicit-any": "off"
31 | }
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/general-settings/test/test-data.ts:
--------------------------------------------------------------------------------
1 | import {
2 | generalSettings,
3 | GeneralSettings,
4 | } from '@app/postgres-db/schemas/general-settings.schema';
5 | import { v4 as uuid } from 'uuid';
6 | import { DbType } from '@app/postgres-db';
7 |
8 | export function getGeneralSetting(tenant: string): GeneralSettings {
9 | return {
10 | id: uuid(),
11 | tenant: tenant,
12 | information: 'https://test.de/information.html',
13 | imprint: 'https://test.de/imprint.html',
14 | privacy: 'https://test.de/privacy.html',
15 | allowThemeSwitching: false,
16 | disclaimer: 'Test Disclaimer',
17 | };
18 | }
19 |
20 | export async function createGeneralSettingsByObject(
21 | dbClient: DbType,
22 | generalSettingValue: GeneralSettings,
23 | ): Promise {
24 | const result = await dbClient
25 | .insert(generalSettings)
26 | .values(generalSettingValue)
27 | .returning();
28 |
29 | return result.length > 0 ? result[0] : null;
30 | }
31 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/defect.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { json, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
3 | import { visibilityEnum } from './dashboard.schema';
4 |
5 | export const defects = pgTable('defect', {
6 | id: uuid('id')
7 | .primaryKey()
8 | .default(sql`gen_random_uuid()`),
9 | location: json('location').notNull(),
10 | category: text('category').notNull(),
11 | imgPath: text('imgPath').notNull(),
12 | description: text('description'),
13 | mail: text('mail'),
14 | phone: text('phone'),
15 | visibility: visibilityEnum('visibility'),
16 | readRoles: text('read_roles').array(),
17 | writeRoles: text('write_roles').array(),
18 | createdAt: timestamp('created_at', { mode: 'date', precision: 6 }).default(
19 | sql`now()`,
20 | ),
21 | updatedAt: timestamp('updated_at', { mode: 'date', precision: 6 }).default(
22 | sql`now()`,
23 | ),
24 | });
25 |
26 | export type Defect = typeof defects.$inferSelect;
27 |
--------------------------------------------------------------------------------
/frontend/api/mail-service.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { env } from 'next-runtime-env';
3 | import { Mail } from '@/types';
4 |
5 | const NEXT_PUBLIC_MAIL_SERVICE_URL = env('NEXT_PUBLIC_MAIL_SERVICE_URL');
6 |
7 | export async function sendMail(
8 | accessToken: string | undefined,
9 | emailContent: Mail,
10 | ): Promise {
11 | const headers: Record = {
12 | 'Content-Type': 'application/json',
13 | };
14 |
15 | if (accessToken) {
16 | headers['Authorization'] = `Bearer ${accessToken}`;
17 | }
18 |
19 | const url = `${NEXT_PUBLIC_MAIL_SERVICE_URL}/mail/send`;
20 |
21 | try {
22 | const response = await axios.post(url, emailContent, { headers: headers });
23 |
24 | return response.data;
25 | } catch (err) {
26 | console.error(err);
27 | if (axios.isAxiosError(err)) {
28 | console.error('HTTP Error on sendMail:', err.response?.status);
29 | console.error('Response body:', err.response?.data);
30 | }
31 | throw err;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/env.example:
--------------------------------------------------------------------------------
1 | NEXT_PUBLIC_VERSION=0.10.1
2 | NEXT_PUBLIC_FRONTEND_URL=http://localhost:3000
3 | NEXT_PUBLIC_BACKEND_URL=http://localhost:8081
4 | NEXT_PUBLIC_NGSI_SERVICE_URL=http://localhost:8082
5 | NEXT_PUBLIC_ORCHIDEO_CONNECT_SERVICE_URL=http://localhost:8083
6 | NEXT_PUBLIC_MAIL_SERVICE_URL=http://localhost:8085
7 | NEXT_PUBLIC_REPORT_SERVICE_URL=http://localhost:8086
8 |
9 | NEXT_PUBLIC_MULTI_TENANCY="true"
10 | NEXT_PUBLIC_BASEPATH="" #MUST start with a slash "/"
11 | NEXT_PUBLIC_ASSET_PREFIX="" #MUST start AND end with a slash "/"
12 |
13 | NEXT_PUBLIC_OIDC_AUTHORITY=http://localhost:8080/realms/myrealm
14 | NEXT_PUBLIC_OIDC_CLIENT_ID=myclient
15 | NEXT_PUBLIC_OIDC_REDIRECT_URI=http://localhost:3000/admin
16 |
17 | NODE_TLS_REJECT_UNAUTHORIZED=0 # set to 1 for production builds
18 |
19 | # Mailer variables
20 | NEXT_PUBLIC_MAIL_TO="smart.city@edag.com"
21 |
22 | # DEV SSR
23 | NODE_TLS_REJECT_UNAUTHORIZED=0 # set to 1 for production builds
24 |
25 | # Mapbox variables
26 | NEXT_PUBLIC_MAPBOX_TOKEN="123YOURKEY"
27 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/sensor-report.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
3 | import { reportThresholdTriggerTypeEnum } from './enums.schema';
4 | import { queries } from './query.schema';
5 |
6 | export const sensorReports = pgTable('sensor_report', {
7 | id: uuid('id')
8 | .primaryKey()
9 | .default(sql`gen_random_uuid()`),
10 | queryId: uuid('query_id').references(() => queries.id),
11 | propertyName: text('property_name').notNull(),
12 | threshold: text('threshold').notNull(),
13 | trigger: reportThresholdTriggerTypeEnum('trigger'),
14 | recipients: text('recipients').array(),
15 | mailText: text('mail_text'),
16 | createdAt: timestamp('created_at', { mode: 'date', precision: 6 }).default(
17 | sql`now()`,
18 | ),
19 | updatedAt: timestamp('updated_at', { mode: 'date', precision: 6 }).default(
20 | sql`now()`,
21 | ),
22 | });
23 |
24 | export type SensorReport = typeof sensorReports.$inferSelect;
25 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/README.adoc:
--------------------------------------------------------------------------------
1 | = Readme for the infopin-service microservice
2 |
3 | == Description
4 |
5 | This service manages the data for the infopin module.
6 | It receives new infopin data and saves it into the database.
7 | Also it provides the frontend with respective data.
8 |
9 | == Environment Variables
10 |
11 | [options="header"]
12 | |===
13 | | Variable | Description | Example Value
14 | | `NEXT_PUBLIC_FRONTEND_URL` | Adress to the frontend. Required to set appropriate CORS settings | `http:localhost:3000`
15 | | `IMAGE_DIR` | Location where images shall be saved | `images`
16 | | `ADMIN_ROLE` | Default admin role | `scs-admin`
17 | | `POSTGRES_USER` | The PostgreSQL database user. | `postgres`
18 | | `POSTGRES_PASSWORD` | The password for the PostgreSQL user. | `postgres`
19 | | `POSTGRES_DB` | The name of the PostgreSQL database. | `scs`
20 | | `POSTGRES_HOST` | The host address of the PostgreSQL server. | `127.0.0.1`
21 | | `POSTGRES_PORT` | The port on which the PostgreSQL server is running. | `5432`
22 | |===
23 |
--------------------------------------------------------------------------------
/frontend/assets/icons/HumidityNormal.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | type SVGProps = {
3 | fontColor?: string;
4 | height?: string;
5 | };
6 |
7 | export default function HumidityNormal(props: SVGProps): ReactElement {
8 | const { fontColor = '#fff', height = 22 } = props;
9 | return (
10 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/defect/test/test-data.ts:
--------------------------------------------------------------------------------
1 | import { DbType } from '@app/postgres-db';
2 | import { v4 as uuid } from 'uuid';
3 | import { Defect, defects } from '@app/postgres-db/schemas/defect.schema';
4 |
5 | export function getDefect(): Defect {
6 | return {
7 | id: '',
8 | location: {
9 | lat: 9.444,
10 | lng: -9.444,
11 | },
12 | category: 'test',
13 | description: 'test description',
14 | imgPath: '/images/test.png',
15 | mail: 'test@test.com',
16 | phone: '+1 555 728',
17 | visibility: 'public',
18 | readRoles: [],
19 | writeRoles: [],
20 | createdAt: new Date(),
21 | updatedAt: new Date(),
22 | };
23 | }
24 |
25 | export async function createDefectByObject(
26 | dbClient: DbType,
27 | defect: Defect,
28 | ): Promise {
29 | defect.id = uuid();
30 |
31 | const createdDefects = await dbClient
32 | .insert(defects)
33 | .values(defect)
34 | .returning();
35 |
36 | return createdDefects.length > 0 ? createdDefects[0] : null;
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/app/(dashboard)/page.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | import { getDashboardUrlByTenantAbbreviation } from '../actions';
3 | import { redirect } from 'next/navigation';
4 | import { cookies } from 'next/headers';
5 | import { jwtDecode } from 'jwt-decode';
6 |
7 | export const dynamic = 'force-dynamic'; // Neeeded to avoid data fetching during build
8 | export const runtime = 'edge';
9 |
10 | export default async function DashboardPageGeneral(): Promise {
11 | const cookieStore = await cookies();
12 | const token = cookieStore.get('access_token')?.value;
13 |
14 | // Try auto redirect for logged in users
15 | if (token) {
16 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
17 | const decodedToken: any = jwtDecode(token);
18 | const mandatorCode = decodedToken.mandator_code;
19 | const url = await getDashboardUrlByTenantAbbreviation(mandatorCode);
20 | redirect(`${mandatorCode}/${url}`);
21 | }
22 |
23 | return Bitte geben Sie einen Mandanten an.
;
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/app/MarkerCluster.css:
--------------------------------------------------------------------------------
1 | /* To solve Next.js issues source from https://github.com/Leaflet/Leaflet.markercluster/blob/master/dist/MarkerCluster.css */
2 | .leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
3 | -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
4 | -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
5 | -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
6 | transition: transform 0.3s ease-out, opacity 0.3s ease-in;
7 | }
8 |
9 | .leaflet-cluster-spider-leg {
10 | /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
11 | -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
12 | -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
13 | -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
14 | transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
15 | }
--------------------------------------------------------------------------------
/microservices/apps/report-service/src/mail/mail.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { SensorReport } from '@app/postgres-db/schemas/sensor-report.schema';
3 | import axios from 'axios';
4 | import { SendMailDto } from '../dto/mail.dto';
5 |
6 | @Injectable()
7 | export class MailService {
8 | notify(reportConfig: SensorReport, reportQueryData: object): void {
9 | const mailServiceUrl = process.env.NEXT_PUBLIC_MAIL_SERVICE_URL;
10 | const value = reportQueryData[reportConfig.propertyName];
11 | const id = reportQueryData['id'];
12 |
13 | const payload: SendMailDto = {
14 | to: reportConfig.recipients.join(','),
15 | subject: 'Sensor Threshold',
16 | body: reportConfig.mailText
17 | .replace('{{sensor}}', id)
18 | .replace('{{threshold}}', reportConfig.threshold)
19 | .replace('{{value}}', value),
20 | };
21 |
22 | try {
23 | axios.post(`${mailServiceUrl}/mail/send`, payload);
24 | } catch (error) {
25 | console.error(error);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/panel/test/test-data.ts:
--------------------------------------------------------------------------------
1 | import { DbType } from '@app/postgres-db';
2 | import { Panel, panels } from '@app/postgres-db/schemas';
3 | import { v4 as uuid } from 'uuid';
4 |
5 | export function getPanel(dashboardId?: string): Panel {
6 | return {
7 | id: uuid(),
8 | dashboardId: dashboardId,
9 | name: 'Sample Panel',
10 | height: 140,
11 | width: 140,
12 | position: 1,
13 | headlineColor: '#000',
14 | info: 'Sample Message',
15 | icon: 'ChevronRight',
16 | generalInfo: 'Sample General Info',
17 | showGeneralInfo: false,
18 | showJumpoffButton: false,
19 | openJumpoffLinkInNewTab: true,
20 | jumpoffLabel: 'Sample Label',
21 | jumpoffIcon: 'ChevronLeft',
22 | jumpoffUrl: 'defaultUrl',
23 | };
24 | }
25 |
26 | export async function createPanelByObject(
27 | db: DbType,
28 | panel: Panel,
29 | ): Promise {
30 | const createdPanels = await db.insert(panels).values(panel).returning();
31 |
32 | return createdPanels.length > 0 ? createdPanels[0] : null;
33 | }
34 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/multer-config.ts:
--------------------------------------------------------------------------------
1 | import multer = require('multer');
2 | import { v4 as uuid } from 'uuid';
3 |
4 | export const multerConfig = {
5 | storage: multer.diskStorage({
6 | destination: (req, file, cb) => {
7 | file ? cb(null, __dirname + process.env.IMAGE_DIR) : cb(null, '');
8 | },
9 | filename: (req, file, cb) => {
10 | file
11 | ? cb(null, `${uuid()}-infopin.${file.mimetype.substring(6)}`)
12 | : cb(null, '');
13 | },
14 | }),
15 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
16 | fileFilter: (req, file, cb) => {
17 | if (
18 | file.mimetype === 'image/png' ||
19 | file.mimetype === 'image/jpg' ||
20 | file.mimetype === 'image/jpeg'
21 | ) {
22 | cb(null, true);
23 | } else {
24 | return cb(
25 | new Error(
26 | 'Zur Zeit werden nur folgende Bildformate unterstüzt: jpg, jpeg, png',
27 | ),
28 | false,
29 | );
30 | }
31 | },
32 | limits: { fileSize: 104857600 },
33 | };
34 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/general-settings/general-settings.module.ts:
--------------------------------------------------------------------------------
1 | import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
2 | import { GeneralSettingsController } from './general-settings.controller';
3 | import { GeneralSettingsRepo } from './general-settings.repo';
4 | import { GeneralSettingsService } from './general-settings.service';
5 | import { AuthHelperMiddleware, AuthHelperUtility } from '@app/auth-helper';
6 | import { TenantRepo } from '../tenant/tenant.repo';
7 |
8 | @Module({
9 | controllers: [GeneralSettingsController],
10 | providers: [
11 | GeneralSettingsRepo,
12 | GeneralSettingsService,
13 | TenantRepo,
14 | AuthHelperUtility,
15 | ],
16 | })
17 | export class GeneralSettingsModule {
18 | configure(consumer: MiddlewareConsumer): void {
19 | // Apply the AuthHelperMiddleware to protect specific routes
20 | consumer.apply(AuthHelperMiddleware).forRoutes(
21 | { path: 'general-settings*', method: RequestMethod.ALL }, // Protect all methods in the "general-settings" route
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/tenant/tenant.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TenantService } from './tenant.service';
3 | import { TenantController } from './tenant.controller';
4 | import { TenantRepo } from './tenant.repo';
5 | import { CorporateInfoService } from '../corporate-info/corporate-info.service';
6 | import { CorporateInfoRepo } from '../corporate-info/corporate-info.repo';
7 | import { CorporateInfoSidebarLogosRepo } from '../corporate-info/corporate-info-sidebar-logos.repo';
8 | import { LogoRepo } from '../logo/logo.repo';
9 | import { LogoService } from '../logo/logo.service';
10 | import { GeneralSettingsRepo } from '../general-settings/general-settings.repo';
11 |
12 | @Module({
13 | providers: [
14 | TenantService,
15 | TenantRepo,
16 | CorporateInfoService,
17 | CorporateInfoRepo,
18 | CorporateInfoSidebarLogosRepo,
19 | LogoRepo,
20 | LogoService,
21 | GeneralSettingsRepo,
22 | ],
23 | controllers: [TenantController],
24 | exports: [TenantService],
25 | })
26 | export class TenantModule {}
27 |
--------------------------------------------------------------------------------
/frontend/assets/smartCityLogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/infopin-service.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ClimateProjectModule } from './climate-project/climate-project.module';
3 | import { ReportModule } from './report/report.module';
4 | import { ClimateProjectController } from './climate-project/climate-project.controller';
5 | import { PostgresDbModule } from '@app/postgres-db';
6 | import { APP_GUARD } from '@nestjs/core';
7 | import { AuthGuard } from '@app/auth-helper/AuthGuard';
8 | import { JwtModule } from '@nestjs/jwt';
9 | import { DefectModule } from './defect/defect.module';
10 | import { ScheduleModule } from '@nestjs/schedule';
11 |
12 | @Module({
13 | imports: [
14 | ClimateProjectModule,
15 | ReportModule,
16 | PostgresDbModule,
17 | JwtModule.register({
18 | global: true,
19 | }),
20 | DefectModule,
21 | ScheduleModule.forRoot(),
22 | ],
23 | providers: [
24 | {
25 | provide: APP_GUARD,
26 | useClass: AuthGuard,
27 | },
28 | ],
29 | controllers: [ClimateProjectController],
30 | })
31 | export class InfopinServiceModule {}
32 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/widget-to-tenant.schema.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm';
2 | import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
3 | import { tenants } from './tenant.schema';
4 | import { widgets } from './dashboard.widget.schema';
5 |
6 | export const widgetsToTenants = pgTable(
7 | 'widget_to_tenant',
8 | {
9 | widgetId: uuid('widget_id').references(() => widgets.id),
10 | tenantId: uuid('tenant_id').references(() => tenants.id),
11 | },
12 | (t) => ({
13 | pk: primaryKey(t.widgetId, t.tenantId),
14 | }),
15 | );
16 |
17 | export const widgetsToTenantsRelations = relations(
18 | widgetsToTenants,
19 | ({ one }) => ({
20 | widget: one(widgets, {
21 | fields: [widgetsToTenants.widgetId],
22 | references: [widgets.id],
23 | }),
24 | tenant: one(tenants, {
25 | fields: [widgetsToTenants.tenantId],
26 | references: [tenants.id],
27 | }),
28 | }),
29 | );
30 |
31 | export type WidgetToTenant = typeof widgetsToTenants.$inferSelect;
32 | export type NewWidgetToTenant = typeof widgetsToTenants.$inferInsert;
33 |
--------------------------------------------------------------------------------
/frontend/assets/icons/Waves.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | type SVGProps = {
3 | fontColor?: string;
4 | height?: string;
5 | };
6 |
7 | export default function Waves(props: SVGProps): ReactElement {
8 | const { fontColor = '#fff', height = 24 } = props;
9 | return (
10 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/database/drizzle.config.ts:
--------------------------------------------------------------------------------
1 | import { config } from 'dotenv';
2 | import type { Config } from 'drizzle-kit';
3 |
4 | config(); // Load environment variables from .env file
5 |
6 | const host = process.env.POSTGRES_HOST;
7 | const port = process.env.POSTGRES_PORT;
8 | const user = process.env.POSTGRES_USER;
9 | const password = process.env.POSTGRES_PASSWORD;
10 | const database =
11 | process.env.NODE_ENV !== 'test'
12 | ? process.env.POSTGRES_DB
13 | : process.env.POSTGRES_DB_TEST;
14 |
15 | if (!host || !port || !user || !password || !database) {
16 | throw new Error('Missing database environment variable(s)');
17 | }
18 |
19 | export default {
20 | // This will find all schemas in the microservices directory as long
21 | // as we adhere to the folder structure and naming conventions
22 | schema: './microservices/libs/postgres-db/src/schemas/*.schema.ts',
23 | out: './database/migrations/generated',
24 | driver: 'pg',
25 | dbCredentials: {
26 | host: host,
27 | port: parseInt(port),
28 | user: user,
29 | password: password,
30 | database: database,
31 | },
32 | } satisfies Config;
33 |
--------------------------------------------------------------------------------
/frontend/assets/icons/Snowflake.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | type SVGProps = {
3 | fontColor?: string;
4 | height?: string;
5 | };
6 |
7 | export default function Snowflake(props: SVGProps): ReactElement {
8 | const { fontColor, height = 24 } = props;
9 | return (
10 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/frontend/ui/HeaderLogo.tsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image';
2 | import { ReactElement } from 'react';
3 | import { useQuery } from '@tanstack/react-query';
4 | import { getCorporateInfosWithLogos } from '@/app/actions';
5 | import { getTenantOfPage } from '@/utils/tenantHelper';
6 | import { env } from 'next-runtime-env';
7 |
8 | export default function HeaderLogo(): ReactElement {
9 | const tenant = getTenantOfPage();
10 | const { data } = useQuery({
11 | queryKey: ['corporate-info'],
12 | queryFn: () => getCorporateInfosWithLogos(tenant),
13 | enabled: false,
14 | });
15 |
16 | const getLogoSrc = (): string => {
17 | const basepath = env('NEXT_PUBLIC_BASEPATH');
18 | const logoUrl = data?.headerLogo?.logo;
19 |
20 | if (logoUrl) {
21 | return logoUrl;
22 | } else {
23 | return `${basepath !== '' ? basepath : ''}/smart-region-logo.svg`;
24 | }
25 | };
26 |
27 | return (
28 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | *node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | pnpm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # OS
15 | .DS_Store
16 |
17 | # Tests
18 | /coverage
19 | /.nyc_output
20 |
21 | # IDEs and editors
22 | /.idea
23 | .project
24 | .classpath
25 | .c9/
26 | *.launch
27 | .settings/
28 | *.sublime-workspace
29 |
30 | # IDE - VSCode
31 | .vscode/*
32 | !.vscode/settings.json
33 | !.vscode/tasks.json
34 | !.vscode/launch.json
35 | !.vscode/extensions.json
36 |
37 | # EDAG CSMS
38 | *SBOMWorkingDir
39 | *sbom-tools.log
40 | *SmartCity_Dashboard_Frontend-7ab44853-c5bb-4c85-ba22-f24c3d29bbf1
41 | *SmartCity_Dashboard_Microservices-f2345b27-f51d-4818-a526-eaeacd7c916f
42 |
43 | # Kubernetes
44 | k8s/helm/charts/*.tgz
45 | k8s/helm/*.lock
46 | k8s/helm/certificates/*
47 | k8s/helm/secrets.yaml
48 |
49 | # Migrationfiles
50 | database/migrations/generated/*
51 |
52 | # Ignore everything in the hcloud folder
53 | hcloud/*
54 |
55 | # Ignore VS Code settings
56 | .vscode/*
57 |
58 | # Keycloak
59 | keycloak/import/production-realm.json
60 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/dashboard.grouping-element.schema.ts:
--------------------------------------------------------------------------------
1 | import { sql } from 'drizzle-orm';
2 | import {
3 | smallint,
4 | boolean,
5 | pgTable,
6 | text,
7 | uuid,
8 | AnyPgColumn,
9 | } from 'drizzle-orm/pg-core';
10 | import { tenants } from './tenant.schema';
11 |
12 | export const groupingElements = pgTable('grouping_element', {
13 | id: uuid('id')
14 | .primaryKey()
15 | .default(sql`gen_random_uuid()`),
16 | name: text('name'),
17 | url: text('url'),
18 | backgroundColor: text('background_color'),
19 | fontColor: text('font_color'),
20 | gradient: boolean('gradient'),
21 | icon: text('icon'),
22 | isDashboard: boolean('is_dashboard'),
23 | position: smallint('position'),
24 | tenantAbbreviation: text('tenant_abbreviation').references(
25 | () => tenants.abbreviation,
26 | ),
27 | parentGroupingElementId: uuid('parent_grouping_element_id').references(
28 | (): AnyPgColumn => groupingElements.id,
29 | ),
30 | });
31 |
32 | export type GroupingElement = typeof groupingElements.$inferSelect;
33 | export type NewGroupingElement = typeof groupingElements.$inferInsert;
34 |
--------------------------------------------------------------------------------
/frontend/Dockerfile.frontend:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files and install dependencies
8 | COPY package*.json /app/
9 | RUN npm ci --force
10 |
11 | # Copy the rest of the application code
12 | COPY . /app
13 |
14 | # Build the application
15 | RUN npm run build
16 |
17 | # Base image for the final stage
18 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
19 |
20 | # Set working directory
21 | WORKDIR /usr/frontend/apps
22 |
23 | # Copy built files from the builder stage
24 | COPY --from=builder /app/next.config.js /usr/frontend/apps
25 | COPY --from=builder /app/public /usr/frontend/apps/public
26 | COPY --from=builder /app/.next /usr/frontend/apps/.next
27 | COPY --from=builder /app/node_modules /usr/frontend/apps/node_modules
28 | COPY --from=builder /app/package.json /usr/frontend/apps/package.json
29 |
30 | # Expose the port Next.js runs on
31 | EXPOSE 3000
32 |
33 | # Command to run the application
34 | CMD [ "npm", "run", "start" ]
35 |
--------------------------------------------------------------------------------
/frontend/providers/CorporateIdentityProvider.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement } from 'react';
2 | import {
3 | dehydrate,
4 | HydrationBoundary,
5 | QueryClient,
6 | } from '@tanstack/react-query';
7 | import { getCorporateInfosWithLogos } from '@/app/actions';
8 | // import { getTenantOfPage } from '@/utils/tenantHelper';
9 |
10 | // Provider to prefetch corporate info
11 | export default async function CorporateIdentityProvider({
12 | children,
13 | tenant,
14 | }: {
15 | children: React.ReactNode;
16 | tenant: string | undefined;
17 | }): Promise {
18 | const queryClient = new QueryClient();
19 | // const tenant = getTenantOfPage();
20 |
21 | // Prefetched and put into the cache for queryKey: 'corporateInfo'
22 | await queryClient.prefetchQuery({
23 | queryKey: ['corporate-info'],
24 | queryFn: () => getCorporateInfosWithLogos(tenant),
25 | });
26 |
27 | return (
28 | // `HydrationBoundary` helps manage hydration of server-side rendered data in a React application.
29 |
30 | {children}
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/query.schema.ts:
--------------------------------------------------------------------------------
1 | import { relations, sql } from 'drizzle-orm';
2 | import { pgTable, text, uuid, json, timestamp } from 'drizzle-orm/pg-core';
3 | import { queryConfigs } from './query-config.schema';
4 |
5 | export const queries = pgTable('query', {
6 | id: uuid('id')
7 | .primaryKey()
8 | .default(sql`gen_random_uuid()`),
9 | queryConfigId: uuid('query_config_id').references(() => queryConfigs.id),
10 | queryData: json('query_data'),
11 | reportData: json('report_data'),
12 | updateMessage: text('update_message').array(),
13 | createdAt: timestamp('created_at', { mode: 'date', precision: 6 }).default(
14 | sql`now()`,
15 | ),
16 | updatedAt: timestamp('updated_at', { mode: 'date', precision: 6 }).default(
17 | sql`now()`,
18 | ),
19 | });
20 |
21 | export const queriesRelations = relations(queries, ({ one }) => ({
22 | queryConfig: one(queryConfigs, {
23 | fields: [queries.queryConfigId],
24 | references: [queryConfigs.id],
25 | }),
26 | }));
27 |
28 | export type Query = typeof queries.$inferSelect;
29 | export type NewQuery = typeof queries.$inferInsert;
30 |
--------------------------------------------------------------------------------
/microservices/apps/test/index.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import './database-operations/database.e2e-spec';
2 | import '../dashboard-service/test/data-source/datasource.e2e-spec';
3 | import '../dashboard-service/test/data-model/datamodel.e2e-spec';
4 | import '../dashboard-service/test/query-config/queryconfig.e2e-spec';
5 | import '../dashboard-service/test/query/query.e2e-spec';
6 | import '../dashboard-service/test/dashboard/dashboard.e2e-spec';
7 | import '../dashboard-service/test/corporate-info/corporate-info.e2e-spec';
8 | import '../dashboard-service/test/tenant/tenant.e2e-spec';
9 | import '../dashboard-service/test/panel/panel.e2e-spec';
10 | import '../dashboard-service/test/logo/logo.e2e-spec';
11 | import '../dashboard-service/test/widget/widget.e2e-spec';
12 | import '../dashboard-service/test/tab/tab.e2e-spec';
13 | import '../dashboard-service/test/grouping-element/groupingElement.e2e-spec';
14 | import '../dashboard-service/test/widget-to-panel/widgetToPanel.e2e-spec';
15 | import '../dashboard-service/test/dashboard-with-content/contentDashboard.e2e-spec';
16 | import '../ngsi-service/src/auth/test/app.e2e-spec';
17 | import '../ngsi-service/test/app.e2e-spec';
18 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/dashboard.widget-to-panel.schema.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm';
2 | import { pgTable, primaryKey, smallint, uuid } from 'drizzle-orm/pg-core';
3 | import { widgets } from './dashboard.widget.schema';
4 | import { panels } from './dashboard.panel.schema';
5 |
6 | export const widgetsToPanels = pgTable(
7 | 'widget_to_panel',
8 | {
9 | widgetId: uuid('widget_id').references(() => widgets.id),
10 | panelId: uuid('panel_id').references(() => panels.id),
11 | position: smallint('position'),
12 | },
13 | (t) => ({
14 | pk: primaryKey(t.widgetId, t.panelId),
15 | }),
16 | );
17 |
18 | export const widgetsToPanelsRelations = relations(
19 | widgetsToPanels,
20 | ({ one }) => ({
21 | widget: one(widgets, {
22 | fields: [widgetsToPanels.widgetId],
23 | references: [widgets.id],
24 | }),
25 | panel: one(panels, {
26 | fields: [widgetsToPanels.panelId],
27 | references: [panels.id],
28 | }),
29 | }),
30 | );
31 |
32 | export type WidgetToPanel = typeof widgetsToPanels.$inferSelect;
33 | export type NewWidgetToPanel = typeof widgetsToPanels.$inferInsert;
34 |
--------------------------------------------------------------------------------
/database/migrations/generated/0018_past_deadpool_0.18.0.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE "tab_component_type" ADD VALUE IF NOT EXISTS 'Wetterwarnungen';--> statement-breakpoint
2 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "weather_warning_bg_color" text;--> statement-breakpoint
3 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "weather_warning_headline_color" text;--> statement-breakpoint
4 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "weather_warning_instructions_color" text;--> statement-breakpoint
5 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "weather_warning_descriptions_color" text;--> statement-breakpoint
6 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "weather_warning_date_color" text;--> statement-breakpoint
7 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "weather_warning_button_background_color" text;--> statement-breakpoint
8 | ALTER TABLE "corporate_info" ADD COLUMN IF NOT EXISTS "weather_warning_button_icon_color" text;--> statement-breakpoint
9 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "image_allow_jumpoff" boolean;--> statement-breakpoint
10 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "image_jumpoff_url" text;
11 |
--------------------------------------------------------------------------------
/microservices/libs/postgres-db/src/schemas/dashboard-to-tenant.schema.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm';
2 | import { pgTable, primaryKey, uuid } from 'drizzle-orm/pg-core';
3 | import { dashboards } from './dashboard.schema';
4 | import { tenants } from './tenant.schema';
5 |
6 | export const dashboardsToTenants = pgTable(
7 | 'dashboard_to_tenant',
8 | {
9 | dashboardId: uuid('dashboard_id').references(() => dashboards.id),
10 | tenantId: uuid('tenant_id').references(() => tenants.id),
11 | },
12 | (t) => ({
13 | pk: primaryKey(t.dashboardId, t.tenantId),
14 | }),
15 | );
16 |
17 | export const dashboardsToTenantsRelations = relations(
18 | dashboardsToTenants,
19 | ({ one }) => ({
20 | dashboard: one(dashboards, {
21 | fields: [dashboardsToTenants.dashboardId],
22 | references: [dashboards.id],
23 | }),
24 | tenant: one(tenants, {
25 | fields: [dashboardsToTenants.tenantId],
26 | references: [tenants.id],
27 | }),
28 | }),
29 | );
30 |
31 | export type DashboardToTenant = typeof dashboardsToTenants.$inferSelect;
32 | export type NewDashboardToTenant = typeof dashboardsToTenants.$inferInsert;
33 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.api-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci api-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build api-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8083
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/api-service/main.js"]
39 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.mail-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci mail-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build mail-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8085
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/mail-service/main.js"]
39 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.ngsi-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci ngsi-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build ngsi-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8082
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/ngsi-service/main.js"]
39 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.report-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci report-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build report-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8086
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/report-service/main.js"]
39 |
--------------------------------------------------------------------------------
/frontend/ui/MultipleColorPickers.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | import ColorPickerComponent from './ColorPickerComponent';
3 |
4 | type MultipleColorPickerProps = {
5 | colors?: string[] | null; // Optional and can be `null`
6 | totalFields: number;
7 | label: string;
8 | onColorChange: (index: number, newColor: string) => void;
9 | };
10 |
11 | export default function MultipleColorPicker({
12 | colors = [],
13 | totalFields,
14 | label,
15 | onColorChange,
16 | }: MultipleColorPickerProps): ReactElement {
17 | const validColors =
18 | colors && colors.length && colors.length > 0
19 | ? colors
20 | : Array(totalFields).fill('#FFFFFF');
21 |
22 | return (
23 |
24 | {Array.from({ length: totalFields }, (_, index) => (
25 |
26 |
29 | onColorChange(index, newColor)
30 | }
31 | label={`${label} ${index + 1}`}
32 | />
33 |
34 | ))}
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.infopin-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci infopin-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build infopin-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8084
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/infopin-service/main.js"]
39 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.dashboard-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci dashboard-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build dashboard-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8081
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/dashboard-service/main.js"]
39 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/widget-to-tenant/widget-to-tenant.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { WidgetToTenantService } from './widget-to-tenant.service';
3 | import { TenantService } from '../tenant/tenant.service';
4 | import { TenantRepo } from '../tenant/tenant.repo';
5 | import { WidgetToTenantRepo } from './widget-to-tenant.repo';
6 | import { CorporateInfoService } from '../corporate-info/corporate-info.service';
7 | import { CorporateInfoRepo } from '../corporate-info/corporate-info.repo';
8 | import { CorporateInfoSidebarLogosRepo } from '../corporate-info/corporate-info-sidebar-logos.repo';
9 | import { LogoRepo } from '../logo/logo.repo';
10 | import { LogoService } from '../logo/logo.service';
11 | import { GeneralSettingsRepo } from '../general-settings/general-settings.repo';
12 |
13 | @Module({
14 | providers: [
15 | WidgetToTenantService,
16 | WidgetToTenantRepo,
17 | CorporateInfoService,
18 | CorporateInfoRepo,
19 | TenantService,
20 | TenantRepo,
21 | CorporateInfoSidebarLogosRepo,
22 | LogoRepo,
23 | LogoService,
24 | GeneralSettingsRepo,
25 | ],
26 | })
27 | export class WidgetToTenantModule {}
28 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.data-translation-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci data-translation-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build data-translation-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | CMD ["node", "/usr/microservices/apps/dist/apps/data-translation-service/main.js"]
37 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.static-data-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci static-data-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build static-data-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8087
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/static-data-service/main.js"]
39 |
--------------------------------------------------------------------------------
/frontend/ui/Buttons/DeleteButton.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement, useState } from 'react';
2 | import DashboardIcons from '../Icons/DashboardIcon';
3 |
4 | type DeleteButtonProps = {
5 | onDeleteClick: () => void;
6 | };
7 |
8 | export default function DeleteButton(props: DeleteButtonProps): ReactElement {
9 | const { onDeleteClick } = props;
10 | const [isDeleting, setIsDeleting] = useState(false);
11 |
12 | const onClick = async (): Promise => {
13 | setIsDeleting(true); // Disable the button
14 | try {
15 | await onDeleteClick();
16 | } finally {
17 | setIsDeleting(false); // Re-enable the button regardless of success or failure
18 | }
19 | };
20 |
21 | return (
22 |
23 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.usi-platform-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci usi-platform-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build usi-platform-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8088
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/usi-platform-service/main.js"]
39 |
--------------------------------------------------------------------------------
/microservices/Dockerfile.orchideo-connect-service:
--------------------------------------------------------------------------------
1 | # Base image for building the application
2 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine AS builder
3 |
4 | # Create app directory in a more standard location
5 | WORKDIR /app
6 |
7 | # Copy package files
8 | COPY package*.json ./
9 |
10 | # Install dependencies
11 | RUN npm ci orchideo-connect-service
12 |
13 | # Copy the rest of the application code
14 | COPY . .
15 |
16 | # Build the application
17 | RUN npm run build orchideo-connect-service
18 |
19 | # Base image for the final stage
20 | FROM nexus.edag.de:8206/mobit-malaysia/scs/node:20.13-alpine
21 |
22 | # Create app directory
23 | WORKDIR /usr/microservices/apps
24 |
25 | # Copy built files from the builder stage
26 | COPY --from=builder /app/dist /usr/microservices/apps/dist
27 | COPY --from=builder /app/node_modules /usr/microservices/apps/node_modules
28 |
29 | # Create logs directory and set appropriate permissions
30 | RUN mkdir -p /usr/microservices/apps/logs \
31 | && chown -R node:node /usr/microservices/apps/logs
32 |
33 | # Switch to the non-root user
34 | USER node
35 |
36 | EXPOSE 8083
37 |
38 | CMD ["node", "/usr/microservices/apps/dist/apps/orchideo-connect-service/main.js"]
39 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/dashboard-to-tenant/dashboard-to-tenant.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DashboardToTenantService } from './dashboard-to-tenant.service';
3 | import { TenantService } from '../tenant/tenant.service';
4 | import { DashboardToTenantRepo } from './dashboard-to-tenant.repo';
5 | import { TenantRepo } from '../tenant/tenant.repo';
6 | import { CorporateInfoService } from '../corporate-info/corporate-info.service';
7 | import { CorporateInfoRepo } from '../corporate-info/corporate-info.repo';
8 | import { CorporateInfoSidebarLogosRepo } from '../corporate-info/corporate-info-sidebar-logos.repo';
9 | import { LogoRepo } from '../logo/logo.repo';
10 | import { LogoService } from '../logo/logo.service';
11 | import { GeneralSettingsRepo } from '../general-settings/general-settings.repo';
12 |
13 | @Module({
14 | providers: [
15 | DashboardToTenantService,
16 | DashboardToTenantRepo,
17 | TenantService,
18 | TenantRepo,
19 | CorporateInfoService,
20 | CorporateInfoRepo,
21 | CorporateInfoSidebarLogosRepo,
22 | LogoRepo,
23 | LogoService,
24 | GeneralSettingsRepo,
25 | ],
26 | })
27 | export class DashboardToTenantModule {}
28 |
--------------------------------------------------------------------------------
/database/migrations/generated/0011_bitter_loa_0.12.0a.sql:
--------------------------------------------------------------------------------
1 | ALTER TYPE "tab_component_sub_type" ADD VALUE IF NOT EXISTS 'Slider Übersicht';--> statement-breakpoint
2 | ALTER TYPE "tab_component_type" ADD VALUE IF NOT EXISTS 'Kombinierte Komponente';--> statement-breakpoint
3 | -- ALTER TABLE "grouping_element" RENAME COLUMN "color" TO "background_color";--> statement-breakpoint
4 | ALTER TABLE "grouping_element" DROP COLUMN IF EXISTS "color";--> statement-breakpoint
5 | ALTER TABLE "grouping_element" ADD COLUMN IF NOT EXISTS "background_color" text;--> statement-breakpoint
6 | ALTER TABLE "grouping_element" ADD COLUMN IF NOT EXISTS "font_color" text;--> statement-breakpoint
7 | ALTER TABLE "panel" ADD COLUMN IF NOT EXISTS "headline_color" text;--> statement-breakpoint
8 | ALTER TABLE "dashboard" ADD COLUMN IF NOT EXISTS "headline_color" text;--> statement-breakpoint
9 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "child_widgets" jsonb;--> statement-breakpoint
10 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "slider_current_attribute" text;--> statement-breakpoint
11 | ALTER TABLE "tab" ADD COLUMN IF NOT EXISTS "slider_maximum_attribute" text;--> statement-breakpoint
12 | ALTER TABLE "widget" ADD COLUMN IF NOT EXISTS "headline_color" text;
13 |
--------------------------------------------------------------------------------
/frontend/ui/VisibilityDisplay.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | import { visibilityEnum } from '@/types';
4 | import DashboardIcons from './Icons/DashboardIcon';
5 | import { visibilityOptions } from '@/utils/enumMapper';
6 |
7 | type VisibilityDisplayProps = {
8 | visibility: visibilityEnum;
9 | };
10 |
11 | export default function VisibilityDisplay(
12 | props: VisibilityDisplayProps,
13 | ): ReactElement {
14 | const { visibility } = props;
15 |
16 | let bgColor;
17 | let textColor;
18 | let icon;
19 | const text = visibilityOptions.find(
20 | (option) => option.value === visibility,
21 | )?.label;
22 |
23 | switch (visibility) {
24 | case 'protected':
25 | icon = 'Key';
26 | bgColor = 'bg-[#FACC15]';
27 | textColor = 'text-[#3D4760]';
28 | break;
29 | case 'public':
30 | default:
31 | icon = 'Eye';
32 | bgColor = 'bg-[#049A1A]';
33 | textColor = 'text-white';
34 | break;
35 | }
36 |
37 | return (
38 |
41 |
42 | {text}
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/k8s/helm/charts/common/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: common
3 | description: A Helm chart for Kubernetes
4 |
5 | # A chart can be either an 'application' or a 'library' chart.
6 | #
7 | # Application charts are a collection of templates that can be packaged into versioned archives
8 | # to be deployed.
9 | #
10 | # Library charts provide useful utilities or functions for the chart developer. They're included as
11 | # a dependency of application charts to inject those utilities and functions into the rendering
12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
13 | type: library
14 |
15 | # This is the chart version. This version number should be incremented each time you make changes
16 | # to the chart and its templates, including the app version.
17 | # Versions are expected to follow Semantic Versioning (https://semver.org/)
18 | version: 0.1.0
19 |
20 | # This is the version number of the application being deployed. This version number should be
21 | # incremented each time you make changes to the application. Versions are not expected to
22 | # follow Semantic Versioning. They should reflect the version the application is using.
23 | # It is recommended to use it with quotes.
24 | appVersion: "0.1.0"
25 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/logo/test/test-data.ts:
--------------------------------------------------------------------------------
1 | import { Logo, logos } from '@app/postgres-db/schemas/logo.schema';
2 | import { DbType } from '@app/postgres-db';
3 | import { v4 as uuid } from 'uuid';
4 | import { createTenantByObject } from '../../tenant/test/test-data';
5 | import { eq } from 'drizzle-orm';
6 |
7 | export function getLogo(): Logo {
8 | return {
9 | id: uuid(),
10 | tenantId: 'edag',
11 | logo: 'https://example.com/logo.png',
12 | logoHeight: 100,
13 | logoWidth: 200,
14 | format: 'png',
15 | size: 'large',
16 | logoName: 'edag_big.png',
17 | };
18 | }
19 |
20 | export async function createLogoByObject(
21 | db: DbType,
22 | logo: Logo,
23 | createTenant?: boolean,
24 | ): Promise {
25 | if (createTenant) {
26 | await createTenantByObject(db, { id: uuid(), abbreviation: logo.tenantId });
27 | }
28 | const createdLogos = await db.insert(logos).values(logo).returning();
29 |
30 | return createdLogos.length > 0 ? createdLogos[0] : null;
31 | }
32 |
33 | export async function getLogosByTenant(
34 | db: DbType,
35 | tenantAbbreviation: string,
36 | ): Promise {
37 | return db.select().from(logos).where(eq(logos.tenantId, tenantAbbreviation));
38 | }
39 |
--------------------------------------------------------------------------------
/microservices/apps/data-translation-service/src/data-translation.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ScheduleService } from './scheduler.service';
3 | import { ScheduleModule } from '@nestjs/schedule';
4 | import { PopulateValueService } from './populate/populate-value.service';
5 | import { PopulateCombinedWidgetService } from './populate/populate-combined-widget.service';
6 | import { PopulateChartService } from './populate/populate-chart.service';
7 | import { DataTranslationService } from './data-translation.service';
8 | import { DataTranslationRepo } from './data-translation.repo';
9 | import { PopulateMapService } from './populate/populate-map.service';
10 | import { PostgresDbModule } from '@app/postgres-db';
11 | import { JwtModule } from '@nestjs/jwt';
12 |
13 | @Module({
14 | imports: [
15 | ScheduleModule.forRoot(),
16 | PostgresDbModule,
17 | JwtModule.register({
18 | global: true,
19 | }),
20 | ],
21 | providers: [
22 | ScheduleService,
23 | DataTranslationService,
24 | DataTranslationRepo,
25 | PopulateChartService,
26 | PopulateCombinedWidgetService,
27 | PopulateValueService,
28 | PopulateMapService,
29 | ],
30 | })
31 | export class DataTranslationServiceModule {}
32 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/report/test/test-data.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SensorReport,
3 | sensorReports,
4 | } from '@app/postgres-db/schemas/sensor-report.schema';
5 | import { v4 as uuid } from 'uuid';
6 | import {
7 | createQuery,
8 | getNGSILiveQuery,
9 | } from '../../../../dashboard-service/src/query/test/test-data';
10 | import { DbType } from '@app/postgres-db';
11 |
12 | export async function getSensorReport(
13 | db: DbType,
14 | queryId?: string,
15 | ): Promise {
16 | if (!queryId) {
17 | const query = await createQuery(db, getNGSILiveQuery());
18 | queryId = query.id;
19 | }
20 |
21 | return {
22 | id: uuid(),
23 | queryId: queryId,
24 | propertyName: 'temperature',
25 | threshold: '20',
26 | trigger: 'falls below',
27 | recipients: ['test@smart-city.de'],
28 | mailText: 'This is a test e-mail.',
29 | createdAt: new Date(),
30 | updatedAt: new Date(),
31 | };
32 | }
33 |
34 | export async function createSensorReport(
35 | db: DbType,
36 | sensorReport: SensorReport,
37 | ): Promise {
38 | const results = await db
39 | .insert(sensorReports)
40 | .values(sensorReport)
41 | .returning();
42 | return results.length > 0 ? results[0] : null;
43 | }
44 |
--------------------------------------------------------------------------------
/frontend/assets/icons/index.tsx:
--------------------------------------------------------------------------------
1 | export { default as Snowflake } from './Snowflake';
2 | export { default as ForestFire } from './ForestFire';
3 | export { default as PartlyCloudy } from './PartlyCloudy';
4 | export { default as Waves } from './Waves';
5 | export { default as SoilMoisture } from './SoilMoisture';
6 | export { default as Trees } from './Trees';
7 | export { default as Climate } from './Climate';
8 | export { default as Heat } from './Heat';
9 | export { default as Mobility } from './Mobility';
10 | export { default as Info } from './Info';
11 | export { default as Emission } from './Emission';
12 | export { default as HumidityNormal } from './HumidityNormal';
13 | export { default as HumidityMedium } from './HumidityMedium';
14 | export { default as HumidityHigh } from './HumidityHigh';
15 | export { default as HumidityPercentage } from './HumidityPercentage';
16 | export { default as Dry } from './Dry';
17 | export { default as RemoteSoil } from './RemoteSoil';
18 | export { default as Pollen } from './Pollen';
19 | export { default as WaterLevelLow } from './WaterLevelLow';
20 | export { default as WaterLevelNormal } from './WaterLevelNormal';
21 | export { default as WaterLevelHigh } from './WaterLevelHigh';
22 | export { default as ActionLink } from './ActionLink';
23 |
--------------------------------------------------------------------------------
/frontend/providers/AuthWrapper.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import React, { ReactElement, useEffect } from 'react';
4 | import { useAuth } from 'react-oidc-context';
5 | import { useSnackbar } from './SnackBarFeedbackProvider';
6 | import Cookies from 'js-cookie';
7 |
8 | const AuthWrapper = ({
9 | children,
10 | }: {
11 | children: React.ReactNode;
12 | }): ReactElement => {
13 | const auth = useAuth();
14 | const { openSnackbar } = useSnackbar();
15 |
16 | useEffect(() => {
17 | if (auth.error) {
18 | openSnackbar(auth.error.toString(), 'error');
19 | }
20 | }, [auth.error, openSnackbar]);
21 |
22 | useEffect(() => {
23 | if (auth.isAuthenticated && auth.user) {
24 | Cookies.set('access_token', auth.user.access_token, {
25 | secure: true,
26 | sameSite: 'Strict',
27 | });
28 | }
29 | }, [auth.isAuthenticated, auth.user]);
30 |
31 | if (auth.activeNavigator === 'signinSilent') {
32 | return Signing you in...
;
33 | }
34 |
35 | if (auth.activeNavigator === 'signoutRedirect') {
36 | return Signing you out...
;
37 | }
38 |
39 | if (auth.isLoading) {
40 | return Loading...
;
41 | }
42 |
43 | return <>{children}>;
44 | };
45 |
46 | export default AuthWrapper;
47 |
--------------------------------------------------------------------------------
/frontend/ui/Icons/DashboardIcon.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | import { FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
3 | import FontAwesomeIcons from './FontAwesomeIcons';
4 | import LocalSvgIcons, { localSvgIconsList } from './LocalSvgIcons';
5 |
6 | type DashboardIcons = {
7 | iconName: string;
8 | color?: string;
9 | size?: FontAwesomeIconProps['size']; // FontAwesome icon size
10 | className?: string;
11 | height?: string; // For SVG icons
12 | };
13 |
14 | export default function DashboardIcon(props: DashboardIcons): ReactElement {
15 | const {
16 | iconName,
17 | color = '#fff',
18 | size = 'lg',
19 | className,
20 | height = '24',
21 | } = props;
22 |
23 | // Check if iconName is in localSvgIcons
24 | const isSvgIcon = localSvgIconsList.some((icon) => icon.name === iconName);
25 |
26 | if (isSvgIcon) {
27 | // Render local SVG icon
28 | return (
29 |
30 | );
31 | } else {
32 | // Render FontAwesome icon
33 | return (
34 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/microservices/apps/ngsi-service/src/ngsi.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AuthService } from './auth/auth.service';
3 | import { NgsiService } from './ngsi.service';
4 | import { HttpModule } from '@nestjs/axios';
5 | import { PostgresDbModule } from '@app/postgres-db';
6 | import { ScheduleService } from './scheduler.service';
7 | import { ScheduleModule } from '@nestjs/schedule';
8 | import { ReportModule } from './report/report.module';
9 | import { DataModule } from './data/data.module';
10 | import { QueryModule } from './query/query.module';
11 | import { FiwareWizardModule } from './fiware-wizard/fiware-wizard.module';
12 | import { FiwareWizardController } from './fiware-wizard/fiware-wizard.controller';
13 | import { FiwareWizardService } from './fiware-wizard/fiware-wizard.service';
14 | import { NgsiController } from './ngsi.controller';
15 |
16 | @Module({
17 | imports: [
18 | PostgresDbModule,
19 | HttpModule,
20 | ScheduleModule.forRoot(),
21 | ReportModule,
22 | DataModule,
23 | QueryModule,
24 | FiwareWizardModule,
25 | ],
26 | providers: [NgsiService, AuthService, ScheduleService, FiwareWizardService],
27 | controllers: [FiwareWizardController, NgsiController],
28 | })
29 | export class NgsiModule {}
30 |
--------------------------------------------------------------------------------
/k8s/helm/charts/migrations/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: migrations
3 | description: A Helm chart for Kubernetes
4 |
5 | # A chart can be either an 'application' or a 'library' chart.
6 | #
7 | # Application charts are a collection of templates that can be packaged into versioned archives
8 | # to be deployed.
9 | #
10 | # Library charts provide useful utilities or functions for the chart developer. They're included as
11 | # a dependency of application charts to inject those utilities and functions into the rendering
12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
13 | type: application
14 |
15 | # This is the chart version. This version number should be incremented each time you make changes
16 | # to the chart and its templates, including the app version.
17 | # Versions are expected to follow Semantic Versioning (https://semver.org/)
18 | version: 0.1.0
19 |
20 | # This is the version number of the application being deployed. This version number should be
21 | # incremented each time you make changes to the application. Versions are not expected to
22 | # follow Semantic Versioning. They should reflect the version the application is using.
23 | # It is recommended to use it with quotes.
24 | appVersion: "0.1.0"
25 |
26 | dependencies:
27 |
--------------------------------------------------------------------------------
/microservices/apps/infopin-service/src/utility/RoleUtil.ts:
--------------------------------------------------------------------------------
1 | import { Defect } from '@app/postgres-db/schemas/defect.schema';
2 | import { Report } from '@app/postgres-db/schemas/report.schema';
3 | import { ClimateProject } from '@app/postgres-db/schemas/climate-project.schema';
4 | import { NewDashboard } from '@app/postgres-db/schemas';
5 | import { AuthData } from '@app/postgres-db/schemas/auth-data.schema';
6 |
7 | export class RoleUtil {
8 | public static populateRoles(
9 | item: Defect | Report | ClimateProject | NewDashboard | AuthData,
10 | rolesFromRequest: string[],
11 | ): void {
12 | if (item.visibility === 'protected') {
13 | if (this.isRoleEmpty(item.writeRoles)) {
14 | item.writeRoles = rolesFromRequest;
15 | }
16 | if (this.isRoleEmpty(item.readRoles)) {
17 | item.readRoles = rolesFromRequest;
18 | }
19 | } else if (item.visibility === 'invisible') {
20 | item.writeRoles = rolesFromRequest;
21 | item.readRoles = rolesFromRequest;
22 | } else if (item.visibility === 'public') {
23 | item.writeRoles = [];
24 | item.readRoles = [];
25 | }
26 | }
27 |
28 | private static isRoleEmpty(roles: string[]): boolean {
29 | return roles === undefined || roles.length === 0;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/microservices/apps/dashboard-service/src/auth-data/auth-data.module.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MiddlewareConsumer,
3 | Module,
4 | NestModule,
5 | RequestMethod,
6 | } from '@nestjs/common';
7 | import { AuthDataController } from './auth-data.controller';
8 | import { DataSourceService } from '../data-source/data-source.service';
9 | import { AuthGuard } from '@app/auth-helper/AuthGuard';
10 | import { AuthDataRepo } from './auth-data.repo';
11 | import { DataSourceRepo } from '../data-source/data-source.repo';
12 | import { AuthDataService } from './auth-data.service';
13 | import { AuthHelperMiddleware, AuthHelperUtility } from '@app/auth-helper';
14 |
15 | @Module({
16 | providers: [
17 | AuthDataService,
18 | AuthDataRepo,
19 | DataSourceService,
20 | AuthGuard,
21 | DataSourceRepo,
22 | AuthHelperUtility,
23 | ],
24 | controllers: [AuthDataController],
25 | exports: [AuthDataRepo],
26 | })
27 | export class AuthDataModule implements NestModule {
28 | configure(consumer: MiddlewareConsumer): void {
29 | // Apply the AuthHelperMiddleware to protect specific routes
30 | consumer.apply(AuthHelperMiddleware).forRoutes(
31 | { path: 'auth-datas*', method: RequestMethod.ALL }, // Protect all methods in the "auth-datas" route
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/frontend/app/(dashboard)/layout.tsx:
--------------------------------------------------------------------------------
1 | import { PublicEnvScript } from 'next-runtime-env';
2 | import { JSX } from 'react';
3 |
4 | import '../globals.css';
5 | import AuthenticationProvider from '@/providers/AuthenticationProvider';
6 | import TanStackQueryProvider from '@/providers/TanStackQueryProvider';
7 | import { SnackbarProvider } from '@/providers/SnackBarFeedbackProvider';
8 |
9 | export const dynamic = 'force-dynamic'; // Needed to avoid data fetching during build
10 | export const runtime = 'edge';
11 |
12 | export default function RootLayout({
13 | children,
14 | }: {
15 | children: React.ReactNode;
16 | }): JSX.Element {
17 | return (
18 |
19 |
20 |
27 |
28 |
29 |
30 |
31 |
32 | {children}
33 |
34 |
35 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/frontend/app/custom-hooks/useAutoScaleFont.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | type UseAutoScaleFontProps = {
4 | minSize: number;
5 | maxSize: number;
6 | divisor: number;
7 | containerRef?: React.RefObject; // Optional reference to container
8 | };
9 |
10 | export default function useAutoScaleFont({
11 | minSize,
12 | maxSize,
13 | divisor,
14 | containerRef, // Pass a container ref if available
15 | }: UseAutoScaleFontProps): number {
16 | const [fontSize, setFontSize] = useState(minSize);
17 |
18 | useEffect(() => {
19 | const handleResize = (): void => {
20 | // Use container width if available, otherwise fallback to window width
21 | const containerWidth = containerRef?.current
22 | ? containerRef.current.offsetWidth
23 | : window.innerWidth;
24 |
25 | const newFontSize = Math.max(
26 | minSize,
27 | Math.min(maxSize, containerWidth / divisor),
28 | );
29 |
30 | setFontSize(newFontSize);
31 | };
32 |
33 | handleResize();
34 |
35 | window.addEventListener('resize', handleResize);
36 |
37 | return () => {
38 | window.removeEventListener('resize', handleResize);
39 | };
40 | }, [minSize, maxSize, divisor, containerRef]);
41 |
42 | return fontSize;
43 | }
44 |
--------------------------------------------------------------------------------
/frontend/ui/Buttons/RedirectPageButton.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 | import { ReactElement } from 'react';
3 |
4 | import DashboardIcons from '../Icons/DashboardIcon';
5 | import Link from 'next/link';
6 |
7 | type RedirectPageButtonProps = {
8 | url: string;
9 | isShortStyle?: boolean;
10 | headerPrimaryColor?: string;
11 | headerFontColor?: string;
12 | };
13 |
14 | export default function RedirectPageButton(
15 | props: RedirectPageButtonProps,
16 | ): ReactElement {
17 | const {
18 | url,
19 | isShortStyle = false,
20 | headerPrimaryColor,
21 | headerFontColor,
22 | } = props;
23 |
24 | const jumpoffButtonStyle = {
25 | backgroundColor: headerPrimaryColor || '#3D4760',
26 | color: headerFontColor || '#FFF',
27 | fontSize: '1rem',
28 | };
29 |
30 | return (
31 |
32 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------