Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4746787484 | |||
| 61d80f352f | |||
| 4ec83f2603 | |||
| 51347669a4 | |||
| 6e8dc8942a |
+5
-3
@@ -1,4 +1,6 @@
|
||||
node_modules/
|
||||
.pnpm-store/
|
||||
.turbo/
|
||||
.next/
|
||||
coverage/
|
||||
.env*
|
||||
@@ -25,6 +27,6 @@ __pycache__/
|
||||
/apps/studio/public/brand/
|
||||
/apps/studio/public/favicon.ico
|
||||
/apps/studio/public/apple-touch-icon.png
|
||||
/apps/waterfall-wiki/public/brand/
|
||||
/apps/waterfall-wiki/public/favicon.ico
|
||||
/apps/waterfall-wiki/public/apple-touch-icon.png
|
||||
/apps/dashboard/public/brand/
|
||||
/apps/dashboard/public/favicon.ico
|
||||
/apps/dashboard/public/apple-touch-icon.png
|
||||
|
||||
@@ -8,7 +8,6 @@ This block is written and re-added by `next dev` — verify at `node_modules/nex
|
||||
|
||||
<!-- END:nextjs-agent-rules -->
|
||||
|
||||
## 构建与维护
|
||||
## 项目维护
|
||||
|
||||
- 构建和网页运行使用用户确认的远程环境。本地可执行差异审查和无需构建的轻量检查。
|
||||
- Wiki 和 Studio 共用 `apps/waterfall-wiki` 的 Pages Router、登录保护和品牌包。保留已发布的路由、媒体标识和数据库迁移。
|
||||
- Wiki 和 Studio 共用 `apps/dashboard` 的 Pages Router、登录保护和品牌包。保留已发布的路由、媒体标识和数据库迁移。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 啡跃 Endoleap
|
||||
|
||||
啡跃把笔记、参考素材和创作工作台放在同一个 Next.js 应用里。应用位于 `apps/waterfall-wiki`,共享主题与品牌资源位于 `packages/brand`。
|
||||
啡跃把笔记、参考素材和创作工作台放在同一个 Next.js 应用里。应用位于 `apps/dashboard`,共享主题与品牌资源位于 `packages/brand`。
|
||||
|
||||
| 入口 | 用途 |
|
||||
| --- | --- |
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
## 运行
|
||||
|
||||
在已确认的远程环境准备 Node.js 24、pnpm 11.25.0、PostgreSQL、S3 兼容对象存储和 FFmpeg。参考 [环境变量示例](apps/waterfall-wiki/.env.example),将变量注入 Web、初始化和媒体 worker 进程。邮箱注册与找回密码需要可用的 SMTP 服务。
|
||||
准备 Node.js 24、pnpm 11.25.0、PostgreSQL、S3 兼容对象存储和 FFmpeg。参考 [环境变量示例](apps/dashboard/.env.example),将变量注入 Web、初始化和媒体 worker 进程。邮箱注册与找回密码需要可用的 SMTP 服务。
|
||||
|
||||
在仓库根目录运行 `pnpm install --frozen-lockfile` 和 `pnpm build`。然后在 `apps/waterfall-wiki` 目录准备数据库和存储,初始化管理员认证并启动 Web。
|
||||
在仓库根目录运行 `pnpm install --frozen-lockfile` 和 `pnpm build`。然后在 `apps/dashboard` 目录准备数据库和存储,初始化管理员认证并启动 Web。
|
||||
|
||||
```sh
|
||||
node dist/bootstrap.cjs
|
||||
@@ -24,3 +24,15 @@ pnpm start
|
||||
```
|
||||
|
||||
媒体 worker 在同一目录作为独立进程运行 `node dist/worker.cjs`。绑定管理员验证器时,在远程交互终端执行 `node dist/auth-cli.cjs qr`。已有实例复用原数据库、对象存储和私有认证文件。
|
||||
|
||||
## Monorepo 与 Turborepo
|
||||
|
||||
仓库在现有 pnpm workspace 上接入 Turborepo,无需运行面向新项目的 `npx create-turbo@latest`。应用仍位于 `apps/dashboard`,源码品牌包仍位于 `packages/brand`;子包脚本和应用产物路径保持原样。依赖统一锁定在根目录 `pnpm-lock.yaml`,安装从仓库根目录执行。
|
||||
|
||||
根目录命令由 Turbo 调度:`pnpm dev` 启动开发任务,`pnpm dev:dashboard` 仅启动 Wiki,`pnpm build` 构建应用及 worker/CLI,`pnpm typecheck` 生成 Next.js 类型并检查 TypeScript。`pnpm start` 继续直接启动现有应用。也可以在子包目录使用原有命令。
|
||||
|
||||
锁文件合并保留应用原有依赖解析。唯一重叠差异是 `@tailwindcss/postcss` 的间接 PostCSS 依赖:品牌包原为 8.5.32,现在沿用应用的 8.5.31;品牌包直接声明的 PostCSS 8.5.6 不变。
|
||||
|
||||
迁移阶段所有任务禁用缓存,根命令使用 `--env-mode=loose` 透传既有环境变量,保留品牌资源准备步骤的每次执行。后续启用缓存前,需明确环境变量输入、跨包资源和所有输出。当前不配置 CI/CD,也不自动发布。
|
||||
|
||||
仅检查任务计划、不执行编译或生成应用产物时,可运行 `pnpm build --dry=json` 和 `pnpm typecheck --dry=json`。完整构建与运行验证另行执行。
|
||||
|
||||
@@ -11,7 +11,7 @@ const nextConfig: NextConfig = {
|
||||
{source:'/studio',destination:'/workspace',permanent:false},
|
||||
]},
|
||||
async rewrites(){return [
|
||||
...Object.entries(managedMedia).filter(([path])=>path.startsWith("apps/waterfall-wiki/public/")).map(([path,asset])=>({source:"/"+path.slice("apps/waterfall-wiki/public/".length),destination:"/api/media/"+asset.id})),
|
||||
...Object.entries(managedMedia).filter(([path])=>path.startsWith("apps/dashboard/public/")).map(([path,asset])=>({source:"/"+path.slice("apps/dashboard/public/".length),destination:"/api/media/"+asset.id})),
|
||||
{source:"/studio/api/:path*",destination:"/api/studio-ui/:path*"},
|
||||
{source:"/studio/brand/:path*",destination:"/brand/:path*"},
|
||||
{source:"/studio/favicon.ico",destination:"/favicon.ico"},
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@prompt/waterfall-wiki",
|
||||
"name": "@prompt/dashboard",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"engines": {
|
||||
|
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 451 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
+24
-24
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"apps/waterfall-wiki/public/learning-skills/cover.png": {
|
||||
"apps/dashboard/public/learning-skills/cover.png": {
|
||||
"id": "a9cafd80-8207-567b-aaa4-ebd250edaab5",
|
||||
"key": "repository-media/sha256/8049b5704e44b2ebf3b97cef2495a85628af9a16d933ede2f7e790bc69b5fab1.png",
|
||||
"sha256": "8049b5704e44b2ebf3b97cef2495a85628af9a16d933ede2f7e790bc69b5fab1",
|
||||
@@ -8,7 +8,7 @@
|
||||
"width": 1080,
|
||||
"height": 1440
|
||||
},
|
||||
"apps/waterfall-wiki/public/skills/shadow-puppet/dancer-example.png": {
|
||||
"apps/dashboard/public/skills/shadow-puppet/dancer-example.png": {
|
||||
"id": "6514a180-bb28-558d-ac9e-0dd49279a058",
|
||||
"key": "repository-media/sha256/94803894dcb06dae84d3744f963f55e077326d34f103a4fce05e32c2f4ab6895.png",
|
||||
"sha256": "94803894dcb06dae84d3744f963f55e077326d34f103a4fce05e32c2f4ab6895",
|
||||
@@ -17,7 +17,7 @@
|
||||
"width": 680,
|
||||
"height": 900
|
||||
},
|
||||
"apps/waterfall-wiki/public/skills/shadow-puppet/egret-example.png": {
|
||||
"apps/dashboard/public/skills/shadow-puppet/egret-example.png": {
|
||||
"id": "63aefa65-5431-5a2e-a20c-4293a010410e",
|
||||
"key": "repository-media/sha256/2dd294282bf6c0dda86902cb2be265ba5b89c160a4d5e01ec50f79dac7ac6b82.png",
|
||||
"sha256": "2dd294282bf6c0dda86902cb2be265ba5b89c160a4d5e01ec50f79dac7ac6b82",
|
||||
@@ -26,7 +26,7 @@
|
||||
"width": 1043,
|
||||
"height": 1380
|
||||
},
|
||||
"apps/waterfall-wiki/public/skills/street-people/reference.png": {
|
||||
"apps/dashboard/public/skills/street-people/reference.png": {
|
||||
"id": "26cdf219-7264-5b51-b1bd-b13eaedcb946",
|
||||
"key": "repository-media/sha256/e488a52338ac3496e6e51ea4843d99b8ca9cfe3bec4fc8d1ade3985bbb4f4c86.png",
|
||||
"sha256": "e488a52338ac3496e6e51ea4843d99b8ca9cfe3bec4fc8d1ade3985bbb4f4c86",
|
||||
@@ -35,7 +35,7 @@
|
||||
"width": 1200,
|
||||
"height": 2133
|
||||
},
|
||||
"apps/waterfall-wiki/public/skills/street-people/sample.png": {
|
||||
"apps/dashboard/public/skills/street-people/sample.png": {
|
||||
"id": "c1d98b87-a103-5cf6-829a-3f46321db72f",
|
||||
"key": "repository-media/sha256/2db53d7593010e6c48dab8f212bc3004b02fc278944ea1686f74d6178a700b88.png",
|
||||
"sha256": "2db53d7593010e6c48dab8f212bc3004b02fc278944ea1686f74d6178a700b88",
|
||||
@@ -44,7 +44,7 @@
|
||||
"width": 1024,
|
||||
"height": 1536
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/original/01-florist.png": {
|
||||
"apps/dashboard/seed/characters/original/01-florist.png": {
|
||||
"id": "c1d98b87-a103-5cf6-829a-3f46321db72f",
|
||||
"key": "repository-media/sha256/2db53d7593010e6c48dab8f212bc3004b02fc278944ea1686f74d6178a700b88.png",
|
||||
"sha256": "2db53d7593010e6c48dab8f212bc3004b02fc278944ea1686f74d6178a700b88",
|
||||
@@ -53,7 +53,7 @@
|
||||
"width": 1024,
|
||||
"height": 1536
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/original/02-reader.png": {
|
||||
"apps/dashboard/seed/characters/original/02-reader.png": {
|
||||
"id": "85c30364-9644-5d4a-9e83-96e982860bbe",
|
||||
"key": "repository-media/sha256/669c85e689e7e88f8eff9f4bdaf9b4b7816df88c1956e817d82f8fd39bcdf278.png",
|
||||
"sha256": "669c85e689e7e88f8eff9f4bdaf9b4b7816df88c1956e817d82f8fd39bcdf278",
|
||||
@@ -62,7 +62,7 @@
|
||||
"width": 1024,
|
||||
"height": 1536
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/original/03-cyclist.png": {
|
||||
"apps/dashboard/seed/characters/original/03-cyclist.png": {
|
||||
"id": "0200f54c-a0a9-50bd-bf04-6512059da0c5",
|
||||
"key": "repository-media/sha256/6dedf97b96fafe9f70a16822b0fe9156a073d9149bad5eeab1450323b67ce575.png",
|
||||
"sha256": "6dedf97b96fafe9f70a16822b0fe9156a073d9149bad5eeab1450323b67ce575",
|
||||
@@ -71,7 +71,7 @@
|
||||
"width": 1347,
|
||||
"height": 1167
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/original/04-coffee.png": {
|
||||
"apps/dashboard/seed/characters/original/04-coffee.png": {
|
||||
"id": "ebd511bf-559e-588d-886a-095b785216bd",
|
||||
"key": "repository-media/sha256/ab337b3a2e5c05406c03aa4a69018191624f18de70081accf740e79993a1835e.png",
|
||||
"sha256": "ab337b3a2e5c05406c03aa4a69018191624f18de70081accf740e79993a1835e",
|
||||
@@ -80,7 +80,7 @@
|
||||
"width": 1024,
|
||||
"height": 1536
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/01-basket.png": {
|
||||
"apps/dashboard/seed/characters/reference/01-basket.png": {
|
||||
"id": "48ff5ce0-2763-5fa5-86cb-86c654bf4e24",
|
||||
"key": "repository-media/sha256/62e6785bb373676cb5f24d44b9fa71a8ec8f413a4d518419aa29006ae62b7af8.png",
|
||||
"sha256": "62e6785bb373676cb5f24d44b9fa71a8ec8f413a4d518419aa29006ae62b7af8",
|
||||
@@ -89,7 +89,7 @@
|
||||
"width": 1106,
|
||||
"height": 1422
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/02-phone.png": {
|
||||
"apps/dashboard/seed/characters/reference/02-phone.png": {
|
||||
"id": "d21179b3-29aa-57c4-bc50-79f2459b5ee8",
|
||||
"key": "repository-media/sha256/f27657c347e73786cc84df290f689f11284bb57822415a651c4c7567c15d034e.png",
|
||||
"sha256": "f27657c347e73786cc84df290f689f11284bb57822415a651c4c7567c15d034e",
|
||||
@@ -98,7 +98,7 @@
|
||||
"width": 1161,
|
||||
"height": 1354
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/03-coffee.png": {
|
||||
"apps/dashboard/seed/characters/reference/03-coffee.png": {
|
||||
"id": "10e3d787-2575-52f5-8403-cb62072cd1f0",
|
||||
"key": "repository-media/sha256/cd53d3d1771c014cb4c5c1b7ed4d1846f72ec996d48a889514268461ed9c5f7c.png",
|
||||
"sha256": "cd53d3d1771c014cb4c5c1b7ed4d1846f72ec996d48a889514268461ed9c5f7c",
|
||||
@@ -107,7 +107,7 @@
|
||||
"width": 1191,
|
||||
"height": 1320
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/04-red-coat.png": {
|
||||
"apps/dashboard/seed/characters/reference/04-red-coat.png": {
|
||||
"id": "3fe8aac2-160f-5069-9b67-6615732fe54f",
|
||||
"key": "repository-media/sha256/39249450ccd7f0a207f191742e2c27117f8404d35dfbe092ccdaa54ae59e218d.png",
|
||||
"sha256": "39249450ccd7f0a207f191742e2c27117f8404d35dfbe092ccdaa54ae59e218d",
|
||||
@@ -116,7 +116,7 @@
|
||||
"width": 1024,
|
||||
"height": 1536
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/05-blue-dogs.png": {
|
||||
"apps/dashboard/seed/characters/reference/05-blue-dogs.png": {
|
||||
"id": "8db94ff7-cee3-526d-a594-282a757317cf",
|
||||
"key": "repository-media/sha256/d6c7ce1c8e3375f8aaa40012a91d11925113a7776b0fe383c74c734d91edfc37.png",
|
||||
"sha256": "d6c7ce1c8e3375f8aaa40012a91d11925113a7776b0fe383c74c734d91edfc37",
|
||||
@@ -125,7 +125,7 @@
|
||||
"width": 1536,
|
||||
"height": 1024
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/06-dark-dog.png": {
|
||||
"apps/dashboard/seed/characters/reference/06-dark-dog.png": {
|
||||
"id": "a3a00127-0f67-5c8f-9ae9-259e9c390be0",
|
||||
"key": "repository-media/sha256/9e19ac9d04c30e5d0a552c7d59a8365ffca8be5df1b7f9ec12fb35bef7aa444b.png",
|
||||
"sha256": "9e19ac9d04c30e5d0a552c7d59a8365ffca8be5df1b7f9ec12fb35bef7aa444b",
|
||||
@@ -134,7 +134,7 @@
|
||||
"width": 1372,
|
||||
"height": 1146
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/07-pockets.png": {
|
||||
"apps/dashboard/seed/characters/reference/07-pockets.png": {
|
||||
"id": "468c8637-c841-5819-8afb-24d246b77821",
|
||||
"key": "repository-media/sha256/158c1f3a492a7a3223bf4ed56d3750d192c696a054dc66d2c3ae65ce9e0a8dad.png",
|
||||
"sha256": "158c1f3a492a7a3223bf4ed56d3750d192c696a054dc66d2c3ae65ce9e0a8dad",
|
||||
@@ -143,7 +143,7 @@
|
||||
"width": 1024,
|
||||
"height": 1536
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/08-blue-board.png": {
|
||||
"apps/dashboard/seed/characters/reference/08-blue-board.png": {
|
||||
"id": "1731746a-2a59-5b8b-b91c-8f862567f7ef",
|
||||
"key": "repository-media/sha256/fa8f0dd989cb8133ad5565c19dc9f798a9c2bd216e3c90342d1a4e45329e8cd9.png",
|
||||
"sha256": "fa8f0dd989cb8133ad5565c19dc9f798a9c2bd216e3c90342d1a4e45329e8cd9",
|
||||
@@ -152,7 +152,7 @@
|
||||
"width": 1130,
|
||||
"height": 1392
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/09-scarf-dog.png": {
|
||||
"apps/dashboard/seed/characters/reference/09-scarf-dog.png": {
|
||||
"id": "7e99a693-1c95-5544-af4c-ae2ce12824c8",
|
||||
"key": "repository-media/sha256/8ee2ea052e4985a28eef1ba415190bdbea70e4ca52eb3bdcd5206210a4285bc7.png",
|
||||
"sha256": "8ee2ea052e4985a28eef1ba415190bdbea70e4ca52eb3bdcd5206210a4285bc7",
|
||||
@@ -161,7 +161,7 @@
|
||||
"width": 1220,
|
||||
"height": 1289
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/10-elder.png": {
|
||||
"apps/dashboard/seed/characters/reference/10-elder.png": {
|
||||
"id": "be125dda-5a86-5a7b-9498-a8dba66df31e",
|
||||
"key": "repository-media/sha256/45bfe1e35f88e52adfc7f97b0834a5dbeea7dbcf283ebdabb9adf4236eb93eab.png",
|
||||
"sha256": "45bfe1e35f88e52adfc7f97b0834a5dbeea7dbcf283ebdabb9adf4236eb93eab",
|
||||
@@ -170,7 +170,7 @@
|
||||
"width": 1195,
|
||||
"height": 1316
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/11-long-coat.png": {
|
||||
"apps/dashboard/seed/characters/reference/11-long-coat.png": {
|
||||
"id": "5bb056a0-735e-5f24-bfd1-1036ebb208e8",
|
||||
"key": "repository-media/sha256/76f00ba08e5b8e7caf4fe2801368cfc5f7cf3c445ae2a80b496a4f431272d053.png",
|
||||
"sha256": "76f00ba08e5b8e7caf4fe2801368cfc5f7cf3c445ae2a80b496a4f431272d053",
|
||||
@@ -179,7 +179,7 @@
|
||||
"width": 1148,
|
||||
"height": 1371
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/12-trolley.png": {
|
||||
"apps/dashboard/seed/characters/reference/12-trolley.png": {
|
||||
"id": "11da89bb-daa9-59b3-b22f-997f1fba46fe",
|
||||
"key": "repository-media/sha256/8235f95071abb54f0618b61d66c20e63633e6d929a8ec4d43d4cef121226e078.png",
|
||||
"sha256": "8235f95071abb54f0618b61d66c20e63633e6d929a8ec4d43d4cef121226e078",
|
||||
@@ -188,7 +188,7 @@
|
||||
"width": 1241,
|
||||
"height": 1267
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/13-bucket.png": {
|
||||
"apps/dashboard/seed/characters/reference/13-bucket.png": {
|
||||
"id": "3f203043-3d69-52c4-91fa-c898daf61eb6",
|
||||
"key": "repository-media/sha256/2a4b34afdb45d439f2588095abc89378f7772fd842bdc38518e4a03756b15b82.png",
|
||||
"sha256": "2a4b34afdb45d439f2588095abc89378f7772fd842bdc38518e4a03756b15b82",
|
||||
@@ -197,7 +197,7 @@
|
||||
"width": 1140,
|
||||
"height": 1380
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/14-curly.png": {
|
||||
"apps/dashboard/seed/characters/reference/14-curly.png": {
|
||||
"id": "5cfb45ab-542f-5bd8-89f5-549072e4caf3",
|
||||
"key": "repository-media/sha256/f10faf8145e56830c9a179b6a52cfdd688de3f460fa33ded82fa2b3fe0678b3e.png",
|
||||
"sha256": "f10faf8145e56830c9a179b6a52cfdd688de3f460fa33ded82fa2b3fe0678b3e",
|
||||
@@ -206,7 +206,7 @@
|
||||
"width": 1141,
|
||||
"height": 1379
|
||||
},
|
||||
"apps/waterfall-wiki/seed/characters/reference/15-red-walk.png": {
|
||||
"apps/dashboard/seed/characters/reference/15-red-walk.png": {
|
||||
"id": "41e92c94-adca-54b4-9fe9-db192a266937",
|
||||
"key": "repository-media/sha256/238eae73ac2faf53e6ac02591b00759115378233fbcbfdcf8e67ce93a804d26e.png",
|
||||
"sha256": "238eae73ac2faf53e6ac02591b00759115378233fbcbfdcf8e67ce93a804d26e",
|
||||
@@ -14,6 +14,6 @@ const pending=new Map<string,Promise<Buffer>>();const cache=join(tmpdir(),'promp
|
||||
export async function compressPreview(input:Buffer,width:number){const max=width<=480?100_000:220_000;let size=width,quality=72;let output:Buffer;do{output=await sharp(input,{limitInputPixels:40_000_000,animated:false}).rotate().resize({width:size,withoutEnlargement:true}).webp({quality,effort:3}).toBuffer();if(output.length<=max)return output;size=Math.max(240,Math.floor(size*.8));quality=Math.max(30,quality-10);}while(size>240||quality>30);return sharp(input,{limitInputPixels:40_000_000}).rotate().resize({width:240,withoutEnlargement:true}).webp({quality:30}).toBuffer();}
|
||||
export async function cachedPreview(identity:string,width:number,load:()=>Promise<Buffer>,quality=false){const key=createHash('sha256').update(identity+':'+width+(quality?':export-v1':':webp-v1')).digest('hex');const file=join(cache,key+'.webp');try{return {bytes:await readFile(file),etag:'"'+key+'"'};}catch{/* cache miss */}let task=pending.get(key);if(!task){task=limited(async()=>{await mkdir(cache,{recursive:true});const input=await load();const bytes=quality?await sharp(input,{limitInputPixels:40_000_000,animated:false}).rotate().resize({width,withoutEnlargement:true}).webp({quality:95,effort:4}).toBuffer():await compressPreview(input,width);const temp=file+'.'+process.pid+'.tmp';await writeFile(temp,bytes);await rename(temp,file);if(++writes%32===0){const files=await readdir(cache);if(files.length>1024){const dates=await Promise.all(files.map(async name=>({name,time:(await stat(join(cache,name)).catch(()=>({mtimeMs:0}))).mtimeMs})));await Promise.all(dates.sort((a,b)=>a.time-b.time).slice(0,files.length-1024).map(f=>unlink(join(cache,f.name)).catch(()=>{})));}}return bytes;});pending.set(key,task);}try{return {bytes:await task,etag:'"'+key+'"'};}finally{pending.delete(key);}}
|
||||
export async function loadStaticPreview(source:string){
|
||||
const asset=(managedMedia as Record<string,{key:string;sha256:string;bytes:number}>)["apps/waterfall-wiki/public"+source];
|
||||
const asset=(managedMedia as Record<string,{key:string;sha256:string;bytes:number}>)["apps/dashboard/public"+source];
|
||||
if(asset){return {identity:asset.key,load:async()=>{const object=await getObject(asset.key);if(asset.bytes>64*1024*1024)throw Error("Image too large");const bytes=Buffer.from(await object.Body!.transformToByteArray());if(bytes.length!==asset.bytes||createHash("sha256").update(bytes).digest("hex")!==asset.sha256)throw Error("Stored image checksum mismatch");return bytes;}};}
|
||||
const root=await realpath(join(process.cwd(),'public'));const file=await realpath(staticPreviewPath(source,root));if(!file.startsWith(root+sep))throw Error('Invalid source');const info=await stat(file);if(info.size>64*1024*1024)throw Error('Image too large');return {identity:file+':'+info.mtimeMs,load:()=>readFile(file)};}
|
||||
+1
-1
@@ -13,7 +13,7 @@ function stableId(name: string) {
|
||||
}
|
||||
// Explicit editorial publication, never part of the generic storage bootstrap.
|
||||
// Re-running updates these three authored posts without duplicating posts or media.
|
||||
export async function publishLearningSeries(asset=managedMedia["apps/waterfall-wiki/public/learning-skills/cover.png"]) {
|
||||
export async function publishLearningSeries(asset=managedMedia["apps/dashboard/public/learning-skills/cover.png"]) {
|
||||
const object=await getObject(asset.key);
|
||||
const cover=Buffer.from(await object.Body!.transformToByteArray());
|
||||
const digest=createHash("sha256").update(cover).digest("hex");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user