feat: add server deployment, desktop builds, and CI/CD pipeline

- Add Dockerfile with adapter-node for server deployment
- Add Kubernetes deployment and ingress manifests
- Add GitHub Actions workflow (server build, desktop builds, K8s deploy)
- Electron now loads hosted URL (https://optima.osdci.net) in production
- Add macOS DMG maker and make:macos script
- Switch to static imports in lib/index.ts
- Add .dockerignore
This commit is contained in:
2026-02-26 12:58:24 -06:00
parent 6791a6735b
commit ae5ac35058
15 changed files with 319 additions and 30 deletions
+16
View File
@@ -0,0 +1,16 @@
node_modules
.svelte-kit
build
out
.vite
.env
.env.*
*.dmg
*.zip
e2e
electron
forge.config.ts
forge.env.d.ts
vite.main.config.ts
vite.preload.config.ts
playwright.config.ts
+133
View File
@@ -0,0 +1,133 @@
name: Build and Publish
on:
release:
types: [created]
jobs:
build-server:
name: Build Server Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push the Docker image
uses: docker/build-push-action@v6
with:
push: true
tags: |
ghcr.io/project-optima/ttscm-ui:latest
ghcr.io/project-optima/ttscm-ui:${{ github.event.release.tag_name }}
build-desktop-macos:
name: Build Desktop (macOS)
runs-on: macos-latest
permissions:
contents: write
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: latest
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build macOS distributables
run: pnpm run make:macos
- name: Upload macOS artifacts to release
uses: softprops/action-gh-release@v2
with:
files: |
out/make/**/*.dmg
out/make/**/*.zip
build-desktop-windows:
name: Build Desktop (Windows)
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: latest
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Windows distributables
run: pnpm run make -- --platform win32
- name: Upload Windows artifacts to release
uses: softprops/action-gh-release@v2
with:
files: |
out/make/**/*.exe
out/make/**/*.nupkg
out/make/**/*.msi
deploy:
name: Deploy
needs: [build-server]
runs-on: ubuntu-latest
steps:
- name: Set the Kubernetes context
uses: azure/k8s-set-context@v2
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Checkout source code
uses: actions/checkout@v4
- name: Lint Kubernetes manifests
uses: azure/k8s-lint@v3
with:
lintType: dryrun
manifests: |
kubernetes/deployment.yaml
kubernetes/ingress.yaml
namespace: optima
- name: Deploy to the Kubernetes cluster
uses: azure/k8s-deploy@v5
with:
namespace: optima
force: true
skip-tls-verify: true
manifests: |
kubernetes/deployment.yaml
kubernetes/ingress.yaml
images: |
ghcr.io/project-optima/ttscm-ui:${{ github.event.release.tag_name }}
+1
View File
@@ -25,3 +25,4 @@ vite.config.ts.timestamp-*
.vite .vite
out out
tailwindcss-*.log
+29
View File
@@ -0,0 +1,29 @@
FROM node:22-alpine AS base
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# Install dependencies
COPY package.json pnpm-lock.yaml ./
COPY patches ./patches
RUN pnpm install --frozen-lockfile
# Build the SvelteKit app with adapter-node
COPY . .
RUN pnpm run build:server
# Production image
FROM node:22-alpine AS production
WORKDIR /app
COPY --from=base /app/build ./build
COPY --from=base /app/package.json ./
ENV NODE_ENV=production
ENV PORT=3000
ENV ORIGIN=https://optima.osdci.net
EXPOSE 3000
CMD ["node", "build/index.js"]
+36 -1
View File
@@ -22,6 +22,7 @@
"@electron-forge/plugin-vite": "^7.11.1", "@electron-forge/plugin-vite": "^7.11.1",
"@electron/fuses": "^1.8.0", "@electron/fuses": "^1.8.0",
"@playwright/test": "^1.58.0", "@playwright/test": "^1.58.0",
"@sveltejs/adapter-node": "^5.5.3",
"@sveltejs/adapter-static": "^3.0.10", "@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.50.1", "@sveltejs/kit": "^2.50.1",
"@sveltejs/vite-plugin-svelte": "^5.1.1", "@sveltejs/vite-plugin-svelte": "^5.1.1",
@@ -238,6 +239,14 @@
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
"@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@29.0.0", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ=="],
"@rollup/plugin-json": ["@rollup/plugin-json@6.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA=="],
"@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="],
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.0", "", { "os": "android", "cpu": "arm" }, "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.0", "", { "os": "android", "cpu": "arm" }, "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.41.0", "", { "os": "android", "cpu": "arm64" }, "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.41.0", "", { "os": "android", "cpu": "arm64" }, "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ=="],
@@ -286,6 +295,8 @@
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "8.14.1" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "8.14.1" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="],
"@sveltejs/adapter-node": ["@sveltejs/adapter-node@5.5.3", "", { "dependencies": { "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.9.5" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-yeWbKXBL9vqDb/7R8ebvRHeuBHN4cRYYBSquNJSMQtS6rIYkXxsVSveaMTUaLvHYQsb1zNa+nH2iLTOMawBohA=="],
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.10", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew=="], "@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.10", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew=="],
"@sveltejs/kit": ["@sveltejs/kit@2.50.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-XRHD2i3zC4ukhz2iCQzO4mbsts081PAZnnMAQ7LNpWeYgeBmwMsalf0FGSwhFXBbtr2XViPKnFJBDCckWqrsLw=="], "@sveltejs/kit": ["@sveltejs/kit@2.50.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-XRHD2i3zC4ukhz2iCQzO4mbsts081PAZnnMAQ7LNpWeYgeBmwMsalf0FGSwhFXBbtr2XViPKnFJBDCckWqrsLw=="],
@@ -372,6 +383,8 @@
"@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], "@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="],
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
"@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "22.15.20" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "22.15.20" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="],
"@types/wrap-ansi": ["@types/wrap-ansi@3.0.0", "", {}, "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="], "@types/wrap-ansi": ["@types/wrap-ansi@3.0.0", "", {}, "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="],
@@ -554,6 +567,8 @@
"commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
"commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
"compare-version": ["compare-version@0.1.2", "", {}, "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A=="], "compare-version": ["compare-version@0.1.2", "", {}, "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
@@ -680,7 +695,7 @@
"estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], "estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "1.0.7" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
@@ -838,6 +853,8 @@
"is-lambda": ["is-lambda@1.0.1", "", {}, "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="], "is-lambda": ["is-lambda@1.0.1", "", {}, "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="],
"is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="],
"is-my-ip-valid": ["is-my-ip-valid@1.0.1", "", {}, "sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg=="], "is-my-ip-valid": ["is-my-ip-valid@1.0.1", "", {}, "sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg=="],
"is-my-json-valid": ["is-my-json-valid@2.20.6", "", { "dependencies": { "generate-function": "2.3.1", "generate-object-property": "1.2.0", "is-my-ip-valid": "1.0.1", "jsonpointer": "5.0.1", "xtend": "4.0.2" } }, "sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw=="], "is-my-json-valid": ["is-my-json-valid@2.20.6", "", { "dependencies": { "generate-function": "2.3.1", "generate-object-property": "1.2.0", "is-my-ip-valid": "1.0.1", "jsonpointer": "5.0.1", "xtend": "4.0.2" } }, "sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw=="],
@@ -1452,6 +1469,18 @@
"@npmcli/move-file/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "7.2.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], "@npmcli/move-file/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "7.2.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"@rollup/plugin-commonjs/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
"@rollup/plugin-commonjs/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"@rollup/plugin-commonjs/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"@rollup/pluginutils/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@rollup/pluginutils/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
@@ -1486,6 +1515,8 @@
"@types/yauzl/@types/node": ["@types/node@22.15.20", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-A6BohGFRGHAscJsTslDCA9JG7qSJr/DWUvrvY8yi9IgnGtMxCyat7vvQ//MFa0DnLsyuS3wYTpLdw4Hf+Q5JXw=="], "@types/yauzl/@types/node": ["@types/node@22.15.20", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-A6BohGFRGHAscJsTslDCA9JG7qSJr/DWUvrvY8yi9IgnGtMxCyat7vvQ//MFa0DnLsyuS3wYTpLdw4Hf+Q5JXw=="],
"@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "1.0.7" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
"cacache/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "1.0.0", "inflight": "1.0.6", "inherits": "2.0.4", "minimatch": "5.1.6", "once": "1.4.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="], "cacache/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "1.0.0", "inflight": "1.0.6", "inherits": "2.0.4", "minimatch": "5.1.6", "once": "1.4.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="],
@@ -1642,6 +1673,10 @@
"@inquirer/core/wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "8.0.0", "is-fullwidth-code-point": "3.0.0", "strip-ansi": "6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "@inquirer/core/wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "8.0.0", "is-fullwidth-code-point": "3.0.0", "strip-ansi": "6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"@rollup/plugin-commonjs/is-reference/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@rollup/plugin-commonjs/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@tailwindcss/node/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], "@tailwindcss/node/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"cacache/glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], "cacache/glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
+5 -10
View File
@@ -7,29 +7,24 @@ if (started) {
app.quit(); app.quit();
} }
const PRODUCTION_URL = "https://optima.osdci.net";
const createWindow = () => { const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: 800, width: 1200,
height: 600, height: 800,
webPreferences: { webPreferences: {
preload: path.join(import.meta.dirname, "preload.js"), preload: path.join(import.meta.dirname, "preload.js"),
}, },
}); });
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(`${MAIN_WINDOW_VITE_DEV_SERVER_URL}/login`); mainWindow.loadURL(`${MAIN_WINDOW_VITE_DEV_SERVER_URL}/login`);
mainWindow.webContents.on("did-frame-finish-load", () => { mainWindow.webContents.on("did-frame-finish-load", () => {
mainWindow.webContents.openDevTools({ mode: "detach" }); mainWindow.webContents.openDevTools({ mode: "detach" });
}); });
} else { } else {
mainWindow.loadFile( mainWindow.loadURL(PRODUCTION_URL);
path.join(
import.meta.dirname,
`../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`,
),
);
} }
}; };
+2
View File
@@ -1,6 +1,7 @@
import type { ForgeConfig } from "@electron-forge/shared-types"; import type { ForgeConfig } from "@electron-forge/shared-types";
import { MakerSquirrel } from "@electron-forge/maker-squirrel"; import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { MakerZIP } from "@electron-forge/maker-zip"; import { MakerZIP } from "@electron-forge/maker-zip";
import { MakerDMG } from "@electron-forge/maker-dmg";
import { MakerDeb } from "@electron-forge/maker-deb"; import { MakerDeb } from "@electron-forge/maker-deb";
import { MakerRpm } from "@electron-forge/maker-rpm"; import { MakerRpm } from "@electron-forge/maker-rpm";
import { VitePlugin } from "@electron-forge/plugin-vite"; import { VitePlugin } from "@electron-forge/plugin-vite";
@@ -15,6 +16,7 @@ const config: ForgeConfig = {
makers: [ makers: [
new MakerSquirrel({}), new MakerSquirrel({}),
new MakerZIP({}, ["darwin"]), new MakerZIP({}, ["darwin"]),
new MakerDMG({}),
new MakerRpm({}), new MakerRpm({}),
new MakerDeb({}), new MakerDeb({}),
], ],
+30
View File
@@ -0,0 +1,30 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: optima-ui
namespace: optima
spec:
selector:
matchLabels:
app: optima-ui
replicas: 1
template:
metadata:
labels:
app: optima-ui
spec:
containers:
- name: optima-ui
image: ghcr.io/project-optima/ttscm-ui:latest
imagePullPolicy: Always
env:
- name: PUBLIC_API_URL
value: "https://opt-api.osdci.net"
- name: ORIGIN
value: "https://optima.osdci.net"
- name: PORT
value: "3000"
ports:
- containerPort: 3000
imagePullSecrets:
- name: github-container-registry
+39
View File
@@ -0,0 +1,39 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: optima-ui-ingress
namespace: optima
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
tls:
- hosts:
- optima.osdci.net
secretName: osdci-net-cert
rules:
- host: optima.osdci.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: optima-ui
port:
number: 3000
---
apiVersion: v1
kind: Service
metadata:
name: optima-ui
namespace: optima
labels:
app: optima-ui
spec:
type: ClusterIP
ports:
- port: 3000
protocol: TCP
selector:
app: optima-ui
+5 -2
View File
@@ -18,8 +18,10 @@
"test": "npm run test:unit -- --run && npm run test:e2e", "test": "npm run test:unit -- --run && npm run test:e2e",
"test:e2e": "playwright test", "test:e2e": "playwright test",
"start": "electron-forge start", "start": "electron-forge start",
"package": "vite build && electron-forge package", "package": "electron-forge package",
"make": "vite build && electron-forge make", "make": "electron-forge make",
"make:macos": "electron-forge make --platform darwin",
"build:server": "vite build",
"publish": "electron-forge publish" "publish": "electron-forge publish"
}, },
"devDependencies": { "devDependencies": {
@@ -34,6 +36,7 @@
"@electron-forge/plugin-vite": "^7.11.1", "@electron-forge/plugin-vite": "^7.11.1",
"@electron/fuses": "^1.8.0", "@electron/fuses": "^1.8.0",
"@playwright/test": "^1.58.0", "@playwright/test": "^1.58.0",
"@sveltejs/adapter-node": "^5.5.3",
"@sveltejs/adapter-static": "^3.0.10", "@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.50.1", "@sveltejs/kit": "^2.50.1",
"@sveltejs/vite-plugin-svelte": "^5.1.1", "@sveltejs/vite-plugin-svelte": "^5.1.1",
+16 -9
View File
@@ -1,18 +1,25 @@
// place files you want to import through the `$lib` alias in this folder. // place files you want to import through the `$lib` alias in this folder.
import { auth } from "./optima-api/modules/auth";
import { company } from "./optima-api/modules/companies";
import { credential } from "./optima-api/modules/credentials";
import { credentialType } from "./optima-api/modules/credentialTypes";
import { role } from "./optima-api/modules/roles";
import { permission } from "./optima-api/modules/permissions";
import { user } from "./optima-api/modules/user"; import { user } from "./optima-api/modules/user";
import { users } from "./optima-api/modules/users";
import { unifi } from "./optima-api/modules/unifi";
export const optima = { export const optima = {
auth: (await import("./optima-api/modules/auth")).auth, auth,
company: (await import("./optima-api/modules/companies")).company, company,
credential: (await import("./optima-api/modules/credentials")).credential, credential,
credentialType: (await import("./optima-api/modules/credentialTypes")) credentialType,
.credentialType, role,
role: (await import("./optima-api/modules/roles")).role, permission,
permission: (await import("./optima-api/modules/permissions")).permission,
user, user,
users: (await import("./optima-api/modules/users")).users, users,
unifi: (await import("./optima-api/modules/unifi")).unifi, unifi,
}; };
/** /**
* @TODO * @TODO
+1
View File
@@ -79,6 +79,7 @@ export const user = {
let settled = false; let settled = false;
const socket = io(`${base}/auth_callback`, { const socket = io(`${base}/auth_callback`, {
transports: ["websocket"], transports: ["websocket"],
rejectUnauthorized: false,
}); });
const timeout = setTimeout( const timeout = setTimeout(
() => { () => {
-1
View File
@@ -1 +0,0 @@
export const ssr = true;
@@ -640,9 +640,10 @@
const updated = result?.data ?? wifiEditState; const updated = result?.data ?? wifiEditState;
selectedWifi = { ...selectedWifi, ...updated } as UnifiWifiNetwork; selectedWifi = { ...selectedWifi, ...updated } as UnifiWifiNetwork;
// Preserve deviceMacs for "devices" mode since the API doesn't return it // Preserve deviceMacs for "devices" mode since the API doesn't return it
const savedDeviceMacs = wifiEditState.apGroupMode === "devices" const savedDeviceMacs =
? (wifiEditState.deviceMacs as string[] ?? []) wifiEditState.apGroupMode === "devices"
: []; ? ((wifiEditState.deviceMacs as string[]) ?? [])
: [];
wifiEditState = { ...updated, deviceMacs: savedDeviceMacs }; wifiEditState = { ...updated, deviceMacs: savedDeviceMacs };
wifiOriginalState = { ...updated, deviceMacs: [...savedDeviceMacs] }; wifiOriginalState = { ...updated, deviceMacs: [...savedDeviceMacs] };
isWifiDirty = false; isWifiDirty = false;
+2 -4
View File
@@ -1,4 +1,4 @@
import adapter from "@sveltejs/adapter-static"; import adapter from "@sveltejs/adapter-node";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
const config = { const config = {
@@ -9,9 +9,7 @@ const config = {
}, },
}, },
kit: { kit: {
adapter: adapter({ adapter: adapter(),
pages: ".vite/renderer/main_window",
}),
router: { router: {
type: "pathname", type: "pathname",
}, },