Reduce Seafile manifest churn when endpoint settings change
Store Seafile references as share tokens and resolve full URLs at sync time, while removing the now-obsolete temporary access notice from the homepage after HTTPS migration. Constraint: Seafile host/domain can change independently from content manifests Rejected: Keeping /f/<token>/?dl=1 in manifest entries | repeats fixed URL boilerplate and increases bulk-edit risk Confidence: high Scope-risk: narrow Directive: Keep Seafile manifest values token-first unless a non-standard external link is explicitly required Tested: npm run build; ./scripts/deploy-homepage.sh --dev-deploy Not-tested: Manual click-through verification for every share/download link on production domain Co-authored-by: OmX <omx@oh-my-codex.dev>
This commit is contained in:
parent
811f8199e0
commit
afa01ce2af
|
|
@ -201,10 +201,11 @@ async function resolveResource(
|
||||||
syncedAt: string;
|
syncedAt: string;
|
||||||
},
|
},
|
||||||
): Promise<ProjectDownload> {
|
): Promise<ProjectDownload> {
|
||||||
|
const resolvedResourceUrl = resolveResourceUrl(resource.url, input.config.baseUrl);
|
||||||
const fallback: ProjectDownload = {
|
const fallback: ProjectDownload = {
|
||||||
name: resource.name,
|
name: resource.name,
|
||||||
description: resource.description,
|
description: resource.description,
|
||||||
url: resource.url,
|
url: resolvedResourceUrl,
|
||||||
type: resource.type,
|
type: resource.type,
|
||||||
platform: resource.platform,
|
platform: resource.platform,
|
||||||
size: resource.size,
|
size: resource.size,
|
||||||
|
|
@ -230,7 +231,7 @@ async function resolveResource(
|
||||||
return {
|
return {
|
||||||
name: resource.name || detail.name || path.basename(resource.path),
|
name: resource.name || detail.name || path.basename(resource.path),
|
||||||
description: resource.description,
|
description: resource.description,
|
||||||
url: downloadUrl || fallback.url,
|
url: resolveResourceUrl(downloadUrl, input.config.baseUrl) || fallback.url,
|
||||||
type: resource.type,
|
type: resource.type,
|
||||||
platform: resource.platform,
|
platform: resource.platform,
|
||||||
size: detail.size ?? resource.size,
|
size: detail.size ?? resource.size,
|
||||||
|
|
@ -355,6 +356,48 @@ function canUseSeafileApi(config: SeafileSyncConfig) {
|
||||||
return Boolean(config.baseUrl && config.token);
|
return Boolean(config.baseUrl && config.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveResourceUrl(value: string | undefined, baseUrl: string) {
|
||||||
|
if (!value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedPath = normalizeSeafilePath(value);
|
||||||
|
if (!normalizedPath) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAbsoluteUrl(normalizedPath)) {
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseUrl) {
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new URL(normalizedPath, `${trimTrailingSlash(baseUrl)}/`).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAbsoluteUrl(value: string) {
|
||||||
|
return /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSeafilePath(value: string) {
|
||||||
|
const trimmed = value.trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAbsoluteUrl(trimmed) || trimmed.startsWith('/')) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^[A-Za-z0-9_-]+$/.test(trimmed)) {
|
||||||
|
return `/f/${trimmed}/?dl=1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
function trimTrailingSlash(value: string) {
|
function trimTrailingSlash(value: string) {
|
||||||
return value.replace(/\/+$/, '');
|
return value.replace(/\/+$/, '');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import path from 'node:path';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
|
|
||||||
type StaticAssetManifestItem = {
|
type StaticAssetManifestItem = {
|
||||||
url: string;
|
source: string;
|
||||||
target: string;
|
target: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -18,6 +18,7 @@ type StaticAssetManifestGroup = {
|
||||||
type StaticAssetConfig = {
|
type StaticAssetConfig = {
|
||||||
rootDir: string;
|
rootDir: string;
|
||||||
manifestPath: string;
|
manifestPath: string;
|
||||||
|
seafileBaseUrl: string;
|
||||||
strict: boolean;
|
strict: boolean;
|
||||||
requestTimeoutMs: number;
|
requestTimeoutMs: number;
|
||||||
};
|
};
|
||||||
|
|
@ -63,6 +64,7 @@ function loadConfig(rootDir: string): StaticAssetConfig {
|
||||||
rootDir,
|
rootDir,
|
||||||
process.env.STATIC_ASSET_MANIFEST ?? 'src/content/static-assets/index.json',
|
process.env.STATIC_ASSET_MANIFEST ?? 'src/content/static-assets/index.json',
|
||||||
),
|
),
|
||||||
|
seafileBaseUrl: process.env.SEAFILE_BASE_URL?.trim() ?? '',
|
||||||
strict: getBooleanEnv('STATIC_ASSET_SYNC_STRICT', false),
|
strict: getBooleanEnv('STATIC_ASSET_SYNC_STRICT', false),
|
||||||
requestTimeoutMs: getNumberEnv('STATIC_ASSET_REQUEST_TIMEOUT_MS', 15000),
|
requestTimeoutMs: getNumberEnv('STATIC_ASSET_REQUEST_TIMEOUT_MS', 15000),
|
||||||
};
|
};
|
||||||
|
|
@ -107,10 +109,10 @@ function normalizeLegacyManifestItem(
|
||||||
record: Record<string, unknown>,
|
record: Record<string, unknown>,
|
||||||
index: number,
|
index: number,
|
||||||
): StaticAssetManifestItem {
|
): StaticAssetManifestItem {
|
||||||
const url = typeof record.url === 'string' ? record.url.trim() : '';
|
const source = typeof record.url === 'string' ? record.url.trim() : '';
|
||||||
const target = typeof record.target === 'string' ? record.target.trim() : '';
|
const target = typeof record.target === 'string' ? record.target.trim() : '';
|
||||||
|
|
||||||
if (!url) {
|
if (!source) {
|
||||||
throw new Error(`manifest item #${index + 1} is missing url`);
|
throw new Error(`manifest item #${index + 1} is missing url`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +120,7 @@ function normalizeLegacyManifestItem(
|
||||||
throw new Error(`manifest item #${index + 1} is missing target`);
|
throw new Error(`manifest item #${index + 1} is missing target`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { url, target };
|
return { source, target };
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeManifestGroup(
|
function normalizeManifestGroup(
|
||||||
|
|
@ -141,10 +143,10 @@ function normalizeManifestGroup(
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = item as Record<string, unknown>;
|
const record = item as Record<string, unknown>;
|
||||||
const url = typeof record.url === 'string' ? record.url.trim() : '';
|
const source = typeof record.url === 'string' ? record.url.trim() : '';
|
||||||
const filename = typeof record.filename === 'string' ? record.filename.trim() : '';
|
const filename = typeof record.filename === 'string' ? record.filename.trim() : '';
|
||||||
|
|
||||||
if (!url) {
|
if (!source) {
|
||||||
throw new Error(`manifest group #${index + 1} file #${fileIndex + 1} is missing url`);
|
throw new Error(`manifest group #${index + 1} file #${fileIndex + 1} is missing url`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,7 +157,7 @@ function normalizeManifestGroup(
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url,
|
source,
|
||||||
target: path.posix.join(targetDir.replace(/\\/g, '/'), filename),
|
target: path.posix.join(targetDir.replace(/\\/g, '/'), filename),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -166,7 +168,8 @@ async function syncAsset(item: StaticAssetManifestItem, config: StaticAssetConfi
|
||||||
await mkdir(path.dirname(targetPath), { recursive: true });
|
await mkdir(path.dirname(targetPath), { recursive: true });
|
||||||
|
|
||||||
const tempPath = `${targetPath}.tmp`;
|
const tempPath = `${targetPath}.tmp`;
|
||||||
const response = await fetchWithTimeout(item.url, config.requestTimeoutMs);
|
const resolvedUrl = resolveManifestUrl(item.source, config.seafileBaseUrl);
|
||||||
|
const response = await fetchWithTimeout(resolvedUrl, config.requestTimeoutMs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const arrayBuffer = await response.arrayBuffer();
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
|
|
@ -176,7 +179,7 @@ async function syncAsset(item: StaticAssetManifestItem, config: StaticAssetConfi
|
||||||
await rename(tempPath, targetPath);
|
await rename(tempPath, targetPath);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`[sync-static-assets] wrote ${path.relative(config.rootDir, targetPath)} <- ${item.url}`,
|
`[sync-static-assets] wrote ${path.relative(config.rootDir, targetPath)} <- ${resolvedUrl}`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await safeRemove(tempPath);
|
await safeRemove(tempPath);
|
||||||
|
|
@ -236,10 +239,54 @@ function getNumberEnv(name: string, fallback: number) {
|
||||||
return Number.isFinite(parsed) ? parsed : fallback;
|
return Number.isFinite(parsed) ? parsed : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveManifestUrl(value: string, baseUrl: string) {
|
||||||
|
const normalizedPath = normalizeSeafilePath(value);
|
||||||
|
if (!normalizedPath) {
|
||||||
|
throw new Error('manifest item url is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAbsoluteUrl(normalizedPath)) {
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseUrl) {
|
||||||
|
throw new Error(
|
||||||
|
`relative asset url "${normalizedPath}" requires SEAFILE_BASE_URL in .env`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new URL(normalizedPath, `${trimTrailingSlash(baseUrl)}/`).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAbsoluteUrl(value: string) {
|
||||||
|
return /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSeafilePath(value: string) {
|
||||||
|
const trimmed = value.trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAbsoluteUrl(trimmed) || trimmed.startsWith('/')) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^[A-Za-z0-9_-]+$/.test(trimmed)) {
|
||||||
|
return `/f/${trimmed}/?dl=1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveFromRoot(rootDir: string, targetPath: string) {
|
function resolveFromRoot(rootDir: string, targetPath: string) {
|
||||||
return path.isAbsolute(targetPath) ? targetPath : path.join(rootDir, targetPath);
|
return path.isAbsolute(targetPath) ? targetPath : path.join(rootDir, targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trimTrailingSlash(value: string) {
|
||||||
|
return value.replace(/\/+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
function isMissingFileError(error: unknown) {
|
function isMissingFileError(error: unknown) {
|
||||||
return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';
|
return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,40 @@
|
||||||
{
|
{
|
||||||
"projects": [
|
"projects": [
|
||||||
|
{
|
||||||
|
"project_repo": "basil/vampire-like",
|
||||||
|
"downloads": [
|
||||||
|
{
|
||||||
|
"name": "Windows 构建包",
|
||||||
|
"description": "项目 3D 类吸血鬼幸存者 Win64 打包文件",
|
||||||
|
"url": "e78c21a182eb431bb169",
|
||||||
|
"repo_id": "",
|
||||||
|
"path": "",
|
||||||
|
"type": "build",
|
||||||
|
"platform": "Win64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"project_repo": "basil/vampire-like",
|
||||||
|
"downloads": [
|
||||||
|
{
|
||||||
|
"name": "Android 构建包",
|
||||||
|
"description": "项目 3D 累吸血鬼幸存者 Android 打包文件",
|
||||||
|
"url": "b30d0af94fc64fa887bd",
|
||||||
|
"repo_id": "",
|
||||||
|
"path": "",
|
||||||
|
"type": "build",
|
||||||
|
"platform": "Android"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"project_repo": "basil/biography-of-lijie",
|
"project_repo": "basil/biography-of-lijie",
|
||||||
"downloads": [
|
"downloads": [
|
||||||
{
|
{
|
||||||
"name": "Windows 构建包",
|
"name": "Windows 构建包",
|
||||||
"description": "项目 李诫传 打包文件",
|
"description": "项目 李诫传 打包文件",
|
||||||
"url": "http://106.12.111.150:8000/f/c6e397439b174fe39106/?dl=1",
|
"url": "c6e397439b174fe39106",
|
||||||
"repo_id": "",
|
"repo_id": "",
|
||||||
"path": "",
|
"path": "",
|
||||||
"type": "build",
|
"type": "build",
|
||||||
|
|
@ -19,7 +47,7 @@
|
||||||
{
|
{
|
||||||
"name": "Unity 游戏客户端开发简历 PDF",
|
"name": "Unity 游戏客户端开发简历 PDF",
|
||||||
"description": "求职 Unity 游戏客户端开发岗位的简历",
|
"description": "求职 Unity 游戏客户端开发岗位的简历",
|
||||||
"url": "http://106.12.111.150:8000/f/937ec9f34ec94d528a52/?dl=1",
|
"url": "937ec9f34ec94d528a52",
|
||||||
"repo_id": "",
|
"repo_id": "",
|
||||||
"path": "",
|
"path": "",
|
||||||
"type": "document"
|
"type": "document"
|
||||||
|
|
@ -27,7 +55,7 @@
|
||||||
{
|
{
|
||||||
"name": "游戏构建开发简历 PDF",
|
"name": "游戏构建开发简历 PDF",
|
||||||
"description": "求职游戏构建开发岗位的简历",
|
"description": "求职游戏构建开发岗位的简历",
|
||||||
"url": "http://106.12.111.150:8000/f/04deda4586a742a6b5a7/?dl=1",
|
"url": "04deda4586a742a6b5a7",
|
||||||
"repo_id": "",
|
"repo_id": "",
|
||||||
"path": "",
|
"path": "",
|
||||||
"type": "document"
|
"type": "document"
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,19 @@
|
||||||
"target_dir": "public/images/projects",
|
"target_dir": "public/images/projects",
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/cfb44c4a96234a939ed2/?dl=1",
|
"url": "cfb44c4a96234a939ed2",
|
||||||
"filename": "vampire-like.png"
|
"filename": "vampire-like.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/85e956b2a4f14ff9bca8/?dl=1",
|
"url": "85e956b2a4f14ff9bca8",
|
||||||
"filename": "geometry-tower-defense.png"
|
"filename": "geometry-tower-defense.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/7f6ac5bd1b094db5ae95/?dl=1",
|
"url": "7f6ac5bd1b094db5ae95",
|
||||||
"filename": "CPU-Renderer.png"
|
"filename": "CPU-Renderer.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/1ac8ebc9936b490586be/?dl=1",
|
"url": "1ac8ebc9936b490586be",
|
||||||
"filename": "biography-of-lijie.png"
|
"filename": "biography-of-lijie.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -24,155 +24,155 @@
|
||||||
"target_dir": "public/images/gallery",
|
"target_dir": "public/images/gallery",
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/0d357852716f492eb92f/?dl=1",
|
"url": "0d357852716f492eb92f",
|
||||||
"filename": "Alphys.png"
|
"filename": "Alphys.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/ec2e2e282ed7478c8fa0/?dl=1",
|
"url": "ec2e2e282ed7478c8fa0",
|
||||||
"filename": "Asgore.png"
|
"filename": "Asgore.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/7b7fb8414ab2485ba05a/?dl=1",
|
"url": "7b7fb8414ab2485ba05a",
|
||||||
"filename": "Asriel.png"
|
"filename": "Asriel.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/bd8f4b71f3264ce5a36f/?dl=1",
|
"url": "bd8f4b71f3264ce5a36f",
|
||||||
"filename": "AUBURY-farwaytown.png"
|
"filename": "AUBURY-farwaytown.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/3f39dda9122f440aafd8/?dl=1",
|
"url": "3f39dda9122f440aafd8",
|
||||||
"filename": "AUBURY-headspace.png"
|
"filename": "AUBURY-headspace.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/92b1355908b74898b7b3/?dl=1",
|
"url": "92b1355908b74898b7b3",
|
||||||
"filename": "BASIL-farwaytown.png"
|
"filename": "BASIL-farwaytown.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/9a3bdc09fa5e4216b7e6/?dl=1",
|
"url": "9a3bdc09fa5e4216b7e6",
|
||||||
"filename": "BASIL-headspace.png"
|
"filename": "BASIL-headspace.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/00fea1e5adae472796ff/?dl=1",
|
"url": "00fea1e5adae472796ff",
|
||||||
"filename": "Flowey.png"
|
"filename": "Flowey.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/edaa7526e5dd4f6e807d/?dl=1",
|
"url": "edaa7526e5dd4f6e807d",
|
||||||
"filename": "HERO-farwaytown.png"
|
"filename": "HERO-farwaytown.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/309a3b70af944247a273/?dl=1",
|
"url": "309a3b70af944247a273",
|
||||||
"filename": "HERO-farwaytown.png"
|
"filename": "HERO-farwaytown.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/cf5f1e4459e1453bbdd6/?dl=1",
|
"url": "cf5f1e4459e1453bbdd6",
|
||||||
"filename": "KEL-farwaytown.png"
|
"filename": "KEL-farwaytown.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/221394e33b3746e89406/?dl=1",
|
"url": "221394e33b3746e89406",
|
||||||
"filename": "KEL-headspace.png"
|
"filename": "KEL-headspace.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/5e129d4d015149e28df8/?dl=1",
|
"url": "5e129d4d015149e28df8",
|
||||||
"filename": "Kris-dark.png"
|
"filename": "Kris-dark.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/c5a27832d49f4e5a8fa2/?dl=1",
|
"url": "c5a27832d49f4e5a8fa2",
|
||||||
"filename": "Kris-light.png"
|
"filename": "Kris-light.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/2dc36c44d04540d29253/?dl=1",
|
"url": "2dc36c44d04540d29253",
|
||||||
"filename": "MARI-farwaytown.png"
|
"filename": "MARI-farwaytown.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/37c25836770c481bb22c/?dl=1",
|
"url": "37c25836770c481bb22c",
|
||||||
"filename": "MARI-headspace.png"
|
"filename": "MARI-headspace.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/accd21bb40ed48de8c48/?dl=1",
|
"url": "accd21bb40ed48de8c48",
|
||||||
"filename": "Mettation.png"
|
"filename": "Mettation.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/b441f2cb16434bf38974/?dl=1",
|
"url": "b441f2cb16434bf38974",
|
||||||
"filename": "Noelle-dark.png"
|
"filename": "Noelle-dark.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/01e300bc2e6e4b3faf4b/?dl=1",
|
"url": "01e300bc2e6e4b3faf4b",
|
||||||
"filename": "Noelle-light.png"
|
"filename": "Noelle-light.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/a3688e213aa249a6bcbb/?dl=1",
|
"url": "a3688e213aa249a6bcbb",
|
||||||
"filename": "OMORI.png"
|
"filename": "OMORI.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/6ea8ec1fc7f84632bf50/?dl=1",
|
"url": "6ea8ec1fc7f84632bf50",
|
||||||
"filename": "Papyrus.png"
|
"filename": "Papyrus.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/806c6cbcee794039b894/?dl=1",
|
"url": "806c6cbcee794039b894",
|
||||||
"filename": "Ralsei-hat.png"
|
"filename": "Ralsei-hat.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/d4dbf86997594a1d9b31/?dl=1",
|
"url": "d4dbf86997594a1d9b31",
|
||||||
"filename": "Ralsei-nohat.png"
|
"filename": "Ralsei-nohat.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/bf5804465283442fbefb/?dl=1",
|
"url": "bf5804465283442fbefb",
|
||||||
"filename": "Sans.png"
|
"filename": "Sans.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/a06f7a1a8f90404ba79a/?dl=1",
|
"url": "a06f7a1a8f90404ba79a",
|
||||||
"filename": "SOMETHING.png"
|
"filename": "SOMETHING.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/e18e05022ede4e659e8f/?dl=1",
|
"url": "e18e05022ede4e659e8f",
|
||||||
"filename": "SUNNY.png"
|
"filename": "SUNNY.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/bd59853e81dd4c349c96/?dl=1",
|
"url": "bd59853e81dd4c349c96",
|
||||||
"filename": "Susie-dark.png"
|
"filename": "Susie-dark.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/80faf6386179400db15b/?dl=1",
|
"url": "80faf6386179400db15b",
|
||||||
"filename": "Susie-light.png"
|
"filename": "Susie-light.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/13e2f6b06db1411bbdeb/?dl=1",
|
"url": "13e2f6b06db1411bbdeb",
|
||||||
"filename": "Toriel.png"
|
"filename": "Toriel.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/d309b43c18ca431eaedf/?dl=1",
|
"url": "d309b43c18ca431eaedf",
|
||||||
"filename": "Undyne.png"
|
"filename": "Undyne.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/c6b19ca33b274ad4b121/?dl=1",
|
"url": "c6b19ca33b274ad4b121",
|
||||||
"filename": "狛枝凪斗.png"
|
"filename": "狛枝凪斗.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/10290be473334433848b/?dl=1",
|
"url": "10290be473334433848b",
|
||||||
"filename": "不二咲千寻.png"
|
"filename": "不二咲千寻.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/dcba07ca07ba4467b21f/?dl=1",
|
"url": "dcba07ca07ba4467b21f",
|
||||||
"filename": "苗木诚.png"
|
"filename": "苗木诚.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/fe817cb7b51f4d2bad41/?dl=1",
|
"url": "fe817cb7b51f4d2bad41",
|
||||||
"filename": "七海千秋.png"
|
"filename": "七海千秋.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/5ed86d51cc834745b127/?dl=1",
|
"url": "5ed86d51cc834745b127",
|
||||||
"filename": "日向创.png"
|
"filename": "日向创.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/36129927521c4f8eac1c/?dl=1",
|
"url": "36129927521c4f8eac1c",
|
||||||
"filename": "十神白夜.png"
|
"filename": "十神白夜.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/8ed6188acc87487faf38/?dl=1",
|
"url": "8ed6188acc87487faf38",
|
||||||
"filename": "雾切响子.png"
|
"filename": "雾切响子.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "http://106.12.111.150:8000/f/d8a1d002c0324ca59e58/?dl=1",
|
"url": "d8a1d002c0324ca59e58",
|
||||||
"filename": "小泉真昼.png"
|
"filename": "小泉真昼.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -37,19 +37,6 @@ const featuredProjectsUpdatedAt = getLatestDate(
|
||||||
<p class="hero-subtitle">{site.tagline}</p>
|
<p class="hero-subtitle">{site.tagline}</p>
|
||||||
<p class="hero-meta">{site.description}</p>
|
<p class="hero-meta">{site.description}</p>
|
||||||
|
|
||||||
<div class="hero-notice" role="note" aria-label="访问提示">
|
|
||||||
<p class="hero-notice__title">临时访问说明</p>
|
|
||||||
<p class="hero-notice__body">
|
|
||||||
如果点击顶部导航后页面跳到
|
|
||||||
<code>106.12.111.150/logs</code>
|
|
||||||
这类
|
|
||||||
<strong>不带 :2000</strong>
|
|
||||||
的地址并出现 404,请手动把地址改成
|
|
||||||
<code>106.12.111.150:2000/对应路径</code>
|
|
||||||
即可正常访问其他视图。这个问题是当前 Nginx 反代导致的临时现象,等域名备案完成后会恢复正常。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="hero-actions">
|
<div class="hero-actions">
|
||||||
<a class="button-primary" href="/projects">查看精选项目</a>
|
<a class="button-primary" href="/projects">查看精选项目</a>
|
||||||
<a class="button-secondary" href="/logs">阅读开发日志</a>
|
<a class="button-secondary" href="/logs">阅读开发日志</a>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue