@@ -1,6 +1,7 @@
import { redirect } from "next/navigation" ;
import { redirect } from "next/navigation" ;
import { getSession } from "@/lib/auth" ;
import { getSession } from "@/lib/auth" ;
import { db } from "@/lib/db" ;
import { db } from "@/lib/db" ;
import { checkRuntimeConfig } from "@/lib/config" ;
export const dynamic = "force-dynamic" ;
export const dynamic = "force-dynamic" ;
@@ -12,6 +13,7 @@ export default async function AdminPage({ searchParams }: { searchParams: Promis
const errors = db . prepare ( "SELECT scope,message,created_at FROM error_events ORDER BY id DESC LIMIT 30" ) . all ( ) as any [ ] ;
const errors = db . prepare ( "SELECT scope,message,created_at FROM error_events ORDER BY id DESC LIMIT 30" ) . all ( ) as any [ ] ;
const audits = db . prepare ( "SELECT a.action,a.target_type,a.target_id,a.metadata_json,a.created_at,u.username FROM audit_events a LEFT JOIN users u ON u.id=a.actor_user_id ORDER BY a.id DESC LIMIT 50" ) . all ( ) as any [ ] ;
const audits = db . prepare ( "SELECT a.action,a.target_type,a.target_id,a.metadata_json,a.created_at,u.username FROM audit_events a LEFT JOIN users u ON u.id=a.actor_user_id ORDER BY a.id DESC LIMIT 50" ) . all ( ) as any [ ] ;
const sources = db . prepare ( "SELECT s.id,s.name,s.base_url,s.sync_status,s.last_synced_at,s.is_enabled,count(sm.user_id) AS member_count FROM sources s LEFT JOIN source_members sm ON sm.source_id=s.id GROUP BY s.id ORDER BY s.id DESC" ) . all ( ) as any [ ] ;
const sources = db . prepare ( "SELECT s.id,s.name,s.base_url,s.sync_status,s.last_synced_at,s.is_enabled,count(sm.user_id) AS member_count FROM sources s LEFT JOIN source_members sm ON sm.source_id=s.id GROUP BY s.id ORDER BY s.id DESC" ) . all ( ) as any [ ] ;
const config = checkRuntimeConfig ( ) ;
return < > < h1 > 管 理 員 < / h1 > { query . updated && < p > 管 理 操 作 已 完 成 。 < / p > } { query . error && < p className = "error" > 管 理 操 作 未 完 成 。 < / p > }
return < > < h1 > 管 理 員 < / h1 > { query . updated && < p > 管 理 操 作 已 完 成 。 < / p > } { query . error && < p className = "error" > 管 理 操 作 未 完 成 。 < / p > }
< section className = "card" > < h2 > 待 審 核 檢 舉 < / h2 > { reports . length ? < ul className = "job-list" > { reports . map ( ( report ) = > < li key = { report . id } > < strong > 貼 文 # { report . post_id } < / strong > · 檢 舉 者 @ { report . username || "已刪除使用者" } < br / > { report . reason } < br / > < span className = "meta" > { report . content . slice ( 0 , 180 ) } · { new Date ( report . created_at + "Z" ) . toLocaleString ( "zh-TW" ) } < / span > < div className = "row" > < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = "hide-post" / > < input type = "hidden" name = "id" value = { report . post_id } / > < button className = "danger" > 隱 藏 貼 文 並 結 案 < / button > < / form > < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = "resolve-report" / > < input type = "hidden" name = "id" value = { report . id } / > < button > 保 留 貼 文 並 結 案 < / button > < / form > < / div > < / li > ) } < / ul > : < p className = "muted" > 沒 有 待 審 核 檢 舉 。 < / p > } < / section >
< section className = "card" > < h2 > 待 審 核 檢 舉 < / h2 > { reports . length ? < ul className = "job-list" > { reports . map ( ( report ) = > < li key = { report . id } > < strong > 貼 文 # { report . post_id } < / strong > · 檢 舉 者 @ { report . username || "已刪除使用者" } < br / > { report . reason } < br / > < span className = "meta" > { report . content . slice ( 0 , 180 ) } · { new Date ( report . created_at + "Z" ) . toLocaleString ( "zh-TW" ) } < / span > < div className = "row" > < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = "hide-post" / > < input type = "hidden" name = "id" value = { report . post_id } / > < button className = "danger" > 隱 藏 貼 文 並 結 案 < / button > < / form > < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = "resolve-report" / > < input type = "hidden" name = "id" value = { report . id } / > < button > 保 留 貼 文 並 結 案 < / button > < / form > < / div > < / li > ) } < / ul > : < p className = "muted" > 沒 有 待 審 核 檢 舉 。 < / p > } < / section >
< section className = "card" > < h2 > 使 用 者 < / h2 > < ul className = "job-list" > { users . map ( ( account ) = > < li key = { account . id } > < strong > @ { account . username } < / strong > · < span className = "tag" > { account . role } < / span > · { account . disabled ? "已停權" : "正常" } < div className = "row" > { account . id !== user . id && < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = { account . disabled ? "enable-user" : "disable-user" } / > < input type = "hidden" name = "id" value = { account . id } / > < button className = { account . disabled ? "" : "danger" } > { account . disabled ? "解除停權" : "停權" } < / button > < / form > } < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = "reset-password" / > < input type = "hidden" name = "id" value = { account . id } / > < input name = "password" type = "password" minLength = { 10 } required placeholder = "管理員重設密碼" / > < button > 重 設 密 碼 < / button > < / form > < / div > < / li > ) } < / ul > < / section >
< section className = "card" > < h2 > 使 用 者 < / h2 > < ul className = "job-list" > { users . map ( ( account ) = > < li key = { account . id } > < strong > @ { account . username } < / strong > · < span className = "tag" > { account . role } < / span > · { account . disabled ? "已停權" : "正常" } < div className = "row" > { account . id !== user . id && < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = { account . disabled ? "enable-user" : "disable-user" } / > < input type = "hidden" name = "id" value = { account . id } / > < button className = { account . disabled ? "" : "danger" } > { account . disabled ? "解除停權" : "停權" } < / button > < / form > } < form action = "/api/admin/moderate" method = "post" > < input type = "hidden" name = "action" value = "reset-password" / > < input type = "hidden" name = "id" value = { account . id } / > < input name = "password" type = "password" minLength = { 10 } required placeholder = "管理員重設密碼" / > < button > 重 設 密 碼 < / button > < / form > < / div > < / li > ) } < / ul > < / section >
@@ -19,5 +21,6 @@ export default async function AdminPage({ searchParams }: { searchParams: Promis
< section className = "card" > < h2 > 最 近 系 統 錯 誤 < / h2 > { errors . length ? < ul className = "job-list" > { errors . map ( ( error , index ) = > < li key = { index } > < strong > { error . scope } < / strong > · < span className = "error" > { error . message } < / span > < br / > < span className = "meta" > { new Date ( error . created_at + "Z" ) . toLocaleString ( "zh-TW" ) } < / span > < / li > ) } < / ul > : < p className = "muted" > 尚 無 記 錄 。 < / p > } < / section >
< section className = "card" > < h2 > 最 近 系 統 錯 誤 < / h2 > { errors . length ? < ul className = "job-list" > { errors . map ( ( error , index ) = > < li key = { index } > < strong > { error . scope } < / strong > · < span className = "error" > { error . message } < / span > < br / > < span className = "meta" > { new Date ( error . created_at + "Z" ) . toLocaleString ( "zh-TW" ) } < / span > < / li > ) } < / ul > : < p className = "muted" > 尚 無 記 錄 。 < / p > } < / section >
< section className = "card" > < h2 > 稽 核 紀 錄 < / h2 > { audits . length ? < ul className = "job-list" > { audits . map ( ( item , index ) = > < li key = { index } > < strong > { item . action } < / strong > · @ { item . username || "system" } · { item . target_type } # { item . target_id || "—" } < br / > < span className = "meta" > { new Date ( item . created_at + "Z" ) . toLocaleString ( "zh-TW" ) } < / span > < / li > ) } < / ul > : < p className = "muted" > 尚 無 稽 核 紀 錄 。 < / p > } < / section >
< section className = "card" > < h2 > 稽 核 紀 錄 < / h2 > { audits . length ? < ul className = "job-list" > { audits . map ( ( item , index ) = > < li key = { index } > < strong > { item . action } < / strong > · @ { item . username || "system" } · { item . target_type } # { item . target_id || "—" } < br / > < span className = "meta" > { new Date ( item . created_at + "Z" ) . toLocaleString ( "zh-TW" ) } < / span > < / li > ) } < / ul > : < p className = "muted" > 尚 無 稽 核 紀 錄 。 < / p > } < / section >
< section className = "card" > < h2 > 所 有 來 源 < / h2 > < ul className = "job-list" > { sources . map ( ( source ) = > < li key = { source . id } > < strong > { source . name } < / strong > · < span className = "tag" > { source . is_enabled ? source . sync_status : "disabled" } < / span > · 成 員 { source . member_count } < br / > < span className = "meta" > # { source . id } · { source . base_url } · 上 次 同 步 : { source . last_synced_at || "尚未完成" } < / span > < / li > ) } < / ul > < / section >
< section className = "card" > < h2 > 所 有 來 源 < / h2 > < ul className = "job-list" > { sources . map ( ( source ) = > < li key = { source . id } > < strong > { source . name } < / strong > · < span className = "tag" > { source . is_enabled ? source . sync_status : "disabled" } < / span > · 成 員 { source . member_count } < br / > < span className = "meta" > # { source . id } · { source . base_url } · 上 次 同 步 : { source . last_synced_at || "尚未完成" } < / span > < / li > ) } < / ul > < / section >
< section className = "card" > < h2 > 正 式 環 境 設 定 < / h2 > { config . ok ? < p > 設 定 檢 查 通 過 。 < / p > : < ul > { config . errors . map ( ( error ) = > < li className = "error" key = { error } > { error } < / li > ) } < / ul > } < / section >
< / > ;
< / > ;
}
}