Skip to content

Commit

Permalink
feat: store and send metadata during invoice creation
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya committed Nov 26, 2024
1 parent ccf1a10 commit d1da89e
Show file tree
Hide file tree
Showing 9 changed files with 4,559 additions and 4,461 deletions.
1 change: 1 addition & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"imports": {
"@nostr/tools": "jsr:@nostr/tools@^2.10.3",
"hono": "jsr:@hono/hono@^4.5.5",
"drizzle-orm": "npm:drizzle-orm@0.33.0",
"drizzle-kit": "npm:drizzle-kit@0.24.2",
Expand Down
8,588 changes: 4,158 additions & 4,430 deletions deno.lock

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions drizzle/0001_fluffy_black_tom.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
CREATE TABLE IF NOT EXISTS "invoices" (
"id" serial PRIMARY KEY NOT NULL,
"user_id" integer NOT NULL,
"amount" integer NOT NULL,
"description" text,
"description_hash" text,
"payment_request" text NOT NULL,
"payment_hash" text NOT NULL,
"preimage" text,
"metadata" jsonb,
"settled_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "invoices_payment_request_unique" UNIQUE("payment_request"),
CONSTRAINT "invoices_payment_hash_unique" UNIQUE("payment_hash")
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "invoices" ADD CONSTRAINT "invoices_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_id_idx" ON "invoices" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "payment_hash_idx" ON "invoices" USING btree ("payment_hash");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "user_payment_hash_idx" ON "invoices" USING btree ("user_id","payment_hash");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "username_idx" ON "users" USING btree ("username");
233 changes: 233 additions & 0 deletions drizzle/meta/0001_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
{
"id": "a90a8c91-5f2c-48da-9109-ae0a37820ceb",
"prevId": "e292703e-d08b-4f8b-a9eb-3937fe872be7",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.invoices": {
"name": "invoices",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"amount": {
"name": "amount",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"description_hash": {
"name": "description_hash",
"type": "text",
"primaryKey": false,
"notNull": false
},
"payment_request": {
"name": "payment_request",
"type": "text",
"primaryKey": false,
"notNull": true
},
"payment_hash": {
"name": "payment_hash",
"type": "text",
"primaryKey": false,
"notNull": true
},
"preimage": {
"name": "preimage",
"type": "text",
"primaryKey": false,
"notNull": false
},
"metadata": {
"name": "metadata",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"settled_at": {
"name": "settled_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"user_id_idx": {
"name": "user_id_idx",
"columns": [
{
"expression": "user_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"payment_hash_idx": {
"name": "payment_hash_idx",
"columns": [
{
"expression": "payment_hash",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"user_payment_hash_idx": {
"name": "user_payment_hash_idx",
"columns": [
{
"expression": "user_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "payment_hash",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"invoices_user_id_users_id_fk": {
"name": "invoices_user_id_users_id_fk",
"tableFrom": "invoices",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"invoices_payment_request_unique": {
"name": "invoices_payment_request_unique",
"nullsNotDistinct": false,
"columns": [
"payment_request"
]
},
"invoices_payment_hash_unique": {
"name": "invoices_payment_hash_unique",
"nullsNotDistinct": false,
"columns": [
"payment_hash"
]
}
}
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"connection_secret": {
"name": "connection_secret",
"type": "text",
"primaryKey": false,
"notNull": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"username_idx": {
"name": "username_idx",
"columns": [
{
"expression": "username",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"users_username_unique": {
"name": "users_username_unique",
"nullsNotDistinct": false,
"columns": [
"username"
]
}
}
}
},
"enums": {},
"schemas": {},
"sequences": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
7 changes: 7 additions & 0 deletions drizzle/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"when": 1728309815221,
"tag": "0000_greedy_phalanx",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1732639452062,
"tag": "0001_fluffy_black_tom",
"breakpoints": true
}
]
}
58 changes: 54 additions & 4 deletions src/db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { migrate } from "drizzle-orm/postgres-js/migrator";
import { nwc } from "npm:@getalby/sdk";
import postgres from "postgres";

import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import { DATABASE_URL } from "../constants.ts";
import { decrypt, encrypt } from "./aesgcm.ts";
import * as schema from "./schema.ts";
import { users } from "./schema.ts";
import { invoices, users } from "./schema.ts";

export async function runMigration() {
const migrationClient = postgres(DATABASE_URL, { max: 1 });
Expand Down Expand Up @@ -52,14 +52,64 @@ export class DB {
return this._db.query.users.findMany();
}

async findWalletConnectionSecret(username: string) {
async findUser(username: string) {
const result = await this._db.query.users.findFirst({
where: eq(users.username, username),
});
if (!result) {
throw new Error("user not found");
}
const connectionSecret = await decrypt(result.encryptedConnectionSecret);
return connectionSecret;
return {
id: result.id,
connectionSecret
};
}

async createInvoice(
userId: number,
transaction: nwc.Nip47Transaction
): Promise<{ identifier: string }> {
await this._db.insert(invoices).values({
userId,
amount: transaction.amount,
description: transaction.description,
descriptionHash: transaction.description_hash,
paymentRequest: transaction.invoice,
paymentHash: transaction.payment_hash,
metadata: transaction.metadata,
});

return { identifier: transaction.payment_hash };
}

async findInvoice(identifier: string) {
const result = await this._db.query.invoices.findFirst({
where: eq(invoices.paymentHash, identifier),
});
if (!result) {
throw new Error("invoice not found");
}
return result;
}

async updateInvoice(
userId: number,
transaction: nwc.Nip47Transaction
): Promise<void> {
await this._db
.update(invoices)
.set({
preimage: transaction.preimage,
settledAt: new Date(transaction.settled_at * 1000),
})
.where(
and(
eq(invoices.userId, userId),
eq(invoices.paymentHash, transaction.payment_hash)
)
)

return;
}
}
26 changes: 25 additions & 1 deletion src/db/schema.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
import { index, integer, jsonb, pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
id: serial("id").primaryKey(),
encryptedConnectionSecret: text("connection_secret").notNull(),
username: text("username").unique().notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
}, (table) => {
return {
usernameIdx: index("username_idx").on(table.username),
};
});

export const invoices = pgTable("invoices", {
id: serial("id").primaryKey(),
userId: integer("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
amount: integer("amount").notNull(),
description: text("description"),
descriptionHash: text("description_hash"),
paymentRequest: text("payment_request").unique().notNull(),
paymentHash: text("payment_hash").unique().notNull(),
preimage: text("preimage"),
metadata: jsonb("metadata"),
settledAt: timestamp("settled_at"),
createdAt: timestamp("created_at").notNull().defaultNow(),
}, (table) => {
return {
userIdIdx: index("user_id_idx").on(table.userId),
paymentHashIdx: index("payment_hash_idx").on(table.paymentHash),
userPaymentHashIdx: index("user_payment_hash_idx").on(table.userId, table.paymentHash),
};
});
Loading

0 comments on commit d1da89e

Please sign in to comment.