feat(portal-shell): enable Apollo Client APQ + PQ manifest generator
Task 12-13 of portal-shell data abstraction plan (M3 security). APQ (Automatic Persisted Queries): - Add createPersistedQueryLink with sha256 to apollo-client.ts - Production: client sends only query hash, not plaintext query - Dev: NEXT_PUBLIC_APOLLO_APQ=false to disable for debugging - Prevents attackers from crafting arbitrary queries via DevTools PQ Manifest generator: - New scripts/generate-pq-manifest.ts iterates operations barrel - Outputs public/pq-manifest.json (sha256 -> query text whitelist) - prebuild hook: codegen + generate-pq-manifest before next build - 51 queries currently registered - crypto-hash dependency added - typecheck + lint (0 errors) + test (85/85) verified
This commit is contained in:
@@ -8,16 +8,28 @@
|
||||
* - 服务端(RSC):createApolloClient() 每次请求新建实例,ssrMode=true
|
||||
* - 客户端:getApolloClient() 单例,复用 InMemoryCache
|
||||
*
|
||||
* 关联:portal-shell spec §5.5 RSC 预取、§5.6 统一 Hook、M8 验收标准
|
||||
* APQ(Automatic Persisted Queries,v2.1 M3 安全加固):
|
||||
* - 生产环境前端只发 query hash(sha256),不发明文 query
|
||||
* - apollo-router 通过 hash 查找 pq-manifest.json 中的 query 文本
|
||||
* - 防止攻击者通过 DevTools 构造任意查询探测 schema
|
||||
* - 开发模式可设 NEXT_PUBLIC_APOLLO_APQ=false 关闭 APQ 便于调试
|
||||
*
|
||||
* 关联:portal-shell spec §4.1 APQ、§5.5 RSC 预取、§5.6 统一 Hook、M8 验收标准
|
||||
*/
|
||||
import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
|
||||
import { setContext } from "@apollo/client/link/context";
|
||||
import { createPersistedQueryLink } from "@apollo/client/link/persisted-queries";
|
||||
import { sha256 } from "crypto-hash";
|
||||
|
||||
const APOLLO_ROUTER_URL =
|
||||
process.env.NEXT_PUBLIC_APOLLO_ROUTER_URL ||
|
||||
process.env.APOLLO_ROUTER_URL ||
|
||||
"http://localhost:3000/graphql";
|
||||
|
||||
// 开发模式可关闭 APQ 便于调试(NEXT_PUBLIC_APOLLO_APQ=false)
|
||||
// 生产环境默认启用(未设置或设置为 true 均启用)
|
||||
const APQ_ENABLED = process.env.NEXT_PUBLIC_APOLLO_APQ !== "false";
|
||||
|
||||
/**
|
||||
* 创建 Apollo Client 实例。
|
||||
*
|
||||
@@ -41,8 +53,16 @@ export function createApolloClient(
|
||||
};
|
||||
});
|
||||
|
||||
// Link 链顺序:authLink → pqLink → httpLink
|
||||
// - authLink 注入 Authorization 头
|
||||
// - pqLink 将 query 替换为 hash(启用时)
|
||||
// - httpLink 发送请求
|
||||
const link = APQ_ENABLED
|
||||
? from([authLink, createPersistedQueryLink({ sha256 }), httpLink])
|
||||
: from([authLink, httpLink]);
|
||||
|
||||
return new ApolloClient({
|
||||
link: from([authLink, httpLink]),
|
||||
link,
|
||||
cache: new InMemoryCache(),
|
||||
ssrMode: typeof window === "undefined",
|
||||
defaultOptions: {
|
||||
|
||||
Reference in New Issue
Block a user