Next.js API无法转发同步URL至PouchDB,数据同步失败
CouchDB 配置文件(local.ini)
[admins] admin = <REDACTED> [couchdb] database_dir = <REDACTED> view_index_dir = <REDACTED> uri_file = <REDACTED> uuid = <REDACTED> [cluster] n = 1 q = 2 [couch_peruser] enable = true delete_dbs = true database_prefix = <REDACTED> [log] writer = file file = <REDACTED> [chttpd_auth] secret = <REDACTED> [chttpd] enable_cors = true bind_address = 0.0.0.0 authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, default_authentication_handler}, {chttpd_auth, proxy_authentication_handler} [jwt_keys] hmac:_default = <REDACTED> required_claims = exp, sub roles_claim_path = _couchdb.roles [cors] credentials = true methods = GET, PUT, POST, HEAD, DELETE headers = accept, authorization, content-type, origin, referer origins = * [httpd] bind_address = 0.0.0.0
问题描述
开发Next.js项目时,采用Cookie身份认证,通过自定义API路由与CouchDB后端同步数据,但当前无法正常获取数据和启动同步。
环境配置
Next.js API 路由(api/sync)
该路由负责转发请求到CouchDB完成数据同步:
import { NextRequest, NextResponse } from "next/server"; import { cookies } from "next/headers"; export const handler = async (request: NextRequest) => { const cookieStore = await cookies(); const authtoken = cookieStore.get("access_token"); const database = cookieStore.get("database_name"); if (!authtoken || !database) { return NextResponse.json( { error: "Missing authentication token or database" }, { status: 400 }, ); } try { const syncResponse = await fetch( `http://127.0.0.1:5984/${database.value}`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${authtoken.value}`, Referer: "http://localhost:3001", }, }, ); if (!syncResponse.ok) { const errorDetails = await syncResponse.text(); console.error( `Sync request failed. Status: ${syncResponse.status}, Details: ${errorDetails}`, ); return NextResponse.json( { error: `Sync failed with status ${syncResponse.status}`, details: errorDetails, }, { status: syncResponse.status }, ); } const syncData = await syncResponse.json(); return NextResponse.json(syncData); } catch (error: any) { console.error("Error forwarding sync request:", error); return NextResponse.json({ error: error.message }, { status: 500 }); } }; export { handler as GET, handler as POST };
PouchDB 同步代码
通过调用上述API实现本地与后端的数据同步:
import PouchDB from "pouchdb"; import PouchDBFind from "pouchdb-find"; import { QueryClient } from "@tanstack/react-query"; // 匿名化常量(替换为通用名称) import { docType1, docType2, docType3, docType4, docType5, docType6, docType7, } from "./constants/global.constants"; PouchDB.plugin(PouchDBFind); export const db = <T extends {}>( queryClient?: QueryClient, ): PouchDB.Database<T> => { let invalidateTimeout: NodeJS.Timeout | null = null; // 防抖失效函数 const debouncedInvalidateQueries = (queryClient: QueryClient | undefined) => { if (invalidateTimeout) clearTimeout(invalidateTimeout); invalidateTimeout = setTimeout(() => { const keys = [ docType1, docType2, docType3, docType4, docType5, docType6, docType7, ]; keys.forEach((key) => queryClient?.invalidateQueries({ queryKey: [key] }), ); }, 500); // 可调整防抖时间 }; let wasPaused = false; // 使用通用API URL,避免暴露敏感服务器地址 const url = "http://localhost:3001/api/sync/"; // 生产环境修改或使用环境变量 const opts = { live: true, retry: true }; // 使用通用数据库名称隐藏应用细节 const localDb = new PouchDB<T>("generic-local-db", { skip_setup: true }); try { localDb.replicate.from(url).on("complete", function (info) { // 双向、持续、可重试的同步 localDb .sync(url, opts) .on("change", (info) => { console.log("Sync change:", info); debouncedInvalidateQueries(queryClient); // 防抖失效 }) .on("paused", (err) => { if (!wasPaused) { console.log("Sync paused:", err); if (err) { console.error("Sync paused due to error:", err); } else { console.log("Sync paused, possibly up-to-date."); } wasPaused = true; // 标记已暂停 } }) .on("active", () => { if (wasPaused) { console.log("Sync resumed."); wasPaused = false; // 标记已恢复 } }) .on("denied", (err) => { console.error("Sync denied due to permissions:", err); }) .on("error", (err) => { console.error("Error during one-off sync:", err); }); }); return localDb; } catch (error) { console.error("Error during one-off sync:", error); throw error; } };
错误日志
GET /dashboard/items 200 (409ms) GET /api/sync 200 (368ms) GET /api/sync 200 (169ms) GET /api/sync 200 (175ms) GET /api 404 (235ms) GET /api 404 (196ms) GET /api 404 (194ms) GET /api/sync 200 (97ms) GET /api/sync 200 (118ms) GET /api/sync/_changes 404 (184ms) GET /api/sync 200 (224ms) GET /api/sync/_changes 404 (211ms) GET /api/sync/_changes 404 (143ms)
临时可行方案
硬编码访问令牌时同步可正常工作,但这种方式会导致Cookie在浏览器中可被JavaScript访问,存在安全风险:
const remoteDb = new PouchDB<T>(remoteDbUrl, { skip_setup: true, fetch: function (url, opts) { const headers = new Headers(opts?.headers); headers.set("Content-Type", "application/json"); headers.set("Authorization", `Bearer ${accessToken}`); return PouchDB.fetch(url, { ...opts, headers }); }, });
内容的提问来源于stack exchange,提问作者Tony Ngomana
相关产品推荐
相关产品推荐

