a tool for shared writing and social publishing
298
fork

Configure Feed

Select the types of activity you want to include in your feed.

at c666be3a5500aee0bcd61a53332c7608dbc209ad 162 lines 5.2 kB view raw
1import { PgTransaction } from "drizzle-orm/pg-core"; 2import * as driz from "drizzle-orm"; 3import * as base64 from "base64-js"; 4import * as Y from "yjs"; 5import { MutationContext } from "./mutations"; 6import { entities, facts } from "drizzle/schema"; 7import { Attribute, Attributes, FilterAttributes } from "./attributes"; 8import { Fact, PermissionToken } from "."; 9import { DeepReadonly } from "replicache"; 10import { createClient } from "@supabase/supabase-js"; 11import { Database } from "supabase/database.types"; 12import { v7 } from "uuid"; 13export function serverMutationContext( 14 tx: PgTransaction<any, any, any>, 15 permission_token_id: string, 16 token_rights: PermissionToken["permission_token_rights"], 17) { 18 let ctx: MutationContext & { 19 checkPermission: (entity: string) => Promise<boolean>; 20 } = { 21 permission_token_id, 22 async runOnServer(cb) { 23 let supabase = createClient<Database>( 24 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, 25 process.env.SUPABASE_SERVICE_ROLE_KEY as string, 26 ); 27 return cb({ supabase }); 28 }, 29 async checkPermission(entity: string) { 30 let [permission_set] = await tx 31 .select({ entity_set: entities.set }) 32 .from(entities) 33 .where(driz.eq(entities.id, entity)); 34 return ( 35 !!permission_set && 36 !!token_rights.find( 37 (r) => r.entity_set === permission_set.entity_set && r.write == true, 38 ) 39 ); 40 }, 41 async runOnClient(_cb) {}, 42 async createEntity({ entityID, permission_set }) { 43 if ( 44 !token_rights.find( 45 (r) => r.entity_set === permission_set && r.write === true, 46 ) 47 ) { 48 return false; 49 } 50 await tx.transaction( 51 async (tx2) => 52 await tx2 53 .insert(entities) 54 .values({ 55 set: permission_set, 56 id: entityID, 57 }) 58 .catch(console.log), 59 ); 60 return true; 61 }, 62 scanIndex: { 63 async eav(entity, attribute) { 64 return (await tx 65 .select({ 66 id: facts.id, 67 data: facts.data, 68 entity: facts.entity, 69 attribute: facts.attribute, 70 }) 71 .from(facts) 72 .where( 73 driz.and( 74 driz.eq(facts.attribute, attribute), 75 driz.eq(facts.entity, entity), 76 ), 77 )) as DeepReadonly<Fact<typeof attribute>>[]; 78 }, 79 }, 80 async assertFact(f) { 81 if (!f.entity) return; 82 let attribute = Attributes[f.attribute as Attribute]; 83 if (!attribute) return; 84 let id = f.id || v7(); 85 let data = { ...f.data }; 86 let [permission_set] = await tx 87 .select({ entity_set: entities.set }) 88 .from(entities) 89 .where(driz.eq(entities.id, f.entity)); 90 if (!(await this.checkPermission(f.entity))) return; 91 if (attribute.cardinality === "one") { 92 let existingFact = await tx 93 .select({ id: facts.id, data: facts.data }) 94 .from(facts) 95 .where( 96 driz.and( 97 driz.eq(facts.attribute, f.attribute), 98 driz.eq(facts.entity, f.entity), 99 ), 100 ); 101 if (existingFact[0]) { 102 id = existingFact[0].id; 103 if (attribute.type === "text") { 104 const oldUpdate = base64.toByteArray( 105 ( 106 existingFact[0]?.data as Fact< 107 keyof FilterAttributes<{ type: "text" }> 108 >["data"] 109 ).value, 110 ); 111 112 let textData = data as Fact< 113 keyof FilterAttributes<{ type: "text" }> 114 >["data"]; 115 const newUpdate = base64.toByteArray(textData.value); 116 const updateBytes = Y.mergeUpdates([oldUpdate, newUpdate]); 117 textData.value = base64.fromByteArray(updateBytes); 118 } 119 } 120 } 121 await tx.transaction( 122 async (tx2) => 123 await tx2 124 .insert(facts) 125 .values({ 126 id: id, 127 entity: f.entity, 128 data: driz.sql`${data}::jsonb`, 129 attribute: f.attribute, 130 }) 131 .onConflictDoUpdate({ 132 target: facts.id, 133 set: { data: driz.sql`${f.data}::jsonb` }, 134 }) 135 .catch((e) => { 136 console.log(`error on inserting fact: `, JSON.stringify(e)); 137 }), 138 ); 139 }, 140 async retractFact(id) { 141 let [f] = await tx 142 .select() 143 .from(facts) 144 .rightJoin(entities, driz.eq(entities.id, facts.entity)) 145 .where(driz.eq(facts.id, id)); 146 if (!f || !(await this.checkPermission(f.entities.id))) return; 147 await tx.delete(facts).where(driz.eq(facts.id, id)); 148 }, 149 async deleteEntity(entity) { 150 if (!(await this.checkPermission(entity))) return; 151 await Promise.all([ 152 tx.delete(entities).where(driz.eq(entities.id, entity)), 153 tx 154 .delete(facts) 155 .where( 156 driz.sql`(data->>'type' = 'ordered-reference' or data ->>'type' = 'reference' or data ->>'type' = 'spatial-reference') and data->>'value' = ${entity}`, 157 ), 158 ]); 159 }, 160 }; 161 return ctx; 162}