Add full application: receipt scanning, auth, billing, and account deletion

Brings the working codebase (Next.js app, auth system, Stripe billing,
Docker/deploy config, tests, docs) into version control on top of the
placeholder initial commit, and adds account self-deletion (Danger Zone
in Settings, password + typed-email confirmation, cascading DB cleanup,
Stripe cancellation) per GDPR right-to-erasure.

Excludes local build caches, node_modules, and internal agent scratch
files; .gitignore hardened to keep those out going forward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Timo
2026-08-19 20:59:04 +02:00
parent 650a74da97
commit 84b9987c49
415 changed files with 96619 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
CREATE TABLE "guest_sessions" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"ip_hash" varchar(64),
"scan_count" numeric DEFAULT '0' NOT NULL,
"last_scan_at" timestamp,
"expires_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "licenses" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64),
"license_key" varchar(128) NOT NULL,
"plan" varchar(32) NOT NULL,
"status" varchar(32) DEFAULT 'active' NOT NULL,
"stripe_customer_id" varchar(128),
"stripe_subscription_id" varchar(128),
"stripe_checkout_session_id" varchar(128),
"activated_at" timestamp DEFAULT now() NOT NULL,
"expires_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "licenses_license_key_unique" UNIQUE("license_key")
);
--> statement-breakpoint
CREATE TABLE "line_items" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"receipt_id" varchar(64) NOT NULL,
"description" text NOT NULL,
"quantity" numeric(10, 3) DEFAULT '1' NOT NULL,
"price" numeric(12, 2) NOT NULL,
"unit_price" numeric(12, 2),
"tax_rate" numeric(5, 2),
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "receipts" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64) NOT NULL,
"image_hash" varchar(64) NOT NULL,
"storage_url" text,
"merchant_name" varchar(255),
"receipt_date" varchar(32),
"receipt_number" varchar(128),
"document_type" varchar(64) DEFAULT 'KASSENBON',
"category" varchar(64) DEFAULT 'Sonstiges',
"currency" varchar(8) DEFAULT 'EUR' NOT NULL,
"total_amount" numeric(12, 2) NOT NULL,
"net_amount" numeric(12, 2),
"tax_7_amount" numeric(12, 2),
"tax_19_amount" numeric(12, 2),
"tip_amount" numeric(12, 2),
"tax_breakdown_json" jsonb,
"line_items_json" jsonb,
"validation_json" jsonb,
"raw_ocr_text" text,
"payment_method" varchar(64),
"is_math_valid" boolean DEFAULT true NOT NULL,
"needs_review" boolean DEFAULT false NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "users" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"email" varchar(255),
"is_guest" boolean DEFAULT true NOT NULL,
"plan" varchar(32) DEFAULT 'free' NOT NULL,
"stripe_customer_id" varchar(128),
"stripe_subscription_id" varchar(128),
"scan_count" numeric DEFAULT '0' NOT NULL,
"expires_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "licenses" ADD CONSTRAINT "licenses_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "line_items" ADD CONSTRAINT "line_items_receipt_id_receipts_id_fk" FOREIGN KEY ("receipt_id") REFERENCES "public"."receipts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "receipts" ADD CONSTRAINT "receipts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_guest_sessions_ip" ON "guest_sessions" USING btree ("ip_hash");--> statement-breakpoint
CREATE INDEX "idx_licenses_key" ON "licenses" USING btree ("license_key");--> statement-breakpoint
CREATE INDEX "idx_licenses_user_id" ON "licenses" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "idx_licenses_sub_id" ON "licenses" USING btree ("stripe_subscription_id");--> statement-breakpoint
CREATE INDEX "idx_line_items_receipt_id" ON "line_items" USING btree ("receipt_id");--> statement-breakpoint
CREATE INDEX "idx_receipts_user_date" ON "receipts" USING btree ("user_id","receipt_date");--> statement-breakpoint
CREATE INDEX "idx_receipts_hash" ON "receipts" USING btree ("image_hash");--> statement-breakpoint
CREATE INDEX "idx_receipts_duplicate_check" ON "receipts" USING btree ("user_id","merchant_name","total_amount","receipt_date");--> statement-breakpoint
CREATE INDEX "idx_users_email" ON "users" USING btree ("email");--> statement-breakpoint
CREATE INDEX "idx_users_guest_created" ON "users" USING btree ("is_guest","created_at");

