SPB Git forge

spb/zyquo-cloud-web

Public MIT

Zyquo Cloud Web — every cloud model, one beautiful chat, entirely in your browser.

12commits 1branches 0releases
1.5 MBsize
maindefault branch
20 days agolast push
TypeScript 81.2% CSS 8.9% JavaScript 8.2% Shell 1% HTML 0.6%

chore(retrait cluster): synchro copie du nœud M2U64 + README « URL live hors fonction » (2026-09-04)

Simon-Pierre Boucher committed 20 days ago (Sep 4, 2026) parent a68e525

2 changed files +94 −0

modified README.md +2 −0
@@ -1,3 +1,5 @@
1 +> ⚠️ **URL live hors fonction pour le moment.** L'application a été retirée du cluster MacLustr le 2026-09-04 (processus arrêtés, copie du nœud supprimée). Ce dépôt spbgit est désormais la seule source de vérité du projet. Ancienne URL : https://www.zyquo.cloud
2 +
1 3 <!--
2 4 README.md
3 5 Zyquo Cloud Web
added server.mjs +92 −0
@@ -0,0 +1,92 @@
1 +/*
2 + * server.mjs
3 + * Zyquo Cloud Web
4 + *
5 + * Author: Simon-Pierre Boucher
6 + * Mail: contact@spboucher.ai
7 + *
8 + * Production static server (zero dependencies): serves the dist/ bundle with
9 + * security headers (CSP mirroring index.html, HSTS, nosniff, referrer
10 + * policy), immutable caching for hashed assets, no-cache for the HTML shell,
11 + * SPA fallback, and a /healthz endpoint. Run: node server.mjs [port] [dir]
12 + */
13 +
14 +import { createServer } from 'node:http'
15 +import { createReadStream, existsSync, statSync } from 'node:fs'
16 +import { extname, join, normalize, resolve } from 'node:path'
17 +
18 +const PORT = Number(process.argv[2] ?? process.env.PORT ?? 8080)
19 +const ROOT = resolve(process.argv[3] ?? join(process.cwd(), 'dist'))
20 +
21 +const CSP = [
22 + "default-src 'self'",
23 + "script-src 'self'",
24 + "style-src 'self' 'unsafe-inline'",
25 + "img-src 'self' data: blob:",
26 + "font-src 'self' data:",
27 + "connect-src 'self' https://api.openai.com https://api.anthropic.com https://api.x.ai https://api.mistral.ai https://generativelanguage.googleapis.com https://dashscope-intl.aliyuncs.com https://api.deepseek.com https://api.moonshot.ai https://api.perplexity.ai https://api.together.xyz https://api.deepinfra.com https://api.cerebras.ai http://localhost:* http://127.0.0.1:*",
28 + "worker-src 'self'",
29 + "object-src 'none'",
30 + "base-uri 'self'",
31 + "form-action 'none'",
32 + "frame-ancestors 'none'",
33 +].join('; ')
34 +
35 +const MIME = {
36 + '.html': 'text/html; charset=utf-8',
37 + '.js': 'text/javascript; charset=utf-8',
38 + '.css': 'text/css; charset=utf-8',
39 + '.json': 'application/json',
40 + '.svg': 'image/svg+xml',
41 + '.png': 'image/png',
42 + '.ico': 'image/x-icon',
43 + '.woff2': 'font/woff2',
44 + '.woff': 'font/woff',
45 + '.webmanifest': 'application/manifest+json',
46 + '.txt': 'text/plain; charset=utf-8',
47 + '.map': 'application/json',
48 +}
49 +
50 +const server = createServer((req, res) => {
51 + const url = new URL(req.url ?? '/', 'http://localhost')
52 + let pathname = decodeURIComponent(url.pathname)
53 +
54 + if (pathname === '/healthz') {
55 + res.writeHead(200, { 'Content-Type': 'text/plain' })
56 + res.end('ok')
57 + return
58 + }
59 +
60 + // Resolve inside ROOT only (no traversal).
61 + let filePath = normalize(join(ROOT, pathname))
62 + if (!filePath.startsWith(ROOT)) {
63 + res.writeHead(403).end()
64 + return
65 + }
66 + if (!existsSync(filePath) || statSync(filePath).isDirectory()) {
67 + filePath = join(ROOT, 'index.html') // SPA fallback (also serves /)
68 + }
69 +
70 + const ext = extname(filePath)
71 + const hashed = /\/assets\//.test(filePath) || /-\w{8,}\./.test(filePath)
72 + const headers = {
73 + 'Content-Type': MIME[ext] ?? 'application/octet-stream',
74 + 'Content-Security-Policy': CSP,
75 + 'Strict-Transport-Security': 'max-age=63072000; includeSubDomains; preload',
76 + 'X-Content-Type-Options': 'nosniff',
77 + 'Referrer-Policy': 'no-referrer',
78 + 'Permissions-Policy': 'camera=(), geolocation=(), payment=()',
79 + 'Cache-Control':
80 + ext === '.html'
81 + ? 'no-cache'
82 + : hashed
83 + ? 'public, max-age=31536000, immutable'
84 + : 'public, max-age=3600',
85 + }
86 + res.writeHead(200, headers)
87 + createReadStream(filePath).pipe(res)
88 +})
89 +
90 +server.listen(PORT, '0.0.0.0', () => {
91 + console.log(`Zyquo Cloud Web serving ${ROOT} on :${PORT}`)
92 +})
93