Skip to content

Commit 9ab4dc1

Browse files
committed
feat(migrations): add migration to drop share links version tables and type
This migration removes the "_share_links_v_version_permissions" and "_share_links_v" tables along with the associated enum type. It also includes a down migration to recreate these structures if needed, ensuring database integrity and allowing for rollback if necessary.
1 parent 04a7783 commit 9ab4dc1

11 files changed

Lines changed: 8747 additions & 2 deletions

AGENTS.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Operational guide for AI agents working in this repository.
88

99
- Write everything in English: code, comments, variable names, documentation.
1010
- Check `.ai/specs/` before coding any non-trivial feature.
11-
- Skills are installed in `.claude/skills/` (Claude Code) and `.agents/skills/` (Codex) — do not edit them directly.
11+
- Skills are installed in `.claude/skills/` (Claude Code) and `.agents/skills/` (Codex). `.agents/skills/` is the local source of truth — edit a skill **only** there, then run `ags push-skill` to propagate it to the source repo (it also syncs the `.claude/skills/` copy). See Installing skills.
1212
- Prefer minimal, focused changes. Do not refactor code outside the task scope.
1313
- Run `yarn build` after every implementation to catch type errors.
1414
- Comment and naming conventions are defined in the `code-style` skill. Load it before writing or reviewing TypeScript.
@@ -52,6 +52,16 @@ npx skills add <source-path-or-url> -a claude-code -a codex --copy
5252

5353
This installs into `.claude/skills/` (Claude Code) and `.agents/skills/` (Codex). A `skills-lock.json` file tracks the source and version of each installed skill.
5454

55+
### Editing skills
56+
57+
`.agents/skills/` is the local source of truth. Edit a skill **only** there, then push it upstream:
58+
59+
```bash
60+
ags push-skill # reads .agents/skills, pushes to the source repo, then syncs .claude/skills
61+
```
62+
63+
Do not edit `.claude/skills/` by hand — it is a derived copy that `ags push-skill` overwrites from `.agents/skills/`. Likewise do not hand-edit the source repo; let `ags push-skill` write it.
64+
5565
### Available skills
5666

5767
| Skill | When to load |

next.config.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,23 @@ const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts')
99
const __filename = fileURLToPath(import.meta.url)
1010
const dirname = path.dirname(__filename)
1111

12+
// Sent on every response. CSP is intentionally limited to `frame-ancestors`
13+
// (clickjacking) — a script/style CSP would break the Payload admin's inline
14+
// assets and needs per-route nonces before it can be tightened.
15+
const securityHeaders = [
16+
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
17+
{ key: 'X-Content-Type-Options', value: 'nosniff' },
18+
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
19+
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
20+
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
21+
{ key: 'Content-Security-Policy', value: "frame-ancestors 'self'" },
22+
]
23+
1224
const nextConfig: NextConfig = {
1325
// output: 'standalone',
26+
async headers() {
27+
return [{ source: '/:path*', headers: securityHeaders }]
28+
},
1429
images: {
1530
localPatterns: [
1631
{

src/collections/clients/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { CollectionConfig } from 'payload'
22
import { isAdmin, adminOrSelf, isAdminField } from '../../access'
3+
import { validatePassword } from '../../lib/validate-password'
34

45
export const Clients: CollectionConfig = {
56
slug: 'clients',
@@ -24,6 +25,11 @@ export const Clients: CollectionConfig = {
2425
delete: isAdmin,
2526
admin: () => false,
2627
},
28+
hooks: {
29+
beforeValidate: [validatePassword],
30+
},
31+
// Audit trail for client account and trainer-note changes.
32+
versions: true,
2733
fields: [
2834
{
2935
name: 'name',

src/collections/plans/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export const Plans: CollectionConfig = {
1414
update: isAdmin,
1515
delete: isAdmin,
1616
},
17+
// Keep an audit trail of every change (no drafts — publish workflow unchanged).
18+
versions: true,
1719
fields: [
1820
{
1921
name: 'client',

src/collections/users/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { CollectionConfig } from 'payload'
2+
import { validatePassword } from '../../lib/validate-password'
23

34
export const Users: CollectionConfig = {
45
slug: 'users',
@@ -14,6 +15,9 @@ export const Users: CollectionConfig = {
1415
sameSite: 'Lax',
1516
},
1617
},
18+
hooks: {
19+
beforeValidate: [validatePassword],
20+
},
1721
fields: [],
1822
versions: false,
1923
}

src/lib/validate-password.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { CollectionBeforeValidateHook } from 'payload'
2+
import { APIError } from 'payload'
3+
4+
// NIST SP 800-63B: favour length over composition rules. 15+ characters.
5+
const MIN_PASSWORD_LENGTH = 15
6+
7+
/**
8+
* `beforeValidate` hook for auth collections. Enforces a minimum password
9+
* length. Payload exposes the plaintext password on `data.password` during
10+
* create and password changes; it is absent on ordinary updates, so this is a
11+
* no-op then.
12+
*/
13+
export const validatePassword: CollectionBeforeValidateHook = async ({ data }) => {
14+
const password = (data as { password?: unknown } | undefined)?.password
15+
if (typeof password !== 'string' || password.length === 0) return data
16+
17+
if (password.length < MIN_PASSWORD_LENGTH) {
18+
throw new APIError(`Password must be at least ${MIN_PASSWORD_LENGTH} characters long.`, 400)
19+
}
20+
21+
return data
22+
}

0 commit comments

Comments
 (0)