112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
// app/api/db-client.server.ts
|
||
import { getUserSession } from '~/api/login/auth.server';
|
||
import { runWithContext } from './postgrest-client';
|
||
|
||
/**
|
||
* 在认证上下文中运行
|
||
*
|
||
* 所有在此上下文中调用的 postgrest 函数(postgrestGet/Post/Put/Delete)
|
||
* 都会自动获取并添加 JWT Token 到 Authorization 头部
|
||
*
|
||
* @param request Remix Request 对象
|
||
* @param fn 要在认证上下文中执行的函数
|
||
* @returns 函数执行结果
|
||
* @throws 如果用户未登录则抛出错误
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* export async function loader({ request }: LoaderFunctionArgs) {
|
||
* return await runInAuthContext(request, async () => {
|
||
* // 所有 postgrest 调用自动带 JWT
|
||
* const users = await postgrestGet('users', { limit: 10 });
|
||
* const docs = await postgrestGet('documents', { filter: { status: 'eq.0' } });
|
||
*
|
||
* return json({ users: users.data, docs: docs.data });
|
||
* });
|
||
* }
|
||
* ```
|
||
*/
|
||
export async function runInAuthContext<T>(
|
||
request: Request,
|
||
fn: () => T | Promise<T>
|
||
): Promise<T> {
|
||
const { frontendJWT, isAuthenticated } = await getUserSession(request);
|
||
|
||
if (!isAuthenticated || !frontendJWT) {
|
||
throw new Error('用户未登录,无法执行需要认证的操作');
|
||
}
|
||
|
||
// 在上下文中设置 JWT,所有 postgrest 调用都会自动使用
|
||
return runWithContext({ jwt: frontendJWT }, fn);
|
||
}
|
||
|
||
/**
|
||
* 在公开上下文中运行(不需要认证)
|
||
*
|
||
* 用于公开数据访问,不会添加 JWT Token
|
||
*
|
||
* @param fn 要执行的函数
|
||
* @returns 函数执行结果
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* export async function loader() {
|
||
* return runInPublicContext(async () => {
|
||
* const articles = await postgrestGet('public_articles', {
|
||
* filter: { published: 'eq.true' }
|
||
* });
|
||
* return json({ articles: articles.data });
|
||
* });
|
||
* }
|
||
* ```
|
||
*/
|
||
export function runInPublicContext<T>(
|
||
fn: () => T | Promise<T>
|
||
): T | Promise<T> {
|
||
// 在空上下文中运行,不设置 JWT
|
||
return runWithContext({}, fn);
|
||
}
|
||
|
||
/**
|
||
* 在可选认证上下文中运行
|
||
*
|
||
* 如果用户已登录,会自动添加 JWT;
|
||
* 如果用户未登录,则不添加 JWT(不会抛出错误)
|
||
*
|
||
* @param request Remix Request 对象
|
||
* @param fn 要执行的函数
|
||
* @returns 函数执行结果
|
||
*
|
||
* @example
|
||
* ```typescript
|
||
* export async function loader({ request }: LoaderFunctionArgs) {
|
||
* return await runInOptionalAuthContext(request, async () => {
|
||
* // 如果用户登录,会带 JWT(可能看到更多内容)
|
||
* // 如果用户未登录,不带 JWT(看到基础内容)
|
||
* const content = await postgrestGet('content', { limit: 20 });
|
||
* return json({ content: content.data });
|
||
* });
|
||
* }
|
||
* ```
|
||
*/
|
||
export async function runInOptionalAuthContext<T>(
|
||
request: Request,
|
||
fn: () => T | Promise<T>
|
||
): Promise<T> {
|
||
try {
|
||
const { frontendJWT, isAuthenticated } = await getUserSession(request);
|
||
|
||
if (isAuthenticated && frontendJWT) {
|
||
// 用户已登录,使用 JWT
|
||
return runWithContext({ jwt: frontendJWT }, fn);
|
||
}
|
||
} catch (error) {
|
||
// 获取会话失败,继续以公开方式运行
|
||
console.warn('获取用户会话失败,以公开方式运行:', error);
|
||
}
|
||
|
||
// 用户未登录或获取会话失败,以公开方式运行
|
||
return runWithContext({}, fn);
|
||
}
|
||
|