View File

@@ -0,0 +1,40 @@
CREATE TABLE "email_verification_tokens" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64) NOT NULL,
"email" varchar(255) NOT NULL,
"expires_at" timestamp NOT NULL,
"consumed_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "oauth_accounts" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64) NOT NULL,
"provider" varchar(32) NOT NULL,
"provider_account_id" varchar(255) NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "sessions" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64) NOT NULL,
"expires_at" timestamp NOT NULL,
"user_agent" varchar(255),
"ip_hash" varchar(64),
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "email_key" varchar(255);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "name" varchar(160);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "password_hash" varchar(255);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "email_verified_at" timestamp;--> statement-breakpoint
ALTER TABLE "email_verification_tokens" ADD CONSTRAINT "email_verification_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "oauth_accounts" ADD CONSTRAINT "oauth_accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_email_verification_user_id" ON "email_verification_tokens" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "idx_email_verification_expires_at" ON "email_verification_tokens" USING btree ("expires_at");--> statement-breakpoint
CREATE UNIQUE INDEX "uq_oauth_provider_account" ON "oauth_accounts" USING btree ("provider","provider_account_id");--> statement-breakpoint
CREATE INDEX "idx_oauth_accounts_user_id" ON "oauth_accounts" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "idx_sessions_user_id" ON "sessions" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "idx_sessions_expires_at" ON "sessions" USING btree ("expires_at");--> statement-breakpoint
CREATE UNIQUE INDEX "uq_users_email_key" ON "users" USING btree ("email_key");

View File

@@ -0,0 +1,12 @@
CREATE TABLE "password_reset_tokens" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64) NOT NULL,
"expires_at" timestamp NOT NULL,
"consumed_at" timestamp,
"requested_ip_hash" varchar(64),
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_password_reset_user_id" ON "password_reset_tokens" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "idx_password_reset_expires_at" ON "password_reset_tokens" USING btree ("expires_at");

View File

@@ -0,0 +1,25 @@
CREATE TABLE "launch_claims" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64) NOT NULL,
"position" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "waitlist" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"email" varchar(255) NOT NULL,
"name" varchar(160),
"source" varchar(100) DEFAULT 'landing_page',
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "company" varchar(255);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "use_case" varchar(100);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "referral_source" varchar(100);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "onboarding_completed_at" timestamp;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "launch_bonus" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "launch_claims" ADD CONSTRAINT "launch_claims_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "uq_launch_claims_user_id" ON "launch_claims" USING btree ("user_id");--> statement-breakpoint
CREATE UNIQUE INDEX "uq_launch_claims_position" ON "launch_claims" USING btree ("position");--> statement-breakpoint
CREATE UNIQUE INDEX "uq_waitlist_email" ON "waitlist" USING btree ("email");--> statement-breakpoint
CREATE INDEX "idx_waitlist_created_at" ON "waitlist" USING btree ("created_at");

View File

@@ -0,0 +1 @@
ALTER TABLE "users" ADD COLUMN "scan_period" varchar(7);--> statement-breakpoint

View File

@@ -0,0 +1,15 @@
CREATE TABLE "security_events" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"type" varchar(64) NOT NULL,
"user_id" varchar(64),
"email" varchar(255),
"ip_hash" varchar(64),
"user_agent" varchar(255),
"metadata_json" jsonb,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "security_events" ADD CONSTRAINT "security_events_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_security_events_type_created" ON "security_events" USING btree ("type","created_at");--> statement-breakpoint
CREATE INDEX "idx_security_events_user_created" ON "security_events" USING btree ("user_id","created_at");--> statement-breakpoint
CREATE INDEX "idx_security_events_ip_created" ON "security_events" USING btree ("ip_hash","created_at");

View File

