feat: add AGENTS.md with technical documentation and configure email sending via Nodemailer
This commit is contained in:
parent
ff1f44832e
commit
1eddf741ff
4 changed files with 135 additions and 2 deletions
107
AGENTS.md
Normal file
107
AGENTS.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# AGENTS.md — Polaris Task Force
|
||||
|
||||
## What this is
|
||||
|
||||
Next.js 16 + Payload CMS 3.68.5 app for an Arma 3 unit. PostgreSQL database via `@payloadcms/db-postgres` + Drizzle. Tailwind CSS v4 (no config file — CSS-based). shadcn/ui (new-york style, `lucide` icons). Dark-themed frontend.
|
||||
|
||||
## Package manager
|
||||
|
||||
**Bun** is the primary package manager (`bun.lock`, `bunfig.toml`). `pnpm-lock.yaml` also exists; use Bun for installs and running scripts.
|
||||
|
||||
## Essential commands
|
||||
|
||||
```bash
|
||||
bun install # install deps
|
||||
bun run dev # dev server (webpack, localhost:3000)
|
||||
bun run devsafe # clears .next cache then dev
|
||||
bun run build # production build (--max-old-space-size=8000, webpack)
|
||||
bun run lint # ESLint (next/core-web-vitals + next/typescript)
|
||||
bun run test # runs test:int then test:e2e sequentially
|
||||
bun run test:int # vitest integration tests only
|
||||
bun run test:e2e # playwright e2e tests only
|
||||
bun run db # drizzle-kit wrapper (e.g. bun run db migrate)
|
||||
bun run generate:types # regenerates payload-types.ts
|
||||
bun run generate:importmap # regenerates payload admin importMap
|
||||
```
|
||||
|
||||
## Type checking / linting order
|
||||
|
||||
`bun run lint` runs ESLint. There is no separate `typecheck` script — TypeScript errors surface during `bun run lint` and `bun run build`.
|
||||
|
||||
## Test details
|
||||
|
||||
- **Integration tests**: `tests/int/**/*.int.spec.ts` — Vitest with jsdom. Requires a live PostgreSQL database (connection from `.env`). Uses `dotenv/config` via `vitest.setup.ts`.
|
||||
- **E2E tests**: `tests/e2e/*.e2e.spec.ts` — Playwright (Chromium only). Auto-starts `bun run dev` via `webServer` config. Currently minimal (homepage smoke test).
|
||||
- Run a single integration test: `bun run vitest run tests/int/api.int.spec.ts`
|
||||
- Run a single e2e test: `bun run playwright test tests/e2e/frontend.e2e.spec.ts`
|
||||
|
||||
## Generated files — never edit manually
|
||||
|
||||
- `src/payload-types.ts` — regenerated by `bun run generate:types`
|
||||
- `src/payload-generated-schema.ts` — regenerated by Payload db-schema generation
|
||||
- `src/app/(payload)/admin/importMap.js` — regenerated by `bun run generate:importmap`
|
||||
- `src/app/(payload)/layout.tsx` — auto-generated by Payload
|
||||
|
||||
## Path aliases
|
||||
|
||||
- `@/*` → `./src/*`
|
||||
- `@payload-config` → `./src/payload.config.ts`
|
||||
|
||||
## App structure
|
||||
|
||||
- `src/app/(frontend)/` — public-facing pages (dashboard, logistics, home). Layout has sidebar + auth check.
|
||||
- `src/app/(payload)/` — Payload admin panel and API routes. Auto-generated layout.
|
||||
- `src/app/my-route/` — example custom API route.
|
||||
|
||||
## Collections (Payload CMS)
|
||||
|
||||
Organized by domain under `src/collections/`:
|
||||
|
||||
- **users/** — Users (auth, username login), Ranks, Profiles, Awards, Qualifications, Assignments, Experience
|
||||
- **intelligence/** — Missions, Campaigns, Factions, Technologies
|
||||
- **logistics/** — Assets, Resources, Vehicles, Structures
|
||||
- **world/** — Maps, NarrativeEvents
|
||||
- **server/** — MissionFiles, ModLists
|
||||
- **game/** — GameRules (global), GameStructures, GameHardResources
|
||||
|
||||
Access control helpers live in `src/utils/access-control/` (isRole, hasRoles). Roles: `guest`, `user`, `admin`, `developer`.
|
||||
|
||||
## Auth
|
||||
|
||||
Username-based login (no email login). Users log in via Payload admin with `username` only.
|
||||
|
||||
## Database
|
||||
|
||||
PostgreSQL via `@payloadcms/db-postgres`. Schema defined in `src/payload-generated-schema.ts`. Migrations in `src/migrations/` (timestamp-named .ts + .json pairs, registered in `migrations/index.ts`). Drizzle config reads `DATABASE_URI` from env.
|
||||
|
||||
In development, `postgresAdapter` uses `push: true` to auto-sync schema.
|
||||
|
||||
## Code style
|
||||
|
||||
- **Prettier**: double quotes, trailing commas (all), 100 char print width, semicolons.
|
||||
- **ESLint**: `next/core-web-vitals` + `next/typescript`. `@typescript-eslint/no-unused-vars` warns (prefix unused with `_`).
|
||||
- **Tailwind v4**: no `tailwind.config` — configured via `@tailwindcss/postcss` in `postcss.config.mjs` and CSS imports. Use `cn()` from `@/lib/utils` for class merging.
|
||||
|
||||
## UI components
|
||||
|
||||
shadcn/ui components live in `src/components/ui/`. Use `bunx shadcn@latest add <component>` to add new ones. Frontend components in `src/components/frontend/`. Payload admin custom components referenced in `payload.config.ts` under `admin.components`.
|
||||
|
||||
## Environment
|
||||
|
||||
- `.env` — local dev (PostgreSQL connection string + PAYLOAD_SECRET)
|
||||
- `.env.example` — template (still shows MongoDB URI — outdated, the app uses PostgreSQL)
|
||||
- `.env.test` — test/staging database
|
||||
- `test.env` — NODE_OPTIONS for playwright (loaded by playwright config)
|
||||
|
||||
## Deploy
|
||||
|
||||
`bun run deploy` bumps patch version (via `bun pm version patch`), builds, then runs `build/deploy.sh`. The `build/` directory is gitignored so the deploy script is not in the repo.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- `.env.example` shows MongoDB URI but the app uses PostgreSQL — trust `DATABASE_URI` format in `.env.test` as the real reference.
|
||||
- `bun run build` passes `--max-old-space-size=8000` — the build is memory-intensive.
|
||||
- The `devturbo` script uses Turbopack; `dev` and `devsafe` use webpack. These are different bundlers with different behavior.
|
||||
- Playwright tests auto-start the dev server — make sure port 3000 is free before running e2e.
|
||||
- Payload admin layout and importMap are auto-generated — do not edit by hand.
|
||||
- `.npmrc` sets `legacy-peer-deps=true` for dependency resolution compatibility.
|
||||
13
bun.lock
13
bun.lock
|
|
@ -6,6 +6,7 @@
|
|||
"name": "polaris-task-force",
|
||||
"dependencies": {
|
||||
"@payloadcms/db-postgres": "3.68.5",
|
||||
"@payloadcms/email-nodemailer": "^3.86.0",
|
||||
"@payloadcms/next": "3.68.5",
|
||||
"@payloadcms/payload-cloud": "3.68.5",
|
||||
"@payloadcms/richtext-lexical": "3.68.5",
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@types/nodemailer": "^8.0.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cross-env": "^10.1.0",
|
||||
|
|
@ -27,6 +29,7 @@
|
|||
"graphql": "^16.12.0",
|
||||
"lucide-react": "^0.561.0",
|
||||
"next": "16.0.10",
|
||||
"nodemailer": "^9.0.3",
|
||||
"payload": "3.68.5",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "19.2.3",
|
||||
|
|
@ -495,7 +498,7 @@
|
|||
|
||||
"@payloadcms/drizzle": ["@payloadcms/drizzle@3.68.5", "", { "dependencies": { "console-table-printer": "2.12.1", "dequal": "2.0.3", "drizzle-orm": "0.44.7", "prompts": "2.4.2", "to-snake-case": "1.0.0", "uuid": "9.0.0" }, "peerDependencies": { "payload": "3.68.5" } }, "sha512-+izZ+08gPD8L7i0hRYOfdoVspUyer4eEHbc1NdD/hYSwLIw/5vdR+Q7T2wswXuqaj8maZZS7dCLdmVxQlHa44g=="],
|
||||
|
||||
"@payloadcms/email-nodemailer": ["@payloadcms/email-nodemailer@3.68.5", "", { "dependencies": { "nodemailer": "7.0.9" }, "peerDependencies": { "payload": "3.68.5" } }, "sha512-aMVzT88vnEw5lChq33vlks68IukNHyhU7skWAFhI/B9e6+ILCVx9uhm/uJZ6CRkO2RP5veIuKMuCgGZy2EBcaQ=="],
|
||||
"@payloadcms/email-nodemailer": ["@payloadcms/email-nodemailer@3.86.0", "", { "dependencies": { "nodemailer": "^9.0.1" }, "peerDependencies": { "payload": "3.86.0" } }, "sha512-z8Knd6w1ToPSc1yunage9dQS8pApIBwTK40PMEsQd7Hl79BxbQcEzGrWTKhTiJHu5K4LZJOXjZOtdNWVI07Jyw=="],
|
||||
|
||||
"@payloadcms/graphql": ["@payloadcms/graphql@3.68.5", "", { "dependencies": { "graphql-scalars": "1.22.2", "pluralize": "8.0.0", "ts-essentials": "10.0.3", "tsx": "4.20.6" }, "peerDependencies": { "graphql": "16.12.0", "payload": "3.68.5" }, "bin": { "payload-graphql": "bin.js" } }, "sha512-Q+cAD/o9p7rOmLWF3lLFQ+s1+d0mECZjDBC2FV3xDYdawCfVZSMtffM6KRbWQzipeKEcSVnz+KeBsEjdN9o6Ag=="],
|
||||
|
||||
|
|
@ -815,6 +818,8 @@
|
|||
|
||||
"@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
|
||||
|
||||
"@types/nodemailer": ["@types/nodemailer@8.0.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw=="],
|
||||
|
||||
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
|
||||
|
||||
"@types/pg": ["@types/pg@8.10.2", "", { "dependencies": { "@types/node": "25.0.3", "pg-protocol": "1.10.3", "pg-types": "4.1.0" } }, "sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw=="],
|
||||
|
|
@ -1579,7 +1584,7 @@
|
|||
|
||||
"node-releases": ["node-releases@2.0.23", "", {}, "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg=="],
|
||||
|
||||
"nodemailer": ["nodemailer@7.0.9", "", {}, "sha512-9/Qm0qXIByEP8lEV2qOqcAW7bRpL8CR9jcTwk3NBnHJNmP9fIJ86g2fgmIXqHY+nj55ZEMwWqYAT2QTDpRUYiQ=="],
|
||||
"nodemailer": ["nodemailer@9.0.3", "", {}, "sha512-n+YP+NKwR5zRWa60k3GiQ6Q3B4KXCoAw40dAKeCtYn020iNN74aWK2liXIC3ZEATeGql7we3tE3t8QwhY0eskw=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
|
|
@ -2081,6 +2086,10 @@
|
|||
|
||||
"@payloadcms/graphql/tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "0.25.11", "get-tsconfig": "4.12.0" }, "optionalDependencies": { "fsevents": "2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="],
|
||||
|
||||
"@payloadcms/payload-cloud/@payloadcms/email-nodemailer": ["@payloadcms/email-nodemailer@3.68.5", "", { "dependencies": { "nodemailer": "7.0.9" }, "peerDependencies": { "payload": "3.68.5" } }, "sha512-aMVzT88vnEw5lChq33vlks68IukNHyhU7skWAFhI/B9e6+ILCVx9uhm/uJZ6CRkO2RP5veIuKMuCgGZy2EBcaQ=="],
|
||||
|
||||
"@payloadcms/payload-cloud/nodemailer": ["nodemailer@7.0.9", "", {}, "sha512-9/Qm0qXIByEP8lEV2qOqcAW7bRpL8CR9jcTwk3NBnHJNmP9fIJ86g2fgmIXqHY+nj55ZEMwWqYAT2QTDpRUYiQ=="],
|
||||
|
||||
"@payloadcms/richtext-lexical/csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "optionalDependencies": { "@types/react": "19.2.7", "@types/react-dom": "19.2.3" }, "peerDependencies": { "react": "19.2.3", "react-dom": "19.2.3" } }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/db-postgres": "3.68.5",
|
||||
"@payloadcms/email-nodemailer": "^3.86.0",
|
||||
"@payloadcms/next": "3.68.5",
|
||||
"@payloadcms/payload-cloud": "3.68.5",
|
||||
"@payloadcms/richtext-lexical": "3.68.5",
|
||||
|
|
@ -39,6 +40,7 @@
|
|||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@types/nodemailer": "^8.0.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cross-env": "^10.1.0",
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
"graphql": "^16.12.0",
|
||||
"lucide-react": "^0.561.0",
|
||||
"next": "16.0.10",
|
||||
"nodemailer": "^9.0.3",
|
||||
"payload": "3.68.5",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "19.2.3",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import { GameRules } from "@/collections/game/GameRules";
|
|||
import { GameStructures } from "@/collections/game/GameStructures";
|
||||
import { GameHardResources } from "@/collections/game/GameHardResources";
|
||||
import { Experience } from "@/collections/users/Experience";
|
||||
import { nodemailerAdapter } from "@payloadcms/email-nodemailer";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
const dirname = path.dirname(filename);
|
||||
|
|
@ -112,4 +114,16 @@ export default buildConfig({
|
|||
// payloadCloudPlugin(),
|
||||
// storage-adapter-placeholder
|
||||
],
|
||||
email: nodemailerAdapter({
|
||||
defaultFromAddress: process.env.EMAIL_FROM_ADDRESS || "",
|
||||
defaultFromName: process.env.EMAIL_FROM_NAME || "",
|
||||
transport: nodemailer.createTransport({
|
||||
host: process.env.EMAIL_HOST || "",
|
||||
port: Number(process.env.EMAIL_PORT) || 587,
|
||||
auth: {
|
||||
user: process.env.EMAIL_USERNAME || "",
|
||||
pass: process.env.EMAIL_PASSWORD || "",
|
||||
},
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue