mirror of
https://github.com/iflytek/skillhub.git
synced 2026-11-14 09:01:16 +00:00
Replace legacy edge fixtures with production assertions
Signed-off-by: 千乘妍 (Xiaoyaner) <258399167+xiaoyaner0201@users.noreply.github.com>
This commit is contained in:
parent
335b692a05
commit
facd54575f
3 changed files with 10 additions and 157 deletions
|
|
@ -15,41 +15,19 @@ afterEach(() => { document.head.replaceChildren(); vi.useRealTimers(); vi.restor
|
|||
const website = '4398de8e-40df-4483-9eea-ed213643525f'
|
||||
const intent = { id: 'skill.example.open', event: 'click' }
|
||||
|
||||
describe('optional edge deployment defaults', () => {
|
||||
const nginx = readFileSync('src/features/analytics/fixtures/pr1-nginx.conf', 'utf8')
|
||||
const compose = readFileSync('src/features/analytics/fixtures/pr1-compose.yaml', 'utf8')
|
||||
|
||||
it('inherits the standard 100 MiB limit on all proxied application routes', () => {
|
||||
// Tokenize quoted strings as units so braces/semicolons in responses do not
|
||||
// affect scope. Track blocks instead of accepting a location-only limit.
|
||||
const tokens = nginx.replace(/#.*$/gm, '').match(/"[^"\n]*"|'[^'\n]*'|[{};]|[^\s{};]+/g) ?? []
|
||||
const blocks: string[] = [], statement: string[] = []
|
||||
const limits: Array<{ scope: string[]; value: string }> = []
|
||||
for (const token of tokens) {
|
||||
if (token === '{') { blocks.push(statement.join(' ')); statement.length = 0 }
|
||||
else if (token === '}') { blocks.pop(); statement.length = 0 }
|
||||
else if (token === ';') {
|
||||
if (statement[0] === 'client_max_body_size') limits.push({ scope: [...blocks], value: statement[1] })
|
||||
statement.length = 0
|
||||
} else statement.push(token)
|
||||
}
|
||||
expect(limits).toContainEqual({ scope: ['server'], value: '100M' })
|
||||
// A collector-only restriction is allowed; application locations must inherit.
|
||||
expect(limits.filter(limit => limit.scope.some(scope => scope.startsWith('location') && !scope.includes('/_skillhub-umami/')))).toEqual([])
|
||||
expect(limits.filter(limit => limit.scope.length === 1)).toEqual([{ scope: ['server'], value: '100M' }])
|
||||
describe('production deployment defaults', () => {
|
||||
it('keeps the 100 MiB application limit at server scope', () => {
|
||||
const nginx = readFileSync('nginx.conf.template', 'utf8')
|
||||
expect(nginx).toMatch(/server\s*\{(?:(?!location)[\s\S])*?client_max_body_size 100M;/)
|
||||
})
|
||||
|
||||
it('defaults to the release Web entry, not the development port', () => {
|
||||
it('keeps the release Web entry and independent collector networking', () => {
|
||||
const release = readFileSync('../compose.release.yml', 'utf8')
|
||||
const overlay = readFileSync('../compose.release.analytics.yml', 'utf8')
|
||||
const collector = readFileSync('../compose.analytics-umami.yml', 'utf8')
|
||||
expect(release).toContain('${WEB_PORT:-80}:80')
|
||||
expect(compose).toContain('SKILLHUB_WEB_UPSTREAM: ${SKILLHUB_WEB_UPSTREAM:-host.docker.internal:80}')
|
||||
expect(compose).not.toContain('host.docker.internal:8080')
|
||||
})
|
||||
|
||||
it('maps the host gateway on native Linux for the edge service', () => {
|
||||
const edge = compose.split('\n edge:\n')[1]
|
||||
expect(edge).toBeDefined()
|
||||
expect(edge).toContain('extra_hosts: ["host.docker.internal:host-gateway"]')
|
||||
expect(overlay).toContain(' web:\n networks:\n - default\n - analytics')
|
||||
expect(collector).toContain(' external: true')
|
||||
expect(collector).toContain('analytics_postgres_data')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
# LOCAL DEMO ONLY. Umami starts with admin / umami: change it immediately.
|
||||
# The DB password and app secret below are public demo values, NOT production secrets.
|
||||
# SkillHub is an existing upstream, not built or configured by this demo.
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_DB: umami
|
||||
POSTGRES_USER: umami
|
||||
POSTGRES_PASSWORD: demo-only-change-me
|
||||
healthcheck:
|
||||
test: [CMD-SHELL, pg_isready -U umami -d umami]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
umami:
|
||||
image: ghcr.io/umami-software/umami:3.4.1
|
||||
environment:
|
||||
DATABASE_URL: postgresql://umami:demo-only-change-me@postgres:5432/umami
|
||||
APP_SECRET: demo-only-change-this-before-use
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
# Direct admin access is LOCAL setup only; never proxy the admin through edge.
|
||||
ports: ["127.0.0.1:8300:3000"]
|
||||
edge:
|
||||
image: nginx:1.30.6-alpine
|
||||
# Docker Desktop/OrbStack provide this name; native Linux needs this mapping.
|
||||
extra_hosts: ["host.docker.internal:host-gateway"]
|
||||
environment:
|
||||
SKILLHUB_WEB_UPSTREAM: ${SKILLHUB_WEB_UPSTREAM:-host.docker.internal:80}
|
||||
UMAMI_UPSTREAM: umami:3000
|
||||
NGINX_ENVSUBST_FILTER: ^(SKILLHUB_WEB_UPSTREAM|UMAMI_UPSTREAM)$$
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/templates/default.conf.template:ro
|
||||
- ./observe/v1/observer.mjs:/srv/_skillhub-observe/v1/observer.mjs:ro
|
||||
- ./observe/v1/skillhub-intents.mjs:/srv/_skillhub-observe/v1/skillhub-intents.mjs:ro
|
||||
- ./observe/v1/umami-adapter.mjs:/srv/_skillhub-observe/v1/umami-adapter.mjs:ro
|
||||
ports: ["127.0.0.1:8081:8081"]
|
||||
depends_on: [umami]
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
# OPTIONAL deployment-owned edge template. Not included by SkillHub.
|
||||
# Demo HTTP listener: publish on loopback only, behind your existing HTTPS edge.
|
||||
# Substitute only SKILLHUB_WEB_UPSTREAM and UMAMI_UPSTREAM (host:port).
|
||||
# Merge existing TLS, CSP, authentication, host validation and security headers.
|
||||
upstream skillhub_analytics_web { server ${SKILLHUB_WEB_UPSTREAM}; }
|
||||
upstream skillhub_analytics_umami { server ${UMAMI_UPSTREAM}; }
|
||||
server {
|
||||
listen 8081;
|
||||
server_name localhost;
|
||||
# Preserve the standard Web upload limit on every application proxy route.
|
||||
client_max_body_size 100M;
|
||||
|
||||
# Install ONLY the three .mjs files here, not declarations or other files.
|
||||
# A single versioned prefix serves the entry AND its relative imports.
|
||||
location ^~ /_skillhub-observe/v1/ {
|
||||
root /srv;
|
||||
try_files $uri =404;
|
||||
error_page 404 = @module_missing;
|
||||
autoindex off;
|
||||
types { application/javascript mjs; }
|
||||
default_type application/javascript;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
location @module_missing {
|
||||
default_type application/javascript;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header Cache-Control "no-store" always;
|
||||
return 404 '// module not found\n';
|
||||
}
|
||||
location /_skillhub-observe/ { return 404; }
|
||||
|
||||
# Expose only two collector endpoints, NEVER Umami's dashboard/admin API.
|
||||
location = /_skillhub-umami/script.js {
|
||||
limit_except GET { deny all; }
|
||||
proxy_pass http://skillhub_analytics_umami/script.js;
|
||||
proxy_set_header Cookie "";
|
||||
proxy_set_header Authorization "";
|
||||
proxy_set_header Referer "";
|
||||
proxy_hide_header Set-Cookie;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
}
|
||||
location = /_skillhub-umami/api/send {
|
||||
limit_except POST { deny all; }
|
||||
proxy_pass http://skillhub_analytics_umami/api/send;
|
||||
proxy_set_header Cookie "";
|
||||
proxy_set_header Authorization "";
|
||||
proxy_set_header Referer "";
|
||||
proxy_hide_header Set-Cookie;
|
||||
proxy_cache off;
|
||||
add_header Cache-Control "no-store" always;
|
||||
}
|
||||
location /_skillhub-umami/ { return 404; }
|
||||
|
||||
# No filtering or cache-policy changes for API, auth, assets, runtime config,
|
||||
# registry guides/downloads or health. Preserve original URI (no trailing /).
|
||||
location ~ ^/(api/|oauth2/|login/oauth2/|\.well-known/|assets/|registry/|runtime-config\.js$|nginx-health) {
|
||||
proxy_pass http://skillhub_analytics_web;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
|
||||
# Only UI HTML is eligible; sub_filter's default MIME is text/html.
|
||||
location / {
|
||||
proxy_pass http://skillhub_analytics_web;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Accept-Encoding "";
|
||||
proxy_set_header If-None-Match "";
|
||||
proxy_set_header If-Modified-Since "";
|
||||
proxy_cache off;
|
||||
proxy_hide_header ETag;
|
||||
proxy_hide_header Last-Modified;
|
||||
proxy_hide_header Content-MD5;
|
||||
proxy_hide_header Cache-Control;
|
||||
proxy_hide_header Expires;
|
||||
etag off;
|
||||
if_modified_since off;
|
||||
add_header Cache-Control "no-store" always;
|
||||
# Also prevent the browser's HTTP Referer from leaking page paths/queries.
|
||||
proxy_hide_header Referrer-Policy;
|
||||
add_header Referrer-Policy "no-referrer" always;
|
||||
sub_filter_once on;
|
||||
sub_filter_last_modified off;
|
||||
sub_filter '</head>' '<script type="module" src="/_skillhub-observe/v1/observer.mjs"></script></head>';
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue