迁移数据库从 SQLite 到 PostgreSQL

- 更新 deno.json 添加 postgres 依赖
- 重构 db/index.ts 使用 PostgreSQL 连接和适配器
- 更新所有路由文件支持异步数据库操作
- 将 SQLite 语法转换为 PostgreSQL 语法
- 添加数据库迁移文档和 schema 文件
This commit is contained in:
ethan.chen
2026-01-08 14:26:27 +08:00
parent 32f7b86f28
commit 2b5b2f1d97
7 changed files with 409 additions and 120 deletions

View File

@@ -1,143 +1,177 @@
import { Hono } from 'hono'
import { db } from '../db/index.ts'
import type { JwtVariables } from 'hono/jwt'
import { authMiddleware } from '../auth.ts'
import { Hono } from "hono";
import { db } from "../db/index.ts";
import type { JwtVariables } from "hono/jwt";
import { authMiddleware } from "../auth.ts";
const media = new Hono<{ Variables: JwtVariables }>()
const media = new Hono<{ Variables: JwtVariables }>();
media.use('/*', authMiddleware)
// 查询满分10分作品不需要JWT验证
media.get("/perfect", async (c) => {
try {
const perfectList = await db
.prepare("SELECT * FROM media WHERE rating = 10")
.all();
return c.json({
code: 0,
data: perfectList,
message: "Success",
});
} catch (error: any) {
return c.json({ code: 1, data: {}, message: error.message }, 500);
}
});
media.use("/*", authMiddleware);
// 获取所有媒体记录
media.get('/list', (c) => {
media.get("/list", async (c) => {
try {
const mediaList = db.prepare('SELECT * FROM media').all()
const mediaList = await db.prepare("SELECT * FROM media").all();
return c.json({
code: 0,
data: mediaList,
message: 'Success'
})
message: "Success",
});
} catch (error: any) {
return c.json({ code: 1, data: {}, message: error.message }, 500)
return c.json({ code: 1, data: {}, message: error.message }, 500);
}
})
});
// 创建新的媒体记录
media.post('/create', async (c) => {
media.post("/create", async (c) => {
try {
const data = await c.req.json()
const { title, type, rating, notes, platform, date } = data
const data = await c.req.json();
const { title, type, rating, notes, platform, date } = data;
const result = db.prepare(`
const result = await db
.prepare(
`
INSERT INTO media (title, type, rating, notes, platform, date, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
`).run(title, type, rating, notes, platform, date)
VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW())
RETURNING id
`
)
.run(title, type, rating, notes, platform, date);
const newMedia = db.prepare('SELECT * FROM media WHERE id = ?').get(result.lastInsertRowid)
const newMedia = await db
.prepare("SELECT * FROM media WHERE id = ?")
.get(result.lastInsertRowid);
return c.json({
code: 0,
data: newMedia,
message: 'Created successfully'
}, 201)
return c.json(
{
code: 0,
data: newMedia,
message: "Created successfully",
},
201
);
} catch (error: any) {
return c.json({ code: 2, data: {}, message: error.message }, 500)
return c.json({ code: 2, data: {}, message: error.message }, 500);
}
})
});
// 更新媒体记录
media.put('/updateById/:id', async (c) => {
media.put("/updateById/:id", async (c) => {
try {
const id = c.req.param('id')
const data = await c.req.json()
const { title, type, rating, notes, platform, date } = data
const id = c.req.param("id");
const data = await c.req.json();
const { title, type, rating, notes, platform, date } = data;
db.prepare(`
await db
.prepare(
`
UPDATE media
SET title = ?, type = ?, rating = ?, notes = ?, platform = ?, date = ?, updated_at = datetime('now')
SET title = ?, type = ?, rating = ?, notes = ?, platform = ?, date = ?, updated_at = NOW()
WHERE id = ?
`).run(title, type, rating, notes, platform, date, id)
`
)
.run(title, type, rating, notes, platform, date, id);
const updatedMedia = db.prepare('SELECT * FROM media WHERE id = ?').get(id)
const updatedMedia = await db
.prepare("SELECT * FROM media WHERE id = ?")
.get(id);
if (!updatedMedia) {
return c.json({ code: 1, data: {}, message: 'Media not found' }, 404)
return c.json({ code: 1, data: {}, message: "Media not found" }, 404);
}
return c.json({
code: 0,
data: updatedMedia,
message: 'Updated successfully'
})
message: "Updated successfully",
});
} catch (error: any) {
return c.json({ code: 2, data: {}, message: error.message }, 500)
return c.json({ code: 2, data: {}, message: error.message }, 500);
}
})
});
// 删除媒体记录
media.delete('/deleteById/:id', async (c) => {
media.delete("/deleteById/:id", async (c) => {
try {
const id = c.req.param('id')
db.prepare('DELETE FROM media WHERE id = ?').run(id)
return c.json({ code: 0, data: {}, message: 'Deleted successfully' })
const id = c.req.param("id");
await db.prepare("DELETE FROM media WHERE id = ?").run(id);
return c.json({ code: 0, data: {}, message: "Deleted successfully" });
} catch (error: any) {
return c.json({ code: 2, data: {}, message: error.message }, 500)
return c.json({ code: 2, data: {}, message: error.message }, 500);
}
})
});
// 分页查询媒体记录
media.get('/page', async (c) => {
media.get("/page", async (c) => {
try {
const type = c.req.query('type')
const currentPage = parseInt(c.req.query('currentPage') || '1')
const pageSize = parseInt(c.req.query('pageSize') || '10')
const title = c.req.query('title') || ''
const startDate = c.req.query('startDate')
const endDate = c.req.query('endDate')
const sortBy = c.req.query('sortBy') || 'date'
const sortType = c.req.query('sortType') || 'desc'
const type = c.req.query("type");
const currentPage = parseInt(c.req.query("currentPage") || "1");
const pageSize = parseInt(c.req.query("pageSize") || "10");
const title = c.req.query("title") || "";
const startDate = c.req.query("startDate");
const endDate = c.req.query("endDate");
const sortBy = c.req.query("sortBy") || "date";
const sortType = c.req.query("sortType") || "desc";
if (!type) {
return c.json({ code: 1, data: {}, message: 'Type is required' }, 400)
return c.json({ code: 1, data: {}, message: "Type is required" }, 400);
}
let query = 'SELECT * FROM media WHERE type = ?'
const params = [type]
let query = "SELECT * FROM media WHERE type = ?";
const params = [type];
if (title) {
query += ' AND title LIKE ?'
params.push(`%${title}%`)
query += " AND title LIKE ?";
params.push(`%${title}%`);
}
if (startDate) {
query += ' AND date >= ?'
params.push(startDate)
query += " AND date >= ?";
params.push(startDate);
}
if (endDate) {
query += ' AND date <= ?'
params.push(endDate)
query += " AND date <= ?";
params.push(endDate);
}
// 添加排序
if (sortBy === 'date') {
query += ` ORDER BY date ${sortType.toUpperCase()}`
} else if (sortBy === 'score') {
query += ` ORDER BY rating ${sortType.toUpperCase()}`
if (sortBy === "date") {
query += ` ORDER BY date ${sortType.toUpperCase()}`;
} else if (sortBy === "score") {
query += ` ORDER BY rating ${sortType.toUpperCase()}`;
}
// 添加分页
const offset = (currentPage - 1) * pageSize
query += ' LIMIT ? OFFSET ?'
params.push(pageSize.toString(), offset.toString())
const offset = (currentPage - 1) * pageSize;
query += " LIMIT ? OFFSET ?";
params.push(pageSize.toString(), offset.toString());
// 获取总数
const countQuery = query
.replace('SELECT *', 'SELECT COUNT(*) as total')
.replace(/ORDER BY.*$/, '') // 移除 ORDER BY 子句
.replace(/LIMIT.*$/, '') // 移除 LIMIT 和 OFFSET 子句
const totalResult = db.prepare(countQuery).get(...params.slice(0, -2))
const total = totalResult?.total || 0
.replace("SELECT *", "SELECT COUNT(*) as total")
.replace(/ORDER BY.*$/, "") // 移除 ORDER BY 子句
.replace(/LIMIT.*$/, ""); // 移除 LIMIT 和 OFFSET 子句
const totalResult = await db
.prepare(countQuery)
.get(...params.slice(0, -2));
const total = totalResult?.total || 0;
// 获取分页数据
const mediaList = db.prepare(query).all(...params)
const mediaList = await db.prepare(query).all(...params);
return c.json({
code: 0,
@@ -145,13 +179,13 @@ media.get('/page', async (c) => {
list: mediaList,
total,
currentPage,
pageSize
pageSize,
},
message: 'Success'
})
message: "Success",
});
} catch (error: any) {
return c.json({ code: 1, data: {}, message: error.message }, 500)
return c.json({ code: 1, data: {}, message: error.message }, 500);
}
})
});
export default media
export default media;