From a1c4895aaf821cde3bf7df185d2385efa3464664 Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Tue, 20 Jan 2026 14:59:39 +0700 Subject: [PATCH 1/8] feat: assign drone to org --- .env.example | 68 ++++++++----- CLA.md | 67 +++++++++++++ LICENSE | 202 +++++++++++++++++++++++++++++++++++++++ README.md | 67 ++++++------- auth-service | 2 +- bootstrap-service | 2 +- broker-bridge-service | 2 +- dashboard-service | 2 +- device-service | 2 +- django-common-utils | 2 +- docker-compose.yml | 211 +++++++++++++++++++++++++---------------- docs/swagger-server.js | 1 + entrypoint.sh | 44 +++++++++ haproxy | 2 +- mpa-service | 2 +- requirements-dev.txt | 4 +- setup.cfg | 4 +- spacedf-admin-portal | 1 + spacedf-web-app | 1 + telemetry-service | 2 +- transformer-service | 2 +- 21 files changed, 536 insertions(+), 154 deletions(-) create mode 100644 CLA.md create mode 100644 LICENSE create mode 100755 entrypoint.sh create mode 160000 spacedf-admin-portal create mode 160000 spacedf-web-app diff --git a/.env.example b/.env.example index 6d30773..ab5d09d 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ # RabbitMQ -RABBITMQ_DEFAULT_USER="__RABBITMQ_DEFAULT_USER__" -RABBITMQ_DEFAULT_PASS="__RABBITMQ_DEFAULT_PASS__" +RABBITMQ_DEFAULT_USER="default" +RABBITMQ_DEFAULT_PASS="password" # Authentication JWT JWT_PRIVATE_KEY="__JWT_PRIVATE_KEY__" @@ -20,7 +20,7 @@ APPLE_CERTIFICATE_KEY="__APPLE_CERTIFICATE_KEY__" # Auth Service AUTH_POSTGRES_PASSWORD="__AUTH_POSTGRES_PASSWORD__" AUTH_SECRET_KEY="__AUTH_SECRET_KEY__" -DEFAULT_TENANT_HOST="__DEFAULT_TENANT_HOST__" +DEFAULT_TENANT_HOST="localhost" ROOT_API_KEY="__ROOT_API_KEY__" # S3 Service @@ -30,45 +30,67 @@ AWS_STORAGE_BUCKET_NAME="spacedf-s3-1f841081-c8e98ef7bb21" AWS_REGION="__AWS_REGION__" # Redis Service -REDIS_HOST="__REDIS_HOST__" +REDIS_HOST="redis://redis:6379/1" # Dashboard Service -DASHBOARD_POSTGRES_PASSWORD="__DASHBOARD_POSTGRES_PASSWORD__" +DASHBOARD_POSTGRES_PASSWORD="postgres" DASHBOARD_SECRET_KEY="__DASHBOARD_SECRET_KEY__" # Device Service -DEVICE_POSTGRES_PASSWORD="__DEVICE_POSTGRES_PASSWORD__" +DEVICE_POSTGRES_PASSWORD="postgres" DEVICE_SECRET_KEY="__DEVICE_SECRET_KEY__" -TELEMETRY_SERVICE_URL="__TELEMETRY_SERVICE_URL__" +TELEMETRY_SERVICE_URL="http://telemetry:8080" # EMQX Service -EMQX_USERNAME="__EMQX_USERNAME__" -EMQX_PASSWORD="__EMQX_PASSWORD__" +EMQX_USERNAME="user" +EMQX_PASSWORD="password123" # Broker Bridge Service -MQTT_BROKER_BRIDGE_USERNAME="__MQTT_BROKER_BRIDGE_USERNAME__" -MQTT_BROKER_BRIDGE_PASSWORD="__MQTT_BROKER_BRIDGE_PASSWORD__" -MQTT_TOPICS="__MQTT_TOPICS__" +MQTT_BROKER_BRIDGE_USERNAME="BrokerBridgeService" +MQTT_BROKER_BRIDGE_PASSWORD="Default@1234" +MQTT_TOPICS="tenant/+/transformed/device/location" # Acount AWS to access to SES service EMAIL_BACKEND="__EMAIL_BACKEND__" EMQX_HOST="__EMQX_HOST__" EMAIL_PORT="__EMAIL_PORT__" EMAIL_USE_TLS="__EMAIL_USE_TLS__" +# Acount AWS to access to SES service EMAIL_HOST_USER="__EMAIL_HOST_USER__" EMAIL_HOST_PASSWORD="__EMAIL_HOST_PASSWORD__" -DEFAULT_FROM_EMAIL="__DEFAULT_FROM_EMAIL__" +DEFAULT_FROM_EMAIL="no-reply@gmail.com" # MPA service -MQTT_BROKER="__MQTT_BROKER__" -MQTT_USERNAME="__MQTT_USERNAME__" -MQTT_PASSWORD="__MQTT_USERNAME__" -MQTT_PORT="__MQTT_PORT__" -MQTT_CLIENT_ID="__MQTT_CLIENT_ID__" -MQTT_TOPIC="__MQTT_TOPIC__" +MQTT_BROKER="emqx" +MQTT_USERNAME="MPAService" +MQTT_PASSWORD="Default@1234" +MQTT_PORT="1883" +MQTT_CLIENT_ID="mpa-service-mqtt-bridge" +MQTT_TOPIC="tenant/{tenant}/device/data" + +# Bootstrap service +HOST="http://localhost:8000" +BOOTSTRAP_POSTGRES_PASSWORD="postgres" +CORS_ALLOWED_ORIGINS="http://localhost,http://localhost:3000" +BOOTSTRAP_SECRET_KEY="__BOOTSTRAP_SECRET_KEY__" # Organization initialization -ORG_NAME="__ORG_NAME__" -ORG_SLUG="__ORG_SLUG__" -OWNER_EMAIL="__OWNER_EMAIL__" -OWNER_PASSWORD="__OWNER_PASSWORD__" \ No newline at end of file +ORG_NAME="Default Organization" +ORG_SLUG="default-org" +OWNER_EMAIL="admin@example.com" +OWNER_PASSWORD="changeme123#Test" + +# NextAuth configuration +PORTAL_NEXTAUTH_SECRET="__PORTAL_NEXTAUTH_SECRET__" +HOST_FRONTEND_ADMIN="http://localhost:3001" + +#This is the random key for nextauth - generate here: https://generate-secret.vercel.app/32 +DASHBOARD_NEXTAUTH_SECRET="__DASHBOARD_NEXTAUTH_SECRET__" +SPACE_API_KEY="__SPACE_API_KEY__" +MAPBOX_ACCESS_TOKEN="__MAPBOX_ACCESS_TOKEN__" +DASHBOARD_MQTT_USERNAME="anonymous" +DASHBOARD_MQTT_PASSWORD="password123" +DASHBOARD_MQTT_PROTOCOL="ws" +DASHBOARD_MQTT_PORT="8883" +DASHBOARD_MQTT_BROKER="emqx.spacedf.net" +HOST_FRONTEND="http://localhost:3000" \ No newline at end of file diff --git a/CLA.md b/CLA.md new file mode 100644 index 0000000..093c6b7 --- /dev/null +++ b/CLA.md @@ -0,0 +1,67 @@ +# Contributor License Agreement (CLA) + +**Version 1.0** + +This Contributor License Agreement (“**Agreement**”) is entered into by **you** (“**Contributor**”) and **Digital Fortress** (“**Company**”) regarding your contributions to the **SpaceDF** project (“**Project**”). + +By submitting any Contribution to the Project, you agree to the following terms: + +## 1. Definitions + +- **“Contribution”** means any source code, documentation, design, or other material submitted by you to the Project. +- **“Submit”** means any form of electronic, written, or verbal communication intended to be included in the Project, including but not limited to pull requests, patches, issues, or comments. + +## 2. Copyright Ownership + +- You retain ownership of the copyright in your Contributions. +- Nothing in this Agreement transfers ownership of your intellectual property to the Company. + +## 3. License Grant + +You grant **Digital Fortress** a **perpetual, worldwide, non-exclusive, royalty-free, and irrevocable license** to: + +- Use +- Modify +- Distribute +- Re-license +- Sublicense +- Commercialize + +your Contributions as part of the Project or in any related products or services. + +This includes, but is not limited to, use in **proprietary**, **SaaS**, and **enterprise** offerings. + +## 4. Patent Grant + +You grant Digital Fortress a **perpetual, worldwide, royalty-free license** to any patent claims you own that are necessarily infringed by your Contributions. + +## 5. Representations + +You represent and warrant that: + +- You have the legal right to submit the Contributions. +- The Contributions do not violate or infringe upon any third-party rights. +- If your employer or organization has intellectual property policies, you have obtained all necessary permissions to make the Contributions. + +## 6. No Obligation + +The Company is **not obligated** to: + +- Accept your Contributions. +- Provide any form of compensation. +- Include your Contributions in any release or distribution. + +## 7. Public Attribution + +The Company **may**, but is not required to, publicly acknowledge or attribute your Contributions. + +## 8. License Compatibility + +- Your Contributions will be licensed to users under the Project’s open-source license (e.g., **Apache License 2.0**). +- This Agreement governs only the relationship between you and the Company and does not modify the Project’s open-source license. + +## 9. Governing Law + +This Agreement shall be governed by and construed in accordance with the laws of **Vietnam**. + +By submitting a Contribution, you confirm that you have read, understood, and agree to the terms of this Agreement. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..10398cd --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 Digital Fortress + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index 83eaea1..6efdd27 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,4 @@ -

