You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
2.9 KiB
102 lines
2.9 KiB
import { dbGlobal } from "drizzle-pkg/lib/db";
|
|
import { articles, articleItems } from "drizzle-pkg/lib/schema/collection";
|
|
import { items, itemTags, tags } from "drizzle-pkg/lib/schema/collection";
|
|
import { eq, and, desc, inArray } from "drizzle-orm";
|
|
import crypto from "node:crypto";
|
|
|
|
export async function listArticles(userId: number) {
|
|
return dbGlobal
|
|
.select()
|
|
.from(articles)
|
|
.where(eq(articles.userId, userId))
|
|
.orderBy(desc(articles.updatedAt));
|
|
}
|
|
|
|
export async function getArticle(id: number, userId: number) {
|
|
const [article] = await dbGlobal
|
|
.select()
|
|
.from(articles)
|
|
.where(and(eq(articles.id, id), eq(articles.userId, userId)));
|
|
|
|
if (!article) return null;
|
|
|
|
// Load linked items
|
|
const linkedItems = await dbGlobal
|
|
.select()
|
|
.from(articleItems)
|
|
.innerJoin(items, eq(articleItems.itemId, items.id))
|
|
.where(eq(articleItems.articleId, id));
|
|
|
|
return { ...article, linkedItems };
|
|
}
|
|
|
|
export async function createArticle(userId: number, data: { title?: string; content?: string }) {
|
|
const [article] = await dbGlobal
|
|
.insert(articles)
|
|
.values({
|
|
userId,
|
|
title: data.title || "无标题文章",
|
|
content: data.content || "",
|
|
wordCount: (data.content || "").length,
|
|
})
|
|
.returning();
|
|
return article;
|
|
}
|
|
|
|
export async function updateArticle(
|
|
id: number,
|
|
userId: number,
|
|
data: { title?: string; content?: string; status?: "draft" | "published"; coverUrl?: string }
|
|
) {
|
|
const [existing] = await dbGlobal
|
|
.select({ id: articles.id })
|
|
.from(articles)
|
|
.where(and(eq(articles.id, id), eq(articles.userId, userId)));
|
|
|
|
if (!existing) return null;
|
|
|
|
const updateData: Record<string, any> = { ...data };
|
|
if (data.content !== undefined) {
|
|
updateData.wordCount = data.content.length;
|
|
}
|
|
|
|
await dbGlobal.update(articles).set(updateData).where(eq(articles.id, id));
|
|
return getArticle(id, userId);
|
|
}
|
|
|
|
export async function deleteArticle(id: number, userId: number) {
|
|
const [existing] = await dbGlobal
|
|
.select({ id: articles.id })
|
|
.from(articles)
|
|
.where(and(eq(articles.id, id), eq(articles.userId, userId)));
|
|
|
|
if (!existing) return false;
|
|
await dbGlobal.delete(articles).where(eq(articles.id, id));
|
|
return true;
|
|
}
|
|
|
|
export async function generateShareToken(id: number, userId: number) {
|
|
const token = crypto.randomUUID().replace(/-/g, "").substring(0, 12);
|
|
await dbGlobal
|
|
.update(articles)
|
|
.set({ shareToken: token })
|
|
.where(and(eq(articles.id, id), eq(articles.userId, userId)));
|
|
return token;
|
|
}
|
|
|
|
export async function getArticleByShareToken(token: string) {
|
|
const [article] = await dbGlobal
|
|
.select()
|
|
.from(articles)
|
|
.where(eq(articles.shareToken, token));
|
|
|
|
if (!article) return null;
|
|
|
|
const linkedItems = await dbGlobal
|
|
.select()
|
|
.from(articleItems)
|
|
.innerJoin(items, eq(articleItems.itemId, items.id))
|
|
.where(eq(articleItems.articleId, article.id));
|
|
|
|
return { ...article, linkedItems };
|
|
}
|
|
|