Compare commits
5
Commits
v1.1.0
...
36f508df75
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36f508df75 | ||
|
|
26d662ed7c | ||
|
|
e35d908672 | ||
|
|
c0db8f6165 | ||
|
|
fd4e69a27c |
@@ -12,3 +12,11 @@ screenshot-*.png
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Local AI coding hosts (procoder init); .procoder/ stays trackable.
|
||||||
|
/skills/procoder/
|
||||||
|
|
||||||
|
# procoder state (regenerable)
|
||||||
|
.procoder/state/
|
||||||
|
.procoder/index/
|
||||||
|
.lycheecache
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[git]
|
||||||
|
default_branch_policy = "report" # rulebook allows docs/evidence commits to main
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Context — anwarpy.ai
|
||||||
|
|
||||||
|
## effective access
|
||||||
|
|
||||||
|
The single server-side decision (launch.ts policy + doc.access + doc.status) consumed identically by the article page, public JSON route, download gate, and llms-full.
|
||||||
|
|
||||||
|
## ghost-read
|
||||||
|
|
||||||
|
Fetching an intended-VIP post logged out to assert what a stranger sees; the acceptance check for any access-logic change.
|
||||||
|
|
||||||
|
## honest 503
|
||||||
|
|
||||||
|
A dependency-down response with a fixed Arabic sentence (e.g. billing disabled, mail transport down) instead of a fake success; reference implementation: /api/notify.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[
|
||||||
|
"skills"
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"synced_at": "2026-10-13T02:31:48Z",
|
||||||
|
"lockfiles": {
|
||||||
|
"web/package-lock.json": "b0168d824c755b061aeb7dc8bddb7faa05b7a236ccc82b196843d5752e3632d3"
|
||||||
|
},
|
||||||
|
"migrations": {
|
||||||
|
"web/src/migrations": {
|
||||||
|
"count": 14,
|
||||||
|
"entries": [
|
||||||
|
"20261002_130505_init.json",
|
||||||
|
"20261002_130505_init.ts",
|
||||||
|
"20261002_170000_add_full_name.ts",
|
||||||
|
"20261003_111451.json",
|
||||||
|
"20261003_111451.ts",
|
||||||
|
"20261003_133758.json",
|
||||||
|
"20261003_133758.ts",
|
||||||
|
"20261003_201454.json",
|
||||||
|
"20261003_201454.ts",
|
||||||
|
"20261009_130817.json",
|
||||||
|
"20261009_130817.ts",
|
||||||
|
"20261010_142214.json",
|
||||||
|
"20261010_142214.ts",
|
||||||
|
"index.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"env_keys": {}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<!-- gstack digest, source: https://github.com/garrytan/gstack (agents-digest/gstack-AGENTS.md) -->
|
<!-- gstack digest, source: https://github.com/garrytan/gstack (agents-digest/gstack-AGENTS.md) -->
|
||||||
|
|
||||||
# gstack digest v1.91.12.0 — regenerate/re-copy after upgrading gstack
|
# gstack digest v1.91.12.0 — regenerate/re-copy after upgrading gstack
|
||||||
|
|
||||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||||
@@ -15,6 +16,7 @@ reviews, and evals on top of these rules.
|
|||||||
## The reuse ladder
|
## The reuse ladder
|
||||||
|
|
||||||
Before writing new code, stop at the first rung that holds:
|
Before writing new code, stop at the first rung that holds:
|
||||||
|
|
||||||
1. A helper, util, or pattern already in this repo.
|
1. A helper, util, or pattern already in this repo.
|
||||||
2. The standard library.
|
2. The standard library.
|
||||||
3. A native platform feature (CSS over JS, DB constraint over app code).
|
3. A native platform feature (CSS over JS, DB constraint over app code).
|
||||||
@@ -86,4 +88,3 @@ ssh root@147.79.102.201 'cd /root/anwarpi && docker compose -f docker-compose.vp
|
|||||||
```
|
```
|
||||||
|
|
||||||
Prod scripts (no `scripts/` in the standalone image): `ssh -f -N -L 15432:<db-container-ip>:5432 root@…`, then locally `set -a; source <tmp env>; set +a; DATABASE_URI=postgresql://anwarpi:$DB_PASSWORD@127.0.0.1:15432/anwarpi npx tsx scripts/<script>.ts`; delete the temp env file after. Never echo the password.
|
Prod scripts (no `scripts/` in the standalone image): `ssh -f -N -L 15432:<db-container-ip>:5432 root@…`, then locally `set -a; source <tmp env>; set +a; DATABASE_URI=postgresql://anwarpi:$DB_PASSWORD@127.0.0.1:15432/anwarpi npx tsx scripts/<script>.ts`; delete the temp env file after. Never echo the password.
|
||||||
|
|
||||||
|
|||||||
+37
-36
@@ -4,18 +4,18 @@
|
|||||||
|
|
||||||
## 1. Stack (pinned 2026-10-11)
|
## 1. Stack (pinned 2026-10-11)
|
||||||
|
|
||||||
| Layer | Choice | Version |
|
| Layer | Choice | Version |
|
||||||
|---|---|---|
|
| ----------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||||
| Framework | Next.js App Router (server components first) | next@15.4.11 |
|
| Framework | Next.js App Router (server components first) | next@15.4.11 — ceiling of `@payloadcms/next` peer support; known CVEs risk-accepted (SECURITY.md §8) |
|
||||||
| CMS / API / admin | Payload 3 (Next-native, Lexical rich text) | ^3.60.0 |
|
| CMS / API / admin | Payload 3 (Next-native, Lexical rich text) | ^3.60.0 |
|
||||||
| Database | PostgreSQL | postgres:16 (container) |
|
| Database | PostgreSQL | postgres:16 (container) |
|
||||||
| DB adapter | @payloadcms/db-postgres (Drizzle migrations) | ^3.60.0 |
|
| DB adapter | @payloadcms/db-postgres (Drizzle migrations) | ^3.60.0 |
|
||||||
| UI runtime | React | ^19.2.0 |
|
| UI runtime | React | ^19.2.0 |
|
||||||
| Styling | Hand-rolled CSS (`src/app/design.css` + per-route CSS). No Tailwind, no component library. | — |
|
| Styling | Hand-rolled CSS (`src/app/design.css` + per-route CSS). No Tailwind, no component library. | — |
|
||||||
| Payments | stripe | ^18.5.0 (scaffolded, gated by `billingEnabled()`) |
|
| Payments | stripe | ^18.5.0 (scaffolded, gated by `billingEnabled()`) |
|
||||||
| Email | Cloudflare Email REST API (ADR-0003) — **never SMTP** | — |
|
| Email | Cloudflare Email REST API (ADR-0003) — **never SMTP** | — |
|
||||||
| Language | TypeScript strict | ^5.9.0 |
|
| Language | TypeScript strict | ^5.9.0 |
|
||||||
| Deploy | Docker Compose on VPS `root@147.79.102.201:/root/anwarpi`, Traefik proxy network, apex anwarpy.ai (www 308) | — |
|
| Deploy | Docker Compose on VPS `root@147.79.102.201:/root/anwarpi`, Traefik proxy network, apex anwarpy.ai (www 308) | — |
|
||||||
|
|
||||||
## 2. Repository layout
|
## 2. Repository layout
|
||||||
|
|
||||||
@@ -55,18 +55,18 @@ launch.ts (policy) + doc.access + doc.status → effective access
|
|||||||
|
|
||||||
## 4. Module boundaries (`src/lib/`)
|
## 4. Module boundaries (`src/lib/`)
|
||||||
|
|
||||||
| Module | Owns | Must not |
|
| Module | Owns | Must not |
|
||||||
|---|---|---|
|
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
||||||
| `launch.ts` | LAUNCH_MODE policy, WPM constant | read env elsewhere for launch state |
|
| `launch.ts` | LAUNCH_MODE policy, WPM constant | read env elsewhere for launch state |
|
||||||
| `viewer.ts` | `getViewer()` — session → `{email, role, id, interest?, level?, emailVerified?, onboarded?}` | touch collections directly beyond auth |
|
| `viewer.ts` | `getViewer()` — session → `{email, role, id, interest?, level?, emailVerified?, onboarded?}` | touch collections directly beyond auth |
|
||||||
| `lexical.ts` | Lexical serialization: `lexicalToText`, `countLexicalWords` (walks `content.root`!), `extractH2s`, `lexicalToPlain` | know about Posts/DB |
|
| `lexical.ts` | Lexical serialization: `lexicalToText`, `countLexicalWords` (walks `content.root`!), `extractH2s`, `lexicalToPlain` | know about Posts/DB |
|
||||||
| `secret-scan.ts` | `scanForSecrets`/`scanAgentPayload` — pattern names only in responses | log or return matched secret text |
|
| `secret-scan.ts` | `scanForSecrets`/`scanAgentPayload` — pattern names only in responses | log or return matched secret text |
|
||||||
| `llms.ts` | llms.txt/full rendering, `fetchIndexDocs(withContent)` | bypass the access decision |
|
| `llms.ts` | llms.txt/full rendering, `fetchIndexDocs(withContent)` | bypass the access decision |
|
||||||
| `topics.ts` | TOPICS registry (agents/models/automation/foundations), `topicsFor()`, `isTopic()` | hardcode slugs elsewhere |
|
| `topics.ts` | TOPICS registry (agents/models/automation/foundations), `topicsFor()`, `isTopic()` | hardcode slugs elsewhere |
|
||||||
| `format.ts` | Arabic number/label agreement (`guidesLabel`, `minutesLabel`, `MEMBERSHIP_PRICE`) | duplicate Arabic plurals inline |
|
| `format.ts` | Arabic number/label agreement (`guidesLabel`, `minutesLabel`, `MEMBERSHIP_PRICE`) | duplicate Arabic plurals inline |
|
||||||
| `mail.ts` | Cloudflare Email REST send | use SMTP; log recipient addresses |
|
| `mail.ts` | Cloudflare Email REST send | use SMTP; log recipient addresses |
|
||||||
| `agent-media.ts` / `agent-files.ts` | pipeline ingestion of media/attachments | accept `access` from the payload |
|
| `agent-media.ts` / `agent-files.ts` | pipeline ingestion of media/attachments | accept `access` from the payload |
|
||||||
| `next.ts` | shared Next helpers | — |
|
| `next.ts` | shared Next helpers | — |
|
||||||
|
|
||||||
Import rule: pages/components may import lib + payload; lib modules never import each other's internals — if two libs need shared logic, promote it to the lower-level module.
|
Import rule: pages/components may import lib + payload; lib modules never import each other's internals — if two libs need shared logic, promote it to the lower-level module.
|
||||||
|
|
||||||
@@ -74,20 +74,21 @@ Import rule: pages/components may import lib + payload; lib modules never import
|
|||||||
|
|
||||||
**Route handlers** (`src/app/api/**/route.ts`) return `NextResponse.json`. Every error body is `{"error": "<real Arabic sentence>"}` — honest, specific, never a bare code.
|
**Route handlers** (`src/app/api/**/route.ts`) return `NextResponse.json`. Every error body is `{"error": "<real Arabic sentence>"}` — honest, specific, never a bare code.
|
||||||
|
|
||||||
| Route | Auth | Notes |
|
| Route | Auth | Notes |
|
||||||
|---|---|---|
|
| ---------------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------- |
|
||||||
| `POST /api/agent/posts`, `PUT /api/agent/posts/[slug]` | `x-agent-key` header | pipeline; 401 bad key, 400 shape, 409 slug conflict (after sourceId dedup), 422 secret scan |
|
| `POST /api/agent/posts`, `PUT /api/agent/posts/[slug]` | `x-agent-key` header | pipeline; 401 bad key, 400 shape, 409 slug conflict (after sourceId dedup), 422 secret scan |
|
||||||
| `GET /api/posts/[slug]` | public | JSON honoring the access decision |
|
| `GET /api/posts/[slug]` | public | JSON honoring the access decision |
|
||||||
| `POST /api/posts/[slug]/comments` | session required | server-forced author; moderated status (ADR-0001/0002) |
|
| `POST /api/posts/[slug]/comments` | session required | server-forced author; moderated status (ADR-0001/0002) |
|
||||||
| `POST /api/posts/[slug]/like` | session required | one per user per post |
|
| `POST /api/posts/[slug]/like` | session required | one per user per post |
|
||||||
| `POST /api/posts/[slug]/view` | public | anonymous view counter |
|
| `POST /api/posts/[slug]/view` | public | anonymous view counter |
|
||||||
| `POST /api/notify` | public | always records; honest 503 when mail transport down; dup → `alreadySubscribed` |
|
| `POST /api/notify` | public | always records; honest 503 when mail transport down; dup → `alreadySubscribed` |
|
||||||
| `POST /api/subscribe` | public | 503 honest Arabic while billing disabled |
|
| `POST /api/subscribe` | public | 503 honest Arabic while billing disabled |
|
||||||
| `POST /api/users`, `/api/auth/verify-email`, `/api/auth/resend-verification` | public / token | verification-token writes at signup; middleware rate-limits this path |
|
| `POST /api/users`, `/api/auth/verify-email`, `/api/auth/resend-verification` | public / token | verification-token writes at signup; middleware rate-limits this path |
|
||||||
| `GET /api/files/[id]` | per-file access gate | streams attachments |
|
| `GET /api/files/[id]` | per-file access gate | streams attachments |
|
||||||
| `POST /api/stripe/webhook` | signature | no-op while `billingEnabled()` is false |
|
| `POST /api/stripe/webhook` | signature | no-op while `billingEnabled()` is false |
|
||||||
|
|
||||||
**Data access patterns:**
|
**Data access patterns:**
|
||||||
|
|
||||||
- Server components use the **Payload Local API** (`payload.find`, `payload.findByID`) — never internal fetch to own routes.
|
- Server components use the **Payload Local API** (`payload.find`, `payload.findByID`) — never internal fetch to own routes.
|
||||||
- Client components are thin: forms POST to route handlers and render the returned Arabic message. No client data-fetching library, no global store; if a page needs reactive data, it re-fetches via a route handler or uses Payload's REST with credentials.
|
- Client components are thin: forms POST to route handlers and render the returned Arabic message. No client data-fetching library, no global store; if a page needs reactive data, it re-fetches via a route handler or uses Payload's REST with credentials.
|
||||||
- Generated types: `npm run generate:types` (runs as part of `build`) — collections' `Collection` types come from `payload-types.ts`, never hand-written interfaces for docs.
|
- Generated types: `npm run generate:types` (runs as part of `build`) — collections' `Collection` types come from `payload-types.ts`, never hand-written interfaces for docs.
|
||||||
|
|||||||
+21
-11
@@ -12,18 +12,19 @@
|
|||||||
|
|
||||||
## 2. Environment variables & secrets
|
## 2. Environment variables & secrets
|
||||||
|
|
||||||
| Var | Scope | Rule |
|
| Var | Scope | Rule |
|
||||||
|---|---|---|
|
| --------------------------------------------------------------- | -------- | --------------------------------------------------- |
|
||||||
| `DATABASE_URI` / `DB_PASSWORD` | server | compose builds the URI; never commit, never echo |
|
| `DATABASE_URI` / `DB_PASSWORD` | server | compose builds the URI; never commit, never echo |
|
||||||
| `PAYLOAD_SECRET` | server | signing/encryption; rotate = invalidate sessions |
|
| `PAYLOAD_SECRET` | server | signing/encryption; rotate = invalidate sessions |
|
||||||
| `AGENT_API_KEY` | server | pipeline auth; header `x-agent-key` |
|
| `AGENT_API_KEY` | server | pipeline auth; header `x-agent-key` |
|
||||||
| `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, `MAIL_FROM` | server | mail transport |
|
| `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, `MAIL_FROM` | server | mail transport |
|
||||||
| `LAUNCH_MODE` | server | launch policy (SECURITY-relevant: gates paid flows) |
|
| `LAUNCH_MODE` | server | launch policy (SECURITY-relevant: gates paid flows) |
|
||||||
| `NEXT_PUBLIC_SITE_URL` | public | canonical/og origin |
|
| `NEXT_PUBLIC_SITE_URL` | public | canonical/og origin |
|
||||||
| `STRIPE_SECRET_KEY`, `STRIPE_PRICE_ID`, `STRIPE_WEBHOOK_SECRET` | server | Phase 3 |
|
| `STRIPE_SECRET_KEY`, `STRIPE_PRICE_ID`, `STRIPE_WEBHOOK_SECRET` | server | Phase 3 |
|
||||||
| `SEED_TEST_USERS` | dev only | never set in prod |
|
| `SEED_TEST_USERS` | dev only | never set in prod |
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
- `.env*` files are gitignored and **excluded from rsync deploys**; prod secrets live only in `/root/anwarpi/.env.production`.
|
- `.env*` files are gitignored and **excluded from rsync deploys**; prod secrets live only in `/root/anwarpi/.env.production`.
|
||||||
- Never print secret values into transcripts, docs, videos, or evidence. Known incident: 8 chars of prod `DB_PASSWORD` leaked in a session transcript on 2026-10-10 → **owner to rotate**; rotation is the standing remedy for any partial leak.
|
- Never print secret values into transcripts, docs, videos, or evidence. Known incident: 8 chars of prod `DB_PASSWORD` leaked in a session transcript on 2026-10-10 → **owner to rotate**; rotation is the standing remedy for any partial leak.
|
||||||
- Read secrets at runtime via `process.env` inside server modules only; never in `'use client'` code (only `NEXT_PUBLIC_*` there).
|
- Read secrets at runtime via `process.env` inside server modules only; never in `'use client'` code (only `NEXT_PUBLIC_*` there).
|
||||||
@@ -38,7 +39,7 @@ Rules:
|
|||||||
## 4. Input handling & pipeline hardening
|
## 4. Input handling & pipeline hardening
|
||||||
|
|
||||||
- **`pickFields`** on the agent API strips privileged fields (`access`, `readingMinutes`, `status` except explicit `publish: true` opt-in on POST; PUT cannot set `status`).
|
- **`pickFields`** on the agent API strips privileged fields (`access`, `readingMinutes`, `status` except explicit `publish: true` opt-in on POST; PUT cannot set `status`).
|
||||||
- **`scanForSecrets` / `scanAgentPayload`** (`src/lib/secret-scan.ts`): POST and PUT scan the *real text* (Lexical → `lexicalToText`, or the raw string) for AWS AKIA keys, PEM blocks, `sk-` API keys, GitHub tokens (`ghp_`…), generic credential shapes → **422 with pattern NAMES only**. Never echo the matched text, never log it.
|
- **`scanForSecrets` / `scanAgentPayload`** (`src/lib/secret-scan.ts`): POST and PUT scan the _real text_ (Lexical → `lexicalToText`, or the raw string) for AWS AKIA keys, PEM blocks, `sk-` API keys, GitHub tokens (`ghp_`…), generic credential shapes → **422 with pattern NAMES only**. Never echo the matched text, never log it.
|
||||||
- String `content` → 400 (Lexical JSON required); no silent placeholder substitution.
|
- String `content` → 400 (Lexical JSON required); no silent placeholder substitution.
|
||||||
- Comment bodies: textarea, moderated (pending until approved), author forced server-side from the session — client-supplied `author`/`authorName` identity is ignored/overwritten (ADR-0002). Display names are a snapshot, not a live join, so renamed users can't retroactively forge old comments.
|
- Comment bodies: textarea, moderated (pending until approved), author forced server-side from the session — client-supplied `author`/`authorName` identity is ignored/overwritten (ADR-0002). Display names are a snapshot, not a live join, so renamed users can't retroactively forge old comments.
|
||||||
- All user-facing error strings are fixed Arabic sentences from the route handlers — errors never reflect raw input back (no echo = reflected-input hygiene).
|
- All user-facing error strings are fixed Arabic sentences from the route handlers — errors never reflect raw input back (no echo = reflected-input hygiene).
|
||||||
@@ -62,3 +63,12 @@ Rules:
|
|||||||
- No WAF/Cloudflare proxy changes without owner approval — the domain's DNS/proxy is Ahmed's console.
|
- No WAF/Cloudflare proxy changes without owner approval — the domain's DNS/proxy is Ahmed's console.
|
||||||
- No secrets in client bundles; no `NEXT_PUBLIC_` for anything except the site URL.
|
- No secrets in client bundles; no `NEXT_PUBLIC_` for anything except the site URL.
|
||||||
- No new auth provider without routing it through `viewer.ts` (see §1).
|
- No new auth provider without routing it through `viewer.ts` (see §1).
|
||||||
|
|
||||||
|
## 8. Accepted risks
|
||||||
|
|
||||||
|
### next@15.4.11 — 28 known CVEs, max 9.5 (risk-accepted by owner, 2026-10-13)
|
||||||
|
|
||||||
|
- **Why pinned**: `@payloadcms/next@3.90.2` peer-supports next `<15.5.0 || >=16.3.3` — no Payload 3.x release supports 15.5.x, so the safe ladder is 15.4.11 → (Payload 4 or a verified Next 16 migration). Forcing 15.5.x would run the CMS on an unsupported combo.
|
||||||
|
- **Exposure analysis**: the 9.5-critical (Image Optimizer DoS, GHSA-9g9p-9gw9-jx7f) requires attacker-reachable `remotePatterns` — this app configures none (local covers only). The cache-poisoning advisory targets SSG/ISR caching; the site is predominantly dynamic SSR via the Payload Local API.
|
||||||
|
- **Revisit trigger**: any of — Payload publishes 15.5/16 peer support (then upgrade next in one verified PR), Payload 4 adoption, or a new next advisory with remote-code-execution class impact.
|
||||||
|
- **Gate note**: `procoder check` re-flags this on any `package-lock.json` change. If you must touch the lockfile before upgrading, either upgrade next in the same PR or skip the gate once with the reason in the commit message. Transitive CVE fixes already applied: undici 8.11.2, sharp 0.35.5, postcss 8.5.30, sass 1.105.1.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Publish and manage blog posts programmatically. Auth: send the agent key in the
|
|||||||
## Create + publish a post with an image
|
## Create + publish a post with an image
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
KEY=033d1eed44ca6abf483de10735c47155d38f7b5bc9d46449
|
KEY="$AGENT_API_KEY" # never hardcode — export AGENT_API_KEY in your shell; it lives in the server's .env.production
|
||||||
|
|
||||||
# Inline base64 image (jpeg/png/webp, ≤5MB)
|
# Inline base64 image (jpeg/png/webp, ≤5MB)
|
||||||
python3 - <<'EOF' > /tmp/post.json
|
python3 - <<'EOF' > /tmp/post.json
|
||||||
@@ -54,11 +54,20 @@ Response `201`:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": 113, "title": "دليلي الجديد", "slug": "my-new-guide",
|
"id": 113,
|
||||||
"status": "published", "access": "vip", "category": "agents",
|
"title": "دليلي الجديد",
|
||||||
|
"slug": "my-new-guide",
|
||||||
|
"status": "published",
|
||||||
|
"access": "vip",
|
||||||
|
"category": "agents",
|
||||||
"cover": { "id": 113, "url": "/api/media/file/my-cover.jpg" },
|
"cover": { "id": 113, "url": "/api/media/file/my-cover.jpg" },
|
||||||
"attachments": [
|
"attachments": [
|
||||||
{ "id": 42, "filename": "agent-state.md", "access": "vip", "url": "/api/files/42" }
|
{
|
||||||
|
"id": 42,
|
||||||
|
"filename": "agent-state.md",
|
||||||
|
"access": "vip",
|
||||||
|
"url": "/api/files/42"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Minimal flat config — procoder's lint domain checks .ts/.tsx with this.
|
||||||
|
// Report-only by default (see .procoder/config.toml policy); add rules as needed.
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
".next/**",
|
||||||
|
"node_modules/**",
|
||||||
|
"src/payload-types.ts", // generated — do not lint
|
||||||
|
"src/migrations/**",
|
||||||
|
"media/**",
|
||||||
|
"files-uploads/**",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...tseslint.configs.recommended.map((c) => ({
|
||||||
|
...c,
|
||||||
|
files: ["**/*.{ts,tsx,mjs,cjs}"],
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
// Generated Payload types use `any` in payload-types.ts; local code may pass
|
||||||
|
// narrow unknowns. Keep the floor permissive until a lint pass is scheduled.
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
Generated
+3395
-870
File diff suppressed because it is too large
Load Diff
+14
-2
@@ -12,6 +12,7 @@
|
|||||||
"covers": "node scripts/generate-covers.mjs"
|
"covers": "node scripts/generate-covers.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@clerk/nextjs": "^7.9.14",
|
||||||
"@payloadcms/db-postgres": "^3.60.0",
|
"@payloadcms/db-postgres": "^3.60.0",
|
||||||
"@payloadcms/next": "^3.60.0",
|
"@payloadcms/next": "^3.60.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.60.0",
|
"@payloadcms/richtext-lexical": "^3.60.0",
|
||||||
@@ -19,17 +20,28 @@
|
|||||||
"payload": "^3.60.0",
|
"payload": "^3.60.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"sharp": "^0.34.0",
|
"sharp": "^0.35.5",
|
||||||
"stripe": "^18.5.0"
|
"stripe": "^18.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.64.0",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"@types/nodemailer": "^8.0.2",
|
"@types/nodemailer": "^8.0.2",
|
||||||
"@types/react": "^19.2.0",
|
"@types/react": "^19.2.0",
|
||||||
"@types/react-dom": "^19.2.0",
|
"@types/react-dom": "^19.2.0",
|
||||||
"typescript": "^5.9.0"
|
"eslint": "^10.12.0",
|
||||||
|
"prettier": "^3.9.10",
|
||||||
|
"typescript": "^5.9.0",
|
||||||
|
"typescript-eslint": "^8.71.2",
|
||||||
|
"vitest": "^5.0.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.9.0"
|
"node": ">=20.9.0"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"undici": "^8.11.2",
|
||||||
|
"sharp": "^0.35.5",
|
||||||
|
"postcss": "^8.5.6",
|
||||||
|
"sass": "^1.79.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user