forked from wangziqi/gongxue-base
81 lines
2.9 KiB
JavaScript
81 lines
2.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
const repoRoot = process.cwd();
|
|
const studentPagesDir = path.join(repoRoot, 'apps', 'taro', 'src', 'pages', 'student');
|
|
const appConfigPath = path.join(repoRoot, 'apps', 'taro', 'src', 'app.config.ts');
|
|
const studentProfileServicePath = path.join(repoRoot, 'apps', 'taro', 'src', 'services', 'profile.ts');
|
|
|
|
function readText(filePath) {
|
|
return fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
|
|
}
|
|
|
|
function walkFiles(dir) {
|
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
return entries.flatMap(entry => {
|
|
const entryPath = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) return walkFiles(entryPath);
|
|
if (!/\.(ts|tsx|css|json)$/.test(entry.name)) return [];
|
|
return [entryPath];
|
|
});
|
|
}
|
|
|
|
function relative(filePath) {
|
|
return path.relative(repoRoot, filePath).replace(/\\/g, '/');
|
|
}
|
|
|
|
const appConfig = readText(appConfigPath);
|
|
assert.ok(
|
|
!/pages\/student\/(?:leaderboard|rank|ranking)\b/i.test(appConfig),
|
|
'Student routes must not include a default leaderboard/ranking page. Leaderboard is optional and tenant-gated.',
|
|
);
|
|
|
|
const studentFiles = walkFiles(studentPagesDir);
|
|
const leaderboardViolations = [];
|
|
const avatarUploadViolations = [];
|
|
const avatarUrlViolations = [];
|
|
|
|
for (const filePath of studentFiles) {
|
|
const text = readText(filePath);
|
|
if (/\bloadLeaderboard\b|\/api\/learning\/leaderboard|learning\/leaderboard|leaderboard|排行榜/.test(text)) {
|
|
leaderboardViolations.push(relative(filePath));
|
|
}
|
|
if (/\bchooseImage\b|\buploadFile\b|sign-upload|avatar\s*upload|头像上传|上传头像|裁剪头像/.test(text)) {
|
|
avatarUploadViolations.push(relative(filePath));
|
|
}
|
|
if (/\bavatarUrl\b|\bavatar_url\b|\bheadimgurl\b|\bheadImgUrl\b|\bfigureurl\b/i.test(text)) {
|
|
avatarUrlViolations.push(relative(filePath));
|
|
}
|
|
}
|
|
|
|
assert.deepEqual(
|
|
[...new Set(leaderboardViolations)].sort(),
|
|
[],
|
|
'Student pages must not request or render leaderboard by default. Add a tenant-gated activity page only after explicit enablement and load testing.',
|
|
);
|
|
|
|
assert.deepEqual(
|
|
[...new Set(avatarUploadViolations)].sort(),
|
|
[],
|
|
'Student pages must not add avatar upload/crop/storage flows. Only avatarPreset=male/female is supported.',
|
|
);
|
|
|
|
assert.deepEqual(
|
|
[...new Set(avatarUrlViolations)].sort(),
|
|
[],
|
|
'Student pages must not read or write avatarUrl/headimgurl/figureurl fields. Use avatarPreset only.',
|
|
);
|
|
|
|
const profileService = readText(studentProfileServicePath);
|
|
assert.ok(
|
|
/avatarPreset\?: 'male' \| 'female'/.test(profileService),
|
|
'Student profile service must expose avatarPreset=male/female.',
|
|
);
|
|
assert.ok(
|
|
!/\bavatarUrl\b|\bavatar_url\b|\bheadimgurl\b|\bheadImgUrl\b|\bfigureurl\b/i.test(profileService),
|
|
'Student profile service must not accept or expose avatar URL fields.',
|
|
);
|
|
|
|
console.log('[PASS] Taro student product guardrails');
|