a tool for shared writing and social publishing
at update/reader 143 lines 4.4 kB view raw
1"use server"; 2 3import { createServerClient } from "@supabase/ssr"; 4import { drizzle } from "drizzle-orm/node-postgres"; 5import type { Fact } from "src/replicache"; 6import type { Attribute } from "src/replicache/attributes"; 7import { Database } from "supabase/database.types"; 8import { v7 } from "uuid"; 9 10import { 11 entities, 12 permission_tokens, 13 permission_token_rights, 14 entity_sets, 15 facts, 16} from "drizzle/schema"; 17import { sql } from "drizzle-orm"; 18import { redirect } from "next/navigation"; 19import { cookies } from "next/headers"; 20import { pool } from "supabase/pool"; 21 22let supabase = createServerClient<Database>( 23 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, 24 process.env.SUPABASE_SERVICE_ROLE_KEY as string, 25 { cookies: {} }, 26); 27 28export async function createNewLeafletFromTemplate( 29 template_id: string, 30 redirectUser?: boolean, 31) { 32 let auth_token = (await cookies()).get("auth_token")?.value; 33 let res = await supabase 34 .from("permission_tokens") 35 .select("*, permission_token_rights(*)") 36 .eq("id", template_id) 37 .single(); 38 let rootEntity = res.data?.root_entity; 39 if (!rootEntity || !res.data) return { error: "Leaflet not found" } as const; 40 let { data } = await supabase.rpc("get_facts", { 41 root: rootEntity, 42 }); 43 let initialFacts = (data as unknown as Fact<Attribute>[]) || []; 44 45 let oldEntityIDToNewID = {} as { [k: string]: string }; 46 let oldEntities = initialFacts.reduce((acc, f) => { 47 if (!acc.includes(f.entity)) acc.push(f.entity); 48 return acc; 49 }, [] as string[]); 50 let newEntities = [] as string[]; 51 52 for (let oldEntity of oldEntities) { 53 let newEntity = v7(); 54 oldEntityIDToNewID[oldEntity] = newEntity; 55 newEntities.push(newEntity); 56 } 57 58 let newFacts = await Promise.all( 59 initialFacts.map(async (fact) => { 60 let entity = oldEntityIDToNewID[fact.entity]; 61 let data = fact.data; 62 if ( 63 data.type === "ordered-reference" || 64 data.type == "spatial-reference" || 65 data.type === "reference" 66 ) { 67 data.value = oldEntityIDToNewID[data.value]; 68 } 69 if (data.type === "image") { 70 let url = data.src.split("?"); 71 let paths = url[0].split("/"); 72 let newID = v7(); 73 await supabase.storage 74 .from("minilink-user-assets") 75 .copy(paths[paths.length - 1], newID); 76 let newPath = [...paths]; 77 newPath[newPath.length - 1] = newID; 78 let newURL = newPath.join("/"); 79 if (url[1]) newURL += `?${url[1]}`; 80 data.src = newURL; 81 } 82 return { entity, attribute: fact.attribute, data }; 83 }), 84 ); 85 86 const client = await pool.connect(); 87 const db = drizzle(client); 88 89 let { permissionToken } = await db.transaction(async (tx) => { 90 // Create a new entity set 91 let [entity_set] = await tx.insert(entity_sets).values({}).returning(); 92 await tx 93 .insert(entities) 94 .values(newEntities.map((e) => ({ id: e, set: entity_set.id }))); 95 await tx.insert(facts).values( 96 newFacts.map((f) => ({ 97 id: v7(), 98 entity: f.entity, 99 attribute: f.attribute, 100 data: sql`${f.data}`, 101 })), 102 ); 103 104 let [permissionToken] = await tx 105 .insert(permission_tokens) 106 .values({ root_entity: oldEntityIDToNewID[rootEntity] }) 107 .returning(); 108 109 //and give it all the permission on that entity set 110 let [rights] = await tx 111 .insert(permission_token_rights) 112 .values({ 113 token: permissionToken.id, 114 entity_set: entity_set.id, 115 read: true, 116 write: true, 117 create_token: true, 118 change_entity_set: true, 119 }) 120 .returning(); 121 122 if (auth_token) { 123 await tx.execute(sql` 124 WITH auth_token AS ( 125 SELECT identities.id as identity_id 126 FROM email_auth_tokens 127 LEFT JOIN identities ON email_auth_tokens.identity = identities.id 128 WHERE email_auth_tokens.id = ${auth_token} 129 AND email_auth_tokens.confirmed = true 130 AND identities.id IS NOT NULL 131 ) 132 INSERT INTO permission_token_on_homepage (token, identity) 133 SELECT ${permissionToken.id}, identity_id 134 FROM auth_token 135 `); 136 } 137 return { permissionToken, rights, entity_set }; 138 }); 139 140 client.release(); 141 if (redirectUser) redirect(`/${permissionToken.id}`); 142 return { id: permissionToken.id, error: null } as const; 143}