diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml new file mode 100644 index 00000000..d5fbc199 --- /dev/null +++ b/.github/workflows/desktop-release.yml @@ -0,0 +1,135 @@ +name: Build Desktop App + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Version to build (e.g., 0.1.0)' + required: false + default: '' + +permissions: + contents: write + +jobs: + build-windows: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build desktop renderer + run: npm run build:desktop + + - name: Build Windows installer + run: npx electron-builder --win --config electron-builder.yml + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Windows artifact + uses: actions/upload-artifact@v4 + with: + name: windows-build + path: dist/*.exe + retention-days: 5 + + build-macos: + runs-on: macos-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build desktop renderer + run: npm run build:desktop + + - name: Build macOS installer + run: npx electron-builder --mac --config electron-builder.yml + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload macOS artifact + uses: actions/upload-artifact@v4 + with: + name: macos-build + path: dist/*.dmg + retention-days: 5 + + build-linux: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build desktop renderer + run: npm run build:desktop + + - name: Build Linux installers + run: npx electron-builder --linux --config electron-builder.yml + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Linux artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-build + path: | + dist/*.AppImage + dist/*.deb + retention-days: 5 + + release: + needs: [build-windows, build-macos, build-linux] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Display downloaded files + run: ls -R artifacts + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + draft: false + prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }} + generate_release_notes: true + files: | + artifacts/windows-build/*.exe + artifacts/macos-build/*.dmg + artifacts/linux-build/*.AppImage + artifacts/linux-build/*.deb + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.replit b/.replit index db76c62c..9a438068 100644 --- a/.replit +++ b/.replit @@ -5,6 +5,7 @@ integrations = ["javascript_gemini_ai_integrations:1.0.0"] [nix] channel = "stable-25_05" +packages = ["imagemagick"] [workflows] runButton = "Project" @@ -53,6 +54,10 @@ externalPort = 80 localPort = 8044 externalPort = 3003 +[[ports]] +localPort = 8080 +externalPort = 8080 + [[ports]] localPort = 38557 externalPort = 3000 @@ -70,3 +75,4 @@ build = ["npm", "run", "build"] [userenv.shared] DISCORD_MAIN_CHAT_CHANNELS = "1425114041021497454" +MAINTENANCE_MODE = "true" diff --git a/attached_assets/Pasted-17s-12s-Run-npx-electron-builder-win-config-electron-bu_1765007431411.txt b/attached_assets/Pasted-17s-12s-Run-npx-electron-builder-win-config-electron-bu_1765007431411.txt new file mode 100644 index 00000000..d118e6d1 --- /dev/null +++ b/attached_assets/Pasted-17s-12s-Run-npx-electron-builder-win-config-electron-bu_1765007431411.txt @@ -0,0 +1,95 @@ +17s +12s +Run npx electron-builder --win --config electron-builder.yml + + • electron-builder version=25.1.8 os=10.0.26100 + • artifacts will be published reason=tag is defined tag=v0.1.1 + • loaded configuration file=D:\a\aethex-forge\aethex-forge\electron-builder.yml + • skipped dependencies rebuild reason=npmRebuild is set to false + • packaging platform=win32 arch=x64 electron=32.3.3 appOutDir=dist\win-unpacked + • downloading url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip size=113 MB parts=8 + • downloaded url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip duration=449ms + • updating asar integrity executable resource executablePath=dist\win-unpacked\AeThex Desktop Terminal.exe + ⨯ icon directory D:\a\aethex-forge\aethex-forge\build\icons doesn't contain icons +github.com/develar/app-builder/pkg/icons.CollectIcons + /Users/runner/work/app-builder/app-builder/pkg/icons/collect-icons.go:73 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:199 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:201 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 + ⨯ D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 failedTask=build stackTrace=Error: D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 + at ChildProcess. (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\util.ts:255:14) + at Object.onceWrapper (node:events:639:26) + at ChildProcess.emit (node:events:524:28) + at ChildProcess.cp.emit (D:\a\aethex-forge\aethex-forge\node_modules\cross-spawn\lib\enoent.js:34:29) + at maybeClose (node:internal/child_process:1104:16) + at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5) +From previous event: + at processImmediate (node:internal/timers:483:21) +From previous event: + at WinPackager.signApp (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\winPackager.ts:270:27) + at WinPackager.doSignAfterPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:346:32) + at WinPackager.doPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:331:7) + at WinPackager.pack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:138:5) + at Packager.doBuild (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:459:9) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) + at Packager.build (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:393:31) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) +Error: Process completed with exit code 1. +0s +0s +2s +Post job cleanup. +"C:\Program Files\Git\bin\git.exe" version +git version 2.52.0.windows.1 +Temporarily overriding HOME='D:\a\_temp\4963c2ff-b212-40d1-906e-719e33666ab2' before making global git config changes +Adding repository directory to the temporary git global config as a safe directory +"C:\Program Files\Git\bin\git.exe" config --global --add safe.directory D:\a\aethex-forge\aethex-forge +"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp core\.sshCommand +"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "sh -c \"git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :\"" +"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader +http.https://github.com/.extraheader +"C:\Program Files\Git\bin\git.exe" config --local --unset-all http.https://github.com/.extraheader +"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "sh -c \"git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :\"" +"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp ^includeIf\.gitdir: +"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "git config --local --show-origin --name-only --get-regexp remote.origin.url" \ No newline at end of file diff --git a/attached_assets/Pasted-17s-12s-Run-npx-electron-builder-win-config-electron-bu_1765007500661.txt b/attached_assets/Pasted-17s-12s-Run-npx-electron-builder-win-config-electron-bu_1765007500661.txt new file mode 100644 index 00000000..d118e6d1 --- /dev/null +++ b/attached_assets/Pasted-17s-12s-Run-npx-electron-builder-win-config-electron-bu_1765007500661.txt @@ -0,0 +1,95 @@ +17s +12s +Run npx electron-builder --win --config electron-builder.yml + + • electron-builder version=25.1.8 os=10.0.26100 + • artifacts will be published reason=tag is defined tag=v0.1.1 + • loaded configuration file=D:\a\aethex-forge\aethex-forge\electron-builder.yml + • skipped dependencies rebuild reason=npmRebuild is set to false + • packaging platform=win32 arch=x64 electron=32.3.3 appOutDir=dist\win-unpacked + • downloading url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip size=113 MB parts=8 + • downloaded url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip duration=449ms + • updating asar integrity executable resource executablePath=dist\win-unpacked\AeThex Desktop Terminal.exe + ⨯ icon directory D:\a\aethex-forge\aethex-forge\build\icons doesn't contain icons +github.com/develar/app-builder/pkg/icons.CollectIcons + /Users/runner/work/app-builder/app-builder/pkg/icons/collect-icons.go:73 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:199 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:201 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 + ⨯ D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 failedTask=build stackTrace=Error: D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 + at ChildProcess. (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\util.ts:255:14) + at Object.onceWrapper (node:events:639:26) + at ChildProcess.emit (node:events:524:28) + at ChildProcess.cp.emit (D:\a\aethex-forge\aethex-forge\node_modules\cross-spawn\lib\enoent.js:34:29) + at maybeClose (node:internal/child_process:1104:16) + at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5) +From previous event: + at processImmediate (node:internal/timers:483:21) +From previous event: + at WinPackager.signApp (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\winPackager.ts:270:27) + at WinPackager.doSignAfterPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:346:32) + at WinPackager.doPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:331:7) + at WinPackager.pack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:138:5) + at Packager.doBuild (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:459:9) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) + at Packager.build (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:393:31) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) +Error: Process completed with exit code 1. +0s +0s +2s +Post job cleanup. +"C:\Program Files\Git\bin\git.exe" version +git version 2.52.0.windows.1 +Temporarily overriding HOME='D:\a\_temp\4963c2ff-b212-40d1-906e-719e33666ab2' before making global git config changes +Adding repository directory to the temporary git global config as a safe directory +"C:\Program Files\Git\bin\git.exe" config --global --add safe.directory D:\a\aethex-forge\aethex-forge +"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp core\.sshCommand +"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "sh -c \"git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :\"" +"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader +http.https://github.com/.extraheader +"C:\Program Files\Git\bin\git.exe" config --local --unset-all http.https://github.com/.extraheader +"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "sh -c \"git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :\"" +"C:\Program Files\Git\bin\git.exe" config --local --name-only --get-regexp ^includeIf\.gitdir: +"C:\Program Files\Git\bin\git.exe" submodule foreach --recursive "git config --local --show-origin --name-only --get-regexp remote.origin.url" \ No newline at end of file diff --git a/attached_assets/Pasted-Run-npx-electron-builder-win-config-electron-builder-ym_1765007031609.txt b/attached_assets/Pasted-Run-npx-electron-builder-win-config-electron-builder-ym_1765007031609.txt new file mode 100644 index 00000000..6e1eb3da --- /dev/null +++ b/attached_assets/Pasted-Run-npx-electron-builder-win-config-electron-builder-ym_1765007031609.txt @@ -0,0 +1,75 @@ +Run npx electron-builder --win --config electron-builder.yml + • electron-builder version=25.1.8 os=10.0.26100 + • artifacts will be published reason=tag is defined tag=v0.1.0 + • loaded configuration file=D:\a\aethex-forge\aethex-forge\electron-builder.yml + • skipped dependencies rebuild reason=npmRebuild is set to false + • packaging platform=win32 arch=x64 electron=32.3.3 appOutDir=dist\win-unpacked + • downloading url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip size=113 MB parts=8 + • downloaded url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip duration=592ms + • updating asar integrity executable resource executablePath=dist\win-unpacked\AeThex Desktop Terminal.exe + ⨯ icon directory D:\a\aethex-forge\aethex-forge\build\icons doesn't contain icons +github.com/develar/app-builder/pkg/icons.CollectIcons + /Users/runner/work/app-builder/app-builder/pkg/icons/collect-icons.go:73 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:199 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:201 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 + ⨯ D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 failedTask=build stackTrace=Error: D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 + at ChildProcess. (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\util.ts:255:14) + at Object.onceWrapper (node:events:639:26) + at ChildProcess.emit (node:events:524:28) + at ChildProcess.cp.emit (D:\a\aethex-forge\aethex-forge\node_modules\cross-spawn\lib\enoent.js:34:29) + at maybeClose (node:internal/child_process:1104:16) + at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5) +From previous event: + at processImmediate (node:internal/timers:483:21) +From previous event: + at WinPackager.signApp (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\winPackager.ts:270:27) + at WinPackager.doSignAfterPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:346:32) + at WinPackager.doPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:331:7) + at WinPackager.pack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:138:5) + at Packager.doBuild (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:459:9) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) + at Packager.build (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:393:31) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) +Error: Process completed with exit code 1. diff --git a/attached_assets/Pasted-Run-npx-electron-builder-win-config-electron-builder-ym_1765007959498.txt b/attached_assets/Pasted-Run-npx-electron-builder-win-config-electron-builder-ym_1765007959498.txt new file mode 100644 index 00000000..015ec83b --- /dev/null +++ b/attached_assets/Pasted-Run-npx-electron-builder-win-config-electron-builder-ym_1765007959498.txt @@ -0,0 +1,75 @@ +Run npx electron-builder --win --config electron-builder.yml + • electron-builder version=25.1.8 os=10.0.26100 + • artifacts will be published reason=tag is defined tag=v0.1.2 + • loaded configuration file=D:\a\aethex-forge\aethex-forge\electron-builder.yml + • skipped dependencies rebuild reason=npmRebuild is set to false + • packaging platform=win32 arch=x64 electron=32.3.3 appOutDir=dist\win-unpacked + • downloading url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip size=113 MB parts=8 + • downloaded url=https://github.com/electron/electron/releases/download/v32.3.3/electron-v32.3.3-win32-x64.zip duration=732ms + • updating asar integrity executable resource executablePath=dist\win-unpacked\AeThex Desktop Terminal.exe + ⨯ icon directory D:\a\aethex-forge\aethex-forge\build\icons doesn't contain icons +github.com/develar/app-builder/pkg/icons.CollectIcons + /Users/runner/work/app-builder/app-builder/pkg/icons/collect-icons.go:73 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:199 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 +github.com/develar/app-builder/pkg/icons.doConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:201 +github.com/develar/app-builder/pkg/icons.ConvertIcon + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:56 +github.com/develar/app-builder/pkg/icons.ConfigureCommand.func1 + /Users/runner/work/app-builder/app-builder/pkg/icons/icon-converter.go:33 +github.com/alecthomas/kingpin.(*actionMixin).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/actions.go:28 +github.com/alecthomas/kingpin.(*Application).applyActions + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:557 +github.com/alecthomas/kingpin.(*Application).execute + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:390 +github.com/alecthomas/kingpin.(*Application).Parse + /Users/runner/go/pkg/mod/github.com/alecthomas/kingpin@v2.2.6+incompatible/app.go:222 +main.main + /Users/runner/work/app-builder/app-builder/main.go:90 +runtime.main + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/proc.go:267 +runtime.goexit + /Users/runner/hostedtoolcache/go/1.21.13/arm64/src/runtime/asm_amd64.s:1650 + ⨯ D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 failedTask=build stackTrace=Error: D:\a\aethex-forge\aethex-forge\node_modules\app-builder-bin\win\x64\app-builder.exe process failed ERR_ELECTRON_BUILDER_CANNOT_EXECUTE +Exit code: +1 + at ChildProcess. (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\util.ts:255:14) + at Object.onceWrapper (node:events:639:26) + at ChildProcess.emit (node:events:524:28) + at ChildProcess.cp.emit (D:\a\aethex-forge\aethex-forge\node_modules\cross-spawn\lib\enoent.js:34:29) + at maybeClose (node:internal/child_process:1104:16) + at Process.ChildProcess._handle.onexit (node:internal/child_process:304:5) +From previous event: + at processImmediate (node:internal/timers:483:21) +From previous event: + at WinPackager.signApp (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\winPackager.ts:270:27) + at WinPackager.doSignAfterPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:346:32) + at WinPackager.doPack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:331:7) + at WinPackager.pack (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\platformPackager.ts:138:5) + at Packager.doBuild (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:459:9) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) + at Packager.build (D:\a\aethex-forge\aethex-forge\node_modules\app-builder-lib\src\packager.ts:393:31) + at executeFinally (D:\a\aethex-forge\aethex-forge\node_modules\builder-util\src\promise.ts:12:14) +Error: Process completed with exit code 1. diff --git a/attached_assets/image_1765005879621.png b/attached_assets/image_1765005879621.png new file mode 100644 index 00000000..f844f967 Binary files /dev/null and b/attached_assets/image_1765005879621.png differ diff --git a/client/App.tsx b/client/App.tsx index 1e1405a5..1c95c759 100644 --- a/client/App.tsx +++ b/client/App.tsx @@ -10,6 +10,8 @@ import { AuthProvider } from "./contexts/AuthContext"; import { Web3Provider } from "./contexts/Web3Context"; import { DocsThemeProvider } from "./contexts/DocsThemeContext"; import { ArmThemeProvider } from "./contexts/ArmThemeContext"; +import { MaintenanceProvider } from "./contexts/MaintenanceContext"; +import MaintenanceGuard from "./components/MaintenanceGuard"; import PageTransition from "./components/PageTransition"; import SkipAgentController from "./components/SkipAgentController"; import Index from "./pages/Index"; @@ -182,9 +184,11 @@ const App = () => ( - - - + + + + + {/* Subdomain Passport (aethex.me and aethex.space) handles its own redirect if not a subdomain */} } /> } /> @@ -795,8 +799,10 @@ const App = () => ( } /> {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} } /> - - + + + + diff --git a/client/components/MaintenanceGuard.tsx b/client/components/MaintenanceGuard.tsx new file mode 100644 index 00000000..72af235f --- /dev/null +++ b/client/components/MaintenanceGuard.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import { useLocation } from "react-router-dom"; +import { useMaintenance } from "@/contexts/MaintenanceContext"; +import MaintenancePage from "@/pages/Maintenance"; + +const ALLOWED_PATHS = ["/login", "/staff/login", "/reset-password", "/health"]; + +interface MaintenanceGuardProps { + children: React.ReactNode; +} + +export default function MaintenanceGuard({ children }: MaintenanceGuardProps) { + const { isMaintenanceMode, canBypass, loading } = useMaintenance(); + const location = useLocation(); + + if (loading) { + return ( +
+
+
+ ); + } + + const isAllowedPath = ALLOWED_PATHS.some(path => + location.pathname === path || location.pathname.startsWith(path) + ); + + if (isMaintenanceMode && !canBypass && !isAllowedPath) { + return ; + } + + return <>{children}; +} diff --git a/client/components/admin/MaintenanceToggle.tsx b/client/components/admin/MaintenanceToggle.tsx new file mode 100644 index 00000000..ba5b0bee --- /dev/null +++ b/client/components/admin/MaintenanceToggle.tsx @@ -0,0 +1,111 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Switch } from "@/components/ui/switch"; +import { Badge } from "@/components/ui/badge"; +import { useMaintenance } from "@/contexts/MaintenanceContext"; +import { Construction, AlertTriangle, Loader2 } from "lucide-react"; +import { useState } from "react"; +import { aethexToast } from "@/lib/aethex-toast"; + +export default function MaintenanceToggle() { + const { isMaintenanceMode, canBypass, toggleMaintenanceMode, loading } = useMaintenance(); + const [toggling, setToggling] = useState(false); + + const handleToggle = async () => { + if (!canBypass) { + aethexToast.error("Only admins can toggle maintenance mode"); + return; + } + + setToggling(true); + try { + await toggleMaintenanceMode(); + aethexToast.success( + isMaintenanceMode + ? "Maintenance mode disabled - site is now live" + : "Maintenance mode enabled - visitors will see maintenance page" + ); + } catch (error: any) { + aethexToast.error(error?.message || "Failed to toggle maintenance mode"); + } finally { + setToggling(false); + } + }; + + if (loading) { + return ( + + + + + + ); + } + + return ( + + +
+
+
+ {isMaintenanceMode ? ( + + ) : ( + + )} +
+
+ Maintenance Mode + + {isMaintenanceMode + ? "Site is currently in maintenance mode" + : "Site is live and accessible"} + +
+
+ + {isMaintenanceMode ? "Active" : "Off"} + +
+
+ +
+
+ {isMaintenanceMode && ( + + )} +
+

+ {isMaintenanceMode + ? "Visitors see maintenance page" + : "All visitors can access the site"} +

+

+ {isMaintenanceMode + ? "Only admins can view the site. Toggle off to go live." + : "Toggle on to show maintenance page to visitors."} +

+
+
+
+ {toggling && } + +
+
+
+
+ ); +} diff --git a/client/contexts/MaintenanceContext.tsx b/client/contexts/MaintenanceContext.tsx new file mode 100644 index 00000000..ae868b2e --- /dev/null +++ b/client/contexts/MaintenanceContext.tsx @@ -0,0 +1,104 @@ +import React, { createContext, useContext, useState, useEffect, useCallback } from "react"; +import { useAuth } from "./AuthContext"; +import { supabase } from "@/lib/supabase"; + +interface MaintenanceContextType { + isMaintenanceMode: boolean; + canBypass: boolean; + loading: boolean; + toggleMaintenanceMode: () => Promise; + refreshStatus: () => Promise; +} + +const MaintenanceContext = createContext(undefined); + +const ADMIN_ROLES = ["admin", "super_admin", "staff", "owner"]; + +export function MaintenanceProvider({ children }: { children: React.ReactNode }) { + const { user, roles, session, loading: authLoading } = useAuth(); + const [isMaintenanceMode, setIsMaintenanceMode] = useState(false); + const [loading, setLoading] = useState(true); + + const isAdmin = roles.some(role => ADMIN_ROLES.includes(role.toLowerCase())); + const canBypass = isAdmin && !!user; + + const fetchMaintenanceStatus = useCallback(async () => { + try { + const response = await fetch("/api/admin/platform/maintenance"); + if (response.ok) { + const data = await response.json(); + setIsMaintenanceMode(data.maintenance_mode ?? false); + } + } catch (error) { + console.error("Failed to fetch maintenance status:", error); + } finally { + setLoading(false); + } + }, []); + + const toggleMaintenanceMode = useCallback(async () => { + if (!canBypass) { + throw new Error("Only admins can toggle maintenance mode"); + } + + const { data: sessionData } = await supabase.auth.getSession(); + const token = sessionData?.session?.access_token; + + if (!token) { + throw new Error("No auth session"); + } + + try { + const response = await fetch("/api/admin/platform/maintenance", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}`, + }, + body: JSON.stringify({ maintenance_mode: !isMaintenanceMode }), + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || "Failed to toggle maintenance mode"); + } + + const data = await response.json(); + setIsMaintenanceMode(data.maintenance_mode); + } catch (error) { + console.error("Failed to toggle maintenance mode:", error); + throw error; + } + }, [canBypass, isMaintenanceMode]); + + const refreshStatus = useCallback(async () => { + setLoading(true); + await fetchMaintenanceStatus(); + }, [fetchMaintenanceStatus]); + + useEffect(() => { + fetchMaintenanceStatus(); + }, [fetchMaintenanceStatus]); + + return ( + + {children} + + ); +} + +export function useMaintenance() { + const context = useContext(MaintenanceContext); + if (!context) { + throw new Error("useMaintenance must be used within MaintenanceProvider"); + } + return context; +} diff --git a/client/pages/Admin.tsx b/client/pages/Admin.tsx index 6b9d9870..6b37c89d 100644 --- a/client/pages/Admin.tsx +++ b/client/pages/Admin.tsx @@ -40,6 +40,7 @@ import AdminStaffChat from "@/components/admin/AdminStaffChat"; import AdminStaffAdmin from "@/components/admin/AdminStaffAdmin"; import AdminStaffDocs from "@/components/admin/AdminStaffDocs"; import AdminStaffAchievements from "@/components/admin/AdminStaffAchievements"; +import MaintenanceToggle from "@/components/admin/MaintenanceToggle"; import AdminSidebar from "@/components/admin/AdminSidebar"; import AdminEthosVerification from "@/pages/admin/AdminEthosVerification"; import AdminGameForgeStudio from "@/components/admin/AdminGameForgeStudio"; @@ -393,6 +394,8 @@ export default function Admin() { + +
diff --git a/client/pages/Maintenance.tsx b/client/pages/Maintenance.tsx new file mode 100644 index 00000000..db4a24a0 --- /dev/null +++ b/client/pages/Maintenance.tsx @@ -0,0 +1,159 @@ +import { useState, useEffect } from "react"; +import { Shield, Mail, Wrench, Clock, Zap } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; +import AeThexOSLogo from "@/components/AeThexOSLogo"; + +export default function MaintenancePage() { + const [currentMessage, setCurrentMessage] = useState(0); + const [dots, setDots] = useState(""); + + const messages = [ + "Upgrading quantum processors...", + "Optimizing neural networks...", + "Synchronizing data streams...", + "Calibrating system modules...", + "Enhancing user experience...", + ]; + + useEffect(() => { + const messageInterval = setInterval(() => { + setCurrentMessage((prev) => (prev + 1) % messages.length); + }, 2500); + + const dotsInterval = setInterval(() => { + setDots((prev) => (prev.length >= 3 ? "" : prev + ".")); + }, 400); + + return () => { + clearInterval(messageInterval); + clearInterval(dotsInterval); + }; + }, [messages.length]); + + return ( +
+
+ {[...Array(20)].map((_, i) => ( +
+ {["01", "10", "//", "{ }", "< >", "=>"][Math.floor(Math.random() * 6)]} +
+ ))} +
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+

+ System Maintenance +

+

+ We're upgrading AeThex to deliver a better experience. +

+
+ +
+ + + Maintenance Mode + + + + Back Soon + +
+
+ + + +
+
+ {[...Array(5)].map((_, i) => ( +
+ ))} +
+ + {messages[currentMessage]}{dots} + +
+ +
+ +
+
+
STATUS
+
+ + ACTIVE +
+
+
+
SYSTEM
+
AeThex v2.0
+
+
+
ETA
+
~30 min
+
+
+ + + + + +
+ + Questions? + + + support@aethex.dev + +
+
+
+ + +
+
+
+ ); +} diff --git a/discord-bot/bot.js b/discord-bot/bot.js index b7b28503..401b1321 100644 --- a/discord-bot/bot.js +++ b/discord-bot/bot.js @@ -311,7 +311,7 @@ async function registerDiscordCommands() { } // Start HTTP health check server -const healthPort = process.env.HEALTH_PORT || 8044; +const healthPort = process.env.HEALTH_PORT || 8080; const ADMIN_TOKEN = process.env.DISCORD_ADMIN_TOKEN || "aethex-bot-admin"; // Helper to check admin authentication diff --git a/package-lock.json b/package-lock.json index 95c5b96e..00c536af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,8 @@ "ethers": "^6.13.0", "express": "^4.18.2", "nodemailer": "^7.0.10", + "png-to-ico": "^3.0.1", + "sharp": "^0.34.5", "stripe": "^15.12.0", "zod": "^3.23.8" }, @@ -1737,6 +1739,23 @@ "node": ">= 10.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, "node_modules/@emotion/cache": { "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", @@ -2364,6 +2383,471 @@ "react-hook-form": "^7.0.0" } }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -13370,6 +13854,32 @@ "node": ">=10.4.0" } }, + "node_modules/png-to-ico": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/png-to-ico/-/png-to-ico-3.0.1.tgz", + "integrity": "sha512-S8BOAoaGd9gT5uaemQ62arIY3Jzco7Uc7LwUTqRyqJDTsKqOAiyfyN4dSdT0D+Zf8XvgztgpRbM5wnQd7EgYwg==", + "license": "MIT", + "dependencies": { + "@types/node": "^22.10.3", + "minimist": "^1.2.8", + "pngjs": "^7.0.0" + }, + "bin": { + "png-to-ico": "bin/cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -14698,6 +15208,50 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index 0ef40b05..20f71d99 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "ethers": "^6.13.0", "express": "^4.18.2", "nodemailer": "^7.0.10", + "png-to-ico": "^3.0.1", + "sharp": "^0.34.5", "stripe": "^15.12.0", "zod": "^3.23.8" }, diff --git a/replit.md b/replit.md index 2a37930d..491a98ae 100644 --- a/replit.md +++ b/replit.md @@ -38,6 +38,8 @@ The monolith (`aethex.dev`) implements split routing to enforce legal separation This ensures the Foundation's user-facing URLs display `aethex.foundation` in the browser, demonstrating operational independence per the Axiom Model. ## Recent Changes (December 2025) +- **Maintenance Mode**: Site-wide maintenance mode with admin bypass. Admins can toggle via Admin Dashboard overview tab. Uses MAINTENANCE_MODE env var for initial state. Allowed paths during maintenance: /login, /staff/login, /reset-password, /health +- **Health Endpoint**: Added /health endpoint at aethex.dev/health that aggregates platform and Discord bot status - **Axiom Model Routing**: Foundation and GameForge routes redirect to `aethex.foundation` domain for legal entity separation - **AI Intelligent Agent Integration**: Added global AI chat with 10 specialized personas (Network Agent, Forge Master, Ethics Sentinel, SBS Architect, Curriculum Weaver, QuantumLeap, Vapor, Apex, Ethos Producer, AeThex Archivist) - **Tiered Access Control**: AI personas gated by user tier (Free/Architect/Council) based on roles diff --git a/server/index.ts b/server/index.ts index e746ff25..dc60f8fb 100644 --- a/server/index.ts +++ b/server/index.ts @@ -457,6 +457,129 @@ export function createServer() { } }); + // Health check endpoint - proxies to Discord bot health + app.get("/health", async (_req, res) => { + try { + const botHealthPort = process.env.HEALTH_PORT || 8080; + const response = await fetch(`http://localhost:${botHealthPort}/health`); + const data = await response.json(); + res.json({ + status: "online", + platform: "aethex.dev", + bot: data, + timestamp: new Date().toISOString(), + }); + } catch (error: any) { + res.json({ + status: "online", + platform: "aethex.dev", + bot: { status: "offline", error: error?.message || "Bot unreachable" }, + timestamp: new Date().toISOString(), + }); + } + }); + + // Maintenance Mode API endpoints + const ADMIN_ROLES = ["admin", "super_admin", "staff", "owner"]; + + // In-memory maintenance state (fallback when DB table doesn't exist) + let maintenanceModeCache: boolean | null = null; + + // Get maintenance status (public) + app.get("/api/admin/platform/maintenance", async (_req, res) => { + try { + // Try database first + const { data, error } = await adminSupabase + .from("platform_settings") + .select("value") + .eq("key", "maintenance_mode") + .single(); + + if (error) { + // If table doesn't exist, use env var or in-memory cache + if (error.code === "42P01" || error.message?.includes("does not exist")) { + const envMaintenance = process.env.MAINTENANCE_MODE === "true"; + res.json({ maintenance_mode: maintenanceModeCache ?? envMaintenance }); + return; + } + // Row not found is OK, means maintenance is off + if (error.code !== "PGRST116") { + throw error; + } + } + + const isActive = data?.value === "true" || data?.value === true; + maintenanceModeCache = isActive; + res.json({ maintenance_mode: isActive }); + } catch (e: any) { + console.error("[Maintenance] Error fetching status:", e?.message); + // Fall back to env var or cache + const envMaintenance = process.env.MAINTENANCE_MODE === "true"; + res.json({ maintenance_mode: maintenanceModeCache ?? envMaintenance }); + } + }); + + // Toggle maintenance mode (admin only) + app.post("/api/admin/platform/maintenance", async (req, res) => { + try { + const authHeader = req.headers.authorization; + if (!authHeader?.startsWith("Bearer ")) { + return res.status(401).json({ error: "Unauthorized" }); + } + + const token = authHeader.substring(7); + const { data: { user }, error: authError } = await adminSupabase.auth.getUser(token); + + if (authError || !user) { + return res.status(401).json({ error: "Invalid token" }); + } + + // Check if user has admin role + const { data: roles } = await adminSupabase + .from("user_roles") + .select("role") + .eq("user_id", user.id); + + const userRoles = roles?.map((r: any) => r.role?.toLowerCase()) || []; + const isAdmin = userRoles.some((role: string) => ADMIN_ROLES.includes(role)); + + if (!isAdmin) { + return res.status(403).json({ error: "Admin access required" }); + } + + const rawValue = req.body?.maintenance_mode; + const newMaintenanceMode = rawValue === true || rawValue === "true"; + + // Try to upsert in database + const { error } = await adminSupabase + .from("platform_settings") + .upsert({ + key: "maintenance_mode", + value: String(newMaintenanceMode), + updated_at: new Date().toISOString(), + updated_by: user.id, + }, { onConflict: "key" }); + + if (error) { + // If table doesn't exist, just use in-memory cache + if (error.code === "42P01" || error.message?.includes("does not exist")) { + maintenanceModeCache = newMaintenanceMode; + console.log(`[Maintenance] Mode set to ${newMaintenanceMode} (in-memory) by ${user.email}`); + return res.json({ maintenance_mode: newMaintenanceMode }); + } + throw error; + } + + maintenanceModeCache = newMaintenanceMode; + console.log(`[Maintenance] Mode set to ${newMaintenanceMode} by ${user.email}`); + + res.json({ maintenance_mode: newMaintenanceMode }); + } catch (e: any) { + console.error("[Maintenance] Error toggling:", e?.message); + res.status(500).json({ error: e?.message || "Failed to toggle maintenance mode" }); + } + }); + // Example API routes app.get("/api/ping", (_req, res) => { const ping = process.env.PING_MESSAGE ?? "ping";