Browse Source

feat(server/timeline): public preview and paginated list by slug

Made-with: Cursor
main
npmrun 8 hours ago
parent
commit
b9b443f30c
  1. 6
      server/api/public/profile/[publicSlug].get.ts
  2. 60
      server/service/timeline/index.ts

6
server/api/public/profile/[publicSlug].get.ts

@ -2,7 +2,7 @@ import { dbGlobal } from "drizzle-pkg/lib/db";
import { users } from "drizzle-pkg/lib/schema/auth";
import { and, eq } from "drizzle-orm";
import { getPublicPostsPreviewBySlug } from "#server/service/posts";
import { listPublicTimelineBySlug } from "#server/service/timeline";
import { getPublicTimelinePreviewBySlug } from "#server/service/timeline";
import { listPublicRssItemsBySlug } from "#server/service/rss";
import { parseSocialLinksJson } from "#server/service/profile";
@ -33,7 +33,7 @@ export default defineEventHandler(async (event) => {
bio: { markdown: string | null } | null;
links: typeof links;
posts: Awaited<ReturnType<typeof getPublicPostsPreviewBySlug>>;
timeline: Awaited<ReturnType<typeof listPublicTimelineBySlug>>;
timeline: Awaited<ReturnType<typeof getPublicTimelinePreviewBySlug>>;
rssItems: Awaited<ReturnType<typeof listPublicRssItemsBySlug>>;
} = {
user: {
@ -44,7 +44,7 @@ export default defineEventHandler(async (event) => {
bio: null,
links,
posts: await getPublicPostsPreviewBySlug(publicSlug),
timeline: await listPublicTimelineBySlug(publicSlug),
timeline: await getPublicTimelinePreviewBySlug(publicSlug),
rssItems: await listPublicRssItemsBySlug(publicSlug),
};

60
server/service/timeline/index.ts

@ -1,8 +1,10 @@
import { dbGlobal } from "drizzle-pkg/lib/db";
import { timelineEvents } from "drizzle-pkg/lib/schema/content";
import { users } from "drizzle-pkg/lib/schema/auth";
import { and, desc, eq } from "drizzle-orm";
import { and, count, desc, eq } from "drizzle-orm";
import { PUBLIC_LIST_PAGE_SIZE, PUBLIC_PREVIEW_LIMIT } from "#server/constants/public-profile-lists";
import { visibilitySchema, type Visibility } from "#server/constants/visibility";
import { normalizePublicListPage } from "#server/utils/public-pagination";
import { visibilityShareToken } from "#server/utils/share-token";
import { nextIntegerId } from "#server/utils/sqlite-id";
@ -100,20 +102,54 @@ export async function deleteTimelineEvent(userId: number, id: number) {
return true;
}
export async function listPublicTimelineBySlug(publicSlug: string) {
const rows = await dbGlobal
.select({ ev: timelineEvents })
.from(timelineEvents)
.innerJoin(users, eq(timelineEvents.userId, users.id))
.where(
and(
function publicTimelineListWhere(publicSlug: string) {
return and(
eq(users.publicSlug, publicSlug),
eq(users.status, "active"),
eq(timelineEvents.visibility, "public"),
),
)
.orderBy(desc(timelineEvents.occurredOn), desc(timelineEvents.id));
return rows.map((r) => r.ev);
);
}
export async function getPublicTimelinePreviewBySlug(publicSlug: string) {
const whereClause = publicTimelineListWhere(publicSlug);
const [countRows, rows] = await Promise.all([
dbGlobal
.select({ total: count() })
.from(timelineEvents)
.innerJoin(users, eq(timelineEvents.userId, users.id))
.where(whereClause),
dbGlobal
.select({ ev: timelineEvents })
.from(timelineEvents)
.innerJoin(users, eq(timelineEvents.userId, users.id))
.where(whereClause)
.orderBy(desc(timelineEvents.occurredOn), desc(timelineEvents.id))
.limit(PUBLIC_PREVIEW_LIMIT),
]);
return { items: rows.map((r) => r.ev), total: countRows[0]?.total ?? 0 };
}
export async function getPublicTimelinePageBySlug(publicSlug: string, pageRaw: unknown) {
const page = normalizePublicListPage(pageRaw);
const pageSize = PUBLIC_LIST_PAGE_SIZE;
const offset = (page - 1) * pageSize;
const whereClause = publicTimelineListWhere(publicSlug);
const [countRows, rows] = await Promise.all([
dbGlobal
.select({ total: count() })
.from(timelineEvents)
.innerJoin(users, eq(timelineEvents.userId, users.id))
.where(whereClause),
dbGlobal
.select({ ev: timelineEvents })
.from(timelineEvents)
.innerJoin(users, eq(timelineEvents.userId, users.id))
.where(whereClause)
.orderBy(desc(timelineEvents.occurredOn), desc(timelineEvents.id))
.limit(pageSize)
.offset(offset),
]);
return { items: rows.map((r) => r.ev), total: countRows[0]?.total ?? 0, page, pageSize };
}
export async function getUnlistedTimeline(publicSlug: string, shareToken: string) {

Loading…
Cancel
Save