a tool for shared writing and social publishing

add token and dynamically lookup route

+155 -22
+42 -21
drizzle/relations.ts
··· 1 import { relations } from "drizzle-orm/relations"; 2 - 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"; 3 4 export const factsRelations = relations(facts, ({one}) => ({ 5 entity: one(entities, { ··· 22 export const entity_setsRelations = relations(entity_sets, ({many}) => ({ 23 entities: many(entities), 24 permission_token_rights: many(permission_token_rights), 25 - })); 26 - 27 - export const permission_tokensRelations = relations(permission_tokens, ({one, many}) => ({ 28 - entity: one(entities, { 29 - fields: [permission_tokens.root_entity], 30 - references: [entities.id] 31 - }), 32 - identities: many(identities), 33 - email_subscriptions_to_entities: many(email_subscriptions_to_entity), 34 - permission_token_on_homepages: many(permission_token_on_homepage), 35 - permission_token_rights: many(permission_token_rights), 36 - })); 37 - 38 - export const identitiesRelations = relations(identities, ({one, many}) => ({ 39 - permission_token: one(permission_tokens, { 40 - fields: [identities.home_page], 41 - references: [permission_tokens.id] 42 - }), 43 - email_auth_tokens: many(email_auth_tokens), 44 - permission_token_on_homepages: many(permission_token_on_homepage), 45 })); 46 47 export const email_subscriptions_to_entityRelations = relations(email_subscriptions_to_entity, ({one}) => ({
··· 1 import { relations } from "drizzle-orm/relations"; 2 + 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"; 3 + 4 + export const custom_domainsRelations = relations(custom_domains, ({one, many}) => ({ 5 + identity: one(identities, { 6 + fields: [custom_domains.identity], 7 + references: [identities.email] 8 + }), 9 + custom_domain_routes: many(custom_domain_routes), 10 + })); 11 + 12 + export const identitiesRelations = relations(identities, ({one, many}) => ({ 13 + custom_domains: many(custom_domains), 14 + permission_token: one(permission_tokens, { 15 + fields: [identities.home_page], 16 + references: [permission_tokens.id] 17 + }), 18 + email_auth_tokens: many(email_auth_tokens), 19 + permission_token_on_homepages: many(permission_token_on_homepage), 20 + })); 21 + 22 + export const custom_domain_routesRelations = relations(custom_domain_routes, ({one}) => ({ 23 + custom_domain: one(custom_domains, { 24 + fields: [custom_domain_routes.domain], 25 + references: [custom_domains.domain] 26 + }), 27 + permission_token: one(permission_tokens, { 28 + fields: [custom_domain_routes.permission_token], 29 + references: [permission_tokens.id] 30 + }), 31 + })); 32 + 33 + export const permission_tokensRelations = relations(permission_tokens, ({one, many}) => ({ 34 + custom_domain_routes: many(custom_domain_routes), 35 + entity: one(entities, { 36 + fields: [permission_tokens.root_entity], 37 + references: [entities.id] 38 + }), 39 + identities: many(identities), 40 + email_subscriptions_to_entities: many(email_subscriptions_to_entity), 41 + permission_token_on_homepages: many(permission_token_on_homepage), 42 + permission_token_rights: many(permission_token_rights), 43 + })); 44 45 export const factsRelations = relations(facts, ({one}) => ({ 46 entity: one(entities, { ··· 63 export const entity_setsRelations = relations(entity_sets, ({many}) => ({ 64 entities: many(entities), 65 permission_token_rights: many(permission_token_rights), 66 })); 67 68 export const email_subscriptions_to_entityRelations = relations(email_subscriptions_to_entity, ({one}) => ({
+25 -1
drizzle/schema.ts
··· 1 - import { pgTable, foreignKey, pgEnum, uuid, text, jsonb, timestamp, bigint, boolean, uniqueIndex, primaryKey } from "drizzle-orm/pg-core" 2 import { sql } from "drizzle-orm" 3 4 export const aal_level = pgEnum("aal_level", ['aal1', 'aal2', 'aal3']) ··· 14 export const equality_op = pgEnum("equality_op", ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in']) 15 16 17 export const facts = pgTable("facts", { 18 id: uuid("id").primaryKey().notNull(), 19 entity: uuid("entity").notNull().references(() => entities.id, { onDelete: "cascade", onUpdate: "restrict" } ), ··· 53 created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 54 home_page: uuid("home_page").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 55 email: text("email"), 56 }); 57 58 export const email_subscriptions_to_entity = pgTable("email_subscriptions_to_entity", { ··· 91 entity: uuid("entity").notNull().references(() => entities.id, { onDelete: "cascade", onUpdate: "cascade" } ), 92 name: text("name").default('').notNull(), 93 country_code: text("country_code").notNull(), 94 }, 95 (table) => { 96 return {
··· 1 + import { pgTable, foreignKey, pgEnum, text, boolean, unique, uuid, jsonb, timestamp, bigint, uniqueIndex, smallint, primaryKey } from "drizzle-orm/pg-core" 2 import { sql } from "drizzle-orm" 3 4 export const aal_level = pgEnum("aal_level", ['aal1', 'aal2', 'aal3']) ··· 14 export const equality_op = pgEnum("equality_op", ['eq', 'neq', 'lt', 'lte', 'gt', 'gte', 'in']) 15 16 17 + export const custom_domains = pgTable("custom_domains", { 18 + domain: text("domain").primaryKey().notNull(), 19 + identity: text("identity").default('').notNull().references(() => identities.email, { onDelete: "cascade", onUpdate: "cascade" } ), 20 + confirmed: boolean("confirmed").notNull(), 21 + }); 22 + 23 + export const custom_domain_routes = pgTable("custom_domain_routes", { 24 + id: uuid("id").defaultRandom().primaryKey().notNull(), 25 + domain: text("domain").notNull().references(() => custom_domains.domain), 26 + route: text("route").notNull(), 27 + permission_token: uuid("permission_token").notNull().references(() => permission_tokens.id, { onDelete: "cascade", onUpdate: "cascade" } ), 28 + }, 29 + (table) => { 30 + return { 31 + custom_domain_routes_domain_route_key: unique("custom_domain_routes_domain_route_key").on(table.domain, table.route), 32 + } 33 + }); 34 + 35 export const facts = pgTable("facts", { 36 id: uuid("id").primaryKey().notNull(), 37 entity: uuid("entity").notNull().references(() => entities.id, { onDelete: "cascade", onUpdate: "restrict" } ), ··· 71 created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 72 home_page: uuid("home_page").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 73 email: text("email"), 74 + }, 75 + (table) => { 76 + return { 77 + identities_email_key: unique("identities_email_key").on(table.email), 78 + } 79 }); 80 81 export const email_subscriptions_to_entity = pgTable("email_subscriptions_to_entity", { ··· 114 entity: uuid("entity").notNull().references(() => entities.id, { onDelete: "cascade", onUpdate: "cascade" } ), 115 name: text("name").default('').notNull(), 116 country_code: text("country_code").notNull(), 117 + plus_ones: smallint("plus_ones").default(0).notNull(), 118 }, 119 (table) => { 120 return {
+16
middleware.ts
··· 1 import { NextRequest, NextResponse } from "next/server"; 2 3 export const config = { 4 matcher: [ ··· 13 ], 14 }; 15 16 export default async function middleware(req: NextRequest) { 17 let hostname = req.headers.get("host")!; 18 if (hostname === "guilds.nyc") 19 return NextResponse.rewrite( 20 new URL("/b64bc712-c9c1-4ed3-a8f4-d33f33d3bfdb", req.url),
··· 1 + import { createClient } from "@supabase/supabase-js"; 2 import { NextRequest, NextResponse } from "next/server"; 3 + import { Database } from "supabase/database.types"; 4 5 export const config = { 6 matcher: [ ··· 15 ], 16 }; 17 18 + let supabase = createClient<Database>( 19 + process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, 20 + process.env.SUPABASE_SERVICE_ROLE_KEY as string, 21 + ); 22 export default async function middleware(req: NextRequest) { 23 let hostname = req.headers.get("host")!; 24 + if (hostname === "leaflet.pub") return; 25 + let { data: route } = await supabase 26 + .from("custom_domain_routes") 27 + .select("*") 28 + .eq("domain", hostname) 29 + .eq("route", req.nextUrl.pathname) 30 + .single(); 31 + if (route) 32 + return NextResponse.rewrite(new URL(`/${route.permission_token}`, req.url)); 33 + 34 if (hostname === "guilds.nyc") 35 return NextResponse.rewrite( 36 new URL("/b64bc712-c9c1-4ed3-a8f4-d33f33d3bfdb", req.url),
+65
supabase/database.types.ts
··· 34 } 35 public: { 36 Tables: { 37 email_auth_tokens: { 38 Row: { 39 confirmation_code: string ··· 358 id: string 359 name: string 360 phone_number: string 361 status: Database["public"]["Enums"]["rsvp_status"] 362 } 363 Insert: { ··· 367 id?: string 368 name?: string 369 phone_number: string 370 status: Database["public"]["Enums"]["rsvp_status"] 371 } 372 Update: { ··· 376 id?: string 377 name?: string 378 phone_number?: string 379 status?: Database["public"]["Enums"]["rsvp_status"] 380 } 381 Relationships: [
··· 34 } 35 public: { 36 Tables: { 37 + custom_domain_routes: { 38 + Row: { 39 + domain: string 40 + id: string 41 + permission_token: string 42 + route: string 43 + } 44 + Insert: { 45 + domain: string 46 + id?: string 47 + permission_token: string 48 + route: string 49 + } 50 + Update: { 51 + domain?: string 52 + id?: string 53 + permission_token?: string 54 + route?: string 55 + } 56 + Relationships: [ 57 + { 58 + foreignKeyName: "custom_domain_routes_domain_fkey" 59 + columns: ["domain"] 60 + isOneToOne: false 61 + referencedRelation: "custom_domains" 62 + referencedColumns: ["domain"] 63 + }, 64 + { 65 + foreignKeyName: "custom_domain_routes_permission_token_fkey" 66 + columns: ["permission_token"] 67 + isOneToOne: false 68 + referencedRelation: "permission_tokens" 69 + referencedColumns: ["id"] 70 + }, 71 + ] 72 + } 73 + custom_domains: { 74 + Row: { 75 + confirmed: boolean 76 + domain: string 77 + identity: string 78 + } 79 + Insert: { 80 + confirmed: boolean 81 + domain: string 82 + identity?: string 83 + } 84 + Update: { 85 + confirmed?: boolean 86 + domain?: string 87 + identity?: string 88 + } 89 + Relationships: [ 90 + { 91 + foreignKeyName: "custom_domains_identity_fkey" 92 + columns: ["identity"] 93 + isOneToOne: false 94 + referencedRelation: "identities" 95 + referencedColumns: ["email"] 96 + }, 97 + ] 98 + } 99 email_auth_tokens: { 100 Row: { 101 confirmation_code: string ··· 420 id: string 421 name: string 422 phone_number: string 423 + plus_ones: number 424 status: Database["public"]["Enums"]["rsvp_status"] 425 } 426 Insert: { ··· 430 id?: string 431 name?: string 432 phone_number: string 433 + plus_ones?: number 434 status: Database["public"]["Enums"]["rsvp_status"] 435 } 436 Update: { ··· 440 id?: string 441 name?: string 442 phone_number?: string 443 + plus_ones?: number 444 status?: Database["public"]["Enums"]["rsvp_status"] 445 } 446 Relationships: [
+7
supabase/migrations/20250117205106_add_token_to_custom_domain_routes.sql
···
··· 1 + alter table "public"."custom_domain_routes" add column "permission_token" uuid not null; 2 + 3 + alter table "public"."custom_domain_routes" enable row level security; 4 + 5 + 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; 6 + 7 + alter table "public"."custom_domain_routes" validate constraint "custom_domain_routes_permission_token_fkey";