fix: some shit
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Deploy to GitHub Pages / build_site (push) Successful in 1m55s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Deploy to GitHub Pages / build_site (push) Successful in 1m55s
				
			This commit is contained in:
		| @@ -44,7 +44,7 @@ for await (const dir of dirs) { | ||||
| async function postNode(node: Node) { | ||||
|   const wasmContent = await Deno.readFile(node.path); | ||||
|  | ||||
|   const url = `http://localhost:8000/v1/nodes`; | ||||
|   const url = `http://localhost:8000/nodes`; | ||||
|  | ||||
|   const res = await fetch(url, { | ||||
|     method: "POST", | ||||
| @@ -55,7 +55,7 @@ async function postNode(node: Node) { | ||||
|     console.log(`Uploaded ${node.id}`); | ||||
|   } else { | ||||
|     const text = await res.text(); | ||||
|     console.log(`Failed to upload ${node.id}: ${text}`); | ||||
|     console.log(`Failed to upload ${node.id}: ${res.status} ${text}`); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								store/drizzle/0001_amazing_weapon_omega.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								store/drizzle/0001_amazing_weapon_omega.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| ALTER TABLE "nodes" ADD COLUMN "createdAt" timestamp DEFAULT now(); | ||||
							
								
								
									
										217
									
								
								store/drizzle/meta/0001_snapshot.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								store/drizzle/meta/0001_snapshot.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| { | ||||
|   "id": "080ee514-5516-4400-9286-295826df6f8a", | ||||
|   "prevId": "b5fc8bcf-82d4-4d2e-bcd1-89d5a238f5e2", | ||||
|   "version": "7", | ||||
|   "dialect": "postgresql", | ||||
|   "tables": { | ||||
|     "public.users": { | ||||
|       "name": "users", | ||||
|       "schema": "", | ||||
|       "columns": { | ||||
|         "id": { | ||||
|           "name": "id", | ||||
|           "type": "uuid", | ||||
|           "primaryKey": true, | ||||
|           "notNull": true, | ||||
|           "default": "gen_random_uuid()" | ||||
|         }, | ||||
|         "name": { | ||||
|           "name": "name", | ||||
|           "type": "text", | ||||
|           "primaryKey": false, | ||||
|           "notNull": true | ||||
|         } | ||||
|       }, | ||||
|       "indexes": {}, | ||||
|       "foreignKeys": {}, | ||||
|       "compositePrimaryKeys": {}, | ||||
|       "uniqueConstraints": { | ||||
|         "users_name_unique": { | ||||
|           "name": "users_name_unique", | ||||
|           "nullsNotDistinct": false, | ||||
|           "columns": [ | ||||
|             "name" | ||||
|           ] | ||||
|         } | ||||
|       }, | ||||
|       "policies": {}, | ||||
|       "checkConstraints": {}, | ||||
|       "isRLSEnabled": false | ||||
|     }, | ||||
|     "public.nodes": { | ||||
|       "name": "nodes", | ||||
|       "schema": "", | ||||
|       "columns": { | ||||
|         "id": { | ||||
|           "name": "id", | ||||
|           "type": "serial", | ||||
|           "primaryKey": true, | ||||
|           "notNull": true | ||||
|         }, | ||||
|         "userId": { | ||||
|           "name": "userId", | ||||
|           "type": "varchar", | ||||
|           "primaryKey": false, | ||||
|           "notNull": true | ||||
|         }, | ||||
|         "createdAt": { | ||||
|           "name": "createdAt", | ||||
|           "type": "timestamp", | ||||
|           "primaryKey": false, | ||||
|           "notNull": false, | ||||
|           "default": "now()" | ||||
|         }, | ||||
|         "systemId": { | ||||
|           "name": "systemId", | ||||
|           "type": "varchar", | ||||
|           "primaryKey": false, | ||||
|           "notNull": true | ||||
|         }, | ||||
|         "nodeId": { | ||||
|           "name": "nodeId", | ||||
|           "type": "varchar", | ||||
|           "primaryKey": false, | ||||
|           "notNull": true | ||||
|         }, | ||||
|         "content": { | ||||
|           "name": "content", | ||||
|           "type": "bytea", | ||||
|           "primaryKey": false, | ||||
|           "notNull": true | ||||
|         }, | ||||
|         "definition": { | ||||
|           "name": "definition", | ||||
|           "type": "json", | ||||
|           "primaryKey": false, | ||||
|           "notNull": true | ||||
|         }, | ||||
|         "hash": { | ||||
|           "name": "hash", | ||||
|           "type": "varchar(8)", | ||||
|           "primaryKey": false, | ||||
|           "notNull": true | ||||
|         }, | ||||
|         "previous": { | ||||
|           "name": "previous", | ||||
|           "type": "varchar(8)", | ||||
|           "primaryKey": false, | ||||
|           "notNull": false | ||||
|         } | ||||
|       }, | ||||
|       "indexes": { | ||||
|         "user_id_idx": { | ||||
|           "name": "user_id_idx", | ||||
|           "columns": [ | ||||
|             { | ||||
|               "expression": "userId", | ||||
|               "isExpression": false, | ||||
|               "asc": true, | ||||
|               "nulls": "last" | ||||
|             } | ||||
|           ], | ||||
|           "isUnique": false, | ||||
|           "concurrently": false, | ||||
|           "method": "btree", | ||||
|           "with": {} | ||||
|         }, | ||||
|         "system_id_idx": { | ||||
|           "name": "system_id_idx", | ||||
|           "columns": [ | ||||
|             { | ||||
|               "expression": "systemId", | ||||
|               "isExpression": false, | ||||
|               "asc": true, | ||||
|               "nulls": "last" | ||||
|             } | ||||
|           ], | ||||
|           "isUnique": false, | ||||
|           "concurrently": false, | ||||
|           "method": "btree", | ||||
|           "with": {} | ||||
|         }, | ||||
|         "node_id_idx": { | ||||
|           "name": "node_id_idx", | ||||
|           "columns": [ | ||||
|             { | ||||
|               "expression": "nodeId", | ||||
|               "isExpression": false, | ||||
|               "asc": true, | ||||
|               "nulls": "last" | ||||
|             } | ||||
|           ], | ||||
|           "isUnique": false, | ||||
|           "concurrently": false, | ||||
|           "method": "btree", | ||||
|           "with": {} | ||||
|         }, | ||||
|         "hash_idx": { | ||||
|           "name": "hash_idx", | ||||
|           "columns": [ | ||||
|             { | ||||
|               "expression": "hash", | ||||
|               "isExpression": false, | ||||
|               "asc": true, | ||||
|               "nulls": "last" | ||||
|             } | ||||
|           ], | ||||
|           "isUnique": false, | ||||
|           "concurrently": false, | ||||
|           "method": "btree", | ||||
|           "with": {} | ||||
|         } | ||||
|       }, | ||||
|       "foreignKeys": { | ||||
|         "nodes_userId_users_name_fk": { | ||||
|           "name": "nodes_userId_users_name_fk", | ||||
|           "tableFrom": "nodes", | ||||
|           "tableTo": "users", | ||||
|           "columnsFrom": [ | ||||
|             "userId" | ||||
|           ], | ||||
|           "columnsTo": [ | ||||
|             "name" | ||||
|           ], | ||||
|           "onDelete": "no action", | ||||
|           "onUpdate": "no action" | ||||
|         }, | ||||
|         "node_previous_fk": { | ||||
|           "name": "node_previous_fk", | ||||
|           "tableFrom": "nodes", | ||||
|           "tableTo": "nodes", | ||||
|           "columnsFrom": [ | ||||
|             "previous" | ||||
|           ], | ||||
|           "columnsTo": [ | ||||
|             "hash" | ||||
|           ], | ||||
|           "onDelete": "no action", | ||||
|           "onUpdate": "no action" | ||||
|         } | ||||
|       }, | ||||
|       "compositePrimaryKeys": {}, | ||||
|       "uniqueConstraints": { | ||||
|         "nodes_hash_unique": { | ||||
|           "name": "nodes_hash_unique", | ||||
|           "nullsNotDistinct": false, | ||||
|           "columns": [ | ||||
|             "hash" | ||||
|           ] | ||||
|         } | ||||
|       }, | ||||
|       "policies": {}, | ||||
|       "checkConstraints": {}, | ||||
|       "isRLSEnabled": false | ||||
|     } | ||||
|   }, | ||||
|   "enums": {}, | ||||
|   "schemas": {}, | ||||
|   "sequences": {}, | ||||
|   "roles": {}, | ||||
|   "policies": {}, | ||||
|   "views": {}, | ||||
|   "_meta": { | ||||
|     "columns": {}, | ||||
|     "schemas": {}, | ||||
|     "tables": {} | ||||
|   } | ||||
| } | ||||
| @@ -8,6 +8,13 @@ | ||||
|       "when": 1734695353420, | ||||
|       "tag": "0000_steep_bromley", | ||||
|       "breakpoints": true | ||||
|     }, | ||||
|     { | ||||
|       "idx": 1, | ||||
|       "version": "7", | ||||
|       "when": 1734696211359, | ||||
|       "tag": "0001_amazing_weapon_omega", | ||||
|       "breakpoints": true | ||||
|     } | ||||
|   ] | ||||
| } | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -78,7 +78,7 @@ nodeRouter.openapi(getNodeCollectionRoute, async (c) => { | ||||
|  | ||||
| const getNodeDefinitionRoute = createRoute({ | ||||
|   method: "get", | ||||
|   path: "/{user}/{system}/{nodeId}{.+\\.json}", | ||||
|   path: "/{user}/{system}/{nodeId}.json", | ||||
|   request: { | ||||
|     params: ParamsSchema, | ||||
|   }, | ||||
| @@ -111,7 +111,7 @@ nodeRouter.openapi(getNodeDefinitionRoute, async (c) => { | ||||
|  | ||||
| const getNodeWasmRoute = createRoute({ | ||||
|   method: "get", | ||||
|   path: "/{user}/{system}/{nodeId}{.+\\.wasm}", | ||||
|   path: "/{user}/{system}/{nodeId}.wasm", | ||||
|   request: { | ||||
|     params: ParamsSchema, | ||||
|   }, | ||||
| @@ -140,6 +140,63 @@ nodeRouter.openapi(getNodeWasmRoute, async (c) => { | ||||
|   return c.body(wasmContent); | ||||
| }); | ||||
|  | ||||
| const getNodeVersionRoute = createRoute({ | ||||
|   method: "get", | ||||
|   path: "/{user}/{system}/{nodeId}@{hash}.json", | ||||
|   request: { | ||||
|     params: ParamsSchema, | ||||
|   }, | ||||
|   responses: { | ||||
|     200: { | ||||
|       content: { | ||||
|         "application/json": { | ||||
|           schema: NodeDefinitionSchema, | ||||
|         }, | ||||
|       }, | ||||
|       description: "Create a single node", | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| nodeRouter.openapi(getNodeVersionRoute, async (c) => { | ||||
|   const { user, system, nodeId } = c.req.valid("param"); | ||||
|  | ||||
|   const nodes = await service.getNodeVersions(user, system, nodeId); | ||||
|  | ||||
|   return c.json(nodes); | ||||
| }); | ||||
|  | ||||
| const getNodeVersionsRoute = createRoute({ | ||||
|   method: "get", | ||||
|   path: "/{user}/{system}/{nodeId}/versions.json", | ||||
|   request: { | ||||
|     params: z.object({ | ||||
|       user: SingleParam("user"), | ||||
|       system: SingleParam("system"), | ||||
|       nodeId: SingleParam("nodeId"), | ||||
|       hash: SingleParam("hash"), | ||||
|     }), | ||||
|   }, | ||||
|   responses: { | ||||
|     200: { | ||||
|       content: { | ||||
|         "application/json": { | ||||
|           schema: NodeDefinitionSchema, | ||||
|         }, | ||||
|       }, | ||||
|       description: "Create a single node", | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| nodeRouter.openapi(getNodeVersionsRoute, async (c) => { | ||||
|   const { user, system, nodeId, hash } = c.req.valid("param"); | ||||
|  | ||||
|   const node = await service.getNodeVersion(user, system, nodeId, hash); | ||||
|  | ||||
|   return c.json(node); | ||||
| }); | ||||
|  | ||||
| const createNodeRoute = createRoute({ | ||||
|   method: "post", | ||||
|   path: "/", | ||||
| @@ -165,8 +222,19 @@ const createNodeRoute = createRoute({ | ||||
| nodeRouter.openapi(createNodeRoute, async (c) => { | ||||
|   const buffer = await c.req.arrayBuffer(); | ||||
|   const bytes = await (await c.req.blob()).bytes(); | ||||
|  | ||||
|   try { | ||||
|     const node = await service.createNode(buffer, bytes); | ||||
|     return c.json(node); | ||||
|   } catch (error) { | ||||
|     if (error instanceof Error && "code" in error) { | ||||
|       switch (error.code) { | ||||
|         case "23505": | ||||
|           throw new HTTPException(409, { message: "node already exists" }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   throw new HTTPException(500); | ||||
| }); | ||||
|  | ||||
| export { nodeRouter }; | ||||
|   | ||||
| @@ -2,14 +2,14 @@ import { | ||||
|   customType, | ||||
|   foreignKey, | ||||
|   index, | ||||
|   integer, | ||||
|   json, | ||||
|   pgTable, | ||||
|   serial, | ||||
|   timestamp, | ||||
|   varchar, | ||||
| } from "drizzle-orm/pg-core"; | ||||
| import { relations } from "drizzle-orm/relations"; | ||||
| import { usersTable } from "../user/user.schema.ts"; | ||||
| import { NodeDefinition } from "./validations/types.ts"; | ||||
|  | ||||
| const bytea = customType<{ | ||||
|   data: ArrayBuffer; | ||||
| @@ -23,12 +23,13 @@ const bytea = customType<{ | ||||
| export const nodeTable = pgTable("nodes", { | ||||
|   id: serial().primaryKey(), | ||||
|   userId: varchar().notNull().references(() => usersTable.name), | ||||
|   createdAt: timestamp().defaultNow(), | ||||
|   systemId: varchar().notNull(), | ||||
|   nodeId: varchar().notNull(), | ||||
|   content: bytea().notNull(), | ||||
|   definition: json().notNull(), | ||||
|   hash: varchar({ length: 8 }).notNull().unique(), | ||||
|   previous: varchar({ length: 8 }), | ||||
|   definition: json().notNull().$type<NodeDefinition>(), | ||||
|   hash: varchar({ length: 16 }).notNull().unique(), | ||||
|   previous: varchar({ length: 16 }), | ||||
| }, (table) => [ | ||||
|   foreignKey({ | ||||
|     columns: [table.previous], | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { db } from "../../db/db.ts"; | ||||
| import { nodeTable } from "./node.schema.ts"; | ||||
| import { NodeDefinition, NodeDefinitionSchema } from "./validations/types.ts"; | ||||
| import { and, eq } from "drizzle-orm"; | ||||
| import { and, asc, eq } from "drizzle-orm"; | ||||
| import { createHash } from "node:crypto"; | ||||
| import { WorkerMessage } from "./worker/messages.ts"; | ||||
|  | ||||
| @@ -15,7 +15,7 @@ export type CreateNodeDTO = { | ||||
| function getNodeHash(content: Uint8Array) { | ||||
|   const hash = createHash("sha256"); | ||||
|   hash.update(content); | ||||
|   return hash.digest("hex").slice(0, 8); | ||||
|   return hash.digest("hex").slice(0, 16); | ||||
| } | ||||
|  | ||||
| function extractDefinition(content: ArrayBuffer): Promise<NodeDefinition> { | ||||
| @@ -40,7 +40,6 @@ function extractDefinition(content: ArrayBuffer): Promise<NodeDefinition> { | ||||
|           res(e.data.result); | ||||
|           break; | ||||
|         case "error": | ||||
|           console.log("Worker error", e.data.error); | ||||
|           rej(e.data.result); | ||||
|           break; | ||||
|         default: | ||||
| @@ -54,36 +53,46 @@ export async function createNode( | ||||
|   wasmBuffer: ArrayBuffer, | ||||
|   content: Uint8Array, | ||||
| ): Promise<NodeDefinition> { | ||||
|   try { | ||||
|   const def = await extractDefinition(wasmBuffer); | ||||
|  | ||||
|   const [userId, systemId, nodeId] = def.id.split("/"); | ||||
|  | ||||
|   const hash = getNodeHash(content); | ||||
|  | ||||
|   const node: typeof nodeTable.$inferInsert = { | ||||
|     userId, | ||||
|     systemId, | ||||
|     nodeId, | ||||
|     definition: def, | ||||
|       hash: getNodeHash(content), | ||||
|     hash, | ||||
|     content: content, | ||||
|   }; | ||||
|  | ||||
|     await db.insert(nodeTable).values(node); | ||||
|     console.log("new node created!"); | ||||
|     return def; | ||||
|   } catch (error) { | ||||
|     console.log("Creation Error", { error }); | ||||
|     throw error; | ||||
|   const previousNode = await db | ||||
|     .select({ hash: nodeTable.hash }) | ||||
|     .from(nodeTable) | ||||
|     .orderBy(asc(nodeTable.createdAt)) | ||||
|     .limit(1); | ||||
|  | ||||
|   if (previousNode[0]) { | ||||
|     node.previous = previousNode[0].hash; | ||||
|   } | ||||
|  | ||||
|   await db.insert(nodeTable).values(node); | ||||
|   return def; | ||||
| } | ||||
|  | ||||
| export function getNodeDefinitionsByUser(userName: string) { | ||||
|   return db.select({ definition: nodeTable.definition }).from(nodeTable) | ||||
| export async function getNodeDefinitionsByUser(userName: string) { | ||||
|   const nodes = await db.select({ definition: nodeTable.definition }).from( | ||||
|     nodeTable, | ||||
|   ) | ||||
|     .where( | ||||
|       and( | ||||
|         eq(nodeTable.userId, userName), | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
|   return nodes.map((n) => n.definition); | ||||
| } | ||||
|  | ||||
| export async function getNodesBySystem( | ||||
| @@ -91,11 +100,14 @@ export async function getNodesBySystem( | ||||
|   systemId: string, | ||||
| ): Promise<NodeDefinition[]> { | ||||
|   const nodes = await db | ||||
|     .select() | ||||
|     .selectDistinctOn( | ||||
|       [nodeTable.userId, nodeTable.systemId, nodeTable.nodeId], | ||||
|       { definition: nodeTable.definition }, | ||||
|     ) | ||||
|     .from(nodeTable) | ||||
|     .where( | ||||
|       and(eq(nodeTable.systemId, systemId), eq(nodeTable.userId, username)), | ||||
|     ); | ||||
|     ).orderBy(nodeTable.userId, nodeTable.systemId, nodeTable.nodeId); | ||||
|  | ||||
|   const definitions = nodes | ||||
|     .map((node) => NodeDefinitionSchema.safeParse(node.definition)) | ||||
| @@ -118,7 +130,9 @@ export async function getNodeWasmById( | ||||
|         eq(nodeTable.systemId, systemId), | ||||
|         eq(nodeTable.nodeId, nodeId), | ||||
|       ), | ||||
|     ).limit(1).execute(); | ||||
|     ) | ||||
|     .orderBy(asc(nodeTable.createdAt)) | ||||
|     .limit(1); | ||||
|   console.log("Time to load wasm", performance.now() - a); | ||||
|  | ||||
|   if (!node[0]) { | ||||
| @@ -141,7 +155,9 @@ export async function getNodeDefinitionById( | ||||
|       eq(nodeTable.systemId, systemId), | ||||
|       eq(nodeTable.nodeId, nodeId), | ||||
|     ), | ||||
|   ).limit(1); | ||||
|   ) | ||||
|     .orderBy(asc(nodeTable.createdAt)) | ||||
|     .limit(1); | ||||
|  | ||||
|   if (!node[0]) { | ||||
|     return; | ||||
| @@ -155,3 +171,55 @@ export async function getNodeDefinitionById( | ||||
|  | ||||
|   return definition.data; | ||||
| } | ||||
|  | ||||
| export async function getNodeVersions( | ||||
|   user: string, | ||||
|   system: string, | ||||
|   nodeId: string, | ||||
| ) { | ||||
|   const nodes = await db.select({ | ||||
|     definition: nodeTable.definition, | ||||
|     hash: nodeTable.hash, | ||||
|   }).from( | ||||
|     nodeTable, | ||||
|   ).where( | ||||
|     and( | ||||
|       eq(nodeTable.userId, user), | ||||
|       eq(nodeTable.systemId, system), | ||||
|       eq(nodeTable.nodeId, nodeId), | ||||
|     ), | ||||
|   ) | ||||
|     .orderBy(asc(nodeTable.createdAt)); | ||||
|  | ||||
|   return nodes.map((node) => ({ | ||||
|     ...node.definition, | ||||
|     id: node.definition.id + "@" + node.hash, | ||||
|   })); | ||||
| } | ||||
|  | ||||
| export async function getNodeVersion( | ||||
|   user: string, | ||||
|   system: string, | ||||
|   nodeId: string, | ||||
|   hash: string, | ||||
| ) { | ||||
|   const nodes = await db.select({ | ||||
|     definition: nodeTable.definition, | ||||
|     hash: nodeTable.hash, | ||||
|   }).from( | ||||
|     nodeTable, | ||||
|   ).where( | ||||
|     and( | ||||
|       eq(nodeTable.userId, user), | ||||
|       eq(nodeTable.systemId, system), | ||||
|       eq(nodeTable.nodeId, nodeId), | ||||
|       eq(nodeTable.hash, hash), | ||||
|     ), | ||||
|   ).limit(1); | ||||
|  | ||||
|   if (nodes.length === 0) { | ||||
|     throw new Error("Node not found"); | ||||
|   } | ||||
|  | ||||
|   return nodes[0].definition; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user