feat: establish production SaaS foundation

This commit is contained in:
Codex
2026-07-12 19:26:57 +08:00
parent 1c2ce38cea
commit 39f7332f33
219 changed files with 20647 additions and 2628 deletions

View File

@@ -2,7 +2,27 @@ import assert from 'node:assert/strict';
import { pathToFileURL } from 'node:url';
const repoRoot = process.cwd();
globalThis.__TARO_PUBLIC_BUILD_CONFIG__ = {
portal: 'student',
target: 'weapp',
releaseMode: 'production',
weappTenantMode: 'fixed',
apiBaseUrl: 'https://compiled-api.gongxue100.com',
supabaseUrl: 'https://compiled-auth.gongxue100.com',
supabasePublishableKey: 'sb_publishable_compiled_key',
tenantCode: 'compiled-tenant',
};
const envModule = await import(pathToFileURL(`${repoRoot}/apps/taro/src/env.ts`).href);
delete globalThis.__TARO_PUBLIC_BUILD_CONFIG__;
assert.equal(envModule.appEnv.portal, 'student');
assert.equal(envModule.appEnv.apiBaseUrl, 'https://compiled-api.gongxue100.com');
assert.equal(envModule.appEnv.supabaseUrl, 'https://compiled-auth.gongxue100.com');
assert.equal(envModule.appEnv.supabasePublishableKey, 'sb_publishable_compiled_key');
assert.equal(envModule.appEnv.tenantCode, 'compiled-tenant');
assert.equal(envModule.taroRuntimeEnv(), 'weapp');
assert.equal(envModule.isWeappRuntime(), true);
assert.equal(envModule.taroWeappTenantMode(), 'fixed');
envModule.applyRuntimeConfig({
portal: 'tenant-admin',
@@ -18,6 +38,9 @@ assert.equal(envModule.appEnv.supabaseUrl, 'https://auth.gongxue100.com');
assert.equal(envModule.appEnv.supabasePublishableKey, 'sb_publishable_public_key');
assert.equal(envModule.appEnv.tenantCode, 'tenant-a');
envModule.applyRuntimeConfig({ tenantCode: '' });
assert.equal(envModule.appEnv.tenantCode, '', 'An empty runtime tenantCode must clear a compiled tenant fallback');
envModule.applyRuntimeConfig({
TARO_APP_PORTAL: 'platform-admin',
TARO_APP_API_BASE_URL: 'https://api2.gongxue100.com',
@@ -50,4 +73,100 @@ assert.throws(
/Unknown key in runtime config: unexpectedFeatureFlag/,
);
globalThis.__TARO_PUBLIC_BUILD_CONFIG__ = {
portal: 'student',
target: 'h5',
releaseMode: 'production',
weappTenantMode: '',
apiBaseUrl: '',
supabaseUrl: '',
supabasePublishableKey: '',
tenantCode: '',
};
globalThis.window = {
location: { origin: 'https://student.example.test' },
fetch: async () => {
throw new Error('network unavailable');
},
};
const productionH5Url = pathToFileURL(`${repoRoot}/apps/taro/src/env.ts`);
productionH5Url.searchParams.set('runtime-config-test', 'production-h5');
const productionH5Env = await import(productionH5Url.href);
assert.equal(productionH5Env.appEnv.apiBaseUrl, '', 'Production H5 must not compile a local API fallback');
assert.equal(productionH5Env.taroReleaseMode(), 'production');
await assert.rejects(
() => productionH5Env.loadRuntimeConfig(),
/Production H5 runtime-config\.json request failed: network unavailable/,
'Production H5 must fail closed when runtime config cannot be fetched',
);
globalThis.window.fetch = async () => ({ ok: false, status: 404 });
await assert.rejects(
() => productionH5Env.loadRuntimeConfig(),
/Production H5 runtime-config\.json request failed with status 404/,
'Production H5 must fail closed when runtime config is missing',
);
globalThis.window.fetch = async () => ({ ok: true, status: 200, text: async () => '' });
await assert.rejects(
() => productionH5Env.loadRuntimeConfig(),
/Production H5 runtime-config\.json is empty or unreadable/,
'Production H5 must fail closed when runtime config is unreadable',
);
globalThis.window.fetch = async () => ({
ok: true,
status: 200,
text: async () => JSON.stringify({ portal: 'student', apiBaseUrl: '' }),
});
await assert.rejects(
() => productionH5Env.loadRuntimeConfig(),
/Production H5 runtime-config\.json apiBaseUrl must be an absolute HTTPS URL and must not use localhost or loopback/,
'Production H5 must fail closed when runtime config omits its API endpoint',
);
for (const apiBaseUrl of [
'http://api.gongxue100.com',
'https://localhost:8787',
'https://127.0.0.1:8787',
'https://[::1]:8787',
]) {
globalThis.window.fetch = async () => ({
ok: true,
status: 200,
text: async () => JSON.stringify({ portal: 'student', apiBaseUrl }),
});
await assert.rejects(
() => productionH5Env.loadRuntimeConfig(),
/must be an absolute HTTPS URL and must not use localhost or loopback/,
`Production H5 must reject unsafe API endpoint ${apiBaseUrl}`,
);
}
globalThis.window.fetch = async () => ({
ok: true,
status: 200,
text: async () => JSON.stringify({
portal: 'student',
apiBaseUrl: 'https://api.gongxue100.com/',
supabaseUrl: 'https://auth.gongxue100.com/',
supabasePublishableKey: 'sb_publishable_runtime_test',
tenantCode: '',
}),
});
await productionH5Env.loadRuntimeConfig();
assert.equal(productionH5Env.appEnv.apiBaseUrl, 'https://api.gongxue100.com');
assert.equal(productionH5Env.appEnv.tenantCode, '', 'Production H5 must remain domain resolved');
globalThis.window.fetch = async () => ({
ok: true,
status: 200,
text: async () => JSON.stringify({
portal: 'student',
apiBaseUrl: 'https://api.gongxue100.com',
tenantCode: 'campus-north',
}),
});
await assert.rejects(
() => productionH5Env.loadRuntimeConfig(),
/tenantCode must be empty; tenant is resolved from the browser origin/,
'Production H5 must reject a fixed tenant override',
);
delete globalThis.window;
delete globalThis.__TARO_PUBLIC_BUILD_CONFIG__;
console.log('[PASS] Taro runtime config guardrails');