Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a006283
feat: add draft proposals functionality
brunod-e May 15, 2026
dd58a36
fix: generated files
brunod-e May 15, 2026
3335775
feat: add UUID and Void scalar types to codegen configuration
brunod-e May 15, 2026
045aac6
feat: add proposal drafts schema and metadata
brunod-e May 15, 2026
90434ed
feat: enhance BodyField component with versioning support in Proposal…
brunod-e May 15, 2026
a529f96
feat: refactor database connections and update draft proposals handling
brunod-e May 19, 2026
74ef659
chore: update generated files
brunod-e May 21, 2026
392ebcd
chore: add changeset entry for api
brunod-e May 21, 2026
86c034c
chore: update gateful.json to have all daos
brunod-e May 21, 2026
8d377a7
Merge branch 'origin/dev' into feat/draft-proposals
brunod-e May 21, 2026
e806fbb
fix: reset draft hydration guard when draftId changes
brunod-e May 21, 2026
686f798
fix(api): scope draft proposal ID lookups and mutations by daoId
brunod-e May 22, 2026
63ec7ed
fix(dashboard): guard against duplicate draft creates and migration l…
brunod-e May 22, 2026
4f5e726
fix(dashboard): set shared-draft hydration guard only after fetch res…
brunod-e May 22, 2026
53eff25
fix(dashboard): expose error + manual retry for draft migration
brunod-e May 22, 2026
ecaa315
fix(dashboard): only set currentDraftId when viewer owns the shared d…
brunod-e May 22, 2026
da43ac6
fix(dashboard): guard draft state updates against stale address fetches
brunod-e May 22, 2026
4e4eb87
fix(dashboard): ignore stale shared-draft fetch results (#3288298975)
brunod-e May 25, 2026
a33c463
fix(create-proposal): remove outdated proposal fixes documentation
brunod-e May 25, 2026
17bbcf2
Merge remote-tracking branch 'origin/dev' into feat/draft-proposals
brunod-e May 28, 2026
6e889d3
Merge branch 'dev' into feat/draft-proposals
brunod-e May 28, 2026
1931b9b
fix: codex issues
brunod-e May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/draft-proposals-db.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@anticapture/api": minor
"@anticapture/dashboard": minor
"@anticapture/api-gateway": patch
"@anticapture/gateful": patch
---

feat(draft-proposals): persist draft proposals in PostgreSQL with SIWE authentication

Moves draft proposal storage from browser localStorage to the API's PostgreSQL database. Adds SIWE-based JWT authentication endpoints (`GET /auth/nonce`, `POST /auth/verify`) and full CRUD endpoints for draft proposals (`/proposal-drafts`). On wallet connect, existing localStorage drafts are automatically migrated to the database. Drafts are scoped per user address and DAO.
18 changes: 18 additions & 0 deletions apps/api/cmd/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
accountInteractions,
dao,
delegationPercentage,
draftProposals,
governanceActivity,
historicalBalances,
historicalVotingPower,
Expand All @@ -42,6 +43,7 @@ import {
health,
revenue,
} from "@/controllers";
import * as generalSchema from "@/database/general-schema";
import * as offchainSchema from "@/database/offchain-schema";
import * as schema from "@/database/schema";
import { docs } from "@/docs";
Expand All @@ -58,6 +60,7 @@ import {
AccountInteractionsRepository,
BalanceVariationsRepository,
DaoMetricsDayBucketRepository,
DraftProposalsRepository,
DrizzleProposalsActivityRepository,
DrizzleRepository,
HealthRepositoryImpl,
Expand All @@ -84,6 +87,7 @@ import {
CoingeckoService,
DaoService,
DelegationPercentageService,
DraftProposalsService,
HealthService,
HistoricalBalancesService,
NFTPriceService,
Expand Down Expand Up @@ -180,6 +184,11 @@ const pgClient = drizzle(env.DATABASE_URL, {
casing: "snake_case",
});

const pgGeneralClient = drizzle(env.DATABASE_URL, {
schema: generalSchema,
casing: "snake_case",
});

health(app, new HealthService(new HealthRepositoryImpl(pgClient), daoClient));

const daoConfig = CONTRACT_ADDRESSES[env.DAO_ID];
Expand Down Expand Up @@ -344,6 +353,15 @@ votes(
),
);
dao(app, daoService);
draftProposals(
app,
wrapWithTracing(
new DraftProposalsService(
wrapWithTracing(new DraftProposalsRepository(pgGeneralClient)),
),
),
env.DAO_ID.toLowerCase(),
);
docs(app);
tokenMetrics(app, tokenMetricsService);

Expand Down
11 changes: 11 additions & 0 deletions apps/api/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from "drizzle-kit";

export default defineConfig({
schema: "./src/database/general-schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
schemaFilter: ["general"],
});
16 changes: 16 additions & 0 deletions apps/api/drizzle/0000_large_mac_gargan.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE SCHEMA "general";
--> statement-breakpoint
CREATE TABLE "general"."proposal_drafts" (
"id" text PRIMARY KEY NOT NULL,
"dao_id" text NOT NULL,
"author" text NOT NULL,
"title" text DEFAULT '' NOT NULL,
"discussion_url" text DEFAULT '' NOT NULL,
"body" text DEFAULT '' NOT NULL,
"actions" jsonb DEFAULT '[]'::jsonb NOT NULL,
"created_at" bigint NOT NULL,
"updated_at" bigint NOT NULL
);
--> statement-breakpoint
CREATE INDEX "proposal_drafts_author_index" ON "general"."proposal_drafts" USING btree ("author");--> statement-breakpoint
CREATE INDEX "proposal_drafts_dao_id_index" ON "general"."proposal_drafts" USING btree ("dao_id");
123 changes: 123 additions & 0 deletions apps/api/drizzle/meta/0000_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
{
"id": "1e4542ba-754f-4b5f-a22b-0b52a98b6364",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"general.proposal_drafts": {
"name": "proposal_drafts",
"schema": "general",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"dao_id": {
"name": "dao_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"author": {
"name": "author",
"type": "text",
"primaryKey": false,
"notNull": true
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"discussion_url": {
"name": "discussion_url",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"body": {
"name": "body",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"actions": {
"name": "actions",
"type": "jsonb",
"primaryKey": false,
"notNull": true,
"default": "'[]'::jsonb"
},
"created_at": {
"name": "created_at",
"type": "bigint",
"primaryKey": false,
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"proposal_drafts_author_index": {
"name": "proposal_drafts_author_index",
"columns": [
{
"expression": "author",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"proposal_drafts_dao_id_index": {
"name": "proposal_drafts_dao_id_index",
"columns": [
{
"expression": "dao_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {
"general": "general"
},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
13 changes: 13 additions & 0 deletions apps/api/drizzle/meta/_journal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1778851591748,
"tag": "0000_large_mac_gargan",
"breakpoints": true
}
]
}
5 changes: 4 additions & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
"build:watch": "tsc --watch",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"dev": "tsx watch cmd/index.ts"
"dev": "tsx watch cmd/index.ts",
"db:push": "drizzle-kit push",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate"
},
"keywords": [],
"author": "",
Expand Down
Loading