- - - - Digital Fortress logo - - -

- ---- - -# Django template +# SpaceDF Core ## Prerequisites - [Docker](https://www.docker.com/) @@ -43,18 +32,43 @@ brew install docker-compose ### Launch ```commandline -docker-compose up +chmod +x entrypoint.sh +./entrypoint.sh ``` ### API documentation http://localhost/docs -### Default Local Database +## Services Overview + +### Python Django Services +- **Auth Service** - Authentication and OAuth credentials management +- **Bootstrap Service** - Initial setup and configuration service +- **Dashboard Service** - Dashboard and UI backend +- **Device Service** - Device management service +- **Django Common Utils** - Shared utilities library + +### Go Services +- **Broker Bridge Service** - Bridge between message brokers +- **MPA Service** - Multi-Protocol Adapter service +- **Telemetry Service** - Telemetry data collection and processing +- **Transformer Service** - Data transformation service + +### Infrastructure +- **EMQX** - MQTT message broker +- **HAProxy** - Load balancer and reverse proxy + +### Default Local Database Configuration #### Auth service - Host: `localhost:5434` - Username: `postgres` - Password: `postgres` - Database: `auth_service` +#### Bootstrap service +- Host: `localhost:5433` +- Username: `postgres` +- Password: `postgres` +- Database: `bootstrap_service` #### Dashboard service - Host: `localhost:5435` - Username: `postgres` @@ -72,26 +86,7 @@ http://localhost/docs - Database: `spacedf_telemetry` ## License +Licensed under the Apache License, Version 2.0 +See the LICENSE file for details. -This project is Copyright (c) 2023 and onwards Digital Fortress. It is free software and may be redistributed under the terms specified in the [LICENSE] file. - -[LICENSE]: /LICENSE - -## About - - - - Digital Fortress logo - - - -This project is made and maintained by Digital Fortress. - -We are an experienced team in R&D, software, hardware, cross-platform mobile and DevOps. - -See more of [our projects][projects] or do you need to complete one? - --> [Let’s connect with us][website] - -[projects]: https://github.com/digitalfortress-dev -[website]: https://www.digitalfortress.dev +[![SpaceDF - A project from Digital Fortress](https://df.technology/images/SpaceDF.png)](https://df.technology/) diff --git a/auth-service b/auth-service index 4f2da05..9a09697 160000 --- a/auth-service +++ b/auth-service @@ -1 +1 @@ -Subproject commit 4f2da059c7530dc775ecf63310fc70ff8c815448 +Subproject commit 9a09697067dc4d1768959413b9c823d1eaa263a5 diff --git a/bootstrap-service b/bootstrap-service index f3c92d6..6cf7cbf 160000 --- a/bootstrap-service +++ b/bootstrap-service @@ -1 +1 @@ -Subproject commit f3c92d60b5253edeabe08eb8d149c2343d48abea +Subproject commit 6cf7cbff781b3b0ec42b2257ab975e61314ad49d diff --git a/broker-bridge-service b/broker-bridge-service index 5f6d962..9773652 160000 --- a/broker-bridge-service +++ b/broker-bridge-service @@ -1 +1 @@ -Subproject commit 5f6d9623a17d213fb6f882cd5a7a4b2c51358aee +Subproject commit 9773652782e0a0abbfc20c963a76f25de2277813 diff --git a/dashboard-service b/dashboard-service index c4adc84..00af685 160000 --- a/dashboard-service +++ b/dashboard-service @@ -1 +1 @@ -Subproject commit c4adc84611c7c967737a9a6c98615c48d7fa4afa +Subproject commit 00af685d19e5b6a9040abc089b5d2a14cd6974be diff --git a/device-service b/device-service index 9a6a0ee..f2cd7d5 160000 --- a/device-service +++ b/device-service @@ -1 +1 @@ -Subproject commit 9a6a0ee999d306cb4e03f21cff16be5faa53cec9 +Subproject commit f2cd7d5cf661893bd35d86b495009e94126ab659 diff --git a/django-common-utils b/django-common-utils index fc25176..f05d9ec 160000 --- a/django-common-utils +++ b/django-common-utils @@ -1 +1 @@ -Subproject commit fc25176ab93ccc94e7a12dd3499c28fdbc72b9fb +Subproject commit f05d9ec7d02204ba78dcdbfb968571702440928b diff --git a/docker-compose.yml b/docker-compose.yml index b529f09..be238dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,17 +12,18 @@ services: - "15672:15672" command: > bash -c " - rabbitmq-plugins enable rabbitmq_mqtt && + rabbitmq-plugins enable --offline rabbitmq_mqtt && rabbitmq-server " volumes: - rabbitmq_data:/var/lib/rabbitmq + restart: unless-stopped healthcheck: - test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"] + test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"] interval: 10s - timeout: 30s + timeout: 60s retries: 5 - start_period: 60s + start_period: 100s # EMQX emqx: @@ -81,13 +82,31 @@ services: - emqx-etc:/opt/emqx/etc depends_on: rabbitmq: - condition: service_started + condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "emqx", "ctl", "status"] interval: 10s timeout: 5s retries: 5 + + bootstrap_postgres: + container_name: bootstrap_postgres + hostname: bootstrap_postgres + image: postgres:15 + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=${BOOTSTRAP_POSTGRES_PASSWORD} + - POSTGRES_DB=bootstrap_service + ports: + - "5433:5432" + volumes: + - bootstrap-postgres-vlm:/var/lib/postgresql/data + healthcheck: + test: "pg_isready --username=$$POSTGRES_USER && psql --username=$$POSTGRES_USER --list" + timeout: 10s + retries: 20 + # bootstrap service bootstrap: container_name: bootstrap_service @@ -95,13 +114,30 @@ services: context: . dockerfile: ./bootstrap-service/Dockerfile environment: - ENV: dev + ENV: ${ENV} EMQX_USERNAME: ${EMQX_USERNAME} EMQX_PASSWORD: ${EMQX_PASSWORD} ORG_NAME: ${ORG_NAME} ORG_SLUG: ${ORG_SLUG} OWNER_EMAIL: ${OWNER_EMAIL} OWNER_PASSWORD: ${OWNER_PASSWORD} + CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS} + REDIS_HOST: ${REDIS_HOST} + HOST: ${HOST} + DB_NAME: bootstrap_service + DB_USERNAME: postgres + DB_PASSWORD: ${BOOTSTRAP_POSTGRES_PASSWORD} + DB_HOST: bootstrap_postgres + DB_PORT: 5432 + JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY} + JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + AWS_STORAGE_BUCKET_NAME: ${AWS_STORAGE_BUCKET_NAME} + AWS_REGION: ${AWS_REGION} + DEFAULT_FROM_EMAIL: ${DEFAULT_FROM_EMAIL} + EMAIL_HOST_USER: ${EMAIL_HOST_USER} + EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD} RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER} RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS} RABBITMQ_URL: amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq @@ -121,6 +157,7 @@ services: condition: service_healthy telemetry: condition: service_healthy + restart: unless-stopped # Auth database auth_postgres: @@ -143,11 +180,10 @@ services: # Auth service auth: container_name: auth_service - build: - context: . - dockerfile: ./auth-service/Dockerfile + image: ghcr.io/space-df/auth-service:latest + pull_policy: always environment: - ENV: dev + ENV: ${ENV} SECRET_KEY: ${AUTH_SECRET_KEY} DB_NAME: auth_service DB_USERNAME: postgres @@ -183,14 +219,15 @@ services: auth_postgres: condition: service_healthy rabbitmq: - condition: service_started + condition: service_healthy + restart: unless-stopped healthcheck: test: ["CMD-SHELL", "curl -fsS http://localhost/auth/api/health || exit 1"] interval: 5s timeout: 5s retries: 5 - start_period: 60s + start_period: 100s # Dashboard database dashboard_postgres: @@ -213,11 +250,10 @@ services: # Dashboard service dashboard: container_name: dashboard_service - build: - context: . - dockerfile: ./dashboard-service/Dockerfile + image: ghcr.io/space-df/dashboard-service:latest + pull_policy: always environment: - ENV: dev + ENV: ${ENV} SECRET_KEY: ${DASHBOARD_SECRET_KEY} DB_NAME: dashboard_service DB_USERNAME: postgres @@ -236,7 +272,8 @@ services: dashboard_postgres: condition: service_healthy rabbitmq: - condition: service_started + condition: service_healthy + restart: unless-stopped healthcheck: test: [ @@ -246,7 +283,7 @@ services: interval: 5s timeout: 5s retries: 5 - start_period: 60s + start_period: 100s # Device database device_postgres: @@ -269,11 +306,10 @@ services: # Device service device: container_name: device_service - build: - context: . - dockerfile: ./device-service/Dockerfile + image: ghcr.io/space-df/device-service:latest + pull_policy: always environment: - ENV: dev + ENV: ${ENV} SECRET_KEY: ${DEVICE_SECRET_KEY} DB_NAME: device_service DB_USERNAME: postgres @@ -296,21 +332,21 @@ services: device_postgres: condition: service_healthy rabbitmq: - condition: service_started + condition: service_healthy + restart: unless-stopped healthcheck: test: ["CMD-SHELL", "curl -fsS http://localhost/device/api/health || exit 1"] interval: 5s timeout: 5s retries: 5 - start_period: 60s + start_period: 100s # Transformer service (Go) - RabbitMQ Consumer transformer: container_name: transformer_service - build: - context: ./transformer-service - dockerfile: Dockerfile + image: ghcr.io/space-df/transformer-service:latest + pull_policy: always hostname: transformer environment: - AMQP_BROKER_URL=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/ @@ -334,7 +370,7 @@ services: - DEVICE_CACHE_REDIS_DIAL_TIMEOUT_MS=2000 depends_on: rabbitmq: - condition: service_started + condition: service_healthy emqx: condition: service_started links: @@ -345,47 +381,11 @@ services: - ./transformer-service:/transformer-service - transformer-logs:/app/logs - # Logrotate service for transformer logs - transformer-logrotate: - container_name: transformer_logrotate - image: alpine:3.18 - hostname: transformer-logrotate - environment: - - TZ=UTC - volumes: - - transformer-logs:/app/logs - - ./transformer-service/logrotate.conf:/etc/logrotate.d/transformer:ro - command: > - sh -c " - apk add --no-cache logrotate dcron && - echo '0 0 * * * /usr/sbin/logrotate -f /etc/logrotate.d/transformer' > /etc/crontabs/root && - crond -f -L /dev/stdout - " - restart: unless-stopped - privileged: true - depends_on: - - transformer - - # Transformer logs HTTP server for auto-loading logs in visualization tool - transformer-logs-server: - image: python:3.12-alpine - container_name: transformer_logs_server - working_dir: /srv - command: python3 -m http.server 8081 - volumes: - - ./transformer-service:/srv:ro - ports: - - "8081:8081" - restart: unless-stopped - depends_on: - - transformer - # Broker Bridge Service - AMQP to EMQX MQTT bridge broker-bridge: container_name: broker_bridge_service - build: - context: ./broker-bridge-service - dockerfile: Dockerfile + image: ghcr.io/space-df/broker-bridge-service:latest + pull_policy: always hostname: broker-bridge environment: - MQTT_BROKER=emqx @@ -418,16 +418,23 @@ services: build: context: . dockerfile: ./haproxy/Dockerfile - args: - HOST: ${HOST} - JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY} + networks: + default: + aliases: + - default-org.haproxy + environment: + ENV: ${ENV} + HOST: ${HOST} + JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY} hostname: haproxy ports: - - "3000:3000" + - "8000:3000" - "443:443" - "8884:8884" - "8883:8883" depends_on: + bootstrap: + condition: service_started auth: condition: service_started dashboard: @@ -441,14 +448,16 @@ services: docs: condition: service_started links: + - bootstrap - auth - dashboard - device - telemetry - mpa - docs + restart: unless-stopped volumes: - - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg + - ./haproxy/config:/usr/local/etc/haproxy/config - ./haproxy/handlers:/usr/local/etc/haproxy/handlers - ./haproxy/routes:/usr/local/share/lua/5.4/routes - ./haproxy/certs:/usr/local/etc/haproxy/certs:ro @@ -467,9 +476,8 @@ services: # MPA Service mpa: container_name: mpa_service - build: - context: ./mpa-service - dockerfile: Dockerfile + image: ghcr.io/space-df/mpa-service:latest + pull_policy: always hostname: mpa environment: - MQTT_BROKER=${MQTT_BROKER} @@ -491,7 +499,7 @@ services: - rabbitmq restart: unless-stopped healthcheck: - test: "curl --silent --fail http://localhost:80/health > /dev/null || exit 1" + test: "curl --silent --fail http://localhost/health > /dev/null || exit 1" interval: 30s timeout: 10s retries: 3 @@ -516,13 +524,12 @@ services: retries: 20 restart: unless-stopped - # Telemetry Service - Consumes device telemetry data and stores in TimescaleDB + # Telemetry Service telemetry: container_name: telemetry_service hostname: telemetry - build: - context: ./telemetry-service - dockerfile: Dockerfile + image: ghcr.io/space-df/telemetry-service:latest + pull_policy: always environment: - AMQP_BROKER_URL=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/ - DB_NAME=spacedf_telemetry @@ -534,7 +541,7 @@ services: - "8080:8080" # API port depends_on: rabbitmq: - condition: service_started + condition: service_healthy timescaledb: condition: service_healthy restart: unless-stopped @@ -551,7 +558,7 @@ services: interval: 30s timeout: 10s retries: 3 - start_period: 30s + start_period: 100s docs: build: @@ -573,6 +580,47 @@ services: volumes: - ./docs:/docs + # Web Application + web: + image: ghcr.io/space-df/spacedf-web-app:latest + pull_policy: always + platform: linux/amd64 + environment: + NEXTAUTH_URL: ${DASHBOARD_NEXTAUTH_URL} + NEXTAUTH_SECRET: ${DASHBOARD_NEXTAUTH_SECRET} + MAPBOX_ACCESS_TOKEN: ${MAPBOX_ACCESS_TOKEN} + AUTH_API: ${PORTAL_AUTH_API} + SPACE_API_KEY: ${SPACE_API_KEY} + DASHBOARD_MQTT_USERNAME: ${DASHBOARD_MQTT_USERNAME} + DASHBOARD_MQTT_PASSWORD: ${DASHBOARD_MQTT_PASSWORD} + DASHBOARD_MQTT_PORT: ${DASHBOARD_MQTT_PORT} + DASHBOARD_MQTT_PROTOCOL: ${DASHBOARD_MQTT_PROTOCOL} + DASHBOARD_MQTT_BROKER: ${DASHBOARD_MQTT_BROKER} + DEBUG: "True" + ports: + - "3000:3000" + depends_on: + haproxy: + condition: service_started + restart: unless-stopped + + # Admin Portal Application + auth-portal: + image: ghcr.io/space-df/spacedf-admin-portal:latest + pull_policy: always + platform: linux/amd64 + environment: + NEXTAUTH_URL: ${PORTAL_NEXTAUTH_URL} + NEXTAUTH_SECRET: ${PORTAL_NEXTAUTH_SECRET} + AUTH_API: ${PORTAL_AUTH_API} + DEBUG: "True" + ports: + - "3001:3001" + depends_on: + haproxy: + condition: service_started + restart: unless-stopped + volumes: auth-postgres-vlm: dashboard-postgres-vlm: @@ -584,3 +632,4 @@ volumes: transformer-logs: rabbitmq_data: timescaledb-data: + bootstrap-postgres-vlm: diff --git a/docs/swagger-server.js b/docs/swagger-server.js index 7a27295..b4e5be4 100644 --- a/docs/swagger-server.js +++ b/docs/swagger-server.js @@ -7,6 +7,7 @@ const port = 3000; const openApiUrls = [ 'http://console/console/docs/?format=openapi', + 'http://bootstrap/bootstrap/docs/?format=openapi', 'http://auth/auth/docs/?format=openapi', 'http://dashboard/dashboard/docs/?format=openapi', 'http://device/device/docs/?format=openapi', diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..76cfd27 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -a +source .env +set +a + +API_URL="${HOST}/docs" +HOST_FRONTEND_ADMIN="${HOST_FRONTEND_ADMIN}" +SCHEME="$(echo "$HOST_FRONTEND" | sed -E 's#(https?://).*#\1#')" +DOMAIN="$(echo "$HOST_FRONTEND" | sed -E 's#https?://##')" +HOST_FRONTEND="${SCHEME}${ORG_SLUG}.${DOMAIN}" + +clear +echo -e "\033[38;5;208m" +cat <<'EOF' + ______ _____ ____ _______ _______ _____ _______ + / ___/ / __ \ / | / ____/ / ____/ / __ \ / ____/ + \__ \ / /_/ / / /| | / / / __/ / / / / / /_ + ___/ / / ____/ / ___ |/ /___ / /___ / /_/ / / __/ +/____/ /_/ /_/ |_|\____/ \____/ /_____/ /_/ +EOF +echo -e "\033[0m" +echo -e "\033[1;36mSpaceDF Core\033[0m" +echo -e "\033[0;36mCloud Native Platform\033[0m" +echo -e "${CYAN}Version: 1.0.0${RESET}" +echo + +# ===== Build & Start ===== +COMPOSE_FILE="./docker-compose.yml" +PROJECT_NAME="spacedf-core" +echo "🚀 Deploying SpaceDF Core..." +echo "⏹ Stopping existing services (if running)..." +docker compose -f "${COMPOSE_FILE}" -p "${PROJECT_NAME}" stop || true +echo "🔨 Building images & starting services..." +docker compose -f "${COMPOSE_FILE}" -p "${PROJECT_NAME}" up -d --build --remove-orphans +sleep 10 + +# ===== Success Message ===== +echo +echo -e "--------------------------------------------------" +echo -e "SpaceDF Core started successfully" +echo "🌐 Frontend Admin : ${HOST_FRONTEND_ADMIN}" +echo "🌐 Frontend Dashboard : ${HOST_FRONTEND}" +echo "🔗 Backend API : ${API_URL}" +echo -e "--------------------------------------------------" \ No newline at end of file diff --git a/haproxy b/haproxy index d639316..0fba46e 160000 --- a/haproxy +++ b/haproxy @@ -1 +1 @@ -Subproject commit d639316cd0a3d9b0f1db85c1aec6ace363f97e1b +Subproject commit 0fba46ef6f2c1886bc2c997c00ccc44202d38546 diff --git a/mpa-service b/mpa-service index 68576ef..3981984 160000 --- a/mpa-service +++ b/mpa-service @@ -1 +1 @@ -Subproject commit 68576ef65923d0bac1498c46007e34f6337210f3 +Subproject commit 39819848ddcfa43cd50b5b2a369b3a658c7bc84c diff --git a/requirements-dev.txt b/requirements-dev.txt index e891ef5..ed81684 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,7 @@ Django==5.0.6 django-environ==0.11.2 gunicorn==22.0.0 -gevent==24.2.1 +gevent django_tenants==3.6.1 celery==5.4.0 cryptography==43.0.3 @@ -10,7 +10,7 @@ authlib==1.3.1 python-dotenv==1.0.1 # For the persistence stores -psycopg2-binary==2.9.9 +psycopg2-binary # Rest apis djangorestframework==3.15.2 diff --git a/setup.cfg b/setup.cfg index 01e8981..3916337 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules,venv [isort] line_length = 88 -known_first_party = console-service +known_first_party = bootstrap-service multi_line_output = 3 default_section = THIRDPARTY skip = venv/ @@ -14,7 +14,7 @@ force_grid_wrap = 0 use_parentheses = true [coverage:run] -include = console-service/* +include = bootstrap-service/* omit = *migrations*, *tests* plugins = django_coverage_plugin diff --git a/spacedf-admin-portal b/spacedf-admin-portal new file mode 160000 index 0000000..7ae0300 --- /dev/null +++ b/spacedf-admin-portal @@ -0,0 +1 @@ +Subproject commit 7ae0300d863f0aae6bd5b4edf03f24bb52aa01ad diff --git a/spacedf-web-app b/spacedf-web-app new file mode 160000 index 0000000..5d190e8 --- /dev/null +++ b/spacedf-web-app @@ -0,0 +1 @@ +Subproject commit 5d190e8d461a735e210911d59dddc7521008fa60 diff --git a/telemetry-service b/telemetry-service index 46d07ae..1604c04 160000 --- a/telemetry-service +++ b/telemetry-service @@ -1 +1 @@ -Subproject commit 46d07aed1b625419d1d3e4060eb634b31f51a9e9 +Subproject commit 1604c041cedc24e6fcef439aadd25bc58633c905 diff --git a/transformer-service b/transformer-service index 2b1e6cf..3a124d0 160000 --- a/transformer-service +++ b/transformer-service @@ -1 +1 @@ -Subproject commit 2b1e6cfe98035ce6da566bfcd0c70f781052ee90 +Subproject commit 3a124d01b826611c5999d217d16b704ea9fd473d From 5b7debcdb223d82b87310104ec6be712cad03a41 Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Thu, 22 Jan 2026 15:40:38 +0700 Subject: [PATCH 2/8] feat: implement license and update docker compose --- .env.example | 22 ++-- README.md | 299 ++++++++++++++++++++++++++++++++++++++++--- device-service | 2 +- docker-compose.yml | 10 +- entrypoint.sh | 22 ++-- spacedf-admin-portal | 1 - spacedf-web-app | 1 - 7 files changed, 310 insertions(+), 47 deletions(-) delete mode 160000 spacedf-admin-portal delete mode 160000 spacedf-web-app diff --git a/.env.example b/.env.example index ab5d09d..d3f2c96 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ +# Environment +ENV="local" + # RabbitMQ RABBITMQ_DEFAULT_USER="default" RABBITMQ_DEFAULT_PASS="password" @@ -18,7 +21,7 @@ APPLE_CLIENT_KEY="__APPLE_CLIENT_KEY__" APPLE_CERTIFICATE_KEY="__APPLE_CERTIFICATE_KEY__" # Auth Service -AUTH_POSTGRES_PASSWORD="__AUTH_POSTGRES_PASSWORD__" +AUTH_POSTGRES_PASSWORD="postgres" AUTH_SECRET_KEY="__AUTH_SECRET_KEY__" DEFAULT_TENANT_HOST="localhost" ROOT_API_KEY="__ROOT_API_KEY__" @@ -26,8 +29,8 @@ ROOT_API_KEY="__ROOT_API_KEY__" # S3 Service AWS_ACCESS_KEY_ID="__AWS_ACCESS_KEY_ID__" AWS_SECRET_ACCESS_KEY="__AWS_SECRET_ACCESS_KEY__" -AWS_STORAGE_BUCKET_NAME="spacedf-s3-1f841081-c8e98ef7bb21" -AWS_REGION="__AWS_REGION__" +AWS_STORAGE_BUCKET_NAME="__AWS_STORAGE_BUCKET_NAME__" +AWS_REGION="us-east-1" # Redis Service REDIS_HOST="redis://redis:6379/1" @@ -42,6 +45,7 @@ DEVICE_SECRET_KEY="__DEVICE_SECRET_KEY__" TELEMETRY_SERVICE_URL="http://telemetry:8080" # EMQX Service +EMQX_HOST="http://emqx:18083/api/v5" EMQX_USERNAME="user" EMQX_PASSWORD="password123" @@ -52,9 +56,9 @@ MQTT_TOPICS="tenant/+/transformed/device/location" # Acount AWS to access to SES service EMAIL_BACKEND="__EMAIL_BACKEND__" -EMQX_HOST="__EMQX_HOST__" EMAIL_PORT="__EMAIL_PORT__" EMAIL_USE_TLS="__EMAIL_USE_TLS__" + # Acount AWS to access to SES service EMAIL_HOST_USER="__EMAIL_HOST_USER__" EMAIL_HOST_PASSWORD="__EMAIL_HOST_PASSWORD__" @@ -71,7 +75,7 @@ MQTT_TOPIC="tenant/{tenant}/device/data" # Bootstrap service HOST="http://localhost:8000" BOOTSTRAP_POSTGRES_PASSWORD="postgres" -CORS_ALLOWED_ORIGINS="http://localhost,http://localhost:3000" +CORS_ALLOWED_ORIGINS="http://localhost,http://localhost:3000,http://localhost:3001" BOOTSTRAP_SECRET_KEY="__BOOTSTRAP_SECRET_KEY__" # Organization initialization @@ -83,14 +87,16 @@ OWNER_PASSWORD="changeme123#Test" # NextAuth configuration PORTAL_NEXTAUTH_SECRET="__PORTAL_NEXTAUTH_SECRET__" HOST_FRONTEND_ADMIN="http://localhost:3001" +PORTAL_NEXTAUTH_URL="http://localhost:3001" +PORTAL_AUTH_API="http://haproxy:3000" #This is the random key for nextauth - generate here: https://generate-secret.vercel.app/32 DASHBOARD_NEXTAUTH_SECRET="__DASHBOARD_NEXTAUTH_SECRET__" SPACE_API_KEY="__SPACE_API_KEY__" -MAPBOX_ACCESS_TOKEN="__MAPBOX_ACCESS_TOKEN__" +MAPBOX_ACCESS_TOKEN="pk.eyJ1IjoiZGYtdGVjaG5vbG9neSIsImEiOiJjbWpnczVucWUxNnRvM2VzbGk4em15MHNnIn0.kX9GwE6Hd36gc9uQoolyFA" DASHBOARD_MQTT_USERNAME="anonymous" DASHBOARD_MQTT_PASSWORD="password123" DASHBOARD_MQTT_PROTOCOL="ws" DASHBOARD_MQTT_PORT="8883" -DASHBOARD_MQTT_BROKER="emqx.spacedf.net" -HOST_FRONTEND="http://localhost:3000" \ No newline at end of file +DASHBOARD_MQTT_BROKER="emqx.localhost:8000" +DASHBOARD_NEXTAUTH_URL="http://localhost:3000" \ No newline at end of file diff --git a/README.md b/README.md index 6efdd27..c9e799f 100644 --- a/README.md +++ b/README.md @@ -2,42 +2,301 @@ ## Prerequisites - [Docker](https://www.docker.com/) -- Docker compose +- [Docker Compose](https://docs.docker.com/compose/install/) -## Clone source code +## Quick Start -``` +### 1. Clone the Repository + +```bash git clone -b dev --recurse-submodules git@github.com:Space-DF/spacedf-backend.git +cd spacedf-backend +git submodule update --init --recursive ``` -To update submodules in exist directory +### 2. Create Environment Configuration -``` -git submodule update --init --recursive +Create `.env` file from `.env.example`: + +```bash +cp .env.example .env ``` -## Setup +**Essential variables to configure:** -### Setup environment +```bash +# Organization +ORG_NAME="Your Organization" +ORG_SLUG="your-org" +OWNER_EMAIL="admin@example.com" +OWNER_PASSWORD="SecurePass123#" -1. Setup [Docker](https://www.docker.com/) -```commandline -brew install colima -colima start -``` -2. Setup docker-compose -```commandline -brew install docker-compose +# Auto-generate JWT keys (run these commands to generate) +# openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_key_size:2048 +# openssl rsa -in private_key.pem -pubout -out public_key.pem +# Then paste the contents of private_key.pem and public_key.pem below: + +JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" +JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" ``` -### Launch -```commandline +For detailed information on all environment variables, see the **Environment Configuration (.env)** section below. + +### 3. Start Services + +```bash chmod +x entrypoint.sh ./entrypoint.sh ``` -### API documentation -http://localhost/docs +### 4. Access Services + +Once ready, access: +- **Frontend Admin**: http://localhost:3001 +- **Frontend Dashboard**: http://your-org.localhost:3000 +- **API Docs**: http://localhost:8000/docs +- **EMQX Dashboard**: http://localhost:18083 +- **RabbitMQ**: http://localhost:15672 + +## Common Commands + +```bash +# View logs +docker-compose -f docker-compose.yml -p spacedf-core logs -f + +# Stop services +docker-compose -f docker-compose.yml -p spacedf-core stop + +# Restart specific service +docker-compose -f docker-compose.yml -p spacedf-core restart auth + +# Fresh start (removes data) +docker-compose -f docker-compose.yml -p spacedf-core down -v +./entrypoint.sh +``` + +## Troubleshooting + +**Services not starting**: Check logs with `docker-compose logs -f` and verify `.env` configuration + +**Port already in use**: Stop conflicting services or change port mappings in `docker-compose.yml` + +**Database issues**: Restart databases: `docker-compose restart auth_postgres bootstrap_postgres` + +**Services keep crashing**: Rebuild with `docker-compose down -v && docker-compose build --no-cache && ./entrypoint.sh` + +#### Core Environment Variables + +**ENV** - Deployment environment mode (dev, local, prod). Determines which HAProxy configuration and feature sets are active. +- Default: `local` +- Example: `dev` or `prod` + +**HOST** - External hostname for JWT issuer validation. This is the public-facing URL used for JWT token verification. +- Default: `http://localhost:8000` +- Example: `http://api.example.com` + +**HOST_FRONTEND_ADMIN** - Admin frontend URL for reference and configuration. +- Default: `http://localhost:3001` +- Example: `http://admin.example.com` + +#### Organization Configuration + +**ORG_NAME** - Display name of your organization, used in UI and initialization. +- Default: `Default Organization` +- Example: `My Company Inc.` + +**ORG_SLUG** - URL-safe identifier for your organization. Used in hostnames, aliases, and internal references. Should be lowercase with hyphens only. +- Default: `default-org` +- Example: `my-company` + +**OWNER_EMAIL** - Email address of the initial organization owner. This account is created during bootstrap. +- Example: `admin@example.com` + +**OWNER_PASSWORD** - Password for the initial owner account. Should be strong and changed after first login. +- Example: `changeme123#Test` + +#### HAProxy Configuration + +**ORG_SLUG** - Used to create dynamic network aliases like `${ORG_SLUG}.haproxy` for internal service communication. + +#### Database Configuration + +**AUTH_POSTGRES_PASSWORD** - Password for Auth Service PostgreSQL database. +- Default: `postgres` +- Database name: `auth_service` +- Port: `5434` + +**BOOTSTRAP_POSTGRES_PASSWORD** - Password for Bootstrap Service PostgreSQL database. +- Default: `postgres` +- Database name: `bootstrap_service` +- Port: `5433` + +**DASHBOARD_POSTGRES_PASSWORD** - Password for Dashboard Service PostgreSQL database. +- Default: `postgres` +- Database name: `dashboard_service` +- Port: `5435` + +**DEVICE_POSTGRES_PASSWORD** - Password for Device Service PostgreSQL database. +- Default: `postgres` +- Database name: `device_service` +- Port: `5436` + +**TIMESCALEDB_PASSWORD** - Password for TimescaleDB (used by Telemetry Service). +- Default: `postgres` +- Database name: `spacedf_telemetry` +- Port: `5437` + +#### Service Secret Keys + +Each service requires a unique secret key for encryption and data protection: + +**AUTH_SECRET_KEY** - Secret key used by Auth Service for session encryption and token generation. + +**BOOTSTRAP_SECRET_KEY** - Secret key used by Bootstrap Service for setup operations. + +**DASHBOARD_SECRET_KEY** - Secret key used by Dashboard Service for authentication. + +**DEVICE_SECRET_KEY** - Secret key used by Device Service for secure operations. + +#### JWT Authentication + +**JWT_PRIVATE_KEY** - RSA private key used to sign JWT tokens. Keep this secure and never expose it. +- Format: PEM-encoded RSA private key +- Used by: Auth Service for token generation + +**JWT_PUBLIC_KEY** - RSA public key used to verify JWT tokens. Can be safely shared. +- Format: PEM-encoded RSA public key +- Used by: HAProxy and all services for token verification + +#### Message Broker Configuration + +**RABBITMQ_DEFAULT_USER** - Username for RabbitMQ message broker connection. +- Default: `default` + +**RABBITMQ_DEFAULT_PASS** - Password for RabbitMQ message broker connection. +- Default: `password` + +**REDIS_HOST** - Redis connection string for caching and session storage. +- Default: `redis://redis:6379/1` +- Format: `redis://[user:password@]host:port/database` + +#### MQTT/EMQX Broker Configuration + +**EMQX_HOST** - EMQX API endpoint for management operations. +- Default: `http://emqx:18083/api/v5` + +**EMQX_USERNAME** - Username for EMQX dashboard and API access. +- Default: `user1` + +**EMQX_PASSWORD** - Password for EMQX dashboard and API access. +- Default: `password123` + +#### MPA Service MQTT Configuration + +**MQTT_BROKER** - Hostname of the MQTT broker (EMQX). +- Default: `emqx` + +**MQTT_USERNAME** - MQTT username for MPA Service connection. +- Default: `MPAService` + +**MQTT_PASSWORD** - MQTT password for MPA Service connection. +- Default: `DF@1234` + +**MQTT_PORT** - MQTT broker port. +- Default: `1883` + +**MQTT_CLIENT_ID** - Client identifier for MQTT connections from MPA Service. +- Default: `mpa-service-mqtt-bridge` + +**MQTT_TOPIC** - Topic pattern for MPA Service to subscribe/publish. Use `{tenant}` placeholder for dynamic tenant names. +- Default: `tenant/{tenant}/device/data` + +#### Broker Bridge Configuration + +**MQTT_BROKER_BRIDGE_USERNAME** - MQTT username for Broker Bridge Service. +- Default: `BrokerBridgeService` + +**MQTT_BROKER_BRIDGE_PASSWORD** - MQTT password for Broker Bridge Service. +- Default: `DF@1234` + +**MQTT_TOPICS** - Comma-separated list of MQTT topics for Broker Bridge to subscribe to. Supports wildcards. +- Default: `tenant/+/transformed/device/location` + +#### AWS S3 Configuration + +**AWS_ACCESS_KEY_ID** - AWS IAM access key for S3 operations. + +**AWS_SECRET_ACCESS_KEY** - AWS IAM secret access key for S3 operations. + +**AWS_STORAGE_BUCKET_NAME** - Name of the S3 bucket for file storage. +- Example: `spacedf-storage-bucket` + +**AWS_REGION** - AWS region where S3 bucket is located. +- Default: `ap-southeast-1` + +#### Email Service Configuration + +**EMAIL_HOST_USER** - AWS SES SMTP username or email service credentials. + +**EMAIL_HOST_PASSWORD** - AWS SES SMTP password or email service credentials. + +**DEFAULT_FROM_EMAIL** - Default sender email address for all outgoing emails. +- Example: `no-reply@example.com` + +#### OAuth Authentication (Google) + +**GOOGLE_CALLBACK_URL** - Google OAuth callback URL (redirect after login). +- Example: `https://api.example.com/api/console/google/callback/` + +**GOOGLE_CLIENT_ID** - Google OAuth application client ID from Google Cloud Console. + +**GOOGLE_CLIENT_SECRET** - Google OAuth application client secret. + +#### OAuth Authentication (Apple) + +**APPLE_CLIENT_ID** - Apple OAuth application identifier. +- Example: `com.example.app` + +**APPLE_CLIENT_SECRET** - Apple OAuth application secret. + +**APPLE_CLIENT_KEY** - Apple OAuth client key. + +**APPLE_CERTIFICATE_KEY** - Apple OAuth certificate private key (PEM format). + +#### NextAuth Configuration + +**PORTAL_NEXTAUTH_URL** - Portal NextAuth URL for authentication callbacks. +- Default: `http://localhost:3001` + +**PORTAL_NEXTAUTH_SECRET** - Secret key for NextAuth portal encryption. Generate using: https://generate-secret.vercel.app/32 +- Must be a random 32-character string + +**DASHBOARD_NEXTAUTH_SECRET** - Secret key for dashboard NextAuth encryption. +- Must be a random string + +**PORTAL_AUTH_API** - Auth API endpoint used by portal for authentication. +- Default: `http://haproxy:3000` +- This points to HAProxy which routes to Auth Service + +#### API Keys and Tokens + +**ROOT_API_KEY** - Master API key for privileged operations and admin access. Keep this secure. + +**SPACE_API_KEY** - API key for Space-related operations and integrations. + +**MAPBOX_ACCESS_TOKEN** - Mapbox API token for mapping and location services. Required for map features. + +#### Application Settings + +**DEFAULT_TENANT_HOST** - Default tenant hostname for multi-tenant setup. +- Default: `localhost` + +**TELEMETRY_SERVICE_URL** - Endpoint for telemetry service API calls. +- Default: `http://telemetry:8080` + +**CORS_ALLOWED_ORIGINS** - Comma-separated list of allowed origins for CORS. Controls which frontend URLs can access the API. +- Default: `http://localhost,http://localhost:3000,http://localhost:3001` +- Example: `http://localhost:3000,https://app.example.com` ## Services Overview diff --git a/device-service b/device-service index f2cd7d5..cb2c8ab 160000 --- a/device-service +++ b/device-service @@ -1 +1 @@ -Subproject commit f2cd7d5cf661893bd35d86b495009e94126ab659 +Subproject commit cb2c8abeff81b92d2b48f844f8d3bf9d56106a39 diff --git a/docker-compose.yml b/docker-compose.yml index be238dc..6ad7a03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,7 +23,7 @@ services: interval: 10s timeout: 60s retries: 5 - start_period: 100s + start_period: 120s # EMQX emqx: @@ -227,7 +227,7 @@ services: interval: 5s timeout: 5s retries: 5 - start_period: 100s + start_period: 120s # Dashboard database dashboard_postgres: @@ -283,7 +283,7 @@ services: interval: 5s timeout: 5s retries: 5 - start_period: 100s + start_period: 120s # Device database device_postgres: @@ -340,7 +340,7 @@ services: interval: 5s timeout: 5s retries: 5 - start_period: 100s + start_period: 120s # Transformer service (Go) - RabbitMQ Consumer transformer: @@ -558,7 +558,7 @@ services: interval: 30s timeout: 10s retries: 3 - start_period: 100s + start_period: 120s docs: build: diff --git a/entrypoint.sh b/entrypoint.sh index 76cfd27..b47b8db 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,32 +5,32 @@ set +a API_URL="${HOST}/docs" HOST_FRONTEND_ADMIN="${HOST_FRONTEND_ADMIN}" -SCHEME="$(echo "$HOST_FRONTEND" | sed -E 's#(https?://).*#\1#')" -DOMAIN="$(echo "$HOST_FRONTEND" | sed -E 's#https?://##')" +SCHEME="$(echo "$DASHBOARD_NEXTAUTH_URL" | sed -E 's#(https?://).*#\1#')" +DOMAIN="$(echo "$DASHBOARD_NEXTAUTH_URL" | sed -E 's#https?://##')" HOST_FRONTEND="${SCHEME}${ORG_SLUG}.${DOMAIN}" clear echo -e "\033[38;5;208m" cat <<'EOF' - ______ _____ ____ _______ _______ _____ _______ - / ___/ / __ \ / | / ____/ / ____/ / __ \ / ____/ - \__ \ / /_/ / / /| | / / / __/ / / / / / /_ - ___/ / / ____/ / ___ |/ /___ / /___ / /_/ / / __/ -/____/ /_/ /_/ |_|\____/ \____/ /_____/ /_/ + _____ ____ ____ ______ ______ ____ ______ + / ___/ / __ \ / | / ____/ / ____/ / __ \ / ____/ + \__ \ / /_/ / / /| | / / / __/ / / / / / /_ + ___/ / / ____/ / ___ | / /___ / /___ / /_/ / / __/ +/____/ /_/ /_/ |_| \____/ \____/ /_____/ /_/ EOF echo -e "\033[0m" echo -e "\033[1;36mSpaceDF Core\033[0m" echo -e "\033[0;36mCloud Native Platform\033[0m" -echo -e "${CYAN}Version: 1.0.0${RESET}" +echo -e "Version: 0.0.1" echo # ===== Build & Start ===== COMPOSE_FILE="./docker-compose.yml" PROJECT_NAME="spacedf-core" -echo "🚀 Deploying SpaceDF Core..." -echo "⏹ Stopping existing services (if running)..." +echo "Deploying SpaceDF Core..." +echo "Stopping existing services (if running)..." docker compose -f "${COMPOSE_FILE}" -p "${PROJECT_NAME}" stop || true -echo "🔨 Building images & starting services..." +echo "Building images & starting services..." docker compose -f "${COMPOSE_FILE}" -p "${PROJECT_NAME}" up -d --build --remove-orphans sleep 10 diff --git a/spacedf-admin-portal b/spacedf-admin-portal deleted file mode 160000 index 7ae0300..0000000 --- a/spacedf-admin-portal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7ae0300d863f0aae6bd5b4edf03f24bb52aa01ad diff --git a/spacedf-web-app b/spacedf-web-app deleted file mode 160000 index 5d190e8..0000000 --- a/spacedf-web-app +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5d190e8d461a735e210911d59dddc7521008fa60 From 144c56f6a19304cb67f7acb89e94fb45e74fc61e Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Fri, 23 Jan 2026 10:48:17 +0700 Subject: [PATCH 3/8] feat: implement license and update docker compose --- .env.example | 5 +++-- README.md | 2 -- auth-service | 2 +- bootstrap-service | 2 +- dashboard-service | 2 +- device-service | 2 +- docker-compose.yml | 14 ++++++-------- entrypoint.sh | 5 ++--- requirements-dev.txt | 4 ++-- 9 files changed, 17 insertions(+), 21 deletions(-) diff --git a/.env.example b/.env.example index d3f2c96..a1c37fe 100644 --- a/.env.example +++ b/.env.example @@ -93,10 +93,11 @@ PORTAL_AUTH_API="http://haproxy:3000" #This is the random key for nextauth - generate here: https://generate-secret.vercel.app/32 DASHBOARD_NEXTAUTH_SECRET="__DASHBOARD_NEXTAUTH_SECRET__" SPACE_API_KEY="__SPACE_API_KEY__" -MAPBOX_ACCESS_TOKEN="pk.eyJ1IjoiZGYtdGVjaG5vbG9neSIsImEiOiJjbWpnczVucWUxNnRvM2VzbGk4em15MHNnIn0.kX9GwE6Hd36gc9uQoolyFA" +MAPTILER_API_KEY="H3MD3Z1wmzMsKpuVstcr" DASHBOARD_MQTT_USERNAME="anonymous" DASHBOARD_MQTT_PASSWORD="password123" DASHBOARD_MQTT_PROTOCOL="ws" DASHBOARD_MQTT_PORT="8883" DASHBOARD_MQTT_BROKER="emqx.localhost:8000" -DASHBOARD_NEXTAUTH_URL="http://localhost:3000" \ No newline at end of file +DASHBOARD_NEXTAUTH_URL="http://localhost:3000" +ASHBOARD_AUTH_API="http://haproxy:3000" \ No newline at end of file diff --git a/README.md b/README.md index c9e799f..3765017 100644 --- a/README.md +++ b/README.md @@ -284,8 +284,6 @@ Each service requires a unique secret key for encryption and data protection: **SPACE_API_KEY** - API key for Space-related operations and integrations. -**MAPBOX_ACCESS_TOKEN** - Mapbox API token for mapping and location services. Required for map features. - #### Application Settings **DEFAULT_TENANT_HOST** - Default tenant hostname for multi-tenant setup. diff --git a/auth-service b/auth-service index 9a09697..8be5ef3 160000 --- a/auth-service +++ b/auth-service @@ -1 +1 @@ -Subproject commit 9a09697067dc4d1768959413b9c823d1eaa263a5 +Subproject commit 8be5ef3c4dede130e4e7553848ed27c1bd468804 diff --git a/bootstrap-service b/bootstrap-service index 6cf7cbf..5d1e554 160000 --- a/bootstrap-service +++ b/bootstrap-service @@ -1 +1 @@ -Subproject commit 6cf7cbff781b3b0ec42b2257ab975e61314ad49d +Subproject commit 5d1e5549da2e7f63211b8563c7dc496aea4e332c diff --git a/dashboard-service b/dashboard-service index 00af685..22e8803 160000 --- a/dashboard-service +++ b/dashboard-service @@ -1 +1 @@ -Subproject commit 00af685d19e5b6a9040abc089b5d2a14cd6974be +Subproject commit 22e880346103a9936cd675332a5ef10734dcaeba diff --git a/device-service b/device-service index cb2c8ab..ca44876 160000 --- a/device-service +++ b/device-service @@ -1 +1 @@ -Subproject commit cb2c8abeff81b92d2b48f844f8d3bf9d56106a39 +Subproject commit ca448768e1639fa167f58c923a5e8160765cf166 diff --git a/docker-compose.yml b/docker-compose.yml index 6ad7a03..6431b8c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -110,9 +110,8 @@ services: # bootstrap service bootstrap: container_name: bootstrap_service - build: - context: . - dockerfile: ./bootstrap-service/Dockerfile + image: ghcr.io/space-df/bootstrap-service:latest + pull_policy: always environment: ENV: ${ENV} EMQX_USERNAME: ${EMQX_USERNAME} @@ -415,9 +414,8 @@ services: haproxy: container_name: haproxy - build: - context: . - dockerfile: ./haproxy/Dockerfile + image: ghcr.io/space-df/haproxy:latest + pull_policy: always networks: default: aliases: @@ -587,9 +585,9 @@ services: platform: linux/amd64 environment: NEXTAUTH_URL: ${DASHBOARD_NEXTAUTH_URL} + MAPTILER_API_KEY: ${MAPTILER_API_KEY} NEXTAUTH_SECRET: ${DASHBOARD_NEXTAUTH_SECRET} - MAPBOX_ACCESS_TOKEN: ${MAPBOX_ACCESS_TOKEN} - AUTH_API: ${PORTAL_AUTH_API} + AUTH_API: ${ASHBOARD_AUTH_API} SPACE_API_KEY: ${SPACE_API_KEY} DASHBOARD_MQTT_USERNAME: ${DASHBOARD_MQTT_USERNAME} DASHBOARD_MQTT_PASSWORD: ${DASHBOARD_MQTT_PASSWORD} diff --git a/entrypoint.sh b/entrypoint.sh index b47b8db..161a701 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -15,7 +15,7 @@ cat <<'EOF' _____ ____ ____ ______ ______ ____ ______ / ___/ / __ \ / | / ____/ / ____/ / __ \ / ____/ \__ \ / /_/ / / /| | / / / __/ / / / / / /_ - ___/ / / ____/ / ___ | / /___ / /___ / /_/ / / __/ + ___/ / / ____/ / /_| | / /___ / /___ / /_/ / / __/ /____/ /_/ /_/ |_| \____/ \____/ /_____/ /_/ EOF echo -e "\033[0m" @@ -40,5 +40,4 @@ echo -e "--------------------------------------------------" echo -e "SpaceDF Core started successfully" echo "🌐 Frontend Admin : ${HOST_FRONTEND_ADMIN}" echo "🌐 Frontend Dashboard : ${HOST_FRONTEND}" -echo "🔗 Backend API : ${API_URL}" -echo -e "--------------------------------------------------" \ No newline at end of file +echo "🔗 Backend API : ${API_URL}" \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index ed81684..e891ef5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,7 @@ Django==5.0.6 django-environ==0.11.2 gunicorn==22.0.0 -gevent +gevent==24.2.1 django_tenants==3.6.1 celery==5.4.0 cryptography==43.0.3 @@ -10,7 +10,7 @@ authlib==1.3.1 python-dotenv==1.0.1 # For the persistence stores -psycopg2-binary +psycopg2-binary==2.9.9 # Rest apis djangorestframework==3.15.2 From 49bdf0e2297242402734104a8a16cbd254b783ae Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Fri, 23 Jan 2026 15:23:31 +0700 Subject: [PATCH 4/8] feat: remove haproxy submodule --- .gitmodules | 4 ---- haproxy | 1 - 2 files changed, 5 deletions(-) delete mode 160000 haproxy diff --git a/.gitmodules b/.gitmodules index 1659562..13060c2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,10 +18,6 @@ path = django-common-utils url = git@github.com:Space-DF/django-common-utils.git branch = dev -[submodule "haproxy"] - path = haproxy - url = git@github.com:Space-DF/haproxy.git - branch = dev [submodule "transformer-service"] path = transformer-service url = git@github.com:Space-DF/transformer-service.git diff --git a/haproxy b/haproxy deleted file mode 160000 index d639316..0000000 --- a/haproxy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d639316cd0a3d9b0f1db85c1aec6ace363f97e1b From 595dc8ee920c6cd2b06f65c92d8cc4279b086cc1 Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Fri, 23 Jan 2026 15:48:39 +0700 Subject: [PATCH 5/8] refactor: update slug generation api --- haproxy/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/haproxy/Dockerfile b/haproxy/Dockerfile index 7b0125c..05cd651 100644 --- a/haproxy/Dockerfile +++ b/haproxy/Dockerfile @@ -18,7 +18,7 @@ COPY haproxy/handlers /usr/local/etc/haproxy/handlers COPY haproxy/routes /usr/local/share/lua/5.4/routes # Copy entrypoint -COPY docker-entrypoint.sh /docker-entrypoint.sh +COPY haproxy/docker-entrypoint.sh /docker-entrypoint.sh RUN chmod +x /docker-entrypoint.sh RUN chown -R haproxy:haproxy /usr/local/etc/haproxy From b1a06cdf5a3307728c901cf844a691418b5a2b51 Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Fri, 23 Jan 2026 16:28:02 +0700 Subject: [PATCH 6/8] fix: update haproxy aliases --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9cd576f..5f13380 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -420,7 +420,7 @@ services: networks: default: aliases: - - default-org.haproxy + - ${ORG_SLUG}.haproxy environment: HOST: ${HOST} JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY} From 1b58c5e221ec1c6656384a30cab2eb0b3e59960d Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Fri, 23 Jan 2026 17:33:55 +0700 Subject: [PATCH 7/8] feat: squash commit code --- auth-service | 2 +- bootstrap-service | 2 +- broker-bridge-service | 2 +- dashboard-service | 2 +- django-common-utils | 2 +- mpa-service | 2 +- telemetry-service | 2 +- transformer-service | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/auth-service b/auth-service index 4f2da05..8be5ef3 160000 --- a/auth-service +++ b/auth-service @@ -1 +1 @@ -Subproject commit 4f2da059c7530dc775ecf63310fc70ff8c815448 +Subproject commit 8be5ef3c4dede130e4e7553848ed27c1bd468804 diff --git a/bootstrap-service b/bootstrap-service index f3c92d6..5d1e554 160000 --- a/bootstrap-service +++ b/bootstrap-service @@ -1 +1 @@ -Subproject commit f3c92d60b5253edeabe08eb8d149c2343d48abea +Subproject commit 5d1e5549da2e7f63211b8563c7dc496aea4e332c diff --git a/broker-bridge-service b/broker-bridge-service index 5f6d962..9773652 160000 --- a/broker-bridge-service +++ b/broker-bridge-service @@ -1 +1 @@ -Subproject commit 5f6d9623a17d213fb6f882cd5a7a4b2c51358aee +Subproject commit 9773652782e0a0abbfc20c963a76f25de2277813 diff --git a/dashboard-service b/dashboard-service index c4adc84..22e8803 160000 --- a/dashboard-service +++ b/dashboard-service @@ -1 +1 @@ -Subproject commit c4adc84611c7c967737a9a6c98615c48d7fa4afa +Subproject commit 22e880346103a9936cd675332a5ef10734dcaeba diff --git a/django-common-utils b/django-common-utils index fc25176..f05d9ec 160000 --- a/django-common-utils +++ b/django-common-utils @@ -1 +1 @@ -Subproject commit fc25176ab93ccc94e7a12dd3499c28fdbc72b9fb +Subproject commit f05d9ec7d02204ba78dcdbfb968571702440928b diff --git a/mpa-service b/mpa-service index 68576ef..3981984 160000 --- a/mpa-service +++ b/mpa-service @@ -1 +1 @@ -Subproject commit 68576ef65923d0bac1498c46007e34f6337210f3 +Subproject commit 39819848ddcfa43cd50b5b2a369b3a658c7bc84c diff --git a/telemetry-service b/telemetry-service index 46d07ae..1604c04 160000 --- a/telemetry-service +++ b/telemetry-service @@ -1 +1 @@ -Subproject commit 46d07aed1b625419d1d3e4060eb634b31f51a9e9 +Subproject commit 1604c041cedc24e6fcef439aadd25bc58633c905 diff --git a/transformer-service b/transformer-service index 2b1e6cf..3a124d0 160000 --- a/transformer-service +++ b/transformer-service @@ -1 +1 @@ -Subproject commit 2b1e6cfe98035ce6da566bfcd0c70f781052ee90 +Subproject commit 3a124d01b826611c5999d217d16b704ea9fd473d From 6b7e357b16ce46fa46bc3dafdc7eda9ac569c56d Mon Sep 17 00:00:00 2001 From: ngovinh2k2 Date: Mon, 26 Jan 2026 11:43:06 +0700 Subject: [PATCH 8/8] feat: auto generate keys --- .env.example | 87 ++++++++++++++++++++++------------------------ README.md | 42 ++++++++++++++-------- docker-compose.yml | 1 - generate-keys.sh | 56 +++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 60 deletions(-) create mode 100755 generate-keys.sh diff --git a/.env.example b/.env.example index a1c37fe..cae1c6c 100644 --- a/.env.example +++ b/.env.example @@ -1,101 +1,98 @@ # Environment -ENV="local" +ENV="dev" # RabbitMQ -RABBITMQ_DEFAULT_USER="default" -RABBITMQ_DEFAULT_PASS="password" +RABBITMQ_DEFAULT_USER= +RABBITMQ_DEFAULT_PASS= # Authentication JWT -JWT_PRIVATE_KEY="__JWT_PRIVATE_KEY__" -JWT_PUBLIC_KEY="__JWT_PUBLIC_KEY__" +JWT_PRIVATE_KEY= +JWT_PUBLIC_KEY= # Google OAuth -GOOGLE_CALLBACK_URL="__GOOGLE_CALLBACK_URL__" -GOOGLE_CLIENT_ID="__GOOGLE_CLIENT_ID__" -GOOGLE_CLIENT_SECRET="__GOOGLE_CLIENT_SECRET__" +GOOGLE_CALLBACK_URL= +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= # Apple OAuth -APPLE_CLIENT_ID="__APPLE_CLIENT_ID__" -APPLE_CLIENT_SECRET="__APPLE_CLIENT_SECRET__" -APPLE_CLIENT_KEY="__APPLE_CLIENT_KEY__" -APPLE_CERTIFICATE_KEY="__APPLE_CERTIFICATE_KEY__" +APPLE_CLIENT_ID= +APPLE_CLIENT_SECRET= +APPLE_CLIENT_KEY= +APPLE_CERTIFICATE_KEY= # Auth Service -AUTH_POSTGRES_PASSWORD="postgres" -AUTH_SECRET_KEY="__AUTH_SECRET_KEY__" +AUTH_POSTGRES_PASSWORD= +AUTH_SECRET_KEY= DEFAULT_TENANT_HOST="localhost" -ROOT_API_KEY="__ROOT_API_KEY__" +ROOT_API_KEY= # S3 Service -AWS_ACCESS_KEY_ID="__AWS_ACCESS_KEY_ID__" -AWS_SECRET_ACCESS_KEY="__AWS_SECRET_ACCESS_KEY__" -AWS_STORAGE_BUCKET_NAME="__AWS_STORAGE_BUCKET_NAME__" +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_STORAGE_BUCKET_NAME= AWS_REGION="us-east-1" # Redis Service -REDIS_HOST="redis://redis:6379/1" +REDIS_HOST= # Dashboard Service -DASHBOARD_POSTGRES_PASSWORD="postgres" -DASHBOARD_SECRET_KEY="__DASHBOARD_SECRET_KEY__" +DASHBOARD_POSTGRES_PASSWORD= +DASHBOARD_SECRET_KEY= # Device Service -DEVICE_POSTGRES_PASSWORD="postgres" -DEVICE_SECRET_KEY="__DEVICE_SECRET_KEY__" +DEVICE_POSTGRES_PASSWORD= +DEVICE_SECRET_KEY= TELEMETRY_SERVICE_URL="http://telemetry:8080" # EMQX Service EMQX_HOST="http://emqx:18083/api/v5" -EMQX_USERNAME="user" -EMQX_PASSWORD="password123" +EMQX_USERNAME= +EMQX_PASSWORD= # Broker Bridge Service MQTT_BROKER_BRIDGE_USERNAME="BrokerBridgeService" -MQTT_BROKER_BRIDGE_PASSWORD="Default@1234" +MQTT_BROKER_BRIDGE_PASSWORD= MQTT_TOPICS="tenant/+/transformed/device/location" # Acount AWS to access to SES service -EMAIL_BACKEND="__EMAIL_BACKEND__" -EMAIL_PORT="__EMAIL_PORT__" -EMAIL_USE_TLS="__EMAIL_USE_TLS__" - -# Acount AWS to access to SES service -EMAIL_HOST_USER="__EMAIL_HOST_USER__" -EMAIL_HOST_PASSWORD="__EMAIL_HOST_PASSWORD__" +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= DEFAULT_FROM_EMAIL="no-reply@gmail.com" +EMAIL_BACKEND= +EMAIL_PORT= +EMAIL_USE_TLS= # MPA service MQTT_BROKER="emqx" MQTT_USERNAME="MPAService" -MQTT_PASSWORD="Default@1234" +MQTT_PASSWORD= MQTT_PORT="1883" MQTT_CLIENT_ID="mpa-service-mqtt-bridge" MQTT_TOPIC="tenant/{tenant}/device/data" # Bootstrap service HOST="http://localhost:8000" -BOOTSTRAP_POSTGRES_PASSWORD="postgres" +BOOTSTRAP_POSTGRES_PASSWORD= CORS_ALLOWED_ORIGINS="http://localhost,http://localhost:3000,http://localhost:3001" -BOOTSTRAP_SECRET_KEY="__BOOTSTRAP_SECRET_KEY__" +BOOTSTRAP_SECRET_KEY= # Organization initialization -ORG_NAME="Default Organization" -ORG_SLUG="default-org" -OWNER_EMAIL="admin@example.com" -OWNER_PASSWORD="changeme123#Test" +ORG_NAME= +ORG_SLUG= +OWNER_EMAIL= +OWNER_PASSWORD= # NextAuth configuration -PORTAL_NEXTAUTH_SECRET="__PORTAL_NEXTAUTH_SECRET__" +PORTAL_NEXTAUTH_SECRET= HOST_FRONTEND_ADMIN="http://localhost:3001" PORTAL_NEXTAUTH_URL="http://localhost:3001" PORTAL_AUTH_API="http://haproxy:3000" #This is the random key for nextauth - generate here: https://generate-secret.vercel.app/32 -DASHBOARD_NEXTAUTH_SECRET="__DASHBOARD_NEXTAUTH_SECRET__" -SPACE_API_KEY="__SPACE_API_KEY__" +DASHBOARD_NEXTAUTH_SECRET= MAPTILER_API_KEY="H3MD3Z1wmzMsKpuVstcr" -DASHBOARD_MQTT_USERNAME="anonymous" -DASHBOARD_MQTT_PASSWORD="password123" +DASHBOARD_MQTT_USERNAME= +DASHBOARD_MQTT_PASSWORD= DASHBOARD_MQTT_PROTOCOL="ws" DASHBOARD_MQTT_PORT="8883" DASHBOARD_MQTT_BROKER="emqx.localhost:8000" diff --git a/README.md b/README.md index 3765017..d2092fd 100644 --- a/README.md +++ b/README.md @@ -22,22 +22,38 @@ Create `.env` file from `.env.example`: cp .env.example .env ``` -**Essential variables to configure:** +**Auto-generate environment variables:** + +Run the setup script to automatically generate all required keys and secrets: ```bash -# Organization -ORG_NAME="Your Organization" -ORG_SLUG="your-org" -OWNER_EMAIL="admin@example.com" -OWNER_PASSWORD="SecurePass123#" +chmod +x generate-keys.sh +./generate-keys.sh +``` + +This script will automatically: +- Generate RSA 2048-bit JWT private and public keys +- Generate random secrets for authentication and NextAuth +- Populate all essential environment variables including: + - Database passwords + - Message broker credentials (RabbitMQ, EMQX, MQTT) + - Service secret keys + - Organization configuration + - OAuth and API secrets + +The generated values are written directly to `.env`, replacing any existing placeholder values. -# Auto-generate JWT keys (run these commands to generate) -# openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_key_size:2048 -# openssl rsa -in private_key.pem -pubout -out public_key.pem -# Then paste the contents of private_key.pem and public_key.pem below: +**Optional: Manual Configuration** -JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" -JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" +If you need to customize any values after running the script, edit `.env` directly. Variables that should be customized: + +```bash +# Organization (customize as needed) +ORG_NAME="Your Organization" +ORG_SLUG="your-org" +OWNER_EMAIL="your-email@example.com" +OWNER_PASSWORD="" +# ... other OAuth configuration ``` For detailed information on all environment variables, see the **Environment Configuration (.env)** section below. @@ -282,8 +298,6 @@ Each service requires a unique secret key for encryption and data protection: **ROOT_API_KEY** - Master API key for privileged operations and admin access. Keep this secure. -**SPACE_API_KEY** - API key for Space-related operations and integrations. - #### Application Settings **DEFAULT_TENANT_HOST** - Default tenant hostname for multi-tenant setup. diff --git a/docker-compose.yml b/docker-compose.yml index 5f13380..04ab234 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -588,7 +588,6 @@ services: MAPTILER_API_KEY: ${MAPTILER_API_KEY} NEXTAUTH_SECRET: ${DASHBOARD_NEXTAUTH_SECRET} AUTH_API: ${ASHBOARD_AUTH_API} - SPACE_API_KEY: ${SPACE_API_KEY} DASHBOARD_MQTT_USERNAME: ${DASHBOARD_MQTT_USERNAME} DASHBOARD_MQTT_PASSWORD: ${DASHBOARD_MQTT_PASSWORD} DASHBOARD_MQTT_PORT: ${DASHBOARD_MQTT_PORT} diff --git a/generate-keys.sh b/generate-keys.sh new file mode 100755 index 0000000..2c7773d --- /dev/null +++ b/generate-keys.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -e +[ -f .env ] || { echo "Error: .env not found"; exit 1; } +mkdir -p ./keys +openssl genrsa -out ./keys/private_key.pem 2048 2>/dev/null +openssl rsa -in ./keys/private_key.pem -pubout -out ./keys/public_key.pem 2>/dev/null +P=$(cat ./keys/private_key.pem | sed 's/$/\\n/' | tr -d '\n' | sed 's/\\n$//') +U=$(cat ./keys/public_key.pem | sed 's/$/\\n/' | tr -d '\n' | sed 's/\\n$//') +export P U PS=$(openssl rand -base64 32) DS=$(openssl rand -base64 32) RP=$(openssl rand -base64 16) OP=$(openssl rand -base64 16) BSK=$(openssl rand -base64 32) DSK=$(openssl rand -base64 32) DBSK=$(openssl rand -base64 32) +python3 << 'EOF' +import os, re + +c = open('.env').read() +c = re.sub(r'^JWT_PRIVATE_KEY=.*$', '', c, flags=re.M) +c = re.sub(r'^JWT_PUBLIC_KEY=.*$', '', c, flags=re.M) +c = re.sub( + r'(# Authentication JWT\n)', + f'\\1JWT_PRIVATE_KEY="{os.environ["P"]}"\nJWT_PUBLIC_KEY="{os.environ["U"]}"\n', + c +) + +env_vars = { + 'RABBITMQ_DEFAULT_USER': 'default', + 'AUTH_POSTGRES_PASSWORD': 'postgres', + 'REDIS_HOST': 'redis://redis:6379/1', + 'DASHBOARD_POSTGRES_PASSWORD': 'postgres', + 'DEVICE_POSTGRES_PASSWORD': 'postgres', + 'BOOTSTRAP_POSTGRES_PASSWORD': 'postgres', + 'EMQX_USERNAME': 'user', + 'EMQX_PASSWORD': 'password123', + 'MQTT_BROKER_BRIDGE_PASSWORD': 'Default@1234', + 'MQTT_PASSWORD': 'Default@1234', + 'ORG_NAME': 'Default Organization', + 'ORG_SLUG': 'default-org', + 'OWNER_EMAIL': 'admin@example.com', + 'OWNER_PASSWORD': 'changeme@Default123', + 'BOOTSTRAP_SECRET_KEY': os.environ['BSK'], + 'DEVICE_SECRET_KEY': os.environ['DSK'], + 'DASHBOARD_SECRET_KEY': os.environ['DBSK'], + 'DASHBOARD_MQTT_USERNAME': 'anonymous', + 'DASHBOARD_MQTT_PASSWORD': 'password123', + 'RABBITMQ_DEFAULT_PASS': 'password', + 'PORTAL_NEXTAUTH_SECRET': os.environ['PS'], + 'DASHBOARD_NEXTAUTH_SECRET': os.environ['DS'] +} + +for key, value in env_vars.items(): + if f'{key}=' in c: + c = re.sub(f'^{key}=.*$', f'{key}="{value}"', c, flags=re.M) + else: + c += f'\n{key}="{value}"' +with open('.env', 'w') as f: + f.write(c) +EOF +rm -rf ./keys +echo "✓ Done"