面向摄影展示、内容创作与跨端照片管理的一体化平台
MO Gallery 由 Next.js Web 站点、Wails Desktop 工作台和 Flutter Mobile 客户端组成,覆盖公开展示、云端内容管理、本地照片资源库、移动上传、故事与博客编辑、胶卷归档、Zine 排版及 AI 辅助创作。
MO Gallery 不只是一个摄影作品网站,而是一套围绕摄影内容建立的跨端工作流:
| 模块 | 定位 | 主要能力 |
|---|---|---|
| Web 公开站点 | 摄影作品与叙事内容展示 | 首页、图库、精选、相册、胶卷、故事、博客、友链、评论、多语言和主题切换 |
| Web 管理后台 | 浏览器内的云端内容管理 | 统一资源库、上传、相册与胶卷、故事与博客编辑、存储整理、评论与登录配置、系统设置 |
| Desktop 工作台 | 云端管理与本地照片工作流 | 云端/本地资源库、批量上传、本地文件组织、照片日志、Zine、AI 助手、缓存与存储管理 |
| Mobile 客户端 | Android-first 移动采集与浏览 | 安全登录、照片选择与预览、后台上传队列、图库浏览、故事阅读、连接设置 |
| API 与数据层 | 多端共享业务接口 | Hono API、Prisma、PostgreSQL、JWT、Linux DO OAuth、统一媒体与内容接口 |
| 存储层 | 可切换的云端媒体存储 | 本地文件系统、S3 兼容对象存储、Cloudflare R2、GitHub 仓库 |
当前版本为 v0.7.0-beta。Web 支持 Vercel、Docker 和 Node.js 自托管;Desktop 当前重点支持 Windows;Mobile 以 Android 为主要目标平台。
Web 端新界面截图待补充。
以下截图展示 Desktop 的主要工作区,界面可能随 Beta 版本持续调整。
| 登录页 | 概览 |
|---|---|
![]() |
![]() |
| 照片库 | 相册管理 |
|---|---|
![]() |
![]() |
| 胶卷管理 | 图片上传 |
|---|---|
![]() |
![]() |
| 照片日志 | Zine |
|---|---|
![]() |
![]() |
| Zine 编辑 | AI 对话 |
|---|---|
![]() |
![]() |
| 存储整理 | 系统配置 |
|---|---|
![]() |
![]() |
友链管理
Web 与 Desktop 都以资源库作为照片管理的主要入口:
- 在同一工作区管理照片、相册和胶卷,减少模块之间的来回跳转。
- 支持列表、宫格、瀑布流和时间线等不同浏览方式。
- 支持搜索、分类、类型、可见性、精选状态和存储来源筛选。
- 通过详情侧栏查看和编辑标题、分类、描述、EXIF、拍摄位置及关联信息。
- 支持批量操作、照片预览、相册组织、胶卷帧排序和资源状态同步。
- Desktop 可在云端资源与本地资源之间切换,并保持相近的交互结构。
Desktop 提供独立于云端图库的本地照片管理能力:
- 创建、打开和切换多个本地图库工作区。
- 扫描与导入本地文件,使用 SQLite 保存索引、组织信息和图库状态。
- 使用物理文件夹、逻辑集合和标签组织照片。
- 支持搜索、筛选、排序、批量选择和大规模图库浏览。
- 支持重命名、移动、删除、恢复、缺失文件处理和外部文件变更协调。
- 生成缩略图与预览缓存,并提供缓存查看和清理能力。
- 支持手动备份、自动备份、恢复及数据库完整性检查。
- 针对 RAW、JPEG、PNG、GIF、AVIF 等格式提供分级预览与原图访问策略。
本地资源库保存的是本机图库索引和组织信息,不会自动等同于 Web 云端图库。需要发布到站点的照片可通过 Desktop 上传工作流进入云端。
- 自动提取相机、镜头、光圈、快门、ISO、拍摄时间和 GPS 等 EXIF 信息。
- 提取图片主色,用于加载占位和界面视觉反馈。
- 支持相册封面、照片关联、排序及公开展示。
- 支持
135与120胶卷格式、胶片预设、元数据、帧数和照片排序。 - 支持数码与胶片上传模式、批量拖拽、压缩、进度展示、失败重试和目标选择。
- 使用文件哈希辅助重复检测,并支持照片公开、精选和分类管理。
- 云端媒体可以保存在 Local、S3、R2 或 GitHub 存储源中。
- 使用 TipTap 3 构建共享的故事和博客编辑体验。
- 支持标题、段落、列表、引用、代码、链接、表格、图片、图片组和媒体嵌入。
- 提供渐进式块编辑交互、上下文工具栏、颜色与格式菜单。
- 支持从图库插入照片、封面裁切、故事内照片排序和 MapLibre 故事地图。
- 通过 IndexedDB 保存 Web/Desktop 编辑草稿,降低意外退出造成的内容丢失。
- Web 与 Desktop 复用
packages/tiptap-editor,减少编辑格式和渲染结果差异。
Desktop 内置摄影 Zine 工作流:
- 创建和管理 Zine 项目,选择页面尺寸与模板。
- 使用跨页画布、页面缩略图和照片托盘完成排版。
- 支持图片槽位、文字槽位、移动、缩放、裁切和直接编辑。
- 提供撤销/重做、编辑历史、保存状态和图片加载状态处理。
- 支持从云端资源库或本地文件导入照片。
- 提供打印/PDF 导出链路及 AI 辅助排版能力。
- 支持 OpenAI 兼容 API,可连接 OpenAI、DeepSeek 或其他兼容服务。
- 支持自定义服务地址、API Key、模型、上下文窗口和模型能力配置。
- 提供多轮对话、图片输入、内容建议、差异预览和受控直接编辑。
- 对视觉、工具调用和结构化输出能力采用显式模型白名单。
- Web 与 Desktop 共享
packages/ai-agent中的领域模型、执行流程和提示词能力。 - AI 配置为可选功能;未配置 AI 服务时,其余图库与内容功能仍可使用。
Flutter 客户端面向移动采集和轻量浏览:
- 连接 MO Gallery 服务端并保存安全会话。
- 支持管理员登录路径门禁,与 Web/Desktop 使用相同认证规则。
- 从系统图库选择照片,预览并设置上传目标。
- 维护本地上传队列,支持任务恢复、状态追踪和错误提示。
- 浏览云端图库、照片详情、故事列表和故事详情。
- 使用 Riverpod 管理状态、GoRouter 管理多标签导航、SQLite 保存本地队列数据。
Mobile 当前以 Android 为主要开发和发布目标;仓库保留 iOS 工程结构,但实际发布状态以 Release 说明为准。
- 评论可使用本地 PostgreSQL 数据库或 Waline/LeanCloud。
- 支持 Linux DO OAuth 登录、用户信息展示和评论访问控制。
- 管理员使用账号密码或允许的 Linux DO 账号登录,并通过 JWT 访问管理 API。
- 可配置隐藏管理员登录路径;Desktop 和 Mobile 连接时必须使用对应的完整登录地址。
- 支持友链展示、管理和排序。
Public Web / Web Admin Desktop (Wails) Mobile (Flutter)
Next.js 16 + React 19 React 19 + Go Riverpod + GoRouter
│ │ │
├──────────── HTTP / JWT ─────┼──────── HTTP / JWT ─────────┤
│ │ │
▼ ├── Local Library Manager └── Local SQLite Queue
Hono API Routes │ SQLite index/cache
│ │ local file operations
▼ │
Prisma 7 + PostgreSQL └── Optional direct DB/service access
│
▼
Local / S3-compatible / R2 / GitHub Storage
Shared packages:
packages/tiptap-editor · packages/ai-agent
- 云端业务数据:照片、相册、胶卷、故事、博客、评论和设置保存在 PostgreSQL。
- 云端媒体文件:由配置的 Local、S3、R2 或 GitHub 存储提供者保存。
- Desktop 本地图库:原图保留在用户选择的本地图库目录,SQLite 保存索引与组织数据。
- Mobile 本地数据:安全会话、上传队列和必要缓存保存在设备本地,业务数据通过 API 同步。
| 分类 | 技术 |
|---|---|
| Web | Next.js 16、React 19、App Router、React Compiler |
| Web API | Hono.js、Next.js Route Handler、Zod |
| Web 数据库 | PostgreSQL 16、Prisma 7 |
| Desktop | Wails 2、Go、React 19、Vite 6、GORM |
| Desktop 本地图库 | Go、SQLite、本地文件系统、缩略图/预览缓存 |
| Mobile | Flutter、Dart、Riverpod、GoRouter、Dio、SQLite |
| 样式与交互 | Tailwind CSS 4、Framer Motion、Lucide Icons |
| 内容编辑 | TipTap 3、React Markdown、Shiki |
| 图片处理 | Sharp、ExifReader、JS/WASM 压缩、Desktop Go 图像处理 |
| 地图 | MapLibre GL、react-map-gl |
| 认证 | JWT、Linux DO OAuth |
| 存储 | Local、S3-compatible、Cloudflare R2、GitHub |
| 工具 | 建议版本 | 用途 |
|---|---|---|
| Node.js | 24.x | Web、共享包和 Desktop 前端 |
| pnpm | 10.x | JavaScript Monorepo 依赖管理 |
| PostgreSQL | 16.x | Web 云端数据库 |
| Go | 1.24.x | Desktop 后端开发 |
| Wails CLI | 2.12.0 | Desktop 开发与构建 |
| Flutter SDK | Dart >=3.5.0 <4.0.0 对应版本 |
Mobile 开发 |
| Android Studio / Android SDK | 当前稳定版 | Android 调试与构建 |
git clone https://github.com/ushaio/mo-gallery-web.git
cd mo-gallery-web
pnpm installpnpm-workspace.yaml 会同时管理根 Web 应用、desktop/frontend 和 packages/*。
cp .env.example .envWindows PowerShell:
Copy-Item .env.example .env至少需要配置数据库、管理员账号和 JWT 密钥:
DATABASE_URL="postgresql://postgres:password@localhost:5432/mo_gallery"
DIRECT_URL="postgresql://postgres:password@localhost:5432/mo_gallery"
ADMIN_USERNAME="admin"
ADMIN_PASSWORD="replace-with-a-strong-password"
JWT_SECRET="replace-with-a-long-random-secret"完整配置见 .env.example。
pnpm run prisma:generate
pnpm run prisma:dev
pnpm run prisma:seed
pnpm run dev默认地址:
- 公开站点:
http://localhost:3000 - 未配置安全后缀时的管理员登录:
http://localhost:3000/login - 配置安全后缀后的管理员登录:
http://localhost:3000/login/{ADMIN_LOGIN_URL}
修改管理员登录后缀会使旧管理员会话失效。Desktop 和 Mobile 连接服务端时,也需要填写包含 /login/{ADMIN_LOGIN_URL} 的完整地址。
go install github.com/wailsapp/wails/v2/cmd/wails@v2.12.0
cd desktop
wails dev开发时建议使用独立配置,避免覆盖正式 Desktop 配置:
wails dev -appargs "-config %APPDATA%\mo-gallery-desktop\config.dev.json"Desktop 可在设置页维护 Web API、数据库、JWT、存储、AI、本地缓存和窗口外观配置。若 Desktop 与 Web 共用认证,Desktop 的 api.jwt_secret 必须与 Web 的 JWT_SECRET 一致。
cd flutter
flutter pub get
flutter run连接服务端时,在应用中填写 Web 管理员登录地址。Android 模拟器访问宿主机服务时,通常需要使用 10.0.2.2 替代 localhost。
cd desktop
# Portable EXE
wails build
# NSIS 安装包
wails build -nsis构建产物位于 desktop/build/bin/。Desktop 前端资源通过 Go embed 内置到可执行文件中,不需要额外携带前端静态目录。
| 发布方式 | 适用场景 | 特点 |
|---|---|---|
| Portable | Beta 测试、内部使用、无管理员权限环境 | 下载后直接运行,更新时替换 EXE |
| Setup | 稳定发布、普通用户、需要系统集成 | 提供安装路径、快捷方式和卸载入口 |
| 系统 | 默认路径 |
|---|---|
| Windows | %APPDATA%\mo-gallery-desktop\config.json |
| macOS | ~/Library/Application Support/mo-gallery-desktop/config.json |
| Linux | ~/.config/mo-gallery-desktop/config.json |
Portable 版本不会把配置保存在 EXE 旁边。删除或替换 EXE 后,用户配置和本地图库数据不会自动删除。
| 变量 | 说明 |
|---|---|
DATABASE_URL |
Web 运行时 PostgreSQL 连接地址 |
DIRECT_URL |
Prisma 迁移使用的数据库直连地址 |
ADMIN_USERNAME |
默认管理员用户名 |
ADMIN_PASSWORD |
默认管理员密码,生产环境必须修改 |
JWT_SECRET |
JWT 签名密钥,生产环境必须使用高强度随机字符串 |
| 变量 | 说明 | 默认/示例 |
|---|---|---|
ADMIN_LOGIN_URL |
管理员登录安全后缀;留空时从 /login 登录 |
留空 |
NEXT_PUBLIC_ADMIN_LOGIN_URL |
旧部署兼容项;服务端配置缺失时作为回退 | 留空 |
SITE_TITLE |
站点标题 | MO GALLERY |
SITE_URL |
服务端使用的公开站点地址 | https://your-domain.com |
NEXT_PUBLIC_SITE_URL |
浏览器使用的公开站点地址 | https://your-domain.com |
SITE_AUTHOR |
首页显示的作者名称 | MO |
CDN_DOMAIN |
媒体 CDN 域名 | 留空 |
API_ORIGIN_CHECK |
是否限制 API 请求来源 | false |
| 变量 | 说明 |
|---|---|
AI_BASE_URL |
OpenAI 兼容 API 根地址 |
AI_API_KEY |
AI 服务密钥 |
AI_MODEL |
默认模型 |
AI_VISION_MODELS |
允许图片输入的模型 ID,逗号分隔 |
AI_TOOL_MODELS |
允许工具调用的模型 ID,逗号分隔 |
AI_STRUCTURED_OUTPUT_MODELS |
允许结构化输出的模型 ID,逗号分隔 |
AI_MODEL_CONTEXT_WINDOWS |
模型上下文窗口配置,JSON 对象 |
| 变量 | 说明 |
|---|---|
COMMENTS_STORAGE |
LOCAL、留空,或 LEANCLOUD |
WALINE_SERVER_URL |
Waline 服务地址 |
LEAN_ID / LEAN_KEY / LEAN_MASTER_KEY |
LeanCloud 应用凭证 |
LINUXDO_CLIENT_ID / LINUXDO_CLIENT_SECRET |
Linux DO OAuth 凭证 |
LINUXDO_REDIRECT_URI |
OAuth 回调地址 |
LINUXDO_ADMIN_USERNAMES |
允许成为管理员的 Linux DO 用户名,逗号分隔 |
LINUXDO_COMMENTS_ONLY |
是否仅允许 Linux DO 用户评论 |
Docker Compose 会启动 PostgreSQL 和 MO Gallery,并持久化数据库与本地上传目录。
cp .env.example .env
# 修改 POSTGRES_PASSWORD、ADMIN_PASSWORD、JWT_SECRET 等生产配置
docker compose up -d --build
docker compose logs -f默认地址:
- Web:
http://localhost:3001 - PostgreSQL:
localhost:5433
可通过 .env 中的 APP_PORT 和 DB_PORT 修改外部端口。
- Fork 本仓库并导入 Vercel。
- 配置
.env.example中需要的环境变量。 - 使用 Neon、Supabase 或其他托管 PostgreSQL。
- 使用 S3、R2 或 GitHub 存储媒体文件。
vercel.json会执行 Prisma 部署、客户端生成和 Next.js 构建。
Vercel 运行文件系统不适合持久化用户上传,生产环境不要使用 Local 云端存储后端。
pnpm run build:node
pnpm run start生产环境还应配置反向代理、HTTPS、进程守护、数据库备份和媒体存储备份。
| 命令 | 说明 |
|---|---|
pnpm run dev |
启动 Next.js 开发服务器 |
pnpm run build |
构建 Web 生产版本 |
pnpm run build:vercel |
Prisma 部署、生成、种子数据和 Vercel 构建 |
pnpm run build:node |
Prisma 部署、生成和 Node.js 自托管构建 |
pnpm run start |
启动 Web 生产服务器 |
pnpm run lint |
运行 ESLint |
pnpm run test:editor-ai-routes |
测试编辑器 AI API 路由 |
pnpm run test:editor-ai-images |
测试编辑器 AI 图片处理 |
pnpm run prisma:generate |
生成 Prisma Client |
pnpm run prisma:dev |
创建并应用开发迁移 |
pnpm run prisma:deploy |
应用生产迁移 |
pnpm run prisma:seed |
写入种子数据 |
| 命令 | 说明 |
|---|---|
cd desktop && wails dev |
启动 Desktop 开发模式 |
cd desktop && wails build |
构建 Desktop Portable EXE |
cd desktop && wails build -nsis |
构建 Desktop NSIS 安装包 |
cd desktop/frontend && pnpm build |
单独验证 Desktop 前端构建 |
cd desktop/frontend && pnpm test:zine |
运行 Zine 编辑器测试 |
cd desktop && go test ./... |
运行 Desktop Go 测试和本地资源库测试 |
| 命令 | 说明 |
|---|---|
cd flutter && flutter pub get |
安装 Flutter 依赖 |
cd flutter && flutter run |
启动 Mobile 调试 |
cd flutter && flutter analyze |
运行 Dart 静态分析 |
cd flutter && flutter test |
运行 Mobile 测试 |
cd flutter && flutter build apk |
构建 Android APK |
mo-gallery-web/
├── src/app/ # Next.js App Router、公开页面与 Web 管理后台
├── src/components/ # Web 页面组件、图库和管理组件
├── src/lib/ # API 客户端、i18n、草稿和内容工具
├── hono/ # Hono API 路由与认证中间件
├── server/ # 查询、存储、EXIF、AI 和服务端基础设施
├── prisma/ # Prisma Schema、迁移与种子脚本
├── packages/
│ ├── ai-agent/ # Web/Desktop 共用 AI Agent
│ └── tiptap-editor/ # Web/Desktop 共用 TipTap 编辑器
├── desktop/ # Go + Wails Desktop
│ ├── frontend/ # React/Vite 桌面前端
│ ├── local_library/ # 本地图库、SQLite、文件操作、缓存和备份
│ ├── services/ # 云端内容、上传、存储和导出服务
│ ├── config/ # Desktop 配置管理
│ ├── db/ # GORM 数据访问与模型
│ └── build/ # 图标、平台清单和构建产物
├── flutter/ # Flutter Mobile 客户端
│ ├── lib/features/ # 登录、上传、图库、故事和设置
│ ├── lib/core/ # API、认证、SQLite 和文件工具
│ └── test/ # Mobile 单元测试
├── docs/
│ ├── adr/ # 架构决策记录
│ ├── requirements/ # 功能规格、领域模型和验证矩阵
│ └── glossary/ # 领域术语说明
├── tests/ # Web/API 聚焦测试
├── scripts/ # 数据修复与回归脚本
├── public/ # Web 静态资源与本地上传目录
├── README.assets/ # README 截图
├── docker-compose.yml # Web + PostgreSQL 编排
├── Dockerfile # Web 容器镜像
└── RELEASE.md # 版本说明
- 不要提交
.env、数据库密码、JWT 密钥、AI Key 或对象存储凭证。 - 生产环境必须修改默认管理员密码,并使用高强度
JWT_SECRET。 - Desktop 与 Web 共用认证时,确保 JWT 配置一致。
- Desktop 本地图库包含索引、缓存和备份,应与原始照片一起纳入备份策略。
- Mobile 应只连接可信 HTTPS 服务端,不要在正式环境使用明文 HTTP。
- 对公开部署启用 HTTPS,并根据需要开启
API_ORIGIN_CHECK。 - 定期备份 PostgreSQL、媒体文件、存储源配置和 Desktop 本地图库。
- 正式分发 Windows Desktop 时建议进行代码签名,减少 SmartScreen 警告。
Web 云端资源库与 Desktop 本地资源库有什么区别?
Web 云端资源库管理已经发布到服务端的照片、相册和胶卷,数据保存在 PostgreSQL 和配置的媒体存储中。Desktop 本地资源库管理用户电脑上的原始文件及本地 SQLite 索引;它可以独立整理照片,再通过上传工作流发布到云端。
为什么 Desktop 下载后可以直接运行?
wails build 默认生成 Portable EXE。React/Vite 前端通过 Go embed 打包进可执行文件,Windows 安装 WebView2 Runtime 后即可直接启动。
Portable 和 Setup 应该选择哪一个?
Beta 测试、内部使用和无管理员权限环境优先选择 Portable;面向普通用户的稳定版本优先选择 Setup。正式发布时可以同时提供两种构建。
为什么 Vercel 不能使用本地存储?
Vercel 函数文件系统不用于持久化用户上传。请使用 S3、Cloudflare R2、GitHub 或其他外部存储后端。
Mobile 为什么无法访问电脑上的 localhost?
真机或模拟器中的 localhost 指向设备自身。Android 模拟器通常使用 10.0.2.2 访问宿主机;真机需要使用电脑在局域网中的 IP,并确保防火墙允许访问。
- LINUX DO — 新的理想型社区
本项目以 MIT License 发布。












