添加账号修改,后台管理

This commit is contained in:
yoyuzh
2026-03-19 17:52:58 +08:00
parent c39fde6b19
commit ff8d47f44f
60 changed files with 4264 additions and 58 deletions

View File

@@ -9,6 +9,8 @@ import {
buildObjectKey,
createAuthorizationHeader,
encodeObjectKey,
getFrontendSpaAliasContentType,
getFrontendSpaAliasKeys,
getCacheControl,
getContentType,
listFiles,
@@ -72,11 +74,12 @@ async function uploadFile({
endpoint,
objectKey,
filePath,
contentTypeOverride,
accessKeyId,
accessKeySecret,
}) {
const body = await fs.readFile(filePath);
const contentType = getContentType(objectKey);
const contentType = contentTypeOverride || getContentType(objectKey);
const date = new Date().toUTCString();
const url = `https://${bucket}.${normalizeEndpoint(endpoint)}/${encodeObjectKey(objectKey)}`;
const authorization = createAuthorizationHeader({
@@ -107,6 +110,39 @@ async function uploadFile({
}
}
async function uploadSpaAliases({
bucket,
endpoint,
distIndexPath,
accessKeyId,
accessKeySecret,
remotePrefix,
dryRun,
}) {
const aliases = getFrontendSpaAliasKeys();
const contentType = getFrontendSpaAliasContentType();
for (const alias of aliases) {
const objectKey = buildObjectKey(remotePrefix, alias);
if (dryRun) {
console.log(`[dry-run] upload alias ${alias} -> ${objectKey}`);
continue;
}
await uploadFile({
bucket,
endpoint,
objectKey,
filePath: distIndexPath,
contentTypeOverride: contentType,
accessKeyId,
accessKeySecret,
});
console.log(`uploaded alias ${objectKey}`);
}
}
async function main() {
const {dryRun, skipBuild} = parseArgs(process.argv.slice(2));
@@ -146,6 +182,16 @@ async function main() {
});
console.log(`uploaded ${objectKey}`);
}
await uploadSpaAliases({
bucket,
endpoint,
distIndexPath: path.join(distDir, 'index.html'),
accessKeyId,
accessKeySecret,
remotePrefix,
dryRun,
});
}
main().catch((error) => {

View File

@@ -17,6 +17,18 @@ const CONTENT_TYPES = new Map([
['.webmanifest', 'application/manifest+json; charset=utf-8'],
]);
const FRONTEND_SPA_ALIASES = [
'overview',
'files',
'school',
'games',
'login',
'admin',
'admin/users',
'admin/files',
'admin/schoolSnapshots',
];
export function normalizeEndpoint(endpoint) {
return endpoint.replace(/^https?:\/\//, '').replace(/\/+$/, '');
}
@@ -44,6 +56,18 @@ export function getContentType(relativePath) {
return CONTENT_TYPES.get(ext) || 'application/octet-stream';
}
export function getFrontendSpaAliasKeys() {
return FRONTEND_SPA_ALIASES.flatMap((alias) => [
alias,
`${alias}/`,
`${alias}/index.html`,
]);
}
export function getFrontendSpaAliasContentType() {
return 'text/html; charset=utf-8';
}
export function createAuthorizationHeader({
method,
bucket,

View File

@@ -4,6 +4,8 @@ import test from 'node:test';
import {
buildObjectKey,
createAuthorizationHeader,
getFrontendSpaAliasContentType,
getFrontendSpaAliasKeys,
getCacheControl,
getContentType,
normalizeEndpoint,
@@ -31,6 +33,15 @@ test('getContentType resolves common frontend asset types', () => {
assert.equal(getContentType('favicon.png'), 'image/png');
});
test('frontend spa aliases are uploaded as html entry points', () => {
const aliases = getFrontendSpaAliasKeys();
assert.ok(aliases.includes('overview'));
assert.ok(aliases.includes('admin/users'));
assert.ok(aliases.includes('admin/schoolSnapshots/index.html'));
assert.equal(getFrontendSpaAliasContentType(), 'text/html; charset=utf-8');
});
test('createAuthorizationHeader is stable for a known request', () => {
const header = createAuthorizationHeader({
method: 'PUT',