exportasyncfunctionPOST(request: Request){try{requireSameOrigin(request);constuser=awaitrequireUser();constform=awaitrequest.formData();consttoken=String(form.get("token")||"");consthash=createHash("sha256").update(token).digest("hex");constinvite=db.prepare("SELECT id,source_id,role FROM source_invites WHERE token_hash=? AND used_at IS NULL AND expires_at>CURRENT_TIMESTAMP").get(hash)as{id: number;source_id: number;role: string}|undefined;if(!invite)thrownewError("邀請不存在、已使用或已過期");db.transaction(()=>{db.prepare("INSERT INTO source_members(source_id,user_id,role) VALUES(?,?,?) ON CONFLICT(source_id,user_id) DO UPDATE SET role=excluded.role").run(invite.source_id,user.id,invite.role);db.prepare("UPDATE source_invites SET used_at=CURRENT_TIMESTAMP WHERE id=?").run(invite.id);})();returnNextResponse.redirect(externalUrl(request,"/dashboard?source=joined"));}catch(error){returnNextResponse.redirect(externalUrl(request,"/dashboard?error="+encodeURIComponent(errorinstanceofError?error.message:"invite")));}}
try{requireSameOrigin(request);constuser=awaitrequireUser();const{id: rawId}=awaitparams;constid=Number(rawId);constsource=db.prepare("SELECT id FROM sources WHERE id=? AND user_id=?").get(id,user.id);if(!source)returnNextResponse.json({error:"Not found"},{status: 404});const{role}=awaitrequest.json()as{role?: string};if(!['viewer','editor'].includes(role||''))returnNextResponse.json({error:"Invalid role"},{status: 400});consttoken=randomBytes(24).toString("base64url");consthash=createHash("sha256").update(token).digest("hex");db.prepare("INSERT INTO source_invites(source_id,token_hash,role,expires_at,created_by) VALUES(?,?,?,datetime('now','+7 days'),?)").run(id,hash,role,user.id);constorigin=(process.env.NEXT_PUBLIC_APP_URL||newURL(request.url).origin).replace(/\/$/,"");returnNextResponse.json({url:`${origin}/invite/${token}`});}catch{returnNextResponse.json({error:"Unauthorized"},{status: 401});}
constsourceRows=db.prepare("SELECT s.id,s.name,s.base_url,s.integration_type,s.rss_feed_url,s.sync_status,s.last_synced_at,s.last_error,s.webhook_secret_hash,s.webhook_mode,s.last_webhook_at,s.user_id AS owner_id,s.is_enabled,s.disabled_at,s.sync_tags_json,s.sync_from,s.sync_to,s.sync_attachment_mode,s.sync_batch_size,s.sync_max_posts,s.sync_cursor,s.sync_imported_count,s.attachment_storage_mode,s.attachment_cache_limit_bytes,s.attachment_cache_error,s.remote_display_name,s.remote_avatar_url,s.last_connection_at,s.last_connection_error FROM sources s JOIN source_members sm ON sm.source_id=s.id WHERE sm.user_id=? ORDER BY s.id DESC").all(user.id)asSource[];
constsourceRows=db.prepare("SELECT s.id,s.name,s.base_url,s.integration_type,s.rss_feed_url,s.sync_status,s.last_synced_at,s.last_error,s.webhook_secret_hash,s.webhook_mode,s.last_webhook_at,s.user_id AS owner_id,sm.role AS membership_role,s.is_enabled,s.disabled_at,s.sync_tags_json,s.sync_from,s.sync_to,s.sync_attachment_mode,s.sync_batch_size,s.sync_max_posts,s.sync_cursor,s.sync_imported_count,s.attachment_storage_mode,s.attachment_cache_limit_bytes,s.attachment_cache_error,s.remote_display_name,s.remote_avatar_url,s.last_connection_at,s.last_connection_error FROM sources s JOIN source_members sm ON sm.source_id=s.id WHERE sm.user_id=? ORDER BY s.id DESC").all(user.id)asSource[];
constsources=sourceRows.map((source)=>({...source,syncTags:(()=>{try{returnJSON.parse(source.sync_tags_json)asstring[];}catch{return[];}})(),members: db.prepare("SELECT u.username,u.id,sm.role FROM source_members sm JOIN users u ON u.id=sm.user_id WHERE sm.source_id=? ORDER BY sm.role DESC,u.username").all(source.id)as{username: string;id: number;role: string}[],jobs: db.prepare("SELECT id,kind,trigger,status,attempts,last_error,created_at,finished_at FROM sync_jobs WHERE source_id=? ORDER BY id DESC LIMIT 5").all(source.id)asJob[]}));
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.