update footer

This commit is contained in:
2026-05-16 10:15:20 +01:00
parent abde715945
commit 28bd265323
4 changed files with 1294 additions and 3 deletions
+1
View File
@@ -7,3 +7,4 @@ npm-debug.log*
vite-dev*.log vite-dev*.log
server-local*.log server-local*.log
.local-storage/ .local-storage/
.claude/
+1215 -1
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -27,6 +27,7 @@
"eslint": "^9.27.0", "eslint": "^9.27.0",
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20", "eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.1.0" "globals": "^16.1.0",
"rollup-plugin-obfuscator": "^1.1.0"
} }
} }
+76 -1
View File
@@ -1,9 +1,84 @@
import crypto from 'node:crypto'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite' import tailwindcss from '@tailwindcss/vite'
import obfuscatorPlugin from 'rollup-plugin-obfuscator'
const OBFUSCATOR_OPTIONS = {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.75,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.4,
debugProtection: false,
disableConsoleOutput: false,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: true,
renameGlobals: false,
selfDefending: true,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 10,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayEncoding: ['base64'],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 2,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 4,
stringArrayWrappersType: 'function',
stringArrayThreshold: 0.75,
transformObjectKeys: true,
unicodeEscapeSequence: false,
}
function obfuscate() {
const factory = obfuscatorPlugin.default || obfuscatorPlugin
const inner = factory({ global: true, options: OBFUSCATOR_OPTIONS })
return { ...inner, apply: 'build', enforce: 'post' }
}
const MAX_TEAM_NAME_LENGTH = 80 const MAX_TEAM_NAME_LENGTH = 80
function sri() {
return {
name: 'sri',
apply: 'build',
enforce: 'post',
transformIndexHtml: {
order: 'post',
handler(html, ctx) {
const bundle = ctx?.bundle
if (!bundle) return html
const hashFor = (fileName) => {
const asset = bundle[fileName]
if (!asset) return null
const source = asset.type === 'asset' ? asset.source : asset.code
const buf = Buffer.isBuffer(source) ? source : Buffer.from(source, 'utf8')
return `sha384-${crypto.createHash('sha384').update(buf).digest('base64')}`
}
const tagPattern = /<(?:script|link)\b[^>]*\b(?:src|href)=["'](\/[^"']+)["'][^>]*>/g
return html.replace(tagPattern, (tag, url) => {
if (/\bintegrity=/.test(tag)) return tag
const fileName = url.replace(/^\//, '').split('?')[0].split('#')[0]
const integrity = hashFor(fileName)
if (!integrity) return tag
const closing = tag.endsWith('/>') ? ' />' : '>'
const body = tag.slice(0, -closing.length)
let out = `${body} integrity="${integrity}"`
if (!/\bcrossorigin\b/.test(tag)) out += ' crossorigin="anonymous"'
return `${out}${closing}`
})
},
},
}
}
function isAllowedApiUrl(req) { function isAllowedApiUrl(req) {
const url = new URL(req.url, 'http://localhost') const url = new URL(req.url, 'http://localhost')
const params = url.searchParams const params = url.searchParams
@@ -62,7 +137,7 @@ function apiGuard() {
} }
export default defineConfig({ export default defineConfig({
plugins: [apiGuard(), react(), tailwindcss()], plugins: [apiGuard(), react(), tailwindcss(), obfuscate(), sri()],
server: { server: {
host: '0.0.0.0', host: '0.0.0.0',
port: 3001, port: 3001,