feat(PBI-96/T-1061): add rate-limit keys + server-only helpers
- lib/rate-limit.ts: 'create-product-doc' (30/min) + 'edit-product-doc' (60/min) in eigen PBI-96-blok na M12-Ideas-keys. - lib/product-docs-server.ts: loadAccessibleProduct + folderApiToDbOrThrow als 'server-only' helpers. Wordt door create/update/list-actions hergebruikt; folder-toggle gebruikt direct user_id-scope (owner-only). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
afafbca855
commit
5ae78ff872
2 changed files with 50 additions and 0 deletions
46
lib/product-docs-server.ts
Normal file
46
lib/product-docs-server.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Server-only helpers voor de Product Docs server-actions. Bevat
|
||||
// prisma-toegang en mag NIET in client-componenten worden geïmporteerd
|
||||
// (zie CLAUDE.md hardstop "Server/client grens").
|
||||
//
|
||||
// Plan: docs/plans/PBI-96-product-docs.md §B.2.
|
||||
|
||||
import 'server-only'
|
||||
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { productAccessFilter } from '@/lib/product-access'
|
||||
import { productDocFolderFromApi } from '@/lib/product-doc-folder'
|
||||
|
||||
import type { ProductDocFolder } from '@prisma/client'
|
||||
|
||||
/**
|
||||
* Laadt het Product gescoped op `productAccessFilter(userId)` voor een
|
||||
* gegeven product_id. Returnt alleen de velden die de write-actions
|
||||
* nodig hebben (`id`, eigenaar, folder-config). Voor folder-toggle
|
||||
* gebruikt de owner-only-check direct `where: { id, user_id }` zodat
|
||||
* ProductMember de folder-config niet kan wijzigen.
|
||||
*
|
||||
* Returnt `null` als de user geen access heeft — caller stuurt dan
|
||||
* 404 (anti-enum, géén 403).
|
||||
*/
|
||||
export async function loadAccessibleProduct(productId: string, userId: string) {
|
||||
return prisma.product.findFirst({
|
||||
where: { id: productId, ...productAccessFilter(userId) },
|
||||
select: { id: true, user_id: true, enabled_doc_folders: true },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Converteert een API-folder-string (`'runbooks'`) naar het Prisma-enum
|
||||
* (`'RUNBOOKS'`). Throwed bij onbekende waarden — server-actions hebben
|
||||
* de input al via zod gevalideerd, dus dit dient alleen als type-narrowing
|
||||
* vangnet.
|
||||
*/
|
||||
export function folderApiToDbOrThrow(folderApi: string): ProductDocFolder {
|
||||
const db = productDocFolderFromApi(folderApi)
|
||||
if (!db) {
|
||||
throw new Error(
|
||||
`Internal: folderApiToDbOrThrow ontving onbekende folder "${folderApi}" — zod-validatie zou dit niet door moeten laten`,
|
||||
)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
|
@ -33,6 +33,10 @@ const CONFIGS: Record<string, RateLimitConfig> = {
|
|||
'start-idea-job': { windowMs: 60_000, max: 10 }, // Grill / Make Plan triggers
|
||||
'materialize-idea': { windowMs: 60_000, max: 5 },
|
||||
'create-user-question': { windowMs: 60_000, max: 20 }, // PLAN_CHAT vragen
|
||||
|
||||
// PBI-96 — Per-product Product Docs (zie docs/plans/PBI-96-product-docs.md)
|
||||
'create-product-doc': { windowMs: 60_000, max: 30 },
|
||||
'edit-product-doc': { windowMs: 60_000, max: 60 },
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: RateLimitConfig = { windowMs: 60_000, max: 10 }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue