fix: sync only API key owner public memos
This commit is contained in:
@@ -3,9 +3,9 @@ import { Attachments } from "./attachments";
|
||||
import { Markdown } from "./markdown";
|
||||
import { canonicalTags } from "@/lib/tags";
|
||||
|
||||
export type PublicPost = { id: number; source_id: number | null; content: string; tags_json: string; attachments_json: string; created_at: string; remote_created_at?: string | null; username: string; name: string | null; source_base_url: string | null; comment_count: number; reaction_count: number };
|
||||
export type PublicPost = { id: number; source_id: number | null; origin: string; content: string; tags_json: string; attachments_json: string; created_at: string; remote_created_at?: string | null; username: string; name: string | null; remote_display_name?: string | null; source_base_url: string | null; comment_count: number; reaction_count: number };
|
||||
|
||||
export function PostCard({ post }: { post: PublicPost }) {
|
||||
let tags: string[] = []; try { tags = canonicalTags(JSON.parse(post.tags_json)); } catch { /* Ignore malformed legacy tags. */ }
|
||||
const publishedAt = post.remote_created_at || post.created_at; return <article className="card"><div className="space"><Link className="meta post-name-link" href={`/posts/${post.id}`}>@{post.username}{post.name ? ` · ${post.name}` : ""}</Link><span className="meta">{new Date(publishedAt).toLocaleString("zh-TW")}</span></div><Markdown content={post.content} tags={tags} compact /><Attachments json={post.attachments_json} sourceBaseUrl={post.source_base_url} compact /><div className="row">{tags.map((tag) => <Link className="tag" href={`/tags/${encodeURIComponent(tag)}`} key={tag}>#{tag}</Link>)}{post.source_id && <Link className="tag" href={`/sources/${post.source_id}`}>來源</Link>}<Link href={`/posts/${post.id}`}>閱讀全文 · 💬 {post.comment_count} 🙂 {post.reaction_count}</Link></div></article>;
|
||||
const publishedAt = post.remote_created_at || post.created_at; const author = post.origin === "memos" ? (post.remote_display_name || post.name || post.username) : post.username; return <article className="card"><div className="space"><Link className="meta post-name-link" href={`/posts/${post.id}`}>@{author}{post.name ? ` · ${post.name}` : ""}</Link><span className="meta">{new Date(publishedAt).toLocaleString("zh-TW")}</span></div><Markdown content={post.content} tags={tags} compact /><Attachments json={post.attachments_json} sourceBaseUrl={post.source_base_url} compact /><div className="row">{tags.map((tag) => <Link className="tag" href={`/tags/${encodeURIComponent(tag)}`} key={tag}>#{tag}</Link>)}{post.source_id && <Link className="tag" href={`/sources/${post.source_id}`}>來源</Link>}<Link href={`/posts/${post.id}`}>閱讀全文 · 💬 {post.comment_count} 🙂 {post.reaction_count}</Link></div></article>;
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export default async function Home({ searchParams }: { searchParams: Promise<Que
|
||||
if (q) { where.push("p.content LIKE ?"); args.push(`%${q}%`); } if (tag) { where.push("p.tags_json LIKE ?"); args.push(`%${JSON.stringify(tag).slice(1, -1)}%`); } if (author) { where.push("u.username LIKE ?"); args.push(`%${author}%`); } if (sourceId) { where.push("s.id=?"); args.push(sourceId); } if (from) { where.push("date(COALESCE(p.remote_created_at,p.created_at)) >= date(?)"); args.push(from); } if (to) { where.push("date(COALESCE(p.remote_created_at,p.created_at)) <= date(?)"); args.push(to); } if (attachments) where.push("p.attachments_json <> '[]'");
|
||||
const joins = " FROM posts p JOIN users u ON u.id=p.author_id LEFT JOIN sources s ON s.id=p.source_id "; const predicate = ` WHERE ${where.join(" AND ")}`;
|
||||
const total = Number((db.prepare(`SELECT count(*) count${joins}${predicate}`).get(...args) as { count: number }).count); const pages = Math.max(1, Math.ceil(total / pageSize)); const safePage = Math.min(page, pages);
|
||||
const posts = db.prepare(`SELECT p.*,u.username,s.name,s.base_url AS source_base_url,(SELECT count(*) FROM comments c WHERE c.post_id=p.id AND c.hidden=0) comment_count,(SELECT count(*) FROM reactions r WHERE r.post_id=p.id) reaction_count${joins}${predicate} ORDER BY COALESCE(p.remote_created_at,p.created_at) DESC LIMIT ? OFFSET ?`).all(...args, pageSize, (safePage - 1) * pageSize) as PublicPost[];
|
||||
const posts = db.prepare(`SELECT p.*,u.username,s.name,s.remote_display_name,s.base_url AS source_base_url,(SELECT count(*) FROM comments c WHERE c.post_id=p.id AND c.hidden=0) comment_count,(SELECT count(*) FROM reactions r WHERE r.post_id=p.id) reaction_count${joins}${predicate} ORDER BY COALESCE(p.remote_created_at,p.created_at) DESC LIMIT ? OFFSET ?`).all(...args, pageSize, (safePage - 1) * pageSize) as PublicPost[];
|
||||
const sources = db.prepare("SELECT id,name FROM sources WHERE is_enabled=1 ORDER BY name").all() as { id: number; name: string }[];
|
||||
const params = new URLSearchParams(); for (const [key, value] of Object.entries(query)) if (value && key !== "page") params.set(key, value); const pageHref = (target: number) => { const next = new URLSearchParams(params); next.set("page", String(target)); return `/?${next}`; };
|
||||
return <><section className="space"><div><h1>公開 Memos Hub</h1><p className="muted">聚合朋友們公開分享的筆記。</p></div><Link className="button" href="/dashboard">發佈/連接來源</Link></section><form className="search-form" method="get"><input name="q" defaultValue={q} placeholder="搜尋公開貼文" /><input name="tag" defaultValue={tag} placeholder="標籤" /><input name="author" defaultValue={author} placeholder="作者" /><select name="source" defaultValue={sourceId || ""}><option value="">所有來源</option>{sources.map((source) => <option key={source.id} value={source.id}>{source.name}</option>)}</select><label>從<input name="from" type="date" defaultValue={from} /></label><label>到<input name="to" type="date" defaultValue={to} /></label><label className="check"><input name="attachments" type="checkbox" value="1" defaultChecked={attachments} />只看附件</label><button>搜尋</button></form><p className="meta">共 {total} 篇公開貼文</p>{posts.length ? posts.map((post) => <PostCard post={post} key={post.id} />) : <p className="muted">尚無符合的公開貼文。</p>}{pages > 1 && <nav className="pagination" aria-label="貼文分頁">{safePage > 1 && <Link href={pageHref(safePage - 1)}>← 上一頁</Link>}<span>第 {safePage}/{pages} 頁</span>{safePage < pages && <Link href={pageHref(safePage + 1)}>下一頁 →</Link>}</nav>}</>;
|
||||
|
||||
@@ -6,6 +6,6 @@ import { PostCard, type PublicPost } from "@/app/components/post-card";
|
||||
export const dynamic = "force-dynamic";
|
||||
export default async function SourcePage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id: rawId } = await params; const id = Number(rawId); const source = db.prepare("SELECT id,name,base_url,remote_display_name,remote_avatar_url FROM sources WHERE id=?").get(id) as { id: number; name: string; base_url: string; remote_display_name: string | null; remote_avatar_url: string | null } | undefined; if (!source) notFound();
|
||||
const posts = db.prepare("SELECT p.*,u.username,s.name,s.base_url AS source_base_url,(SELECT count(*) FROM comments c WHERE c.post_id=p.id AND c.hidden=0) comment_count,(SELECT count(*) FROM reactions r WHERE r.post_id=p.id) reaction_count FROM posts p JOIN users u ON u.id=p.author_id LEFT JOIN sources s ON s.id=p.source_id WHERE p.source_id=? AND p.visibility='PUBLIC' AND p.hidden=0 ORDER BY COALESCE(p.remote_created_at,p.created_at) DESC LIMIT 100").all(id) as PublicPost[];
|
||||
const posts = db.prepare("SELECT p.*,u.username,s.name,s.remote_display_name,s.base_url AS source_base_url,(SELECT count(*) FROM comments c WHERE c.post_id=p.id AND c.hidden=0) comment_count,(SELECT count(*) FROM reactions r WHERE r.post_id=p.id) reaction_count FROM posts p JOIN users u ON u.id=p.author_id LEFT JOIN sources s ON s.id=p.source_id WHERE p.source_id=? AND p.visibility='PUBLIC' AND p.hidden=0 ORDER BY COALESCE(p.remote_created_at,p.created_at) DESC LIMIT 100").all(id) as PublicPost[];
|
||||
return <><p><Link href="/">← 探索</Link></p><h1>{source.name}</h1><p className="meta">{source.remote_avatar_url && <img className="avatar" src={source.remote_avatar_url} alt="" />} {source.remote_display_name || "Memos"}<br />{source.base_url} · {posts.length} 篇公開貼文</p>{posts.map((post) => <PostCard key={post.id} post={post} />)}</>;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ import { PostCard, type PublicPost } from "@/app/components/post-card";
|
||||
export const dynamic = "force-dynamic";
|
||||
export default async function TagPage({ params }: { params: Promise<{ tag: string }> }) {
|
||||
const { tag: encoded } = await params; const tag = decodeURIComponent(encoded).trim(); if (!tag) notFound();
|
||||
const posts = db.prepare("SELECT p.*,u.username,s.name,s.base_url AS source_base_url,(SELECT count(*) FROM comments c WHERE c.post_id=p.id AND c.hidden=0) comment_count,(SELECT count(*) FROM reactions r WHERE r.post_id=p.id) reaction_count FROM posts p JOIN users u ON u.id=p.author_id LEFT JOIN sources s ON s.id=p.source_id WHERE p.visibility='PUBLIC' AND p.hidden=0 AND p.tags_json LIKE ? ORDER BY COALESCE(p.remote_created_at,p.created_at) DESC LIMIT 100").all(`%${JSON.stringify(tag).slice(1, -1)}%`) as PublicPost[];
|
||||
const posts = db.prepare("SELECT p.*,u.username,s.name,s.remote_display_name,s.base_url AS source_base_url,(SELECT count(*) FROM comments c WHERE c.post_id=p.id AND c.hidden=0) comment_count,(SELECT count(*) FROM reactions r WHERE r.post_id=p.id) reaction_count FROM posts p JOIN users u ON u.id=p.author_id LEFT JOIN sources s ON s.id=p.source_id WHERE p.visibility='PUBLIC' AND p.hidden=0 AND p.tags_json LIKE ? ORDER BY COALESCE(p.remote_created_at,p.created_at) DESC LIMIT 100").all(`%${JSON.stringify(tag).slice(1, -1)}%`) as PublicPost[];
|
||||
return <><p><Link href="/">← 探索</Link></p><h1>#{tag}</h1><p className="muted">{posts.length} 篇公開貼文</p>{posts.map((post) => <PostCard key={post.id} post={post} />)}</>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user