mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Refactor frontend build and deployment scripts; consolidate services into a single app
- Updated package.json in StatusPage to correct dependency path for Common. - Enhanced main package.json with new scripts for building and watching frontend applications. - Modified tsconfig.json to exclude frontend directories from compilation. - Simplified Nginx configuration by removing individual upstreams for each service and routing all to a single app upstream. - Refactored configure.sh to streamline Dockerfile generation. - Cleaned up docker-compose files by consolidating services and removing unnecessary definitions. - Introduced new frontend handling logic in Index.ts for rendering different frontend applications. - Added utility functions for managing status page data and RSS feeds in StatusPage.ts. - Created dev.sh and frontend-run.sh scripts to facilitate development and build processes for frontend applications.
This commit is contained in:
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Accounts/Dockerfile .
|
||||
command: sudo docker build --no-cache -f ./App/Accounts/Dockerfile .
|
||||
|
||||
docker-build-home:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -219,7 +219,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./AdminDashboard/Dockerfile .
|
||||
command: sudo docker build --no-cache -f ./App/AdminDashboard/Dockerfile .
|
||||
|
||||
docker-build-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -242,7 +242,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./Dashboard/Dockerfile .
|
||||
command: sudo docker build --no-cache -f ./App/Dashboard/Dockerfile .
|
||||
|
||||
docker-build-probe:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -311,7 +311,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 3
|
||||
command: sudo docker build --no-cache -f ./StatusPage/Dockerfile .
|
||||
command: sudo docker build --no-cache -f ./App/StatusPage/Dockerfile .
|
||||
|
||||
docker-build-test-server:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
8
.github/workflows/compile.yml
vendored
8
.github/workflows/compile.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Accounts && npm install && npm run compile && npm run dep-check
|
||||
command: cd App/Accounts && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-common:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd AdminDashboard && npm install && npm run compile && npm run dep-check
|
||||
command: cd App/AdminDashboard && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -196,7 +196,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd Dashboard && npm install && npm run compile && npm run dep-check
|
||||
command: cd App/Dashboard && npm install && npm run compile && npm run dep-check
|
||||
|
||||
|
||||
compile-e2e:
|
||||
@@ -268,7 +268,7 @@ jobs:
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
command: cd StatusPage && npm install && npm run compile && npm run dep-check
|
||||
command: cd App/StatusPage && npm install && npm run compile && npm run dep-check
|
||||
|
||||
compile-test-server:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -556,7 +556,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image status-page \
|
||||
--version "${{needs.read-version.outputs.major_minor}}" \
|
||||
--dockerfile ./StatusPage/Dockerfile \
|
||||
--dockerfile ./App/StatusPage/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}"
|
||||
@@ -829,7 +829,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image admin-dashboard \
|
||||
--version "${{needs.read-version.outputs.major_minor}}" \
|
||||
--dockerfile ./AdminDashboard/Dockerfile \
|
||||
--dockerfile ./App/AdminDashboard/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}"
|
||||
@@ -898,7 +898,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image dashboard \
|
||||
--version "${{needs.read-version.outputs.major_minor}}" \
|
||||
--dockerfile ./Dashboard/Dockerfile \
|
||||
--dockerfile ./App/Dashboard/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}"
|
||||
@@ -1035,7 +1035,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image accounts \
|
||||
--version "${{needs.read-version.outputs.major_minor}}" \
|
||||
--dockerfile ./Accounts/Dockerfile \
|
||||
--dockerfile ./App/Accounts/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}"
|
||||
|
||||
8
.github/workflows/test-release.yaml
vendored
8
.github/workflows/test-release.yaml
vendored
@@ -502,7 +502,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image status-page \
|
||||
--version "${{needs.read-version.outputs.major_minor}}-test" \
|
||||
--dockerfile ./StatusPage/Dockerfile \
|
||||
--dockerfile ./App/StatusPage/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}" \
|
||||
@@ -785,7 +785,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image dashboard \
|
||||
--version "${{needs.read-version.outputs.major_minor}}-test" \
|
||||
--dockerfile ./Dashboard/Dockerfile \
|
||||
--dockerfile ./App/Dashboard/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}" \
|
||||
@@ -855,7 +855,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image admin-dashboard \
|
||||
--version "${{needs.read-version.outputs.major_minor}}-test" \
|
||||
--dockerfile ./AdminDashboard/Dockerfile \
|
||||
--dockerfile ./App/AdminDashboard/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}" \
|
||||
@@ -998,7 +998,7 @@ jobs:
|
||||
bash ./Scripts/GHA/build_docker_images.sh \
|
||||
--image accounts \
|
||||
--version "${{needs.read-version.outputs.major_minor}}-test" \
|
||||
--dockerfile ./Accounts/Dockerfile \
|
||||
--dockerfile ./App/Accounts/Dockerfile \
|
||||
--context . \
|
||||
--platforms linux/amd64,linux/arm64 \
|
||||
--git-sha "${{ github.sha }}" \
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -127,6 +127,7 @@ MCP/build/
|
||||
MCP/.env
|
||||
MCP/node_modules
|
||||
Dashboard/public/sw.js
|
||||
App/Dashboard/public/sw.js
|
||||
.claude/settings.local.json
|
||||
Common/.claude/settings.local.json
|
||||
E2E/Terraform/e2e-tests/test-env.sh
|
||||
|
||||
@@ -65,7 +65,7 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY ./Accounts/package*.json /usr/src/app/
|
||||
COPY ./App/Accounts/package*.json /usr/src/app/
|
||||
RUN npm install
|
||||
|
||||
# Expose ports.
|
||||
@@ -82,7 +82,7 @@ EXPOSE 3003
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
{{ else }}
|
||||
# Copy app source
|
||||
COPY ./Accounts /usr/src/app
|
||||
COPY ./App/Accounts /usr/src/app
|
||||
# Bundle app source
|
||||
|
||||
RUN npm run build
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"watch": ["./","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"watch": ["./","../../Common/UI", "../../Common/Types", "../../Common/Utils", "../../Common/Models"],
|
||||
"ext": "ts,tsx",
|
||||
"ignore": [
|
||||
"./node_modules/**",
|
||||
@@ -11,7 +11,7 @@
|
||||
"./build/**",
|
||||
"./build/dist/*",
|
||||
"./build/dist/**",
|
||||
"../Common/Server/**"
|
||||
"../../Common/Server/**"
|
||||
],
|
||||
"exec": "npm run dev-build && npm run start"
|
||||
}
|
||||
145
App/Accounts/package-lock.json
generated
145
App/Accounts/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"name": "@oneuptime/accounts",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"Common": "file:../../Common",
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -24,9 +24,150 @@
|
||||
"ts-node": "^10.9.1"
|
||||
}
|
||||
},
|
||||
"../../Common": {
|
||||
"name": "@oneuptime/common",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "^7.3.2",
|
||||
"@bull-board/express": "^5.21.4",
|
||||
"@clickhouse/client": "^1.10.1",
|
||||
"@elastic/elasticsearch": "^8.12.1",
|
||||
"@hcaptcha/react-hcaptcha": "^1.14.0",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/api-logs": "^0.206.0",
|
||||
"@opentelemetry/context-zone": "^1.25.1",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.207.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.207.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.207.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.207.0",
|
||||
"@opentelemetry/id-generator-aws-xray": "^1.2.2",
|
||||
"@opentelemetry/instrumentation": "^0.207.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.207.0",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.207.0",
|
||||
"@opentelemetry/resources": "^1.25.1",
|
||||
"@opentelemetry/sdk-logs": "^0.207.0",
|
||||
"@opentelemetry/sdk-metrics": "^1.25.1",
|
||||
"@opentelemetry/sdk-node": "^0.207.0",
|
||||
"@opentelemetry/sdk-trace-web": "^1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.37.0",
|
||||
"@remixicon/react": "^4.2.0",
|
||||
"@simplewebauthn/server": "^13.2.2",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/web-push": "^3.6.4",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.12.0",
|
||||
"botbuilder": "^4.23.3",
|
||||
"bullmq": "^5.61.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"cron-parser": "^4.8.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.4.4",
|
||||
"ejs": "^3.1.10",
|
||||
"elkjs": "^0.10.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"expo-server-sdk": "^3.15.0",
|
||||
"express": "^4.21.1",
|
||||
"formik": "^2.4.6",
|
||||
"history": "^5.3.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"isolated-vm": "^6.0.2",
|
||||
"json2csv": "^5.0.7",
|
||||
"json5": "^2.2.3",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"marked": "^12.0.2",
|
||||
"mermaid": "^11.12.2",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"multer": "^2.0.2",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^7.0.7",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.16.3",
|
||||
"playwright": "^1.56.0",
|
||||
"posthog-js": "^1.275.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.19.4",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-router-dom": "^6.30.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^16.0.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"recharts": "^2.12.7",
|
||||
"redis-semaphore": "^5.5.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"slackify-markdown": "^4.4.0",
|
||||
"slugify": "^1.6.5",
|
||||
"socket.io": "^4.7.4",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"stripe": "^10.17.0",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"twilio": "^4.22.0",
|
||||
"typeorm": "^0.3.26",
|
||||
"typeorm-extension": "^2.2.13",
|
||||
"universal-cookie": "^7.2.1",
|
||||
"use-async-effect": "^2.2.6",
|
||||
"uuid": "^8.3.2",
|
||||
"web-push": "^3.6.7",
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/cookie-parser": "^1.4.4",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/ejs": "^3.1.1",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^28.1.4",
|
||||
"@types/json2csv": "^5.0.3",
|
||||
"@types/jsonwebtoken": "^8.5.9",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/node-cron": "^3.0.7",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/react": "^18.2.38",
|
||||
"@types/react-beautiful-dnd": "^13.1.2",
|
||||
"@types/react-big-calendar": "^1.8.5",
|
||||
"@types/react-color": "^3.0.6",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/react-toggle": "^4.0.3",
|
||||
"jest": "^28.1.1",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-mock-extended": "^3.0.5",
|
||||
"react-test-renderer": "^18.2.0",
|
||||
"sass": "^1.89.2",
|
||||
"ts-jest": "^28.0.5"
|
||||
}
|
||||
},
|
||||
"../Common": {
|
||||
"name": "@oneuptime/common",
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "^7.3.2",
|
||||
@@ -569,7 +710,7 @@
|
||||
}
|
||||
},
|
||||
"node_modules/Common": {
|
||||
"resolved": "../Common",
|
||||
"resolved": "../../Common",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"Common": "file:../../Common",
|
||||
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -63,7 +63,7 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY ./AdminDashboard/package*.json /usr/src/app/
|
||||
COPY ./App/AdminDashboard/package*.json /usr/src/app/
|
||||
RUN npm install
|
||||
|
||||
# Expose ports.
|
||||
@@ -79,7 +79,7 @@ EXPOSE 3158
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
{{ else }}
|
||||
# Copy app source
|
||||
COPY ./AdminDashboard /usr/src/app
|
||||
COPY ./App/AdminDashboard /usr/src/app
|
||||
# Bundle app source
|
||||
RUN npm run build
|
||||
# Set permission to write logs and cache in case container run as non root
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"watch": ["./","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"watch": ["./","../../Common/UI", "../../Common/Types", "../../Common/Utils", "../../Common/Models"],
|
||||
"ext": "ts,tsx",
|
||||
"ignore": [
|
||||
"./node_modules/**",
|
||||
@@ -11,7 +11,7 @@
|
||||
"./build/**",
|
||||
"./build/dist/*",
|
||||
"./build/dist/**",
|
||||
"../Common/Server/**"
|
||||
"../../Common/Server/**"
|
||||
],
|
||||
"exec": " npm run dev-build && npm run start"
|
||||
}
|
||||
145
App/AdminDashboard/package-lock.json
generated
145
App/AdminDashboard/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"name": "@oneuptime/admin-dashboard",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"Common": "file:../../Common",
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -23,9 +23,150 @@
|
||||
"ts-node": "^10.9.1"
|
||||
}
|
||||
},
|
||||
"../../Common": {
|
||||
"name": "@oneuptime/common",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "^7.3.2",
|
||||
"@bull-board/express": "^5.21.4",
|
||||
"@clickhouse/client": "^1.10.1",
|
||||
"@elastic/elasticsearch": "^8.12.1",
|
||||
"@hcaptcha/react-hcaptcha": "^1.14.0",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/api-logs": "^0.206.0",
|
||||
"@opentelemetry/context-zone": "^1.25.1",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.207.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.207.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.207.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.207.0",
|
||||
"@opentelemetry/id-generator-aws-xray": "^1.2.2",
|
||||
"@opentelemetry/instrumentation": "^0.207.0",
|
||||
"@opentelemetry/instrumentation-fetch": "^0.207.0",
|
||||
"@opentelemetry/instrumentation-xml-http-request": "^0.207.0",
|
||||
"@opentelemetry/resources": "^1.25.1",
|
||||
"@opentelemetry/sdk-logs": "^0.207.0",
|
||||
"@opentelemetry/sdk-metrics": "^1.25.1",
|
||||
"@opentelemetry/sdk-node": "^0.207.0",
|
||||
"@opentelemetry/sdk-trace-web": "^1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "^1.37.0",
|
||||
"@remixicon/react": "^4.2.0",
|
||||
"@simplewebauthn/server": "^13.2.2",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react-highlight": "^0.12.8",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/web-push": "^3.6.4",
|
||||
"acme-client": "^5.3.0",
|
||||
"airtable": "^0.12.2",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.12.0",
|
||||
"botbuilder": "^4.23.3",
|
||||
"bullmq": "^5.61.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"cron-parser": "^4.8.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.4.4",
|
||||
"ejs": "^3.1.10",
|
||||
"elkjs": "^0.10.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"expo-server-sdk": "^3.15.0",
|
||||
"express": "^4.21.1",
|
||||
"formik": "^2.4.6",
|
||||
"history": "^5.3.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"isolated-vm": "^6.0.2",
|
||||
"json2csv": "^5.0.7",
|
||||
"json5": "^2.2.3",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"marked": "^12.0.2",
|
||||
"mermaid": "^11.12.2",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"multer": "^2.0.2",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^7.0.7",
|
||||
"otpauth": "^9.3.1",
|
||||
"pg": "^8.16.3",
|
||||
"playwright": "^1.56.0",
|
||||
"posthog-js": "^1.275.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-big-calendar": "^1.19.4",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^4.0.13",
|
||||
"react-highlight": "^0.15.0",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-router-dom": "^6.30.1",
|
||||
"react-select": "^5.4.0",
|
||||
"react-spinners": "^0.14.1",
|
||||
"react-syntax-highlighter": "^16.0.0",
|
||||
"react-toggle": "^4.1.3",
|
||||
"reactflow": "^11.11.4",
|
||||
"recharts": "^2.12.7",
|
||||
"redis-semaphore": "^5.5.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"slackify-markdown": "^4.4.0",
|
||||
"slugify": "^1.6.5",
|
||||
"socket.io": "^4.7.4",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"stripe": "^10.17.0",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"twilio": "^4.22.0",
|
||||
"typeorm": "^0.3.26",
|
||||
"typeorm-extension": "^2.2.13",
|
||||
"universal-cookie": "^7.2.1",
|
||||
"use-async-effect": "^2.2.6",
|
||||
"uuid": "^8.3.2",
|
||||
"web-push": "^3.6.7",
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/cookie-parser": "^1.4.4",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/ejs": "^3.1.1",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^28.1.4",
|
||||
"@types/json2csv": "^5.0.3",
|
||||
"@types/jsonwebtoken": "^8.5.9",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/node-cron": "^3.0.7",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/react": "^18.2.38",
|
||||
"@types/react-beautiful-dnd": "^13.1.2",
|
||||
"@types/react-big-calendar": "^1.8.5",
|
||||
"@types/react-color": "^3.0.6",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/react-toggle": "^4.0.3",
|
||||
"jest": "^28.1.1",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-mock-extended": "^3.0.5",
|
||||
"react-test-renderer": "^18.2.0",
|
||||
"sass": "^1.89.2",
|
||||
"ts-jest": "^28.0.5"
|
||||
}
|
||||
},
|
||||
"../Common": {
|
||||
"name": "@oneuptime/common",
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "^7.3.2",
|
||||
@@ -553,7 +694,7 @@
|
||||
}
|
||||
},
|
||||
"node_modules/Common": {
|
||||
"resolved": "../Common",
|
||||
"resolved": "../../Common",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"url": "https://github.com/OneUptime/oneuptime"
|
||||
},
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"Common": "file:../../Common",
|
||||
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -57,7 +57,7 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY ./Dashboard/package*.json /usr/src/app/
|
||||
COPY ./App/Dashboard/package*.json /usr/src/app/
|
||||
RUN npm install
|
||||
|
||||
# Expose ports.
|
||||
@@ -73,7 +73,7 @@ EXPOSE 3009
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
{{ else }}
|
||||
# Copy app source
|
||||
COPY ./Dashboard /usr/src/app
|
||||
COPY ./App/Dashboard /usr/src/app
|
||||
# Bundle app source
|
||||
RUN npm run build
|
||||
# Set permission to write logs and cache in case container run as non root
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"watch": ["./","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"watch": ["./","../../Common/UI", "../../Common/Types", "../../Common/Utils", "../../Common/Models"],
|
||||
"ext": "ts,tsx",
|
||||
"ignore": [
|
||||
"./node_modules/**",
|
||||
@@ -11,7 +11,7 @@
|
||||
"./build/**",
|
||||
"./build/dist/*",
|
||||
"./build/dist/**",
|
||||
"../Common/Server/**"
|
||||
"../../Common/Server/**"
|
||||
],
|
||||
"exec": " npm run dev-build && npm run start"
|
||||
}
|
||||
20571
App/Dashboard/package-lock.json
generated
20571
App/Dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@
|
||||
"dependencies": {
|
||||
"@stripe/react-stripe-js": "^1.15.0",
|
||||
"@stripe/stripe-js": "^1.44.1",
|
||||
"Common": "file:../Common",
|
||||
"Common": "file:../../Common",
|
||||
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const { generateServiceWorker } = require('../../Common/Scripts/generate-service-worker');
|
||||
const { generateServiceWorker } = require('../../../Common/Scripts/generate-service-worker');
|
||||
|
||||
// Generate Dashboard service worker
|
||||
const templatePath = path.join(__dirname, '..', 'sw.js.template');
|
||||
|
||||
@@ -56,6 +56,24 @@ COPY ./App/package*.json /usr/src/app/
|
||||
RUN sed -i "s/\"version\": \".*\"/\"version\": \"$APP_VERSION\"/g" /usr/src/app/package.json
|
||||
RUN npm install
|
||||
|
||||
WORKDIR /usr/src/app/Accounts
|
||||
COPY ./App/Accounts/package*.json /usr/src/app/Accounts/
|
||||
RUN npm install
|
||||
|
||||
WORKDIR /usr/src/app/Dashboard
|
||||
COPY ./App/Dashboard/package*.json /usr/src/app/Dashboard/
|
||||
RUN npm install
|
||||
|
||||
WORKDIR /usr/src/app/AdminDashboard
|
||||
COPY ./App/AdminDashboard/package*.json /usr/src/app/AdminDashboard/
|
||||
RUN npm install
|
||||
|
||||
WORKDIR /usr/src/app/StatusPage
|
||||
COPY ./App/StatusPage/package*.json /usr/src/app/StatusPage/
|
||||
RUN npm install
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Expose ports.
|
||||
# - 3002: OneUptime-backend
|
||||
EXPOSE 3002
|
||||
@@ -66,6 +84,13 @@ CMD [ "npm", "run", "dev" ]
|
||||
{{ else }}
|
||||
# Copy app source
|
||||
COPY ./App /usr/src/app
|
||||
# Copy frontend sources
|
||||
COPY ./App/Accounts /usr/src/app/Accounts
|
||||
COPY ./App/Dashboard /usr/src/app/Dashboard
|
||||
COPY ./App/AdminDashboard /usr/src/app/AdminDashboard
|
||||
COPY ./App/StatusPage /usr/src/app/StatusPage
|
||||
# Bundle frontend source
|
||||
RUN npm run build-frontends:prod
|
||||
# Bundle app source
|
||||
RUN npm run compile
|
||||
# Set permission to write logs and cache in case container run as non root
|
||||
|
||||
476
App/FeatureSet/Frontend/Index.ts
Normal file
476
App/FeatureSet/Frontend/Index.ts
Normal file
@@ -0,0 +1,476 @@
|
||||
import UserMiddleware from "Common/Server/Middleware/UserAuthorization";
|
||||
import {
|
||||
IsBillingEnabled,
|
||||
getFrontendEnvVars,
|
||||
} from "Common/Server/EnvironmentConfig";
|
||||
import Express, {
|
||||
ExpressApplication,
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressStatic,
|
||||
NextFunction,
|
||||
} from "Common/Server/Utils/Express";
|
||||
import JSONWebToken from "Common/Server/Utils/JsonWebToken";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import Response from "Common/Server/Utils/Response";
|
||||
import NotAuthorizedException from "Common/Types/Exception/NotAuthorizedException";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import { handleRSS, StatusPageData, getStatusPageData } from "./Utils/StatusPage";
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
|
||||
const AccountsPublicPath: string = "/usr/src/app/Accounts/public";
|
||||
const AccountsViewPath: string = "/usr/src/app/Accounts/views/index.ejs";
|
||||
|
||||
const DashboardPublicPath: string = "/usr/src/app/Dashboard/public";
|
||||
const DashboardViewPath: string = "/usr/src/app/Dashboard/views/index.ejs";
|
||||
|
||||
const AdminPublicPath: string = "/usr/src/app/AdminDashboard/public";
|
||||
const AdminViewPath: string = "/usr/src/app/AdminDashboard/views/index.ejs";
|
||||
|
||||
const StatusPagePublicPath: string = "/usr/src/app/StatusPage/public";
|
||||
const StatusPageViewPath: string = "/usr/src/app/StatusPage/views/index.ejs";
|
||||
|
||||
interface FrontendConfig {
|
||||
routePrefix: string;
|
||||
publicPath: string;
|
||||
indexViewPath: string;
|
||||
primaryHostOnly?: boolean;
|
||||
getVariablesToRenderIndexPage?: (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
) => Promise<JSONObject>;
|
||||
}
|
||||
|
||||
interface RenderFrontendOptions {
|
||||
req: ExpressRequest;
|
||||
res: ExpressResponse;
|
||||
next: NextFunction;
|
||||
frontendConfig: FrontendConfig;
|
||||
}
|
||||
|
||||
const DashboardFallbackRoutePrefixesToSkip: Array<string> = [
|
||||
"/status-page",
|
||||
"/status-page-api",
|
||||
"/status-page-sso-api",
|
||||
"/status-page-identity-api",
|
||||
"/api",
|
||||
"/identity",
|
||||
"/notification",
|
||||
"/telemetry",
|
||||
"/incoming-request-ingest",
|
||||
"/otlp",
|
||||
"/opentelemetry.proto.collector",
|
||||
"/probe-ingest",
|
||||
"/ingestor",
|
||||
"/server-monitor",
|
||||
"/realtime",
|
||||
"/workflow",
|
||||
"/workers",
|
||||
"/mcp",
|
||||
"/analytics-api",
|
||||
"/heartbeat",
|
||||
"/incoming-email",
|
||||
"/file",
|
||||
"/docs",
|
||||
"/reference",
|
||||
"/worker",
|
||||
"/.well-known",
|
||||
"/l",
|
||||
"/manifest.json",
|
||||
"/service-worker.js",
|
||||
"/sw.js",
|
||||
"/browserconfig.xml",
|
||||
"/rss",
|
||||
];
|
||||
|
||||
const StatusPageDomainFallbackRoutePrefixesToSkip: Array<string> = [
|
||||
"/status-page-api",
|
||||
"/status-page-sso-api",
|
||||
"/status-page-identity-api",
|
||||
"/.well-known",
|
||||
"/rss",
|
||||
];
|
||||
|
||||
const StatusPageFrontendConfig: FrontendConfig = {
|
||||
routePrefix: "/status-page",
|
||||
publicPath: StatusPagePublicPath,
|
||||
indexViewPath: StatusPageViewPath,
|
||||
getVariablesToRenderIndexPage: async (
|
||||
req: ExpressRequest,
|
||||
_res: ExpressResponse,
|
||||
): Promise<JSONObject> => {
|
||||
const statusPageData: StatusPageData | null = await getStatusPageData(req);
|
||||
|
||||
if (statusPageData) {
|
||||
return {
|
||||
title: statusPageData.title,
|
||||
description: statusPageData.description,
|
||||
faviconUrl: statusPageData.faviconUrl,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
title: "Status Page",
|
||||
description:
|
||||
"Status Page lets you see real-time information about the status of our services.",
|
||||
faviconUrl:
|
||||
"/status-page-api/favicon/" + ObjectID.getZeroObjectID().toString(),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const DashboardFrontendConfig: FrontendConfig = {
|
||||
routePrefix: "/dashboard",
|
||||
publicPath: DashboardPublicPath,
|
||||
indexViewPath: DashboardViewPath,
|
||||
primaryHostOnly: true,
|
||||
};
|
||||
|
||||
const DashboardRootPwaFileMap: Array<{ route: string; file: string }> = [
|
||||
{ route: "/manifest.json", file: "manifest.json" },
|
||||
{ route: "/sw.js", file: "sw.js" },
|
||||
{ route: "/service-worker.js", file: "sw.js" },
|
||||
{ route: "/browserconfig.xml", file: "browserconfig.xml" },
|
||||
];
|
||||
|
||||
const normalizeHostname: (host: string) => string = (host: string): string => {
|
||||
const hostParts: Array<string> = host.split(":");
|
||||
const hostPart: string | undefined = hostParts[0];
|
||||
|
||||
if (!hostPart) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return hostPart.trim().toLowerCase();
|
||||
};
|
||||
|
||||
const getPrimaryHosts: () => Set<string> = (): Set<string> => {
|
||||
const hostSet: Set<string> = new Set<string>();
|
||||
|
||||
const hostCandidates: Array<string> = [
|
||||
process.env["HOST"] || "",
|
||||
"localhost",
|
||||
"ingress",
|
||||
];
|
||||
|
||||
for (const hostCandidate of hostCandidates) {
|
||||
const normalizedHost: string = normalizeHostname(hostCandidate);
|
||||
if (normalizedHost) {
|
||||
hostSet.add(normalizedHost);
|
||||
}
|
||||
}
|
||||
|
||||
return hostSet;
|
||||
};
|
||||
|
||||
const PrimaryHosts: Set<string> = getPrimaryHosts();
|
||||
|
||||
const getRequestHostname: (req: ExpressRequest) => string = (
|
||||
req: ExpressRequest,
|
||||
): string => {
|
||||
if (req.hostname) {
|
||||
return normalizeHostname(req.hostname.toString());
|
||||
}
|
||||
|
||||
const hostHeader: string | Array<string> | undefined = req.headers["host"];
|
||||
if (typeof hostHeader === "string") {
|
||||
return normalizeHostname(hostHeader);
|
||||
}
|
||||
|
||||
if (Array.isArray(hostHeader)) {
|
||||
const firstHostHeader: string | undefined = hostHeader[0];
|
||||
if (!firstHostHeader) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return normalizeHostname(firstHostHeader);
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const isPrimaryHostRequest: (req: ExpressRequest) => boolean = (
|
||||
req: ExpressRequest,
|
||||
): boolean => {
|
||||
const requestHost: string = getRequestHostname(req);
|
||||
if (!requestHost) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return PrimaryHosts.has(requestHost);
|
||||
};
|
||||
|
||||
const shouldSkipDashboardFallbackRoute: (path: string) => boolean = (
|
||||
path: string,
|
||||
): boolean => {
|
||||
return DashboardFallbackRoutePrefixesToSkip.some((prefix: string) => {
|
||||
if (path === prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return path.startsWith(`${prefix}/`);
|
||||
});
|
||||
};
|
||||
|
||||
const shouldSkipStatusPageDomainFallbackRoute: (path: string) => boolean = (
|
||||
path: string,
|
||||
): boolean => {
|
||||
return StatusPageDomainFallbackRoutePrefixesToSkip.some((prefix: string) => {
|
||||
if (path === prefix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return path.startsWith(`${prefix}/`);
|
||||
});
|
||||
};
|
||||
|
||||
const sendFrontendEnvScript: (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction,
|
||||
) => Promise<void> = async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const env: JSONObject = getFrontendEnvVars();
|
||||
|
||||
const script: string = `
|
||||
if(!window.process){
|
||||
window.process = {}
|
||||
}
|
||||
|
||||
if(!window.process.env){
|
||||
window.process.env = {}
|
||||
}
|
||||
window.process.env = ${JSON.stringify(env)};
|
||||
`;
|
||||
|
||||
Response.sendJavaScriptResponse(req, res, script);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
const renderFrontendIndexPage: (
|
||||
options: RenderFrontendOptions,
|
||||
) => Promise<void> = async (
|
||||
options: RenderFrontendOptions,
|
||||
): Promise<void> => {
|
||||
const { req, res, next, frontendConfig } = options;
|
||||
|
||||
try {
|
||||
let variables: JSONObject = {};
|
||||
|
||||
if (frontendConfig.getVariablesToRenderIndexPage) {
|
||||
try {
|
||||
const variablesToRenderIndexPage: JSONObject =
|
||||
await frontendConfig.getVariablesToRenderIndexPage(req, res);
|
||||
|
||||
variables = {
|
||||
...variables,
|
||||
...variablesToRenderIndexPage,
|
||||
};
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.headersSent) {
|
||||
return;
|
||||
}
|
||||
|
||||
res.render(frontendConfig.indexViewPath, {
|
||||
enableGoogleTagManager: IsBillingEnabled || false,
|
||||
...variables,
|
||||
});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
const ensureMasterAdminAccess: (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
) => Promise<JSONObject> = async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<JSONObject> => {
|
||||
try {
|
||||
const accessToken: string | undefined =
|
||||
UserMiddleware.getAccessTokenFromExpressRequest(req);
|
||||
|
||||
if (!accessToken) {
|
||||
Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthorizedException(
|
||||
"Unauthorized: Only master admins can access the admin dashboard.",
|
||||
),
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
const authData = JSONWebToken.decode(accessToken);
|
||||
|
||||
if (!authData.isMasterAdmin) {
|
||||
Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthorizedException(
|
||||
"Unauthorized: Only master admins can access the admin dashboard.",
|
||||
),
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new NotAuthorizedException(
|
||||
"Unauthorized: Only master admins can access the admin dashboard.",
|
||||
),
|
||||
);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const registerFrontendApp: (frontendConfig: FrontendConfig) => void = (
|
||||
frontendConfig: FrontendConfig,
|
||||
): void => {
|
||||
const staticHandler = ExpressStatic(frontendConfig.publicPath);
|
||||
|
||||
app.use(
|
||||
frontendConfig.routePrefix,
|
||||
(req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
if (frontendConfig.primaryHostOnly && !isPrimaryHostRequest(req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return staticHandler(req, res, next);
|
||||
},
|
||||
);
|
||||
|
||||
app.get(
|
||||
`${frontendConfig.routePrefix}/env.js`,
|
||||
(req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
if (frontendConfig.primaryHostOnly && !isPrimaryHostRequest(req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return sendFrontendEnvScript(req, res, next);
|
||||
},
|
||||
);
|
||||
|
||||
app.get(
|
||||
[frontendConfig.routePrefix, `${frontendConfig.routePrefix}/*`],
|
||||
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
if (frontendConfig.primaryHostOnly && !isPrimaryHostRequest(req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return renderFrontendIndexPage({
|
||||
req,
|
||||
res,
|
||||
next,
|
||||
frontendConfig,
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const registerStatusPageCustomDomainFallback: () => void = (): void => {
|
||||
app.get("*", async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
if (isPrimaryHostRequest(req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (shouldSkipStatusPageDomainFallbackRoute(req.path)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return renderFrontendIndexPage({
|
||||
req,
|
||||
res,
|
||||
next,
|
||||
frontendConfig: StatusPageFrontendConfig,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const registerDashboardFallbackForPrimaryHost: () => void = (): void => {
|
||||
app.get("*", async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
if (IsBillingEnabled) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!isPrimaryHostRequest(req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (shouldSkipDashboardFallbackRoute(req.path)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return renderFrontendIndexPage({
|
||||
req,
|
||||
res,
|
||||
next,
|
||||
frontendConfig: DashboardFrontendConfig,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const registerDashboardRootPwaFiles: () => void = (): void => {
|
||||
for (const pwaFileRoute of DashboardRootPwaFileMap) {
|
||||
app.get(
|
||||
pwaFileRoute.route,
|
||||
(req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
||||
if (IsBillingEnabled || !isPrimaryHostRequest(req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return res.sendFile(`${DashboardPublicPath}/${pwaFileRoute.file}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
app.get("/rss", handleRSS);
|
||||
app.get("/status-page/:statusPageId/rss", handleRSS);
|
||||
|
||||
registerFrontendApp({
|
||||
routePrefix: "/accounts",
|
||||
publicPath: AccountsPublicPath,
|
||||
indexViewPath: AccountsViewPath,
|
||||
primaryHostOnly: true,
|
||||
});
|
||||
|
||||
registerFrontendApp(DashboardFrontendConfig);
|
||||
|
||||
registerFrontendApp({
|
||||
routePrefix: "/admin",
|
||||
publicPath: AdminPublicPath,
|
||||
indexViewPath: AdminViewPath,
|
||||
primaryHostOnly: true,
|
||||
getVariablesToRenderIndexPage: ensureMasterAdminAccess,
|
||||
});
|
||||
|
||||
registerFrontendApp(StatusPageFrontendConfig);
|
||||
|
||||
registerDashboardRootPwaFiles();
|
||||
registerStatusPageCustomDomainFallback();
|
||||
registerDashboardFallbackForPrimaryHost();
|
||||
};
|
||||
|
||||
export default {
|
||||
init,
|
||||
};
|
||||
252
App/FeatureSet/Frontend/Utils/StatusPage.ts
Normal file
252
App/FeatureSet/Frontend/Utils/StatusPage.ts
Normal file
@@ -0,0 +1,252 @@
|
||||
import { StatusPageApiInternalUrl } from "Common/Server/EnvironmentConfig";
|
||||
import { ExpressRequest, ExpressResponse } from "Common/Server/Utils/Express";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import { JSONArray, JSONObject } from "Common/Types/JSON";
|
||||
import API from "Common/Utils/API";
|
||||
|
||||
export interface StatusPageData {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
faviconUrl: string;
|
||||
}
|
||||
|
||||
export const getStatusPageData: (
|
||||
req: ExpressRequest,
|
||||
) => Promise<StatusPageData | null> = async (
|
||||
req: ExpressRequest,
|
||||
): Promise<StatusPageData | null> => {
|
||||
try {
|
||||
logger.debug("Getting status page data");
|
||||
|
||||
let statusPageIdOrDomain: string = "";
|
||||
let isPreview: boolean = false;
|
||||
|
||||
const path: string = req.path;
|
||||
logger.debug(`Request path: ${path}`);
|
||||
|
||||
if (path && path.includes("/status-page/")) {
|
||||
statusPageIdOrDomain =
|
||||
path.split("/status-page/")[1]?.split("/")[0] || "";
|
||||
isPreview = true;
|
||||
logger.debug(`Found status page ID in URL: ${statusPageIdOrDomain}`);
|
||||
} else {
|
||||
const host: string =
|
||||
req.hostname?.toString() || req.headers["host"]?.toString() || "";
|
||||
if (host) {
|
||||
statusPageIdOrDomain = host;
|
||||
logger.debug(
|
||||
`Found domain in request headers: ${statusPageIdOrDomain}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!statusPageIdOrDomain) {
|
||||
logger.debug("No status page ID or domain found");
|
||||
return null;
|
||||
}
|
||||
|
||||
let statusPageId: string;
|
||||
let title: string = "Status Page";
|
||||
let description: string =
|
||||
"Status Page lets you see real-time information about the status of our services.";
|
||||
|
||||
if (isPreview) {
|
||||
// For preview pages, use the extracted ID directly.
|
||||
statusPageId = statusPageIdOrDomain;
|
||||
} else {
|
||||
logger.debug(
|
||||
`Pinging the API with statusPageIdOrDomain: ${statusPageIdOrDomain}`,
|
||||
);
|
||||
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.get({
|
||||
url: URL.fromString(StatusPageApiInternalUrl.toString()).addRoute(
|
||||
`/seo/${statusPageIdOrDomain}`,
|
||||
),
|
||||
});
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
logger.debug(`Received error response from API: ${response}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("Successfully received response from API");
|
||||
|
||||
statusPageId = response.data?.["_id"] as string;
|
||||
if (!statusPageId) {
|
||||
logger.debug("No status page ID in response");
|
||||
return null;
|
||||
}
|
||||
|
||||
title = (response.data?.["title"] as string) || title;
|
||||
description = (response.data?.["description"] as string) || description;
|
||||
}
|
||||
|
||||
return {
|
||||
id: statusPageId,
|
||||
title,
|
||||
description,
|
||||
faviconUrl: `/status-page-api/favicon/${statusPageIdOrDomain}`,
|
||||
};
|
||||
} catch (err) {
|
||||
logger.error("Error getting status page data:");
|
||||
logger.error(err);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
type RSSItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
link: string;
|
||||
pubDate: string;
|
||||
};
|
||||
|
||||
export const handleRSS: (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
) => Promise<void> = async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const statusPageData: StatusPageData | null = await getStatusPageData(req);
|
||||
|
||||
if (!statusPageData) {
|
||||
res.status(404).send("Status page not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const { id: statusPageId, title, description } = statusPageData;
|
||||
|
||||
const incidentsResponse: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post({
|
||||
url: URL.fromString(StatusPageApiInternalUrl.toString()).addRoute(
|
||||
`/incidents/${statusPageId}`,
|
||||
),
|
||||
data: {},
|
||||
headers: {
|
||||
"status-page-id": statusPageId,
|
||||
},
|
||||
});
|
||||
|
||||
const announcementsResponse: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post({
|
||||
url: URL.fromString(StatusPageApiInternalUrl.toString()).addRoute(
|
||||
`/announcements/${statusPageId}`,
|
||||
),
|
||||
data: {},
|
||||
headers: {
|
||||
"status-page-id": statusPageId,
|
||||
},
|
||||
});
|
||||
|
||||
const scheduledResponse: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
||||
await API.post({
|
||||
url: URL.fromString(StatusPageApiInternalUrl.toString()).addRoute(
|
||||
`/scheduled-maintenance-events/${statusPageId}`,
|
||||
),
|
||||
data: {},
|
||||
headers: {
|
||||
"status-page-id": statusPageId,
|
||||
},
|
||||
});
|
||||
|
||||
const items: RSSItem[] = [];
|
||||
|
||||
const isPreview: boolean = req.path.includes("/status-page/");
|
||||
const baseUrl: string = isPreview
|
||||
? `${req.protocol}://${req.get("host")}/status-page/${statusPageId}`
|
||||
: `${req.protocol}://${req.get("host")}`;
|
||||
|
||||
if (
|
||||
incidentsResponse instanceof HTTPResponse &&
|
||||
incidentsResponse.data?.["incidents"]
|
||||
) {
|
||||
const incidents: JSONArray = incidentsResponse.data[
|
||||
"incidents"
|
||||
] as JSONArray;
|
||||
incidents.forEach((incident: JSONObject) => {
|
||||
items.push({
|
||||
title: `Incident: ${incident["title"]}`,
|
||||
description: (incident["description"] as string) || "",
|
||||
link: `${baseUrl}/incidents/${incident["_id"]}`,
|
||||
pubDate: new Date(incident["createdAt"] as string).toUTCString(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
announcementsResponse instanceof HTTPResponse &&
|
||||
announcementsResponse.data?.["announcements"]
|
||||
) {
|
||||
const announcements: JSONArray = announcementsResponse.data[
|
||||
"announcements"
|
||||
] as JSONArray;
|
||||
announcements.forEach((announcement: JSONObject) => {
|
||||
items.push({
|
||||
title: `Announcement: ${announcement["title"]}`,
|
||||
description: (announcement["description"] as string) || "",
|
||||
link: `${baseUrl}/announcements/${announcement["_id"]}`,
|
||||
pubDate: new Date(announcement["createdAt"] as string).toUTCString(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
scheduledResponse instanceof HTTPResponse &&
|
||||
scheduledResponse.data?.["scheduledMaintenanceEvents"]
|
||||
) {
|
||||
const scheduled: JSONArray = scheduledResponse.data[
|
||||
"scheduledMaintenanceEvents"
|
||||
] as JSONArray;
|
||||
scheduled.forEach((event: JSONObject) => {
|
||||
items.push({
|
||||
title: `Scheduled Maintenance: ${event["title"]}`,
|
||||
description: (event["description"] as string) || "",
|
||||
link: `${baseUrl}/scheduled-events/${event["_id"]}`,
|
||||
pubDate: new Date(event["createdAt"] as string).toUTCString(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
items.sort((a: RSSItem, b: RSSItem) => {
|
||||
return new Date(b.pubDate).getTime() - new Date(a.pubDate).getTime();
|
||||
});
|
||||
|
||||
const feedUrl: string = `${req.protocol}://${req.get("host")}${req.path}`;
|
||||
|
||||
let rssXml: string = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>${title} Updates</title>
|
||||
<description>${description}</description>
|
||||
<link>${baseUrl}</link>
|
||||
<atom:link href="${feedUrl}" rel="self" type="application/rss+xml" />
|
||||
`;
|
||||
|
||||
items.forEach((item: RSSItem) => {
|
||||
rssXml += `
|
||||
<item>
|
||||
<title><![CDATA[${item.title}]]></title>
|
||||
<description><![CDATA[${item.description}]]></description>
|
||||
<link>${item.link}</link>
|
||||
<guid>${item.link}</guid>
|
||||
<pubDate>${item.pubDate}</pubDate>
|
||||
</item>`;
|
||||
});
|
||||
|
||||
rssXml += `
|
||||
</channel>
|
||||
</rss>`;
|
||||
|
||||
res.set("Content-Type", "application/rss+xml");
|
||||
res.send(rssXml);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
res.status(500).send("Internal Server Error");
|
||||
}
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import BaseAPIRoutes from "./FeatureSet/BaseAPI/Index";
|
||||
import FrontendRoutes from "./FeatureSet/Frontend/Index";
|
||||
// import FeatureSets.
|
||||
import IdentityRoutes from "./FeatureSet/Identity/Index";
|
||||
import MCPRoutes from "./FeatureSet/MCP/Index";
|
||||
@@ -96,6 +97,7 @@ const init: PromiseVoidFunction = async (): Promise<void> => {
|
||||
await NotificationRoutes.init();
|
||||
await BaseAPIRoutes.init();
|
||||
await MCPRoutes.init();
|
||||
await FrontendRoutes.init();
|
||||
|
||||
// Add default routes to the app
|
||||
await App.addDefaultRoutes();
|
||||
|
||||
@@ -62,7 +62,7 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
COPY ./StatusPage/package*.json /usr/src/app/
|
||||
COPY ./App/StatusPage/package*.json /usr/src/app/
|
||||
RUN npm install
|
||||
|
||||
# Expose ports.
|
||||
@@ -80,7 +80,7 @@ EXPOSE 3106
|
||||
CMD [ "npm", "run", "dev" ]
|
||||
{{ else }}
|
||||
# Copy app source
|
||||
COPY ./StatusPage /usr/src/app
|
||||
COPY ./App/StatusPage /usr/src/app
|
||||
# Bundle app source
|
||||
RUN npm run build
|
||||
# Set permission to write logs and cache in case container run as non root
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"watch": ["./*","../Common/UI", "../Common/Types", "../Common/Utils", "../Common/Models"],
|
||||
"watch": ["./*","../../Common/UI", "../../Common/Types", "../../Common/Utils", "../../Common/Models"],
|
||||
"ext": "ts,tsx",
|
||||
"ignore": [
|
||||
"./node_modules/**",
|
||||
@@ -11,7 +11,7 @@
|
||||
"./build/**",
|
||||
"./build/dist/*",
|
||||
"./build/dist/**",
|
||||
"../Common/Server/**"
|
||||
"../../Common/Server/**"
|
||||
],
|
||||
"exec": " npm run dev-build && npm run start"
|
||||
}
|
||||
21000
App/StatusPage/package-lock.json
generated
21000
App/StatusPage/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"Common": "file:../Common",
|
||||
"Common": "file:../../Common",
|
||||
|
||||
"ejs": "^3.1.10",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -8,10 +8,25 @@
|
||||
},
|
||||
"main": "Index.ts",
|
||||
"scripts": {
|
||||
"build-frontend:accounts": "bash ./scripts/frontend-run.sh Accounts dev-build",
|
||||
"build-frontend:dashboard": "bash ./scripts/frontend-run.sh Dashboard dev-build",
|
||||
"build-frontend:admin-dashboard": "bash ./scripts/frontend-run.sh AdminDashboard dev-build",
|
||||
"build-frontend:status-page": "bash ./scripts/frontend-run.sh StatusPage dev-build",
|
||||
"build-frontends": "npm run build-frontend:accounts && npm run build-frontend:dashboard && npm run build-frontend:admin-dashboard && npm run build-frontend:status-page",
|
||||
"build-frontend:accounts:prod": "bash ./scripts/frontend-run.sh Accounts build",
|
||||
"build-frontend:dashboard:prod": "bash ./scripts/frontend-run.sh Dashboard build",
|
||||
"build-frontend:admin-dashboard:prod": "bash ./scripts/frontend-run.sh AdminDashboard build",
|
||||
"build-frontend:status-page:prod": "bash ./scripts/frontend-run.sh StatusPage build",
|
||||
"build-frontends:prod": "npm run build-frontend:accounts:prod && npm run build-frontend:dashboard:prod && npm run build-frontend:admin-dashboard:prod && npm run build-frontend:status-page:prod",
|
||||
"watch-frontend:accounts": "bash ./scripts/frontend-run.sh Accounts dev-build --watch",
|
||||
"watch-frontend:dashboard": "bash ./scripts/frontend-run.sh Dashboard dev-build --watch",
|
||||
"watch-frontend:admin-dashboard": "bash ./scripts/frontend-run.sh AdminDashboard dev-build --watch",
|
||||
"watch-frontend:status-page": "bash ./scripts/frontend-run.sh StatusPage dev-build --watch",
|
||||
"dev:api": "npx nodemon",
|
||||
"start": "export NODE_OPTIONS='--max-old-space-size=8096' && node --require ts-node/register Index.ts",
|
||||
"compile": "tsc",
|
||||
"clear-modules": "rm -rf node_modules && rm package-lock.json && npm install",
|
||||
"dev": "npx nodemon",
|
||||
"dev": "bash ./scripts/dev.sh",
|
||||
"audit": "npm audit --audit-level=low",
|
||||
"dep-check": "npm install -g depcheck && depcheck ./ --skip-missing=true",
|
||||
"test": "rm -rf build && jest --detectOpenHandles --passWithNoTests",
|
||||
|
||||
34
App/scripts/dev.sh
Normal file
34
App/scripts/dev.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
pids=()
|
||||
|
||||
cleanup() {
|
||||
for pid in "${pids[@]}"; do
|
||||
if kill -0 "$pid" >/dev/null 2>&1; then
|
||||
kill "$pid" >/dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
npm run build-frontends
|
||||
|
||||
npm run watch-frontend:accounts &
|
||||
pids+=($!)
|
||||
|
||||
npm run watch-frontend:dashboard &
|
||||
pids+=($!)
|
||||
|
||||
npm run watch-frontend:admin-dashboard &
|
||||
pids+=($!)
|
||||
|
||||
npm run watch-frontend:status-page &
|
||||
pids+=($!)
|
||||
|
||||
npm run dev:api &
|
||||
pids+=($!)
|
||||
|
||||
wait -n "${pids[@]}"
|
||||
32
App/scripts/frontend-run.sh
Executable file
32
App/scripts/frontend-run.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -lt 2 ]; then
|
||||
echo "Usage: $0 <FrontendDirName> <NpmScript> [script args...]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
app_dir="$(cd "${script_dir}/.." && pwd)"
|
||||
|
||||
frontend_dir_name="$1"
|
||||
shift
|
||||
|
||||
frontend_script="$1"
|
||||
shift
|
||||
|
||||
if [ -d "${app_dir}/${frontend_dir_name}" ]; then
|
||||
frontend_dir="${app_dir}/${frontend_dir_name}"
|
||||
elif [ -d "${app_dir}/../${frontend_dir_name}" ]; then
|
||||
frontend_dir="${app_dir}/../${frontend_dir_name}"
|
||||
else
|
||||
echo "Frontend directory not found for ${frontend_dir_name}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
npm --prefix "$frontend_dir" run "$frontend_script" -- "$@"
|
||||
else
|
||||
npm --prefix "$frontend_dir" run "$frontend_script"
|
||||
fi
|
||||
@@ -108,5 +108,11 @@
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"Accounts",
|
||||
"Dashboard",
|
||||
"AdminDashboard",
|
||||
"StatusPage"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
server_names_hash_bucket_size ${SERVER_NAMES_HASH_BUCKET_SIZE};
|
||||
server_names_hash_max_size ${SERVER_NAMES_HASH_MAX_SIZE};
|
||||
|
||||
upstream accounts {
|
||||
server ${SERVER_ACCOUNTS_HOSTNAME}:${ACCOUNTS_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
upstream app {
|
||||
server ${SERVER_APP_HOSTNAME}:${APP_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
@@ -15,15 +11,6 @@ upstream telemetry {
|
||||
server ${SERVER_TELEMETRY_HOSTNAME}:${TELEMETRY_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
upstream dashboard {
|
||||
server ${SERVER_DASHBOARD_HOSTNAME}:${DASHBOARD_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
upstream admin-dashboard {
|
||||
server ${SERVER_ADMIN_DASHBOARD_HOSTNAME}:${ADMIN_DASHBOARD_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
|
||||
upstream worker {
|
||||
server ${SERVER_WORKER_HOSTNAME}:${WORKER_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
@@ -40,10 +27,6 @@ upstream home {
|
||||
server ${SERVER_HOME_HOSTNAME}:${HOME_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
upstream status-page {
|
||||
server ${SERVER_STATUS_PAGE_HOSTNAME}:${STATUS_PAGE_PORT} weight=10 max_fails=3 fail_timeout=30s;
|
||||
}
|
||||
|
||||
upstream opentelemetry-collector-http {
|
||||
server ${SERVER_OTEL_COLLECTOR_HOSTNAME}:4318;
|
||||
}
|
||||
@@ -92,7 +75,7 @@ server {
|
||||
}
|
||||
|
||||
if ($billing_enabled != true) {
|
||||
proxy_pass http://status-page;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -111,7 +94,7 @@ server {
|
||||
}
|
||||
|
||||
if ($billing_enabled != true) {
|
||||
proxy_pass http://status-page;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +206,7 @@ server {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_pass http://status-page;
|
||||
proxy_pass http://app;
|
||||
|
||||
|
||||
}
|
||||
@@ -289,7 +272,7 @@ server {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_pass http://status-page;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,7 +322,7 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE}
|
||||
}
|
||||
|
||||
if ($billing_enabled != true) {
|
||||
proxy_pass http://dashboard;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +384,7 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE}
|
||||
}
|
||||
|
||||
if ($billing_enabled != true) {
|
||||
proxy_pass http://dashboard;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,7 +450,7 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_pass http://accounts;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
|
||||
|
||||
@@ -644,7 +627,7 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE}
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
proxy_pass http://dashboard;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
|
||||
# PWA manifest and service worker with proper headers
|
||||
@@ -670,7 +653,7 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE}
|
||||
add_header Cache-Control "public, max-age=86400" always;
|
||||
}
|
||||
|
||||
proxy_pass http://dashboard;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
|
||||
|
||||
@@ -686,7 +669,7 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE}
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_pass http://admin-dashboard;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
|
||||
location /worker {
|
||||
@@ -717,7 +700,7 @@ ${PROVISION_SSL_CERTIFICATE_KEY_DIRECTIVE}
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_pass http://status-page;
|
||||
proxy_pass http://app;
|
||||
}
|
||||
|
||||
location /identity {
|
||||
|
||||
12
configure.sh
12
configure.sh
@@ -196,16 +196,12 @@ main() {
|
||||
export $(grep -v '^#' config.env | xargs)
|
||||
|
||||
print_info "Generating Dockerfile configurations..."
|
||||
find . -maxdepth 1 -type d -exec sh -c '
|
||||
for dir do
|
||||
if [ -f "$dir/Dockerfile.tpl" ]; then
|
||||
cat "$dir/Dockerfile.tpl" | gomplate > "$dir/Dockerfile"
|
||||
fi
|
||||
done
|
||||
' sh {} +
|
||||
while IFS= read -r dockerfile_template; do
|
||||
cat "$dockerfile_template" | gomplate > "${dockerfile_template%.tpl}"
|
||||
done < <(find . -type f -name "Dockerfile.tpl" -not -path "*/node_modules/*")
|
||||
|
||||
print_success "OneUptime installation completed successfully! 🚀"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
main
|
||||
|
||||
@@ -28,14 +28,14 @@ x-common-variables: &common-variables
|
||||
|
||||
ALLOWED_ACTIVE_MONITOR_COUNT_IN_FREE_PLAN: ${ALLOWED_ACTIVE_MONITOR_COUNT_IN_FREE_PLAN}
|
||||
|
||||
SERVER_ACCOUNTS_HOSTNAME: accounts
|
||||
SERVER_ACCOUNTS_HOSTNAME: app
|
||||
SERVER_APP_HOSTNAME: app
|
||||
SERVER_ALERT_HOSTNAME: alert
|
||||
SERVER_TELEMETRY_HOSTNAME: telemetry
|
||||
SERVER_TEST_SERVER_HOSTNAME: test-server
|
||||
SERVER_STATUS_PAGE_HOSTNAME: status-page
|
||||
SERVER_DASHBOARD_HOSTNAME: dashboard
|
||||
SERVER_ADMIN_DASHBOARD_HOSTNAME: admin-dashboard
|
||||
SERVER_STATUS_PAGE_HOSTNAME: app
|
||||
SERVER_DASHBOARD_HOSTNAME: app
|
||||
SERVER_ADMIN_DASHBOARD_HOSTNAME: app
|
||||
SERVER_OTEL_COLLECTOR_HOSTNAME: otel-collector
|
||||
SERVER_WORKER_HOSTNAME: worker
|
||||
SERVER_HOME_HOSTNAME: home
|
||||
@@ -216,20 +216,6 @@ services:
|
||||
options:
|
||||
max-size: "1000m"
|
||||
|
||||
accounts:
|
||||
networks:
|
||||
- oneuptime
|
||||
restart: always
|
||||
environment:
|
||||
<<: *common-runtime-variables
|
||||
PORT: ${ACCOUNTS_PORT}
|
||||
DISABLE_TELEMETRY: ${DISABLE_TELEMETRY_FOR_ACCOUNTS}
|
||||
logging:
|
||||
driver: "local"
|
||||
options:
|
||||
max-size: "1000m"
|
||||
|
||||
|
||||
llm:
|
||||
networks:
|
||||
- oneuptime
|
||||
@@ -246,49 +232,6 @@ services:
|
||||
options:
|
||||
max-size: "1000m"
|
||||
|
||||
admin-dashboard:
|
||||
networks:
|
||||
- oneuptime
|
||||
restart: always
|
||||
environment:
|
||||
<<: *common-runtime-variables
|
||||
PORT: ${ADMIN_DASHBOARD_PORT}
|
||||
DISABLE_TELEMETRY: ${DISABLE_TELEMETRY_FOR_ADMIN_DASHBOARD}
|
||||
logging:
|
||||
driver: "local"
|
||||
options:
|
||||
max-size: "1000m"
|
||||
|
||||
dashboard:
|
||||
networks:
|
||||
- oneuptime
|
||||
restart: always
|
||||
environment:
|
||||
<<: *common-runtime-variables
|
||||
PORT: ${DASHBOARD_PORT}
|
||||
DISABLE_TELEMETRY: ${DISABLE_TELEMETRY_FOR_DASHBOARD}
|
||||
logging:
|
||||
driver: "local"
|
||||
options:
|
||||
max-size: "1000m"
|
||||
|
||||
|
||||
|
||||
status-page:
|
||||
networks:
|
||||
- oneuptime
|
||||
restart: always
|
||||
environment:
|
||||
<<: *common-runtime-variables
|
||||
PORT: ${STATUS_PAGE_PORT}
|
||||
DISABLE_TELEMETRY: ${DISABLE_TELEMETRY_FOR_STATUS_PAGE}
|
||||
logging:
|
||||
driver: "local"
|
||||
options:
|
||||
max-size: "1000m"
|
||||
|
||||
|
||||
|
||||
test-server:
|
||||
networks:
|
||||
- oneuptime
|
||||
@@ -524,4 +467,4 @@ volumes:
|
||||
|
||||
networks:
|
||||
oneuptime:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
|
||||
@@ -45,107 +45,6 @@ services:
|
||||
context: .
|
||||
dockerfile: ./OTelCollector/Dockerfile
|
||||
|
||||
accounts:
|
||||
ports:
|
||||
- '${ACCOUNTS_PORT}:${ACCOUNTS_PORT}'
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: accounts
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
volumes:
|
||||
- ./Accounts:/usr/src/app:cached
|
||||
# Use node modules of the container and not host system.
|
||||
# https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder
|
||||
- /usr/src/app/dev-env
|
||||
- /usr/src/app/node_modules/
|
||||
- ./Common:/usr/src/Common:cached
|
||||
|
||||
- /usr/src/Common/node_modules/
|
||||
|
||||
|
||||
build:
|
||||
network: host
|
||||
context: .
|
||||
dockerfile: ./Accounts/Dockerfile
|
||||
|
||||
|
||||
dashboard:
|
||||
ports:
|
||||
- '${DASHBOARD_PORT}:${DASHBOARD_PORT}'
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: dashboard
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
volumes:
|
||||
- ./Dashboard:/usr/src/app:cached
|
||||
# Use node modules of the container and not host system.
|
||||
# https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder
|
||||
- /usr/src/app/dev-env
|
||||
- /usr/src/app/node_modules/
|
||||
- ./Common:/usr/src/Common:cached
|
||||
|
||||
- /usr/src/Common/node_modules/
|
||||
|
||||
|
||||
build:
|
||||
network: host
|
||||
context: .
|
||||
dockerfile: ./Dashboard/Dockerfile
|
||||
|
||||
|
||||
|
||||
admin-dashboard:
|
||||
ports:
|
||||
- '${ADMIN_DASHBOARD_PORT}:${ADMIN_DASHBOARD_PORT}'
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: admin-dashboard
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
volumes:
|
||||
- ./AdminDashboard:/usr/src/app:cached
|
||||
# Use node modules of the container and not host system.
|
||||
# https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder
|
||||
- /usr/src/app/dev-env
|
||||
- /usr/src/app/node_modules/
|
||||
- ./Common:/usr/src/Common:cached
|
||||
|
||||
- /usr/src/Common/node_modules/
|
||||
|
||||
|
||||
build:
|
||||
network: host
|
||||
context: .
|
||||
dockerfile: ./AdminDashboard/Dockerfile
|
||||
|
||||
|
||||
status-page:
|
||||
ports:
|
||||
- '${STATUS_PAGE_PORT}:${STATUS_PAGE_PORT}'
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: status-page
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
volumes:
|
||||
- ./StatusPage:/usr/src/app:cached
|
||||
# Use node modules of the container and not host system.
|
||||
# https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder
|
||||
- /usr/src/app/dev-env
|
||||
- /usr/src/app/node_modules/
|
||||
- ./Common:/usr/src/Common:cached
|
||||
|
||||
- /usr/src/Common/node_modules/
|
||||
|
||||
|
||||
build:
|
||||
network: host
|
||||
context: .
|
||||
dockerfile: ./StatusPage/Dockerfile
|
||||
|
||||
|
||||
test-server:
|
||||
volumes:
|
||||
- ./TestServer:/usr/src/app:cached
|
||||
@@ -255,9 +154,17 @@ services:
|
||||
app:
|
||||
volumes:
|
||||
- ./App:/usr/src/app:cached
|
||||
- ./App/Accounts:/usr/src/app/Accounts:cached
|
||||
- ./App/Dashboard:/usr/src/app/Dashboard:cached
|
||||
- ./App/AdminDashboard:/usr/src/app/AdminDashboard:cached
|
||||
- ./App/StatusPage:/usr/src/app/StatusPage:cached
|
||||
# Use node modules of the container and not host system.
|
||||
# https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder
|
||||
- /usr/src/app/node_modules/
|
||||
- /usr/src/app/Accounts/node_modules/
|
||||
- /usr/src/app/Dashboard/node_modules/
|
||||
- /usr/src/app/AdminDashboard/node_modules/
|
||||
- /usr/src/app/StatusPage/node_modules/
|
||||
- ./Common:/usr/src/Common:cached
|
||||
- /usr/src/Common/node_modules/
|
||||
extends:
|
||||
|
||||
@@ -35,38 +35,6 @@ services:
|
||||
file: ./docker-compose.base.yml
|
||||
service: otel-collector
|
||||
|
||||
accounts:
|
||||
image: oneuptime/accounts:${APP_TAG}
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: accounts
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
|
||||
dashboard:
|
||||
image: oneuptime/dashboard:${APP_TAG}
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: dashboard
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
|
||||
admin-dashboard:
|
||||
image: oneuptime/admin-dashboard:${APP_TAG}
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: admin-dashboard
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
|
||||
status-page:
|
||||
image: oneuptime/status-page:${APP_TAG}
|
||||
extends:
|
||||
file: ./docker-compose.base.yml
|
||||
service: status-page
|
||||
depends_on:
|
||||
<<: *common-depends-on
|
||||
|
||||
app:
|
||||
image: oneuptime/app:${APP_TAG}
|
||||
extends:
|
||||
|
||||
Reference in New Issue
Block a user