319 lines
6.6 KiB
Markdown
319 lines
6.6 KiB
Markdown
# 🐉 Loongyan
|
||
|
||
一个轻量级、自托管的全栈相册管理系统。无需数据库,直接以文件系统存储,支持图片和视频,提供动态缩略图生成和现代化的 Web 界面。
|
||
|
||

|
||

|
||
|
||
---
|
||
|
||
## ✨ 功能特性
|
||
|
||
- 📁 **零配置相册管理** - 直接扫描 `data/` 文件夹,子目录自动识别为相册
|
||
- 🖼️ **多格式支持** - 支持 JPEG、PNG、GIF、BMP、WebP 及 MP4、MOV 等视频格式
|
||
- ⚡ **动态缩略图** - 基于 libvips 实时生成,支持自定义尺寸参数
|
||
- 🌊 **流式传输** - 大文件流式输出,节省内存,防止泄漏
|
||
- 🎨 **现代化界面** - SvelteKit 5 驱动,响应式设计,流畅体验
|
||
- 🔧 **高性能解析** - 自定义二进制解析器读取图片尺寸,无需 ImageMagick
|
||
- 🐳 **Docker 支持** - 多阶段构建,一键部署
|
||
- 🌍 **国际化** - 支持中英文切换(Paraglide JS)
|
||
- 🔐 **用户认证** - 集成 Better Auth 认证系统
|
||
|
||
---
|
||
|
||
## 🏗️ 项目结构
|
||
|
||
```
|
||
loongyan/
|
||
├── bin/ # Dart 后端服务器
|
||
│ ├── server.dart # 服务入口
|
||
│ ├── router.dart # API 路由定义
|
||
│ ├── middleware/ # 中间件(异常处理等)
|
||
│ ├── domain/ # 领域层
|
||
│ │ ├── entities/ # 实体(Album, Photo)
|
||
│ │ └── repositories/ # 数据仓库
|
||
│ └── util/ # 工具类(Vips 图片处理)
|
||
├── web/ # SvelteKit 前端
|
||
│ ├── src/
|
||
│ │ ├── lib/
|
||
│ │ │ ├── components/ # UI 组件
|
||
│ │ │ └── server/ # 服务端逻辑
|
||
│ │ └── routes/ # 页面路由
|
||
│ └── package.json
|
||
├── data/ # 数据目录(相册文件夹)
|
||
├── cache/ # 缩略图缓存
|
||
├── Dockerfile # 容器化配置
|
||
└── pubspec.yaml # Dart 依赖
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 快速开始
|
||
|
||
### 环境要求
|
||
|
||
- **后端**: Dart SDK ^3.10.7, libvips
|
||
- **前端**: Node.js 18+, pnpm/npm
|
||
|
||
### 1. 克隆项目
|
||
|
||
```bash
|
||
git clone <repository-url>
|
||
cd loongyan
|
||
```
|
||
|
||
### 2. 准备数据目录
|
||
|
||
```bash
|
||
mkdir -p data/我的相册
|
||
cp /path/to/your/photos/* data/我的相册/
|
||
```
|
||
|
||
### 3. 运行后端
|
||
|
||
```bash
|
||
# 安装依赖
|
||
dart pub get
|
||
|
||
# 开发运行(端口 8080)
|
||
dart run bin/server.dart
|
||
|
||
# 输出: Server listening on port 8080
|
||
```
|
||
|
||
### 4. 运行前端
|
||
|
||
```bash
|
||
cd web
|
||
pnpm install
|
||
pnpm dev
|
||
|
||
# 访问 http://localhost:5173
|
||
```
|
||
|
||
---
|
||
|
||
## 🐳 Docker 部署
|
||
|
||
### 构建镜像
|
||
|
||
```bash
|
||
docker build . -t loongyan
|
||
```
|
||
|
||
### 运行容器
|
||
|
||
```bash
|
||
docker run -d \
|
||
--name loongyan \
|
||
-p 8080:8080 \
|
||
-v $(pwd)/data:/app/data \
|
||
-v $(pwd)/cache:/app/cache \
|
||
loongyan
|
||
```
|
||
|
||
### Docker Compose(推荐)
|
||
|
||
```yaml
|
||
version: '3.8'
|
||
services:
|
||
loongyan:
|
||
build: .
|
||
ports:
|
||
- "8080:8080"
|
||
volumes:
|
||
- ./data:/app/data
|
||
- ./cache:/app/cache
|
||
environment:
|
||
- PORT=8080
|
||
restart: unless-stopped
|
||
```
|
||
|
||
```bash
|
||
docker-compose up -d
|
||
```
|
||
|
||
---
|
||
|
||
## 📡 API 文档
|
||
|
||
### 基础信息
|
||
|
||
- **基础 URL**: `http://localhost:8080/api/v1`
|
||
- **响应格式**: JSON
|
||
|
||
### 端点列表
|
||
|
||
| 方法 | 端点 | 描述 | 参数 |
|
||
|------|------|------|------|
|
||
| GET | `/` | 测试接口 | - |
|
||
| GET | `/album` | 获取所有相册 | - |
|
||
| GET | `/album/<id>` | 获取相册详情 | `id`: 相册ID |
|
||
| GET | `/album/<id>/photo` | 获取相册内照片 | `id`: 相册ID |
|
||
| GET | `/photo/<id>` | 获取照片元数据 | `id`: 照片ID |
|
||
| GET | `/photo/<id>/file` | 获取原图文件 | `id`: 照片ID |
|
||
| GET | `/photo/<id>/preview` | 获取缩略图 | `id`: 照片ID, `w`: 宽度, `h`: 高度 |
|
||
| GET | `/echo/<message>` | 回声测试 | `message`: 任意消息 |
|
||
|
||
### 示例请求
|
||
|
||
```bash
|
||
# 获取相册列表
|
||
curl http://localhost:8080/api/v1/album
|
||
|
||
# 获取相册内照片
|
||
curl http://localhost:8080/api/v1/album/123456/photo
|
||
|
||
# 获取缩略图(宽度 300px)
|
||
curl http://localhost:8080/api/v1/photo/789012/preview?w=300
|
||
```
|
||
|
||
### 响应示例
|
||
|
||
**相册列表** (`GET /album`)
|
||
```json
|
||
[
|
||
{
|
||
"id": 123456789,
|
||
"name": "我的相册",
|
||
"createdAt": "2024-01-15T08:30:00.000",
|
||
"updatedAt": "2024-01-15T08:30:00.000"
|
||
}
|
||
]
|
||
```
|
||
|
||
**照片详情** (`GET /photo/<id>`)
|
||
```json
|
||
{
|
||
"id": 987654321,
|
||
"albumId": 123456789,
|
||
"filePath": "data/我的相册/IMG_001.jpg",
|
||
"fileName": "IMG_001.jpg",
|
||
"fileSize": 2048576,
|
||
"mimeType": "image/jpeg",
|
||
"width": 1920,
|
||
"height": 1080,
|
||
"createdAt": "2024-01-15T08:30:00.000"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🛠️ 开发指南
|
||
|
||
### 后端开发
|
||
|
||
```bash
|
||
# 运行测试
|
||
dart test
|
||
|
||
# 代码检查
|
||
dart analyze
|
||
|
||
# 格式化代码
|
||
dart format .
|
||
|
||
# AOT 编译(生产)
|
||
dart compile exe bin/server.dart -o bin/server
|
||
```
|
||
|
||
### 前端开发
|
||
|
||
```bash
|
||
cd web
|
||
|
||
# 安装依赖
|
||
pnpm install
|
||
|
||
# 开发服务器
|
||
pnpm dev
|
||
|
||
# 构建生产版本
|
||
pnpm build
|
||
|
||
# 预览生产构建
|
||
pnpm preview
|
||
|
||
# 运行测试
|
||
pnpm test
|
||
|
||
# 代码检查
|
||
pnpm lint
|
||
|
||
# 格式化
|
||
pnpm format
|
||
|
||
# 类型检查
|
||
pnpm check
|
||
```
|
||
|
||
### 生成认证 Schema
|
||
|
||
```bash
|
||
cd web
|
||
pnpm auth:schema
|
||
```
|
||
|
||
---
|
||
|
||
## ⚙️ 配置说明
|
||
|
||
### 后端环境变量
|
||
|
||
| 变量名 | 默认值 | 说明 |
|
||
|--------|--------|------|
|
||
| `PORT` | `8080` | 服务器监听端口 |
|
||
|
||
### 前端环境变量
|
||
|
||
前端使用 `.env` 文件配置,参考 `.env.example`:
|
||
|
||
```bash
|
||
# web/.env
|
||
PUBLIC_API_URL=http://localhost:8080/api/v1
|
||
```
|
||
|
||
---
|
||
|
||
## 📦 技术栈
|
||
|
||
### 后端
|
||
- [Dart](https://dart.dev/) - 编程语言
|
||
- [Shelf](https://pub.dev/packages/shelf) - Web 框架
|
||
- [shelf_router](https://pub.dev/packages/shelf_router) - 路由库
|
||
- [libvips](https://www.libvips.org/) - 高性能图片处理
|
||
|
||
### 前端
|
||
- [Svelte 5](https://svelte.dev/) - 前端框架
|
||
- [SvelteKit](https://kit.svelte.dev/) - 全栈框架
|
||
- [Vite](https://vitejs.dev/) - 构建工具
|
||
- [Better Auth](https://www.better-auth.com/) - 认证库
|
||
- [Paraglide JS](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) - 国际化
|
||
- [Drizzle ORM](https://orm.drizzle.team/) - 数据库 ORM
|
||
|
||
---
|
||
|
||
## 🤝 贡献指南
|
||
|
||
1. Fork 本仓库
|
||
2. 创建特性分支 (`git checkout -b feature/amazing-feature`)
|
||
3. 提交更改 (`git commit -m 'Add amazing feature'`)
|
||
4. 推送到分支 (`git push origin feature/amazing-feature`)
|
||
5. 创建 Pull Request
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 🙏 致谢
|
||
|
||
- 感谢 [Shelf](https://pub.dev/packages/shelf) 团队提供的 Dart Web 框架
|
||
- 感谢 [Svelte](https://svelte.dev/) 团队带来的优秀前端体验
|
||
- 感谢 [libvips](https://www.libvips.org/) 提供的高性能图片处理能力
|
||
|
||
---
|
||
|
||
<div align="center">
|
||
<sub>Made with ❤️ by Loongyan Team</sub>
|
||
</div>
|