v3: added site-wide categories, added admin category management, various visual fixes
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 2m52s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 2m52s
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
} from "../config.ts";
|
||||
import { linkAttachments } from "./attachment-service.ts";
|
||||
import { relinkBacklinks } from "./backlink-service.ts";
|
||||
import { attachCategoryIds, setDumpCategories } from "./category-service.ts";
|
||||
|
||||
function isAllowedMime(mime: string): boolean {
|
||||
return DUMP_ALLOWED_MIME_PREFIXES.some((p) => mime.startsWith(p)) ||
|
||||
@@ -78,6 +79,9 @@ export async function createUrlDump(
|
||||
isPrivate ? 1 : 0,
|
||||
);
|
||||
|
||||
const categoryIds = [...new Set(request.categoryIds ?? [])];
|
||||
setDumpCategories(dumpId, categoryIds);
|
||||
|
||||
const dump: Dump = {
|
||||
id: dumpId,
|
||||
kind: "url",
|
||||
@@ -91,6 +95,7 @@ export async function createUrlDump(
|
||||
voteCount: 0,
|
||||
commentCount: 0,
|
||||
isPrivate,
|
||||
categoryIds,
|
||||
};
|
||||
if (!isPrivate) {
|
||||
broadcastNewDump(dump);
|
||||
@@ -110,6 +115,7 @@ export async function createFileDump(
|
||||
userId: string,
|
||||
isPrivate = false,
|
||||
title?: string,
|
||||
categoryIds: string[] = [],
|
||||
): Promise<Dump> {
|
||||
if (!isAllowedMime(file.type)) {
|
||||
throw new APIException(
|
||||
@@ -130,6 +136,7 @@ export async function createFileDump(
|
||||
const createdAt = new Date();
|
||||
const finalTitle = title?.trim() || file.name;
|
||||
const slug = makeSlug(finalTitle, dumpId);
|
||||
const dedupedCategoryIds = [...new Set(categoryIds)];
|
||||
|
||||
await Deno.mkdir(DUMPS_DIR, { recursive: true });
|
||||
const data = new Uint8Array(await file.arrayBuffer());
|
||||
@@ -153,6 +160,7 @@ export async function createFileDump(
|
||||
file.size,
|
||||
isPrivate ? 1 : 0,
|
||||
);
|
||||
setDumpCategories(dumpId, dedupedCategoryIds);
|
||||
} catch (err) {
|
||||
// Roll back the file if DB insert fails
|
||||
await Deno.remove(`${DUMPS_DIR}/${dumpId}`).catch(() => {});
|
||||
@@ -173,6 +181,7 @@ export async function createFileDump(
|
||||
voteCount: 0,
|
||||
commentCount: 0,
|
||||
isPrivate,
|
||||
categoryIds: dedupedCategoryIds,
|
||||
};
|
||||
if (!isPrivate) {
|
||||
broadcastNewDump(dump);
|
||||
@@ -205,6 +214,7 @@ export function getDump(dumpId: string, requestingUserId?: string): Dump {
|
||||
if (dump.isPrivate && dump.userId !== requestingUserId) {
|
||||
throw new APIException(APIErrorCode.NOT_FOUND, 404, "Dump not found");
|
||||
}
|
||||
attachCategoryIds([dump]);
|
||||
return dump;
|
||||
}
|
||||
|
||||
@@ -229,10 +239,9 @@ export function searchDumps(
|
||||
const totalRow = db.prepare(
|
||||
`SELECT COUNT(*) as count FROM dumps WHERE (is_private = 0 OR user_id = ?) AND ${searchClause};`,
|
||||
).get(requestingUserId, ...searchParams) as { count: number } | undefined;
|
||||
return {
|
||||
items: rows.filter(isDumpRow).map(dumpRowToApi),
|
||||
total: totalRow?.count ?? 0,
|
||||
};
|
||||
const items = rows.filter(isDumpRow).map(dumpRowToApi);
|
||||
attachCategoryIds(items);
|
||||
return { items, total: totalRow?.count ?? 0 };
|
||||
} else {
|
||||
const rows = db.prepare(
|
||||
`SELECT ${SELECT_COLS} FROM dumps WHERE is_private = 0 AND ${searchClause} ORDER BY created_at DESC LIMIT ? OFFSET ?;`,
|
||||
@@ -240,10 +249,9 @@ export function searchDumps(
|
||||
const totalRow = db.prepare(
|
||||
`SELECT COUNT(*) as count FROM dumps WHERE is_private = 0 AND ${searchClause};`,
|
||||
).get(...searchParams) as { count: number } | undefined;
|
||||
return {
|
||||
items: rows.filter(isDumpRow).map(dumpRowToApi),
|
||||
total: totalRow?.count ?? 0,
|
||||
};
|
||||
const items = rows.filter(isDumpRow).map(dumpRowToApi);
|
||||
attachCategoryIds(items);
|
||||
return { items, total: totalRow?.count ?? 0 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +285,9 @@ export function listDumps(
|
||||
);
|
||||
}
|
||||
|
||||
return { items: rows.map(dumpRowToApi), total: totalRow?.count ?? 0 };
|
||||
const items = rows.map(dumpRowToApi);
|
||||
attachCategoryIds(items);
|
||||
return { items, total: totalRow?.count ?? 0 };
|
||||
}
|
||||
|
||||
export async function updateDump(
|
||||
@@ -314,6 +324,13 @@ export async function updateDump(
|
||||
now.toISOString(),
|
||||
dumpId,
|
||||
);
|
||||
if (request.categoryIds !== undefined) {
|
||||
const ids = [...new Set(request.categoryIds)];
|
||||
setDumpCategories(dumpId, ids);
|
||||
updatedDump.categoryIds = ids;
|
||||
} else {
|
||||
attachCategoryIds([updatedDump]);
|
||||
}
|
||||
if (updatedDump.isPrivate && !dump.isPrivate) broadcastDumpDeleted(dumpId);
|
||||
else if (!updatedDump.isPrivate) broadcastDumpUpdated(updatedDump);
|
||||
if (updatedDump.comment) {
|
||||
@@ -382,6 +399,14 @@ export async function updateDump(
|
||||
throw new APIException(APIErrorCode.NOT_FOUND, 404, "Dump not found");
|
||||
}
|
||||
|
||||
if (request.categoryIds !== undefined) {
|
||||
const ids = [...new Set(request.categoryIds)];
|
||||
setDumpCategories(dumpId, ids);
|
||||
updatedDump.categoryIds = ids;
|
||||
} else {
|
||||
attachCategoryIds([updatedDump]);
|
||||
}
|
||||
|
||||
if (updatedDump.isPrivate && !dump.isPrivate) broadcastDumpDeleted(dumpId);
|
||||
else if (!updatedDump.isPrivate) broadcastDumpUpdated(updatedDump);
|
||||
if (updatedDump.comment) {
|
||||
@@ -403,6 +428,7 @@ export async function replaceFileDump(
|
||||
file: File,
|
||||
comment: string | undefined,
|
||||
title?: string,
|
||||
categoryIds?: string[],
|
||||
): Promise<Dump> {
|
||||
if (!isAllowedMime(file.type)) {
|
||||
throw new APIException(
|
||||
@@ -455,6 +481,14 @@ export async function replaceFileDump(
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (categoryIds !== undefined) {
|
||||
const ids = [...new Set(categoryIds)];
|
||||
setDumpCategories(dumpId, ids);
|
||||
dump.categoryIds = ids;
|
||||
} else {
|
||||
attachCategoryIds([dump]);
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
notifyMentions(dump.userId, comment, "dump", dumpId, finalTitle);
|
||||
linkAttachments(comment, dumpId);
|
||||
@@ -493,7 +527,9 @@ export function getDumpsByUser(
|
||||
"Malformed dump data",
|
||||
);
|
||||
}
|
||||
return { items: rows.map(dumpRowToApi), total: totalRow?.count ?? 0 };
|
||||
const items = rows.map(dumpRowToApi);
|
||||
attachCategoryIds(items);
|
||||
return { items, total: totalRow?.count ?? 0 };
|
||||
}
|
||||
|
||||
export function getVotedDumpsByUser(
|
||||
@@ -544,7 +580,9 @@ export function getVotedDumpsByUser(
|
||||
"Malformed dump data",
|
||||
);
|
||||
}
|
||||
return { items: rows.map(dumpRowToApi), total: totalRow?.count ?? 0 };
|
||||
const items = rows.map(dumpRowToApi);
|
||||
attachCategoryIds(items);
|
||||
return { items, total: totalRow?.count ?? 0 };
|
||||
}
|
||||
|
||||
export async function refreshDumpMetadata(dumpId: string): Promise<Dump> {
|
||||
|
||||
Reference in New Issue
Block a user