diff --git a/apps/web/package.json b/apps/web/package.json index fef6e167..a2f63d8e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,7 +10,6 @@ "scripts": { "analyze": "analyzer=1 vite build", "build": "tsx scripts/build.ts", - "build:manifest": "tsx src/core/cli.ts", "dev": "tsx scripts/dev.ts", "format": "prettier --write \"src/**/*.ts\" ", "lint": "eslint --fix", @@ -18,8 +17,6 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@aws-sdk/client-s3": "3.826.0", - "@aws-sdk/s3-request-presigner": "3.826.0", "@essentials/request-timeout": "1.3.0", "@headlessui/react": "2.2.4", "@photo-gallery/data": "workspace:*", @@ -41,11 +38,8 @@ "consola": "3.4.2", "dotenv": "16.5.0", "es-toolkit": "1.39.3", - "exif-reader": "2.0.2", "foxact": "0.2.46", "fuji-recipes": "1.0.2", - "heic-convert": "2.1.0", - "heic-to": "1.1.13", "immer": "10.1.1", "jotai": "2.12.5", "masonic": "4.1.0", @@ -62,7 +56,6 @@ "react-scan": "0.3.4", "react-use-measure": "2.1.7", "react-zoom-pan-pinch": "3.7.0", - "sharp": "0.34.2", "sonner": "2.0.5", "swiper": "11.2.8", "tailwind-merge": "3.3.0", @@ -97,4 +90,4 @@ "tailwindcss-uikit-colors": "1.0.0-alpha.1", "vite-plugin-html": "3.2.2" } -} +} \ No newline at end of file diff --git a/apps/web/scripts/precheck.ts b/apps/web/scripts/precheck.ts index 30b15a4a..bd49c960 100644 --- a/apps/web/scripts/precheck.ts +++ b/apps/web/scripts/precheck.ts @@ -29,7 +29,10 @@ export const precheck = async () => { if (builderConfig.repo.enable) { await pullAndLinkRemoteRepo() } else { - await $({ cwd: workdir, stdio: 'inherit' })`pnpm build:manifest` + await $({ + cwd: workdir, + stdio: 'inherit', + })`pnpm --filter @photo-gallery/builder cli` } } } diff --git a/builder.config.ts b/builder.config.ts index f874e99d..09f9aaeb 100644 --- a/builder.config.ts +++ b/builder.config.ts @@ -3,10 +3,10 @@ import { existsSync, readFileSync } from 'node:fs' import os from 'node:os' import { inspect } from 'node:util' +import type { StorageConfig } from '@photo-gallery/builder' import consola from 'consola' import { merge } from 'es-toolkit' -import type { StorageConfig } from './apps/web/src/core/storage/interfaces.js' import { env } from './env.js' export interface BuilderConfig { diff --git a/config.example.json b/config.example.json index 13fe2b6a..b1356f73 100644 --- a/config.example.json +++ b/config.example.json @@ -4,10 +4,6 @@ "description": "Capturing beautiful moments in life, documenting daily warmth and emotions through my lens.", "url": "https://gallery.innei.in", "accentColor": "#007bff", - "ogImage": { - "width": 1200, - "height": 630 - }, "author": { "name": "Photo Gallery", "url": "https://innei.in/" diff --git a/package.json b/package.json index 38f29511..a2ba3bc5 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "scripts": { "build": "pnpm --filter @photo-gallery/web build", - "build:manifest": "pnpm --filter @photo-gallery/web build:manifest", + "build:manifest": "pnpm --filter @photo-gallery/builder cli", "build:update-remote-repo": "sh scripts/build-update-remote-repo.sh", "dev": "pnpm --filter @photo-gallery/web dev", "extract:font": "tsx scripts/extract-font-glyphs.ts", @@ -20,6 +20,7 @@ "prepare": "simple-git-hooks" }, "dependencies": { + "@photo-gallery/builder": "workspace:*", "@t3-oss/env-core": "0.13.8", "dotenv": "16.5.0", "es-toolkit": "1.39.3", diff --git a/packages/builder/.env b/packages/builder/.env new file mode 120000 index 00000000..c7360fb8 --- /dev/null +++ b/packages/builder/.env @@ -0,0 +1 @@ +../../.env \ No newline at end of file diff --git a/apps/web/src/core/README.md b/packages/builder/README.md similarity index 99% rename from apps/web/src/core/README.md rename to packages/builder/README.md index 52c21d07..6329f2e5 100644 --- a/apps/web/src/core/README.md +++ b/packages/builder/README.md @@ -1,4 +1,4 @@ -# Photo Gallery Core 架构 +# Photo Gallery Builder 这是照片库构建系统的核心模块,采用模块化设计,将不同功能分离到各自的模块中。 diff --git a/packages/builder/package.json b/packages/builder/package.json new file mode 100644 index 00000000..5e5388aa --- /dev/null +++ b/packages/builder/package.json @@ -0,0 +1,23 @@ +{ + "name": "@photo-gallery/builder", + "type": "module", + "version": "0.0.1", + "private": true, + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "cli": "tsx src/cli.ts" + }, + "dependencies": { + "@aws-sdk/client-s3": "3.826.0", + "@aws-sdk/s3-request-presigner": "3.826.0", + "blurhash": "2.0.5", + "execa": "9.6.0", + "exif-reader": "2.0.2", + "fuji-recipes": "1.0.2", + "heic-convert": "2.1.0", + "heic-to": "1.1.13", + "sharp": "0.34.2" + } +} \ No newline at end of file diff --git a/apps/web/src/core/builder/builder.ts b/packages/builder/src/builder/builder.ts similarity index 100% rename from apps/web/src/core/builder/builder.ts rename to packages/builder/src/builder/builder.ts diff --git a/apps/web/src/core/builder/index.ts b/packages/builder/src/builder/index.ts similarity index 100% rename from apps/web/src/core/builder/index.ts rename to packages/builder/src/builder/index.ts diff --git a/apps/web/src/core/cli.ts b/packages/builder/src/cli.ts similarity index 96% rename from apps/web/src/core/cli.ts rename to packages/builder/src/cli.ts index 39edfd46..df05a5a0 100644 --- a/apps/web/src/core/cli.ts +++ b/packages/builder/src/cli.ts @@ -2,16 +2,15 @@ import cluster from 'node:cluster' import { existsSync } from 'node:fs' import path from 'node:path' import process from 'node:process' -import { fileURLToPath } from 'node:url' import { builderConfig } from '@builder' import { $ } from 'execa' import { defaultBuilder } from './builder/index.js' import { logger } from './logger/index.js' +import { workdir } from './path.js' import { runAsWorker } from './runAsWorker.js' -const __dirname = path.dirname(fileURLToPath(import.meta.url)) async function main() { // 检查是否作为 cluster worker 运行 if ( @@ -26,7 +25,7 @@ async function main() { // 如果配置了远程仓库,则使用远程仓库 if (builderConfig.repo.enable) { // 拉取远程仓库 - const workdir = path.resolve(__dirname, '..', '..') + const hasExist = existsSync(path.resolve(workdir, 'assets-git')) if (!hasExist) { await $({ @@ -128,7 +127,6 @@ async function main() { } } logger.main.info(` 默认并发数:${config.options.defaultConcurrency}`) - logger.main.info(` 最大照片数:${config.options.maxPhotos}`) logger.main.info( ` Live Photo 检测:${config.options.enableLivePhotoDetection ? '启用' : '禁用'}`, ) diff --git a/apps/web/src/core/constants/index.ts b/packages/builder/src/constants/index.ts similarity index 100% rename from apps/web/src/core/constants/index.ts rename to packages/builder/src/constants/index.ts diff --git a/apps/web/src/core/image/blurhash.ts b/packages/builder/src/image/blurhash.ts similarity index 100% rename from apps/web/src/core/image/blurhash.ts rename to packages/builder/src/image/blurhash.ts diff --git a/apps/web/src/core/image/exif.ts b/packages/builder/src/image/exif.ts similarity index 100% rename from apps/web/src/core/image/exif.ts rename to packages/builder/src/image/exif.ts diff --git a/apps/web/src/core/image/processor.ts b/packages/builder/src/image/processor.ts similarity index 100% rename from apps/web/src/core/image/processor.ts rename to packages/builder/src/image/processor.ts diff --git a/apps/web/src/core/image/thumbnail.ts b/packages/builder/src/image/thumbnail.ts similarity index 95% rename from apps/web/src/core/image/thumbnail.ts rename to packages/builder/src/image/thumbnail.ts index e5f96bdd..f82d227b 100644 --- a/apps/web/src/core/image/thumbnail.ts +++ b/packages/builder/src/image/thumbnail.ts @@ -4,6 +4,8 @@ import { fileURLToPath } from 'node:url' import sharp from 'sharp' +import { workdir } from '~/path.js' + import type { Logger } from '../logger/index.js' import type { ThumbnailResult } from '../types/photo.js' import { generateBlurhash } from './blurhash.js' @@ -15,8 +17,8 @@ const __dirname = path.dirname(__filename) export async function thumbnailExists(photoId: string): Promise { try { const thumbnailPath = path.join( - __dirname, - '../../../public/thumbnails', + workdir, + 'public/thumbnails', `${photoId}.webp`, ) await fs.access(thumbnailPath) @@ -42,7 +44,7 @@ export async function generateThumbnailAndBlurhash( const blurhashLog = workerLogger?.blurhash try { - const thumbnailDir = path.join(__dirname, '../../../public/thumbnails') + const thumbnailDir = path.join(workdir, 'public/thumbnails') await fs.mkdir(thumbnailDir, { recursive: true }) const thumbnailPath = path.join(thumbnailDir, `${photoId}.webp`) diff --git a/apps/web/src/core/index.ts b/packages/builder/src/index.ts similarity index 95% rename from apps/web/src/core/index.ts rename to packages/builder/src/index.ts index 0d03063c..6af8c1d6 100644 --- a/apps/web/src/core/index.ts +++ b/packages/builder/src/index.ts @@ -4,7 +4,7 @@ export { defaultBuilder, PhotoGalleryBuilder, } from './builder/index.js' - +export type { StorageConfig } from './storage/interfaces.js' // 日志系统 export { type Logger, logger, type WorkerLogger } from './logger/index.js' diff --git a/apps/web/src/core/logger/index.ts b/packages/builder/src/logger/index.ts similarity index 100% rename from apps/web/src/core/logger/index.ts rename to packages/builder/src/logger/index.ts diff --git a/apps/web/src/core/manifest/manager.ts b/packages/builder/src/manifest/manager.ts similarity index 85% rename from apps/web/src/core/manifest/manager.ts rename to packages/builder/src/manifest/manager.ts index 0d915db6..40ffdd47 100644 --- a/apps/web/src/core/manifest/manager.ts +++ b/packages/builder/src/manifest/manager.ts @@ -1,22 +1,17 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { fileURLToPath } from 'node:url' import type { _Object } from '@aws-sdk/client-s3' +import { workdir } from '~/path.js' + import type { Logger } from '../logger/index.js' import type { PhotoManifestItem } from '../types/photo.js' -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) +const manifestPath = path.join(workdir, 'src/data/photos-manifest.json') -// 读取现有的 manifest export async function loadExistingManifest(): Promise { try { - const manifestPath = path.join( - __dirname, - '../../../src/data/photos-manifest.json', - ) const manifestContent = await fs.readFile(manifestPath, 'utf-8') return JSON.parse(manifestContent) as PhotoManifestItem[] } catch { @@ -43,11 +38,6 @@ export async function saveManifest( manifest: PhotoManifestItem[], fsLogger?: Logger['fs'], ): Promise { - const manifestPath = path.join( - __dirname, - '../../../src/data/photos-manifest.json', - ) - // 按日期排序(最新的在前) const sortedManifest = [...manifest].sort( (a, b) => new Date(b.dateTaken).getTime() - new Date(a.dateTaken).getTime(), @@ -82,8 +72,8 @@ export async function handleDeletedPhotos( // 删除对应的缩略图文件 try { const thumbnailPath = path.join( - __dirname, - '../../../public/thumbnails', + workdir, + 'public/thumbnails', `${existingItem.id}.webp`, ) await fs.unlink(thumbnailPath) diff --git a/packages/builder/src/path.ts b/packages/builder/src/path.ts new file mode 100644 index 00000000..9cbd0111 --- /dev/null +++ b/packages/builder/src/path.ts @@ -0,0 +1,6 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +export const workdir = path.resolve(__dirname, '../../../apps/web') diff --git a/apps/web/src/core/photo/info-extractor.ts b/packages/builder/src/photo/info-extractor.ts similarity index 100% rename from apps/web/src/core/photo/info-extractor.ts rename to packages/builder/src/photo/info-extractor.ts diff --git a/apps/web/src/core/photo/processor.ts b/packages/builder/src/photo/processor.ts similarity index 99% rename from apps/web/src/core/photo/processor.ts rename to packages/builder/src/photo/processor.ts index 57dd3746..5a79362d 100644 --- a/apps/web/src/core/photo/processor.ts +++ b/packages/builder/src/photo/processor.ts @@ -4,6 +4,8 @@ import type { _Object } from '@aws-sdk/client-s3' import type { Exif } from 'exif-reader' import sharp from 'sharp' +import { workdir } from '~/path.js' + import { HEIC_FORMATS } from '../constants/index.js' import { extractExifData } from '../image/exif.js' import { @@ -142,7 +144,7 @@ export async function processPhoto( try { const fs = await import('node:fs/promises') const thumbnailPath = path.join( - process.cwd(), + workdir, 'public/thumbnails', `${photoId}.webp`, ) diff --git a/apps/web/src/core/runAsWorker.ts b/packages/builder/src/runAsWorker.ts similarity index 100% rename from apps/web/src/core/runAsWorker.ts rename to packages/builder/src/runAsWorker.ts diff --git a/apps/web/src/core/s3/client.ts b/packages/builder/src/s3/client.ts similarity index 100% rename from apps/web/src/core/s3/client.ts rename to packages/builder/src/s3/client.ts diff --git a/apps/web/src/core/s3/operations.ts b/packages/builder/src/s3/operations.ts similarity index 100% rename from apps/web/src/core/s3/operations.ts rename to packages/builder/src/s3/operations.ts diff --git a/apps/web/src/core/storage/adapters.ts b/packages/builder/src/storage/adapters.ts similarity index 100% rename from apps/web/src/core/storage/adapters.ts rename to packages/builder/src/storage/adapters.ts diff --git a/apps/web/src/core/storage/factory.ts b/packages/builder/src/storage/factory.ts similarity index 100% rename from apps/web/src/core/storage/factory.ts rename to packages/builder/src/storage/factory.ts diff --git a/apps/web/src/core/storage/index.ts b/packages/builder/src/storage/index.ts similarity index 100% rename from apps/web/src/core/storage/index.ts rename to packages/builder/src/storage/index.ts diff --git a/apps/web/src/core/storage/interfaces.ts b/packages/builder/src/storage/interfaces.ts similarity index 99% rename from apps/web/src/core/storage/interfaces.ts rename to packages/builder/src/storage/interfaces.ts index 1b6fd886..5c287a09 100644 --- a/apps/web/src/core/storage/interfaces.ts +++ b/packages/builder/src/storage/interfaces.ts @@ -1,4 +1,3 @@ - import type { Logger } from '../logger/index.js' // 存储对象的通用接口 diff --git a/apps/web/src/core/storage/manager.ts b/packages/builder/src/storage/manager.ts similarity index 100% rename from apps/web/src/core/storage/manager.ts rename to packages/builder/src/storage/manager.ts diff --git a/apps/web/src/core/storage/providers/README.md b/packages/builder/src/storage/providers/README.md similarity index 100% rename from apps/web/src/core/storage/providers/README.md rename to packages/builder/src/storage/providers/README.md diff --git a/apps/web/src/core/storage/providers/github-provider.ts b/packages/builder/src/storage/providers/github-provider.ts similarity index 100% rename from apps/web/src/core/storage/providers/github-provider.ts rename to packages/builder/src/storage/providers/github-provider.ts diff --git a/apps/web/src/core/storage/providers/s3-provider.ts b/packages/builder/src/storage/providers/s3-provider.ts similarity index 100% rename from apps/web/src/core/storage/providers/s3-provider.ts rename to packages/builder/src/storage/providers/s3-provider.ts diff --git a/apps/web/src/core/types/photo.ts b/packages/builder/src/types/photo.ts similarity index 100% rename from apps/web/src/core/types/photo.ts rename to packages/builder/src/types/photo.ts diff --git a/apps/web/src/core/worker/cluster-pool.ts b/packages/builder/src/worker/cluster-pool.ts similarity index 100% rename from apps/web/src/core/worker/cluster-pool.ts rename to packages/builder/src/worker/cluster-pool.ts diff --git a/apps/web/src/core/worker/pool.ts b/packages/builder/src/worker/pool.ts similarity index 100% rename from apps/web/src/core/worker/pool.ts rename to packages/builder/src/worker/pool.ts diff --git a/packages/builder/tsconfig.json b/packages/builder/tsconfig.json new file mode 100644 index 00000000..81b7e4df --- /dev/null +++ b/packages/builder/tsconfig.json @@ -0,0 +1,42 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "skipDefaultLibCheck": true, + "noImplicitAny": false, + "noEmit": true, + "jsx": "preserve", + "paths": { + "~/*": [ + "./src/*" + ], + "@pkg": [ + "./package.json" + ], + "@builder": [ + "../../builder.config.ts" + ], + "@env": [ + "../../env.ts" + ] + }, + }, + "include": [ + "./src/**/*", + "./scripts/**/*" + ] +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bfc2ceb2..d30896b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@photo-gallery/builder': + specifier: workspace:* + version: link:packages/builder '@t3-oss/env-core': specifier: 0.13.8 version: 0.13.8(typescript@5.8.3)(zod@3.25.57) @@ -106,12 +109,6 @@ importers: apps/web: dependencies: - '@aws-sdk/client-s3': - specifier: 3.826.0 - version: 3.826.0 - '@aws-sdk/s3-request-presigner': - specifier: 3.826.0 - version: 3.826.0 '@essentials/request-timeout': specifier: 1.3.0 version: 1.3.0 @@ -175,21 +172,12 @@ importers: es-toolkit: specifier: 1.39.3 version: 1.39.3 - exif-reader: - specifier: 2.0.2 - version: 2.0.2 foxact: specifier: 0.2.46 version: 0.2.46(react@19.1.0) fuji-recipes: specifier: 1.0.2 version: 1.0.2 - heic-convert: - specifier: 2.1.0 - version: 2.1.0 - heic-to: - specifier: 1.1.13 - version: 1.1.13 immer: specifier: 10.1.1 version: 10.1.1 @@ -238,9 +226,6 @@ importers: react-zoom-pan-pinch: specifier: 3.7.0 version: 3.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - sharp: - specifier: 0.34.2 - version: 0.34.2 sonner: specifier: 2.0.5 version: 2.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -336,6 +321,36 @@ importers: specifier: 3.2.2 version: 3.2.2(vite@6.3.5(@types/node@22.15.31)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.41.0)(tsx@4.19.4)(yaml@2.8.0)) + packages/builder: + dependencies: + '@aws-sdk/client-s3': + specifier: 3.826.0 + version: 3.826.0 + '@aws-sdk/s3-request-presigner': + specifier: 3.826.0 + version: 3.826.0 + blurhash: + specifier: 2.0.5 + version: 2.0.5 + execa: + specifier: 9.6.0 + version: 9.6.0 + exif-reader: + specifier: 2.0.2 + version: 2.0.2 + fuji-recipes: + specifier: 1.0.2 + version: 1.0.2 + heic-convert: + specifier: 2.1.0 + version: 2.1.0 + heic-to: + specifier: 1.1.13 + version: 1.1.13 + sharp: + specifier: 0.34.2 + version: 0.34.2 + packages/data: dependencies: exif-reader: