chore: clear audit backlog (types sync, email norm, docs, members shortcut)
Finishes all remaining tracked work from the full-app audit. - types/database.ts synced: added the 8 missing tables (forum_threads, comments, space_tiers, space_invites, space_onboarding_steps, space_role_labels, space_custom_roles, space_member_custom_roles), the comment_entity_type enum (type + Constants), and the missing columns (space_members.tier_id/onboarding_completed_at/ onboarding_progress, secrets.encrypted_value/encryption_version, knowledge_base.render_markdown). - Email canonicalization: new emailField() helper (trim+lowercase) applied to signIn/signUp/addMember/updateMember/createContact and the import path; prevents case-variant duplicates and sign-in mismatches. - Members directory: per-row "make area lead" picker (admin/board) assigns a member to any area-lead role; mirrors Customize. - Docs refreshed and re-dated: DATABASE_SCHEMA.md (migrations 014-024 summary), DB_SCHEMA_MAP.md (new-table quick map), API_REFERENCE.md (all server actions from migrations 016-024). schema.sql named as source of truth. Backlog is now empty. 271/271 tests pass, build clean. No migration.
This commit is contained in:
+37
-1
@@ -1,7 +1,8 @@
|
||||
# Hackerspace.sh - API Reference
|
||||
|
||||
> **Last Updated**: 2026-03-10
|
||||
> **Last Updated**: 2026-05-15
|
||||
> Complete reference for all server actions, database operations, and API patterns.
|
||||
> Actions added since the original baseline are listed in "Server actions added (migrations 016-024)" at the end.
|
||||
|
||||
---
|
||||
|
||||
@@ -851,3 +852,38 @@ async function handleSubmit() {
|
||||
| "Treasurer access required" | Payment role check | Show permission error |
|
||||
| "Invalid invite code" | Code not found | Ask user to verify |
|
||||
| "A space with this URL slug already exists" | Duplicate slug | Suggest different slug |
|
||||
|
||||
---
|
||||
|
||||
## Server actions added (migrations 016-024)
|
||||
|
||||
All are `'use server'` in `lib/actions/*`, re-exported from `lib/actions/index.ts`,
|
||||
validate input with Zod via `parseInput`, authorize via `requireMember` /
|
||||
`requireMemberWithRole`, scope writes by `member.space_id`, and `revalidatePath`.
|
||||
|
||||
### Governance — `proposals.ts`, `incidents.ts`, `policies.ts`
|
||||
`createProposal`, `openProposal`, `castVote`, `decideProposal`, `withdrawProposal`, `deleteProposal`; `fileIncident`, `updateIncidentStatus`, `addIncidentUpdate`, `appealIncident`; `createPolicy`, `supersedePolicy`, `updatePolicyStatus`.
|
||||
|
||||
### Areas — `areas.ts`
|
||||
`createArea`, `updateArea`, `deleteArea` (admin/board; delete admin).
|
||||
|
||||
### Forum + comments — `forum.ts`
|
||||
`createForumThread`, `updateForumThread` (pin/lock = admin/board), `deleteForumThread`, `addComment`, `editComment`, `deleteComment`. All space-scoped in code.
|
||||
|
||||
### Chat channels — `comms.ts`
|
||||
`createChannel` (any member), `renameChannel`, `deleteChannel` (creator or admin/board; default channels protected).
|
||||
|
||||
### Tiers / roles / invites — `tiers.ts`, `roles.ts`, `invites.ts`
|
||||
`createTier`/`updateTier`/`deleteTier`; `upsertRoleLabel`, `createCustomRole`/`updateCustomRole`/`deleteCustomRole`, `assignCustomRole`/`unassignCustomRole`; `createInvite`/`updateInvite`/`deleteInvite`.
|
||||
|
||||
### Onboarding — `onboarding.ts`
|
||||
`createOnboardingStep`/`updateOnboardingStep`/`deleteOnboardingStep` (admin/board); `markOnboardingStepDone`, `finishOnboarding`, `skipOnboarding` (member; completion writes via the service client after the server-side required-steps check, so the self-change trigger cannot be bypassed).
|
||||
|
||||
### Permissions / Ops ACLs / area-lead — `permissions.ts`
|
||||
`setRolePermissions(subject, permissions[])`, `setOpsAcl(entity_type, entity_id, roles[])` (admin/board), `createAreaLeadRole`, `assignAreaLead`, `unassignAreaLead`, `deleteAreaLeadRole`. ACLs are additive: empty list falls back to the item's default visibility.
|
||||
|
||||
### Secrets — `secrets.ts`
|
||||
`createSecret`, `updateSecret`, `revealSecret` (server-only plaintext path, audit-logged, errors on corrupt ciphertext), `deleteSecret`. The list query never selects `value`/`encrypted_value`.
|
||||
|
||||
### Bulk import — `imports.ts`, `payments.ts`
|
||||
`importMembers`, `importPaymentsCsv`: per-row Zod validation, lowercased emails, `flexibleDateTime` date normalization, enum-checked platform/tier, positive finite amounts; invalid rows skipped and returned as a `skipped` count.
|
||||
|
||||
+36
-4
@@ -1,9 +1,12 @@
|
||||
# Hackerspace.sh - Database Schema Reference
|
||||
|
||||
> **Last Updated**: 2026-03-10
|
||||
> **Database**: PostgreSQL via Supabase
|
||||
> **Tables**: 13
|
||||
> **Source of Truth**: Live database schema
|
||||
> **Last Updated**: 2026-05-15
|
||||
> **Database**: PostgreSQL via self-hosted Supabase
|
||||
> **Tables**: ~30
|
||||
> **Source of Truth**: `scripts/schema.sql` (canonical, idempotent); numbered
|
||||
> migrations `scripts/0NN_*.sql` upgrade existing deployments. The 13-table
|
||||
> reference below is the original baseline; tables added by migrations
|
||||
> 014-024 are summarized in the "Migrations 014-024" section at the end.
|
||||
|
||||
---
|
||||
|
||||
@@ -680,3 +683,32 @@ CREATE INDEX idx_activity_space ON activity_log(space_id, created_at);
|
||||
| `009_fix_member_insert_rls.sql` | 1.8 | Fixed member INSERT RLS for admin/board |
|
||||
| `010_fix_channel_trigger.sql` | 1.9 | Removed non-existent description column from trigger |
|
||||
| `011_fix_member_status_enum.sql` | 2.0 | Fixed enum values (active→current, pending→unverified) |
|
||||
|
||||
---
|
||||
|
||||
## Migrations 014-024 (additions since the 13-table baseline)
|
||||
|
||||
`scripts/schema.sql` is the canonical idempotent schema; each numbered
|
||||
migration is mirrored as a section in it. Tables/columns added:
|
||||
|
||||
| Mig | Adds |
|
||||
|-----|------|
|
||||
| 014 | `space_members.user_id` made nullable (offline/imported members) |
|
||||
| 015 | `prevent_member_self_role_change()` trigger (self-escalation guard) |
|
||||
| 016 | Governance kernel: `proposals`, `proposal_votes`, `incidents`, `incident_updates`, `policies` + quorum/tally/SLA functions |
|
||||
| 017 | Governance RLS hardening (cross-tenant edge cases) |
|
||||
| 018 | `space_members.skills/interests/willing_to/affiliations/coi_last_disclosed_at`; `knowledge_base.is_meeting_minutes/meeting_date`; financial/member-directory visibility settings |
|
||||
| 019 | Auto-expire open proposals past their voting window |
|
||||
| 020 | `space_areas` (per-space configurable areas) + seeded defaults |
|
||||
| 021 | `forum_threads`, `comments` (polymorphic: forum_thread/proposal/incident/policy), `space_tiers` + `space_members.tier_id`, `space_role_labels`, `space_custom_roles`, `space_member_custom_roles`, `space_invites`; `secrets.encrypted_value/encryption_version`; `knowledge_base.render_markdown`; comms_channels member-creatable |
|
||||
| 022 | `space_onboarding_steps`; `space_members.onboarding_completed_at/onboarding_progress`; seed default steps |
|
||||
| 023 | `space_role_permissions`, `ops_acl`; `user_effective_roles()`, `user_has_permission()` (SECURITY DEFINER); `secrets`/`knowledge_base` SELECT rewritten as `existing-rule OR ops_acl-match` (additive) |
|
||||
| 024 | `prevent_member_self_role_change()` extended to also block self-change of `tier_id` and `onboarding_completed_at` |
|
||||
|
||||
New enum: `comment_entity_type` = `forum_thread | proposal | incident | policy`.
|
||||
|
||||
Every new table has RLS enabled with SELECT/INSERT/UPDATE/DELETE policies
|
||||
(writes admin/board, deletes admin, reads space members) and, where it has
|
||||
`updated_at`, a `touch_updated_at` trigger. The ACL model is additive:
|
||||
with zero `ops_acl`/`space_role_permissions` rows the app behaves exactly
|
||||
as the role-based baseline.
|
||||
|
||||
+7
-5
@@ -28,12 +28,14 @@ Five parallel read-only audits (security/RLS, server actions/validation, routes/
|
||||
- **KB/process per-item ACL wired.** `OpsAclEditor` renders in the KB/process viewer modal (admin/board); entity type derived from the `process` tag. Per-item Ops ACLs now cover secrets + KB + processes.
|
||||
- `/ops` and `/import` denial paths left as-is: unreachable behind the `(app)` layout gate, or already a clear denial with no data leak.
|
||||
|
||||
### Tracked backlog (not security-critical, zero runtime impact)
|
||||
### Pass 16c — backlog cleared (same day)
|
||||
|
||||
- `types/database.ts` still missing 8 prior-pass tables + `comment_entity_type` enum + 3 `space_members` columns (loose casts; build green via `ignoreBuildErrors`).
|
||||
- `docs/DATABASE_SCHEMA.md`, `DB_SCHEMA_MAP.md`, `docs/API_REFERENCE.md` stale since 2026-03-10 (missing migrations 016-024).
|
||||
- Email `.toLowerCase()` still missing on the non-import auth/member schemas (import path now normalizes).
|
||||
- Members-row "Make area lead" shortcut (Customize -> Area leads fully covers assignment).
|
||||
- **`types/database.ts` synced.** Added the 8 missing tables (forum_threads, comments, space_tiers, space_invites, space_onboarding_steps, space_role_labels, space_custom_roles, space_member_custom_roles), the `comment_entity_type` enum (both type union and Constants array), and the missing columns: `space_members.tier_id/onboarding_completed_at/onboarding_progress`, `secrets.encrypted_value/encryption_version`, `knowledge_base.render_markdown`.
|
||||
- **Email normalized everywhere.** New `emailField()` helper (trim + lowercase) applied to signIn/signUp/addMember/updateMember/createContact and the import path. Prevents case-variant duplicate members/contacts.
|
||||
- **Members-row "Make area lead".** A per-row picker in the members directory (admin/board) assigns a member to any area-lead role via `assignAreaLead`; same capability also in Customize -> Area leads.
|
||||
- **Docs refreshed.** `DATABASE_SCHEMA.md` (header + migrations 014-024 summary), `DB_SCHEMA_MAP.md` (table quick-map + helpers), `docs/API_REFERENCE.md` (all server actions added in migrations 016-024). All re-dated 2026-05-15 with `scripts/schema.sql` named as source of truth.
|
||||
|
||||
Backlog is now empty. Remaining known non-issues: `/ops` + `/import` `return null` are unreachable behind the `(app)` layout gate; `next.config.mjs` keeps `ignoreBuildErrors` (pre-existing legacy app/ TS noise) — the new code is type-clean.
|
||||
|
||||
### Verification
|
||||
|
||||
|
||||
Reference in New Issue
Block a user