diff --git a/README.md b/README.md index ab6df1af..516765b4 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,6 @@ photo-gallery-site/ │ │ │ └── worker/ # 并发处理 │ │ ├── modules/ # 功能模块 │ │ ├── pages/ # 页面组件 -│ │ └── data/ # 数据文件 │ ├── public/ # 静态资源 │ └── scripts/ # 构建脚本 ├── packages/webgl-viewer/ # WebGL 图像查看器 diff --git a/apps/ssr/package.json b/apps/ssr/package.json index 6eb248b8..62538d9b 100644 --- a/apps/ssr/package.json +++ b/apps/ssr/package.json @@ -18,6 +18,7 @@ "start": "next start" }, "dependencies": { + "@photo-gallery/data": "workspace:*", "linkedom": "0.18.11", "react": "19.1.0", "react-dom": "19.1.0" diff --git a/apps/ssr/src/app/[photoId]/route.ts b/apps/ssr/src/app/[photoId]/route.ts index f3a7c22b..9b3d83aa 100644 --- a/apps/ssr/src/app/[photoId]/route.ts +++ b/apps/ssr/src/app/[photoId]/route.ts @@ -1,11 +1,16 @@ +import { photoLoader } from '@photo-gallery/data' +import type { PhotoManifest } from '@photo-gallery/data/types' import { DOMParser } from 'linkedom' -import type { HTMLDocument } from 'linkedom/types/html/document' import type { NextRequest } from 'next/server' -import { photoLoader } from '../../../../web/src/data/photos' -import type { PhotoManifest } from '../../../../web/src/types/photo' import { getIndexHtml } from '../../constants' +type HtmlElement = ReturnType +type OnlyHTMLDocument = HtmlElement extends infer T + ? T extends { [key: string]: any; head: any } + ? T + : never + : never export const runtime = 'edge' export const GET = async ( request: NextRequest, @@ -54,7 +59,7 @@ export const GET = async ( } const createAndInsertOpenGraphMeta = ( - document: HTMLDocument, + document: OnlyHTMLDocument, photo: PhotoManifest, request: NextRequest, ) => { @@ -68,7 +73,7 @@ const createAndInsertOpenGraphMeta = ( } for (const [property, content] of Object.entries(ogTags)) { - const ogMeta = document.createElement('meta') + const ogMeta = document.createElement('meta', {}) ogMeta.setAttribute('property', property) ogMeta.setAttribute('content', content) document.head.append(ogMeta as unknown as Node) @@ -83,7 +88,7 @@ const createAndInsertOpenGraphMeta = ( } for (const [name, content] of Object.entries(twitterTags)) { - const twitterMeta = document.createElement('meta') + const twitterMeta = document.createElement('meta', {}) twitterMeta.setAttribute('name', name) twitterMeta.setAttribute('content', content) document.head.append(twitterMeta as unknown as Node) diff --git a/apps/ssr/src/app/og/[photoId]/route.tsx b/apps/ssr/src/app/og/[photoId]/route.tsx index 8e6116ef..785e5898 100644 --- a/apps/ssr/src/app/og/[photoId]/route.tsx +++ b/apps/ssr/src/app/og/[photoId]/route.tsx @@ -1,8 +1,7 @@ +import { photoLoader } from '@photo-gallery/data' import { ImageResponse } from 'next/og' import type { NextRequest } from 'next/server' -import { photoLoader } from '../../../../../web/src/data/photos' - export const runtime = 'edge' export const GET = async ( diff --git a/apps/ssr/tsconfig.json b/apps/ssr/tsconfig.json index f619795e..d8b9592b 100644 --- a/apps/ssr/tsconfig.json +++ b/apps/ssr/tsconfig.json @@ -13,7 +13,7 @@ "incremental": true, "module": "esnext", "esModuleInterop": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", @@ -32,4 +32,4 @@ "exclude": [ "node_modules" ] -} +} \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index d2c0a8b8..38227b83 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -22,6 +22,7 @@ "@aws-sdk/s3-request-presigner": "3.824.0", "@essentials/request-timeout": "1.3.0", "@headlessui/react": "2.2.4", + "@photo-gallery/data": "workspace:*", "@photo-gallery/webgl-viewer": "workspace:*", "@radix-ui/react-context-menu": "2.2.15", "@radix-ui/react-dropdown-menu": "2.1.15", diff --git a/apps/web/src/hooks/usePhotoViewer.ts b/apps/web/src/hooks/usePhotoViewer.ts index 914da0d6..f3f8062f 100644 --- a/apps/web/src/hooks/usePhotoViewer.ts +++ b/apps/web/src/hooks/usePhotoViewer.ts @@ -1,9 +1,9 @@ +import { photoLoader } from '@photo-gallery/data' import { atom, useAtom, useAtomValue } from 'jotai' import { useCallback, useEffect, useMemo } from 'react' import { useLocation, useNavigate } from 'react-router' import { gallerySettingAtom } from '~/atoms/app' -import { photoLoader } from '~/data/photos' const openAtom = atom(false) const currentIndexAtom = atom(0) diff --git a/apps/web/src/modules/gallery/ActionGroup.tsx b/apps/web/src/modules/gallery/ActionGroup.tsx index c9d24ae2..2e3cc2c6 100644 --- a/apps/web/src/modules/gallery/ActionGroup.tsx +++ b/apps/web/src/modules/gallery/ActionGroup.tsx @@ -1,4 +1,5 @@ import { siteConfig } from '@config' +import { photoLoader } from '@photo-gallery/data' import { repository } from '@pkg' import { useAtom } from 'jotai' import { useRef, useState } from 'react' @@ -13,7 +14,6 @@ import { DropdownMenuTrigger, } from '~/components/ui/dropdown-menu' import { Slider } from '~/components/ui/slider' -import { photoLoader } from '~/data/photos' import { useMobile } from '~/hooks/useMobile' const allTags = photoLoader.getAllTags() diff --git a/apps/web/src/modules/gallery/MasonryHeaderMasonryItem.tsx b/apps/web/src/modules/gallery/MasonryHeaderMasonryItem.tsx index a7887ccd..2cffb854 100644 --- a/apps/web/src/modules/gallery/MasonryHeaderMasonryItem.tsx +++ b/apps/web/src/modules/gallery/MasonryHeaderMasonryItem.tsx @@ -1,6 +1,6 @@ import { siteConfig } from '@config' +import { photoLoader } from '@photo-gallery/data' -import { photoLoader } from '~/data/photos' import { clsxm } from '~/lib/cn' import { ActionGroup } from './ActionGroup' diff --git a/apps/web/src/pages/(debug)/blurhash.tsx b/apps/web/src/pages/(debug)/blurhash.tsx index db9ad4ff..bbaf8bff 100644 --- a/apps/web/src/pages/(debug)/blurhash.tsx +++ b/apps/web/src/pages/(debug)/blurhash.tsx @@ -1,7 +1,6 @@ +import { photoLoader } from '@photo-gallery/data' import { Blurhash } from 'react-blurhash' -import { photoLoader } from '~/data/photos' - export const Component = () => { const photos = photoLoader.getPhotos() diff --git a/apps/web/src/pages/(main)/layout.tsx b/apps/web/src/pages/(main)/layout.tsx index 33f1bec9..15eaca44 100644 --- a/apps/web/src/pages/(main)/layout.tsx +++ b/apps/web/src/pages/(main)/layout.tsx @@ -1,11 +1,11 @@ import { siteConfig } from '@config' +import { photoLoader } from '@photo-gallery/data' import { useAtomValue, useSetAtom } from 'jotai' import { useEffect, useRef } from 'react' import { Outlet, useParams, useSearchParams } from 'react-router' import { gallerySettingAtom } from '~/atoms/app' import { ScrollArea } from '~/components/ui/scroll-areas/ScrollArea' -import { photoLoader } from '~/data/photos' import { usePhotoViewer } from '~/hooks/usePhotoViewer' import { MasonryRoot } from '~/modules/gallery/MasonryRoot' diff --git a/packages/data/package.json b/packages/data/package.json new file mode 100644 index 00000000..a6d318e6 --- /dev/null +++ b/packages/data/package.json @@ -0,0 +1,13 @@ +{ + "name": "@photo-gallery/data", + "type": "module", + "private": true, + "exports": { + ".": "./src/index.ts", + "./types": "./src/types.ts" + }, + "dependencies": { + "exif-reader": "2.0.2", + "fuji-recipes": "1.0.2" + } +} \ No newline at end of file diff --git a/apps/web/src/data/photos.ts b/packages/data/src/index.ts similarity index 94% rename from apps/web/src/data/photos.ts rename to packages/data/src/index.ts index 92cb8c5a..179dad97 100644 --- a/apps/web/src/data/photos.ts +++ b/packages/data/src/index.ts @@ -1,5 +1,5 @@ -import type { PhotoManifest } from '../types/photo' import PhotosManifest from './photos-manifest.json' +import type { PhotoManifest } from './types' class PhotoLoader { private photos: PhotoManifest[] = [] diff --git a/packages/data/src/photos-manifest.json b/packages/data/src/photos-manifest.json new file mode 120000 index 00000000..b8e039a7 --- /dev/null +++ b/packages/data/src/photos-manifest.json @@ -0,0 +1 @@ +../../../apps/web/src/data/photos-manifest.json \ No newline at end of file diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts new file mode 100644 index 00000000..40cf5585 --- /dev/null +++ b/packages/data/src/types.ts @@ -0,0 +1,23 @@ +import type { Exif } from 'exif-reader' +import type getRecipe from 'fuji-recipes' + +export interface PhotoManifest { + id: string + title: string + description: string + views: number + tags: string[] + originalUrl: string + thumbnailUrl: string + blurhash: string + width: number + height: number + aspectRatio: number + s3Key: string + lastModified: string + size: number + exif: Exif & { FujiRecipe?: ReturnType } + isLivePhoto?: boolean + livePhotoVideoUrl?: string + livePhotoVideoS3Key?: string +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6d2e58a..99f3417a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,6 +69,9 @@ importers: apps/ssr: dependencies: + '@photo-gallery/data': + specifier: workspace:* + version: link:../../packages/data linkedom: specifier: 0.18.11 version: 0.18.11 @@ -109,6 +112,9 @@ importers: '@headlessui/react': specifier: 2.2.4 version: 2.2.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@photo-gallery/data': + specifier: workspace:* + version: link:../../packages/data '@photo-gallery/webgl-viewer': specifier: workspace:* version: link:../../packages/webgl-viewer @@ -318,6 +324,15 @@ importers: specifier: 1.0.0-alpha.0 version: 1.0.0-alpha.0 + packages/data: + dependencies: + exif-reader: + specifier: 2.0.2 + version: 2.0.2 + fuji-recipes: + specifier: 1.0.2 + version: 1.0.2 + packages/webgl-viewer: dependencies: react: @@ -1588,7 +1603,7 @@ packages: resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} peerDependencies: '@types/react': '*' - react: 18.3.1 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true