@@ -0,0 +1,19 @@
CREATE TABLE "projects" (
"id" varchar(64) PRIMARY KEY NOT NULL,
"user_id" varchar(64) NOT NULL,
"name" varchar(120) NOT NULL,
"color" varchar(16),
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "receipts" ADD COLUMN "project_id" varchar(64);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "monthly_volume" varchar(50);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "export_format" varchar(100);--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "main_pain_point" varchar(100);--> statement-breakpoint
ALTER TABLE "projects" ADD CONSTRAINT "projects_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_projects_user_id" ON "projects" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "idx_projects_user_created" ON "projects" USING btree ("user_id","created_at");--> statement-breakpoint
CREATE UNIQUE INDEX "uq_projects_user_name" ON "projects" USING btree ("user_id","name");--> statement-breakpoint
ALTER TABLE "receipts" ADD CONSTRAINT "receipts_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_receipts_project" ON "receipts" USING btree ("user_id","project_id");

View File

@@ -0,0 +1,2 @@
ALTER TABLE "receipts" ALTER COLUMN "image_hash" SET DATA TYPE varchar(128);--> statement-breakpoint
ALTER TABLE "receipts" ADD COLUMN "extraction_json" jsonb;

View File

@@ -0,0 +1,706 @@
{
"id": "4d3733ee-e4ad-4775-8cd6-1d64569d7ef0",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.guest_sessions": {
"name": "guest_sessions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(64)",
"primaryKey": true,
"notNull": true
},
"ip_hash": {
"name": "ip_hash",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false
},
"scan_count": {
"name": "scan_count",
"type": "numeric",
"primaryKey": false,
"notNull": true,
"default": "'0'"
},
"last_scan_at": {
"name": "last_scan_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"idx_guest_sessions_ip": {
"name": "idx_guest_sessions_ip",
"columns": [
{
"expression": "ip_hash",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.licenses": {
"name": "licenses",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(64)",
"primaryKey": true,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false
},
"license_key": {
"name": "license_key",
"type": "varchar(128)",
"primaryKey": false,
"notNull": true
},
"plan": {
"name": "plan",
"type": "varchar(32)",
"primaryKey": false,
"notNull": true
},
"status": {
"name": "status",
"type": "varchar(32)",
"primaryKey": false,
"notNull": true,
"default": "'active'"
},
"stripe_customer_id": {
"name": "stripe_customer_id",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_id": {
"name": "stripe_subscription_id",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false
},
"stripe_checkout_session_id": {
"name": "stripe_checkout_session_id",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false
},
"activated_at": {
"name": "activated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"idx_licenses_key": {
"name": "idx_licenses_key",
"columns": [
{
"expression": "license_key",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"idx_licenses_user_id": {
"name": "idx_licenses_user_id",
"columns": [
{
"expression": "user_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"idx_licenses_sub_id": {
"name": "idx_licenses_sub_id",
"columns": [
{
"expression": "stripe_subscription_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"licenses_user_id_users_id_fk": {
"name": "licenses_user_id_users_id_fk",
"tableFrom": "licenses",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"licenses_license_key_unique": {
"name": "licenses_license_key_unique",
"nullsNotDistinct": false,
"columns": [
"license_key"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.line_items": {
"name": "line_items",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(64)",
"primaryKey": true,
"notNull": true
},
"receipt_id": {
"name": "receipt_id",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": true
},
"quantity": {
"name": "quantity",
"type": "numeric(10, 3)",
"primaryKey": false,
"notNull": true,
"default": "'1'"
},
"price": {
"name": "price",
"type": "numeric(12, 2)",
"primaryKey": false,
"notNull": true
},
"unit_price": {
"name": "unit_price",
"type": "numeric(12, 2)",
"primaryKey": false,
"notNull": false
},
"tax_rate": {
"name": "tax_rate",
"type": "numeric(5, 2)",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"idx_line_items_receipt_id": {
"name": "idx_line_items_receipt_id",
"columns": [
{
"expression": "receipt_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"line_items_receipt_id_receipts_id_fk": {
"name": "line_items_receipt_id_receipts_id_fk",
"tableFrom": "line_items",
"tableTo": "receipts",
"columnsFrom": [
"receipt_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.receipts": {
"name": "receipts",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(64)",
"primaryKey": true,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true
},
"image_hash": {
"name": "image_hash",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true
},
"storage_url": {
"name": "storage_url",
"type": "text",
"primaryKey": false,
"notNull": false
},
"merchant_name": {
"name": "merchant_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"receipt_date": {
"name": "receipt_date",
"type": "varchar(32)",
"primaryKey": false,
"notNull": false
},
"receipt_number": {
"name": "receipt_number",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false
},
"document_type": {
"name": "document_type",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"default": "'KASSENBON'"
},
"category": {
"name": "category",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"default": "'Sonstiges'"
},
"currency": {
"name": "currency",
"type": "varchar(8)",
"primaryKey": false,
"notNull": true,
"default": "'EUR'"
},
"total_amount": {
"name": "total_amount",
"type": "numeric(12, 2)",
"primaryKey": false,
"notNull": true
},
"net_amount": {
"name": "net_amount",
"type": "numeric(12, 2)",
"primaryKey": false,
"notNull": false
},
"tax_7_amount": {
"name": "tax_7_amount",
"type": "numeric(12, 2)",
"primaryKey": false,
"notNull": false
},
"tax_19_amount": {
"name": "tax_19_amount",
"type": "numeric(12, 2)",
"primaryKey": false,
"notNull": false
},
"tip_amount": {
"name": "tip_amount",
"type": "numeric(12, 2)",
"primaryKey": false,
"notNull": false
},
"tax_breakdown_json": {
"name": "tax_breakdown_json",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"line_items_json": {
"name": "line_items_json",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"validation_json": {
"name": "validation_json",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"raw_ocr_text": {
"name": "raw_ocr_text",
"type": "text",
"primaryKey": false,
"notNull": false
},
"payment_method": {
"name": "payment_method",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false
},
"is_math_valid": {
"name": "is_math_valid",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": true
},
"needs_review": {
"name": "needs_review",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"idx_receipts_user_date": {
"name": "idx_receipts_user_date",
"columns": [
{
"expression": "user_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "receipt_date",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"idx_receipts_hash": {
"name": "idx_receipts_hash",
"columns": [
{
"expression": "image_hash",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"idx_receipts_duplicate_check": {
"name": "idx_receipts_duplicate_check",
"columns": [
{
"expression": "user_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "merchant_name",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "total_amount",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "receipt_date",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"receipts_user_id_users_id_fk": {
"name": "receipts_user_id_users_id_fk",
"tableFrom": "receipts",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(64)",
"primaryKey": true,
"notNull": true
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"is_guest": {
"name": "is_guest",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": true
},
"plan": {
"name": "plan",
"type": "varchar(32)",
"primaryKey": false,
"notNull": true,
"default": "'free'"
},
"stripe_customer_id": {
"name": "stripe_customer_id",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_id": {
"name": "stripe_subscription_id",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false
},
"scan_count": {
"name": "scan_count",
"type": "numeric",
"primaryKey": false,
"notNull": true,
"default": "'0'"
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"idx_users_email": {
"name": "idx_users_email",
"columns": [
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"idx_users_guest_created": {
"name": "idx_users_guest_created",
"columns": [
{
"expression": "is_guest",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "created_at",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1786900750452,
"tag": "0000_noisy_magik",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1786950089451,
"tag": "0001_thin_blur",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1786952172929,
"tag": "0002_giant_maria_hill",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1786963011817,
"tag": "0003_flaky_blackheart",
"breakpoints": true
},
{
"idx": 4,
"version": "7",
"when": 1786967682012,
"tag": "0004_add_scan_period",
"breakpoints": true
},
{
"idx": 5,
"version": "7",
"when": 1786974929524,
"tag": "0005_add_security_events",
"breakpoints": true
},
{
"idx": 6,
"version": "7",
"when": 1787073012101,
"tag": "0006_add_projects",
"breakpoints": true
},
{
"idx": 7,
"version": "7",
"when": 1787130000000,
"tag": "0007_extraction_json_and_hash",
"breakpoints": true
}
]
}