From 19562cd369675d1d94f2ca1cf0aa82192026bae9 Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Fri, 17 Jan 2025 20:51:39 +0000 Subject: [PATCH] add token and dynamically lookup route --- middleware.ts | 16 ++++++++++++++++ drizzle/relations.ts | 63 ++++++++++++++++++++++++++++++++++++++++++--------------------- drizzle/schema.ts | 26 +++++++++++++++++++++++++- supabase/database.types.ts | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ supabase/migrations/20250117205106_add_token_to_custom_domain_routes.sql | 7 +++++++ 5 file(s) changed, 155 insertion(s)(+), 22 deletion(s)(-) diff --git a/middleware.ts b/middleware.ts --- a/middleware.ts +++ b/middleware.ts @@ -1,4 +1,6 @@ +import { createClient } from "@supabase/supabase-js"; import { NextRequest, NextResponse } from "next/server"; +import { Database } from "supabase/database.types"; export const config = { matcher: [ @@ -13,8 +15,22 @@ ], }; +let supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, + process.env.SUPABASE_SERVICE_ROLE_KEY as string, +); export default async function middleware(req: NextRequest) { let hostname = req.headers.get("host")!; + if (hostname === "leaflet.pub") return; + let { data: route } = await supabase + .from("custom_domain_routes") + .select("*") + .eq("domain", hostname) + .eq("route", req.nextUrl.pathname) + .single(); + if (route) + return NextResponse.rewrite(new URL(`/${route.permission_token}`, req.url)); + if (hostname === "guilds.nyc") return NextResponse.rewrite( new URL("/b64bc712-c9c1-4ed3-a8f4-d33f33d3bfdb", req.url), diff --git a/drizzle/relations.ts b/drizzle/relations.ts --- a/drizzle/relations.ts +++ b/drizzle/relations.ts @@ -1,5 +1,46 @@ import { relations } from "drizzle-orm/relations"; -import { entities, facts, entity_sets, permission_tokens, identities, email_subscriptions_to_entity, email_auth_tokens, phone_rsvps_to_entity, permission_token_on_homepage, permission_token_rights } from "./schema"; +import { identities, custom_domains, custom_domain_routes, permission_tokens, entities, facts, entity_sets, email_subscriptions_to_entity, email_auth_tokens, phone_rsvps_to_entity, permission_token_on_homepage, permission_token_rights } from "./schema"; + +export const custom_domainsRelations = relations(custom_domains, ({one, many}) => ({ + identity: one(identities, { + fields: [custom_domains.identity], + references: [identities.email] + }), + custom_domain_routes: many(custom_domain_routes), +})); + +export const identitiesRelations = relations(identities, ({one, many}) => ({ + custom_domains: many(custom_domains), + permission_token: one(permission_tokens, { + fields: [identities.home_page], + references: [permission_tokens.id] + }), + email_auth_tokens: many(email_auth_tokens), + permission_token_on_homepages: many(permission_token_on_homepage), +})); + +export const custom_domain_routesRelations = relations(custom_domain_routes, ({one}) => ({ + custom_domain: one(custom_domains, { + fields: [custom_domain_routes.domain], + references: [custom_domains.domain] + }), + permission_token: one(permission_tokens, { + fields: [custom_domain_routes.permission_token], + references: [permission_tokens.id] + }), +})); + +export const permission_tokensRelations = relations(permission_tokens, ({one, many}) => ({ + custom_domain_routes: many(custom_domain_routes), + entity: one(entities, { + fields: [permission_tokens.root_entity], + references: [entities.id] + }), + identities: many(identities), + email_subscriptions_to_entities: many(email_subscriptions_to_entity), + permission_token_on_homepages: many(permission_token_on_homepage), + permission_token_rights: many(permission_token_rights), +})); export const factsRelations = relations(facts, ({one}) => ({ entity: one(entities, { @@ -22,26 +63,6 @@ export const entity_setsRelations = relations(entity_sets, ({many}) => ({ entities: many(entities), permission_token_rights: many(permission_token_rights), -})); - -export const permission_tokensRelations = relations(permission_tokens, ({one, many}) => ({ - entity: one(entities, { - fields: [permission_tokens.root_entity], - references: [entities.id] - }), - identities: many(identities), - email_subscriptions_to_entities: many(email_subscriptions_to_entity), - permission_token_on_homepages: many(permission_token_on_homepage), - permission_token_rights: many(permission_token_rights), -})); - -export const identitiesRelations = relations(identities, ({one, many}) => ({ - permission_token: one(permission_tokens, { - fields: [identities.home_page], - references: [permission_tokens.id] - }), - email_auth_tokens: many(email_auth_tokens), - permission_token_on_homepages: many(permission_token_on_homepage), })); export const email_subscriptions_to_entityRelations = relations(email_subscriptions_to_entity, ({one}) => ({ diff --git a/drizzle/schema.ts b/drizzle/schema.ts --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -1,4 +1,4 @@ -import { pgTable, foreignKey, pgEnum, uuid, text, jsonb, timestamp, bigint, boolean, uniqueIndex, primaryKey } from "drizzle-orm/pg-core" +import { pgTable, foreignKey, pgEnum, text, boolean, unique, uuid, jsonb, timestamp, bigint, uniqueIndex, smallint, primaryKey } from "drizzle-orm/pg-core" import { sql } from "drizzle-orm" export const aal_level = pgEnum("aal_level", ['aal1', 'aal2', 'aal3']) @@ -13,6 +13,24 @@ export const action = pgEnum("action", ['INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'ERROR']) export const equality_op = pgEnum("equality_op", ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in']) + +export const custom_domains = pgTable("custom_domains", { + domain: text("domain").primaryKey().notNull(), + identity: text("identity").default('').notNull().references(() => identities.email, { onDelete: "cascade", onUpdate: "cascade" } ), + confirmed: boolean("confirmed").notNull(), +}); + +export const custom_domain_routes = pgTable("custom_domain_routes", { + id: uuid("id").defaultRandom().primaryKey().notNull(), + domain: text("domain").notNull().references(() => custom_domains.domain), + route: text("route").notNull(), + permission_token: uuid("permission_token").notNull().references(() => permission_tokens.id, { onDelete: "cascade", onUpdate: "cascade" } ), +}, +(table) => { + return { + custom_domain_routes_domain_route_key: unique("custom_domain_routes_domain_route_key").on(table.domain, table.route), + } +}); export const facts = pgTable("facts", { id: uuid("id").primaryKey().notNull(), @@ -53,6 +71,11 @@ created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), home_page: uuid("home_page").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), email: text("email"), +}, +(table) => { + return { + identities_email_key: unique("identities_email_key").on(table.email), + } }); export const email_subscriptions_to_entity = pgTable("email_subscriptions_to_entity", { @@ -91,6 +114,7 @@ entity: uuid("entity").notNull().references(() => entities.id, { onDelete: "cascade", onUpdate: "cascade" } ), name: text("name").default('').notNull(), country_code: text("country_code").notNull(), + plus_ones: smallint("plus_ones").default(0).notNull(), }, (table) => { return { diff --git a/supabase/database.types.ts b/supabase/database.types.ts --- a/supabase/database.types.ts +++ b/supabase/database.types.ts @@ -34,6 +34,68 @@ } public: { Tables: { + custom_domain_routes: { + Row: { + domain: string + id: string + permission_token: string + route: string + } + Insert: { + domain: string + id?: string + permission_token: string + route: string + } + Update: { + domain?: string + id?: string + permission_token?: string + route?: string + } + Relationships: [ + { + foreignKeyName: "custom_domain_routes_domain_fkey" + columns: ["domain"] + isOneToOne: false + referencedRelation: "custom_domains" + referencedColumns: ["domain"] + }, + { + foreignKeyName: "custom_domain_routes_permission_token_fkey" + columns: ["permission_token"] + isOneToOne: false + referencedRelation: "permission_tokens" + referencedColumns: ["id"] + }, + ] + } + custom_domains: { + Row: { + confirmed: boolean + domain: string + identity: string + } + Insert: { + confirmed: boolean + domain: string + identity?: string + } + Update: { + confirmed?: boolean + domain?: string + identity?: string + } + Relationships: [ + { + foreignKeyName: "custom_domains_identity_fkey" + columns: ["identity"] + isOneToOne: false + referencedRelation: "identities" + referencedColumns: ["email"] + }, + ] + } email_auth_tokens: { Row: { confirmation_code: string @@ -358,6 +420,7 @@ id: string name: string phone_number: string + plus_ones: number status: Database["public"]["Enums"]["rsvp_status"] } Insert: { @@ -367,6 +430,7 @@ id?: string name?: string phone_number: string + plus_ones?: number status: Database["public"]["Enums"]["rsvp_status"] } Update: { @@ -376,6 +440,7 @@ id?: string name?: string phone_number?: string + plus_ones?: number status?: Database["public"]["Enums"]["rsvp_status"] } Relationships: [ diff --git a/supabase/migrations/20250117205106_add_token_to_custom_domain_routes.sql b/supabase/migrations/20250117205106_add_token_to_custom_domain_routes.sql new file mode 100644 --- /dev/null +++ b/supabase/migrations/20250117205106_add_token_to_custom_domain_routes.sql @@ -0,0 +1,7 @@ +alter table "public"."custom_domain_routes" add column "permission_token" uuid not null; + +alter table "public"."custom_domain_routes" enable row level security; + +alter table "public"."custom_domain_routes" add constraint "custom_domain_routes_permission_token_fkey" FOREIGN KEY (permission_token) REFERENCES permission_tokens(id) ON UPDATE CASCADE ON DELETE CASCADE not valid; + +alter table "public"."custom_domain_routes" validate constraint "custom_domain_routes_permission_token_fkey"; -- tangled.sh