Compare commits
6 Commits
main
...
new-schema
Author | SHA1 | Date | |
---|---|---|---|
|
0828209455 | ||
|
94ca5b2c9f | ||
|
2a58d0a5e3 | ||
|
30c9da559f | ||
|
2262adca74 | ||
|
b486d22111 |
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "sqlite",
|
||||
"id": "6570ba20-a672-400c-8147-7ba533784918",
|
||||
"id": "afe10bff-362b-402c-bdb5-038341692f35",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"sandbox": {
|
||||
@ -35,12 +35,36 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"likeCount": {
|
||||
"name": "likeCount",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"viewCount": {
|
||||
"name": "viewCount",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
@ -93,6 +117,43 @@
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"avatarUrl": {
|
||||
"name": "avatarUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP"
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"generations": {
|
||||
"name": "generations",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
@ -102,6 +163,13 @@
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_username_unique": {
|
||||
"name": "user_username_unique",
|
||||
"columns": [
|
||||
"username"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
@ -124,6 +192,13 @@
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sharedOn": {
|
||||
"name": "sharedOn",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "sqlite",
|
||||
"id": "9f64104a-4954-40c0-8155-17755ea0a243",
|
||||
"prevId": "6570ba20-a672-400c-8147-7ba533784918",
|
||||
"id": "e570d5ac-700d-4e62-8a46-482b21ae1fe1",
|
||||
"prevId": "afe10bff-362b-402c-bdb5-038341692f35",
|
||||
"tables": {
|
||||
"sandbox": {
|
||||
"name": "sandbox",
|
||||
@ -35,12 +35,36 @@
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP"
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"likeCount": {
|
||||
"name": "likeCount",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"viewCount": {
|
||||
"name": "viewCount",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
@ -94,12 +118,35 @@
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"avatarUrl": {
|
||||
"name": "avatarUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP"
|
||||
},
|
||||
"generations": {
|
||||
"name": "generations",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
@ -109,6 +156,13 @@
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_username_unique": {
|
||||
"name": "user_username_unique",
|
||||
"columns": [
|
||||
"username"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
@ -131,6 +185,13 @@
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sharedOn": {
|
||||
"name": "sharedOn",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
|
@ -1,168 +0,0 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "sqlite",
|
||||
"id": "5baf10d6-7697-42ba-a11a-ee4c7bd7e91e",
|
||||
"prevId": "9f64104a-4954-40c0-8155-17755ea0a243",
|
||||
"tables": {
|
||||
"sandbox": {
|
||||
"name": "sandbox",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"visibility": {
|
||||
"name": "visibility",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"sandbox_id_unique": {
|
||||
"name": "sandbox_id_unique",
|
||||
"columns": [
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"sandbox_user_id_user_id_fk": {
|
||||
"name": "sandbox_user_id_user_id_fk",
|
||||
"tableFrom": "sandbox",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_id_unique": {
|
||||
"name": "user_id_unique",
|
||||
"columns": [
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"users_to_sandboxes": {
|
||||
"name": "users_to_sandboxes",
|
||||
"columns": {
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sandboxId": {
|
||||
"name": "sandboxId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"users_to_sandboxes_userId_user_id_fk": {
|
||||
"name": "users_to_sandboxes_userId_user_id_fk",
|
||||
"tableFrom": "users_to_sandboxes",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"users_to_sandboxes_sandboxId_sandbox_id_fk": {
|
||||
"name": "users_to_sandboxes_sandboxId_sandbox_id_fk",
|
||||
"tableFrom": "users_to_sandboxes",
|
||||
"tableTo": "sandbox",
|
||||
"columnsFrom": [
|
||||
"sandboxId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
}
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "sqlite",
|
||||
"id": "37e38b82-1494-4818-8c26-b9024cce3fa9",
|
||||
"prevId": "5baf10d6-7697-42ba-a11a-ee4c7bd7e91e",
|
||||
"tables": {
|
||||
"sandbox": {
|
||||
"name": "sandbox",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"visibility": {
|
||||
"name": "visibility",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"sandbox_id_unique": {
|
||||
"name": "sandbox_id_unique",
|
||||
"columns": [
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"sandbox_user_id_user_id_fk": {
|
||||
"name": "sandbox_user_id_user_id_fk",
|
||||
"tableFrom": "sandbox",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_id_unique": {
|
||||
"name": "user_id_unique",
|
||||
"columns": [
|
||||
"id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"users_to_sandboxes": {
|
||||
"name": "users_to_sandboxes",
|
||||
"columns": {
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sandboxId": {
|
||||
"name": "sandboxId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"users_to_sandboxes_userId_user_id_fk": {
|
||||
"name": "users_to_sandboxes_userId_user_id_fk",
|
||||
"tableFrom": "users_to_sandboxes",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"users_to_sandboxes_sandboxId_sandbox_id_fk": {
|
||||
"name": "users_to_sandboxes_sandboxId_sandbox_id_fk",
|
||||
"tableFrom": "users_to_sandboxes",
|
||||
"tableTo": "sandbox",
|
||||
"columnsFrom": [
|
||||
"sandboxId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
}
|
||||
}
|
@ -5,50 +5,29 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "5",
|
||||
"when": 1714540200800,
|
||||
"tag": "0000_big_rogue",
|
||||
"when": 1731288423588,
|
||||
"tag": "0000_cuddly_patriot",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "5",
|
||||
"when": 1714541190588,
|
||||
"tag": "0001_empty_black_knight",
|
||||
"when": 1731290863632,
|
||||
"tag": "0001_opposite_newton_destine",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "5",
|
||||
"when": 1714541209173,
|
||||
"tag": "0002_sour_ego",
|
||||
"when": 1731296235880,
|
||||
"tag": "0002_rainy_fantastic_four",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "5",
|
||||
"when": 1714541233589,
|
||||
"tag": "0003_pale_overlord",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "5",
|
||||
"when": 1714565073180,
|
||||
"tag": "0004_cuddly_wolf_cub",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "5",
|
||||
"when": 1714950365718,
|
||||
"tag": "0005_last_the_twelve",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "5",
|
||||
"when": 1716432225404,
|
||||
"tag": "0006_lively_mattie_franklin",
|
||||
"when": 1731297339306,
|
||||
"tag": "0003_lying_snowbird",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
192
backend/database/package-lock.json
generated
192
backend/database/package-lock.json
generated
@ -23,14 +23,15 @@
|
||||
"drizzle-kit": "^0.20.17",
|
||||
"typescript": "^5.0.4",
|
||||
"vitest": "1.3.0",
|
||||
"wrangler": "^3.0.0"
|
||||
"wrangler": "^3.86.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/kv-asset-handler": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.2.tgz",
|
||||
"integrity": "sha512-EeEjMobfuJrwoctj7FA1y1KEbM0+Q1xSjobIEyie9k4haVEBB7vkDvsasw1pM3rO39mL2akxIAzLMUAtrMHZhA==",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz",
|
||||
"integrity": "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"mime": "^3.0.0"
|
||||
},
|
||||
@ -188,11 +189,26 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workers-shared": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.7.1.tgz",
|
||||
"integrity": "sha512-46cP5FCrl3TrvHeoHLb5SRuiDMKH5kc9Yvo36SAfzt8dqJI/qJRoY1GP3ioHn/gP7v2QIoUOTAzIl7Ml7MnfrA==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"mime": "^3.0.0",
|
||||
"zod": "^3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workers-types": {
|
||||
"version": "4.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240512.0.tgz",
|
||||
"integrity": "sha512-o2yTEWg+YK/I1t/Me+dA0oarO0aCbjibp6wSeaw52DSE9tDyKJ7S+Qdyw/XsMrKn4t8kF6f/YOba+9O4MJfW9w==",
|
||||
"devOptional": true
|
||||
"version": "4.20241106.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20241106.0.tgz",
|
||||
"integrity": "sha512-pI4ivacmp+vgNO/siHDsZ6BdITR0LC4Mh/1+yzVLcl9U75pt5DUDCOWOiqIRFXRq6H65DPnJbEPFo3x9UfgofQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT OR Apache-2.0"
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
@ -1823,6 +1839,17 @@
|
||||
"integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@ -1874,6 +1901,13 @@
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
|
||||
@ -2999,6 +3033,13 @@
|
||||
"resolved": "https://registry.npmjs.org/itty-router-extras/-/itty-router-extras-0.4.6.tgz",
|
||||
"integrity": "sha512-6r7HQBkFMPSJfcKksrKC7avEQnPCSSEvoz6PAAZMNhz8hthYu1pzedXDrvTFDWXJosfuaittzoNciWHO/TxMaw=="
|
||||
},
|
||||
"node_modules/itty-time": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/itty-time/-/itty-time-1.0.6.tgz",
|
||||
"integrity": "sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
|
||||
@ -3306,6 +3347,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ohash": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz",
|
||||
"integrity": "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@ -3359,10 +3407,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz",
|
||||
"integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
|
||||
"dev": true
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
||||
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "1.1.2",
|
||||
@ -4073,10 +4122,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ufo": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz",
|
||||
"integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
|
||||
"dev": true
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
|
||||
"integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.28.4",
|
||||
@ -4096,6 +4146,20 @@
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/unenv": {
|
||||
"name": "unenv-nightly",
|
||||
"version": "2.0.0-20241024-111401-d4156ac",
|
||||
"resolved": "https://registry.npmjs.org/unenv-nightly/-/unenv-nightly-2.0.0-20241024-111401-d4156ac.tgz",
|
||||
"integrity": "sha512-xJO1hfY+Te+/XnfCYrCbFbRcgu6XEODND1s5wnVbaBCkuQX7JXF7fHEXPrukFE2j8EOH848P8QN19VO47XN8hw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"defu": "^6.1.4",
|
||||
"ohash": "^1.1.4",
|
||||
"pathe": "^1.1.2",
|
||||
"ufo": "^1.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@ -4706,24 +4770,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler": {
|
||||
"version": "3.57.1",
|
||||
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.57.1.tgz",
|
||||
"integrity": "sha512-M8YnWUwdrb8AFiRePtVnzlDn02OX4osWvdl8oVh6eyZqqkqXYg7lwlYBr14Qj92pMN4JvMBmDZoukkYHvwpJRg==",
|
||||
"version": "3.86.0",
|
||||
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.86.0.tgz",
|
||||
"integrity": "sha512-jL670AFVPLTILvEjAL165aYM/ZqtZCqT+e6LKiniflRZxSGKu4o/wyHeOmOM6i5kYJHSmF40E4lOZqapDtkF8g==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@cloudflare/kv-asset-handler": "0.3.2",
|
||||
"@cloudflare/kv-asset-handler": "0.3.4",
|
||||
"@cloudflare/workers-shared": "0.7.1",
|
||||
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
||||
"blake3-wasm": "^2.1.5",
|
||||
"chokidar": "^3.5.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"esbuild": "0.17.19",
|
||||
"miniflare": "3.20240512.0",
|
||||
"itty-time": "^1.0.6",
|
||||
"miniflare": "3.20241106.0",
|
||||
"nanoid": "^3.3.3",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"path-to-regexp": "^6.3.0",
|
||||
"resolve": "^1.22.8",
|
||||
"resolve.exports": "^2.0.2",
|
||||
"selfsigned": "^2.0.1",
|
||||
"source-map": "0.6.1",
|
||||
"source-map": "^0.6.1",
|
||||
"unenv": "npm:unenv-nightly@2.0.0-20241024-111401-d4156ac",
|
||||
"workerd": "1.20241106.1",
|
||||
"xxhash-wasm": "^1.0.1"
|
||||
},
|
||||
"bin": {
|
||||
@ -4737,7 +4807,7 @@
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240512.0"
|
||||
"@cloudflare/workers-types": "^4.20241106.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cloudflare/workers-types": {
|
||||
@ -4746,13 +4816,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-darwin-64": {
|
||||
"version": "1.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20240512.0.tgz",
|
||||
"integrity": "sha512-VMp+CsSHFALQiBzPdQ5dDI4T1qwLu0mQ0aeKVNDosXjueN0f3zj/lf+mFil5/9jBbG3t4mG0y+6MMnalP9Lobw==",
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-zxvaToi1m0qzAScrxFt7UvFVqU8DxrCO2CinM1yQkv5no7pA1HolpIrwZ0xOhR3ny64Is2s/J6BrRjpO5dM9Zw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@ -4762,13 +4833,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-darwin-arm64": {
|
||||
"version": "1.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20240512.0.tgz",
|
||||
"integrity": "sha512-lZktXGmzMrB5rJqY9+PmnNfv1HKlj/YLZwMjPfF0WVKHUFdvQbAHsi7NlKv6mW9uIvlZnS+K4sIkWc0MDXcRnA==",
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-j3dg/42D/bPgfNP3cRUBxF+4waCKO/5YKwXNj+lnVOwHxDu+ne5pFw9TIkKYcWTcwn0ZUkbNZNM5rhJqRn4xbg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@ -4778,13 +4850,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-linux-64": {
|
||||
"version": "1.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20240512.0.tgz",
|
||||
"integrity": "sha512-wrHvqCZZqXz6Y3MUTn/9pQNsvaoNjbJpuA6vcXsXu8iCzJi911iVW2WUEBX+MpUWD+mBIP0oXni5tTlhkokOPw==",
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-Ih+Ye8E1DMBXcKrJktGfGztFqHKaX1CeByqshmTbODnWKHt6O65ax3oTecUwyC0+abuyraOpAtdhHNpFMhUkmw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -4794,13 +4867,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-linux-arm64": {
|
||||
"version": "1.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20240512.0.tgz",
|
||||
"integrity": "sha512-YPezHMySL9J9tFdzxz390eBswQ//QJNYcZolz9Dgvb3FEfdpK345cE/bsWbMOqw5ws2f82l388epoenghtYvAg==",
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-mdQFPk4+14Yywn7n1xIzI+6olWM8Ybz10R7H3h+rk0XulMumCWUCy1CzIDauOx6GyIcSgKIibYMssVHZR30ObA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@ -4810,13 +4884,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-windows-64": {
|
||||
"version": "1.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20240512.0.tgz",
|
||||
"integrity": "sha512-SxKapDrIYSscMR7lGIp/av0l6vokjH4xQ9ACxHgXh+OdOus9azppSmjaPyw4/ePvg7yqpkaNjf9o258IxWtvKQ==",
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-4rtcss31E/Rb/PeFocZfr+B9i1MdrkhsTBWizh8siNR4KMmkslU2xs2wPaH1z8+ErxkOsHrKRa5EPLh5rIiFeg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@ -4826,10 +4901,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/miniflare": {
|
||||
"version": "3.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240512.0.tgz",
|
||||
"integrity": "sha512-X0PlKR0AROKpxFoJNmRtCMIuJxj+ngEcyTOlEokj2rAQ0TBwUhB4/1uiPvdI6ofW5NugPOD1uomAv+gLjwsLDQ==",
|
||||
"version": "3.20241106.0",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20241106.0.tgz",
|
||||
"integrity": "sha512-PjOoJKjUUofCueQskfhXlGvvHxZj36UAJAp1DnquMK88MFF50zCULblh0KXMSNM+bXeQYA94Gj06a7kfmBGxPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "0.8.1",
|
||||
"acorn": "^8.8.0",
|
||||
@ -4838,11 +4914,11 @@
|
||||
"exit-hook": "^2.2.1",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"stoppable": "^1.1.0",
|
||||
"undici": "^5.28.2",
|
||||
"workerd": "1.20240512.0",
|
||||
"ws": "^8.11.0",
|
||||
"undici": "^5.28.4",
|
||||
"workerd": "1.20241106.1",
|
||||
"ws": "^8.18.0",
|
||||
"youch": "^3.2.2",
|
||||
"zod": "^3.20.6"
|
||||
"zod": "^3.22.3"
|
||||
},
|
||||
"bin": {
|
||||
"miniflare": "bootstrap.js"
|
||||
@ -4852,11 +4928,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/workerd": {
|
||||
"version": "1.20240512.0",
|
||||
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20240512.0.tgz",
|
||||
"integrity": "sha512-VUBmR1PscAPHEE0OF/G2K7/H1gnr9aDWWZzdkIgWfNKkv8dKFCT75H+GJtUHjfwqz3rYCzaNZmatSXOpLGpF8A==",
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20241106.1.tgz",
|
||||
"integrity": "sha512-1GdKl0kDw8rrirr/ThcK66Kbl4/jd4h8uHx5g7YHBrnenY5SX1UPuop2cnCzYUxlg55kPjzIqqYslz1muRFgFw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"workerd": "bin/workerd"
|
||||
},
|
||||
@ -4864,11 +4941,11 @@
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@cloudflare/workerd-darwin-64": "1.20240512.0",
|
||||
"@cloudflare/workerd-darwin-arm64": "1.20240512.0",
|
||||
"@cloudflare/workerd-linux-64": "1.20240512.0",
|
||||
"@cloudflare/workerd-linux-arm64": "1.20240512.0",
|
||||
"@cloudflare/workerd-windows-64": "1.20240512.0"
|
||||
"@cloudflare/workerd-darwin-64": "1.20241106.1",
|
||||
"@cloudflare/workerd-darwin-arm64": "1.20241106.1",
|
||||
"@cloudflare/workerd-linux-64": "1.20241106.1",
|
||||
"@cloudflare/workerd-linux-arm64": "1.20241106.1",
|
||||
"@cloudflare/workerd-windows-64": "1.20241106.1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
@ -4877,10 +4954,11 @@
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
|
||||
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
@ -18,7 +18,7 @@
|
||||
"drizzle-kit": "^0.20.17",
|
||||
"typescript": "^5.0.4",
|
||||
"vitest": "1.3.0",
|
||||
"wrangler": "^3.0.0"
|
||||
"wrangler": "^3.86.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
|
@ -169,6 +169,7 @@ export default {
|
||||
name: sb.name,
|
||||
type: sb.type,
|
||||
author: sb.author.name,
|
||||
authorAvatarUrl: sb.author.avatarUrl,
|
||||
sharedOn: r.sharedOn,
|
||||
}
|
||||
})
|
||||
@ -282,14 +283,26 @@ export default {
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
username: z.string(),
|
||||
avatarUrl: z.string().optional(),
|
||||
createdAt: z.string().optional(),
|
||||
generations: z.number().optional(),
|
||||
})
|
||||
|
||||
const body = await request.json()
|
||||
const { id, name, email } = userSchema.parse(body)
|
||||
const { id, name, email, username, avatarUrl, createdAt, generations } = userSchema.parse(body)
|
||||
|
||||
const res = await db
|
||||
.insert(user)
|
||||
.values({ id, name, email })
|
||||
.values({
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
username,
|
||||
avatarUrl,
|
||||
createdAt: createdAt ? new Date(createdAt) : new Date(),
|
||||
generations,
|
||||
})
|
||||
.returning()
|
||||
.get()
|
||||
return json({ res })
|
||||
@ -303,6 +316,20 @@ export default {
|
||||
} else {
|
||||
return methodNotAllowed
|
||||
}
|
||||
} else if (path === "/api/user/check-username") {
|
||||
if (method === "GET") {
|
||||
const params = url.searchParams
|
||||
const username = params.get("username")
|
||||
|
||||
if (!username) return invalidRequest
|
||||
|
||||
const exists = await db.query.user.findFirst({
|
||||
where: (user, { eq }) => eq(user.username, username)
|
||||
})
|
||||
|
||||
return json({ exists: !!exists })
|
||||
}
|
||||
return methodNotAllowed
|
||||
} else return notFound
|
||||
},
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { createId } from "@paralleldrive/cuid2"
|
||||
import { relations } from "drizzle-orm"
|
||||
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
|
||||
import { sql } from "drizzle-orm"
|
||||
|
||||
export const user = sqliteTable("user", {
|
||||
id: text("id")
|
||||
@ -9,7 +10,10 @@ export const user = sqliteTable("user", {
|
||||
.unique(),
|
||||
name: text("name").notNull(),
|
||||
email: text("email").notNull(),
|
||||
image: text("image"),
|
||||
username: text("username").notNull().unique(),
|
||||
avatarUrl: text("avatarUrl"),
|
||||
createdAt: integer("createdAt", { mode: "timestamp_ms" })
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
generations: integer("generations").default(0),
|
||||
})
|
||||
|
||||
@ -28,10 +32,13 @@ export const sandbox = sqliteTable("sandbox", {
|
||||
name: text("name").notNull(),
|
||||
type: text("type").notNull(),
|
||||
visibility: text("visibility", { enum: ["public", "private"] }),
|
||||
createdAt: integer("createdAt", { mode: "timestamp_ms" }),
|
||||
createdAt: integer("createdAt", { mode: "timestamp_ms" })
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
likeCount: integer("likeCount").default(0),
|
||||
viewCount: integer("viewCount").default(0),
|
||||
})
|
||||
|
||||
export type Sandbox = typeof sandbox.$inferSelect
|
||||
|
257
backend/storage/package-lock.json
generated
257
backend/storage/package-lock.json
generated
@ -13,10 +13,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
||||
"@cloudflare/workers-types": "^4.20240419.0",
|
||||
"@cloudflare/workers-types": "^4.20241106.0",
|
||||
"typescript": "^5.0.4",
|
||||
"vitest": "1.3.0",
|
||||
"wrangler": "^3.0.0"
|
||||
"wrangler": "^3.86.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/kv-asset-handler": {
|
||||
@ -169,11 +169,26 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workers-shared": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-shared/-/workers-shared-0.7.1.tgz",
|
||||
"integrity": "sha512-46cP5FCrl3TrvHeoHLb5SRuiDMKH5kc9Yvo36SAfzt8dqJI/qJRoY1GP3ioHn/gP7v2QIoUOTAzIl7Ml7MnfrA==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"mime": "^3.0.0",
|
||||
"zod": "^3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workers-types": {
|
||||
"version": "4.20240419.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20240419.0.tgz",
|
||||
"integrity": "sha512-UM16sr4HEe0mDj6C5OFcodzdj/CnEp0bfncAq3g7OpDsoZ1sBrfsMrb7Yc4f8J81EemvmQZyE6sSanpURtVkcQ==",
|
||||
"dev": true
|
||||
"version": "4.20241106.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20241106.0.tgz",
|
||||
"integrity": "sha512-pI4ivacmp+vgNO/siHDsZ6BdITR0LC4Mh/1+yzVLcl9U75pt5DUDCOWOiqIRFXRq6H65DPnJbEPFo3x9UfgofQ==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0"
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
@ -1192,6 +1207,17 @@
|
||||
"integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
@ -1221,6 +1247,13 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/devalue": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.3.tgz",
|
||||
@ -1547,6 +1580,13 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/itty-time": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/itty-time/-/itty-time-1.0.6.tgz",
|
||||
"integrity": "sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
|
||||
@ -1757,6 +1797,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ohash": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz",
|
||||
"integrity": "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@ -1811,10 +1858,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz",
|
||||
"integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
|
||||
"dev": true
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
||||
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "1.1.2",
|
||||
@ -2278,10 +2326,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ufo": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz",
|
||||
"integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
|
||||
"dev": true
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
|
||||
"integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.28.4",
|
||||
@ -2301,6 +2350,20 @@
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/unenv": {
|
||||
"name": "unenv-nightly",
|
||||
"version": "2.0.0-20241024-111401-d4156ac",
|
||||
"resolved": "https://registry.npmjs.org/unenv-nightly/-/unenv-nightly-2.0.0-20241024-111401-d4156ac.tgz",
|
||||
"integrity": "sha512-xJO1hfY+Te+/XnfCYrCbFbRcgu6XEODND1s5wnVbaBCkuQX7JXF7fHEXPrukFE2j8EOH848P8QN19VO47XN8hw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"defu": "^6.1.4",
|
||||
"ohash": "^1.1.4",
|
||||
"pathe": "^1.1.2",
|
||||
"ufo": "^1.5.4"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
|
||||
@ -2885,25 +2948,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler": {
|
||||
"version": "3.51.2",
|
||||
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.51.2.tgz",
|
||||
"integrity": "sha512-8TRUwzPHj6+uPDzY0hBJ9/YwniEF9pqMGe5qbqLP/XsHTCWxIFib5go374zyCkmuVh23AwV7NuTA6gUtSqZ8pQ==",
|
||||
"version": "3.86.0",
|
||||
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.86.0.tgz",
|
||||
"integrity": "sha512-jL670AFVPLTILvEjAL165aYM/ZqtZCqT+e6LKiniflRZxSGKu4o/wyHeOmOM6i5kYJHSmF40E4lOZqapDtkF8g==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@cloudflare/kv-asset-handler": "0.3.1",
|
||||
"@cloudflare/kv-asset-handler": "0.3.4",
|
||||
"@cloudflare/workers-shared": "0.7.1",
|
||||
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
||||
"blake3-wasm": "^2.1.5",
|
||||
"chokidar": "^3.5.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"esbuild": "0.17.19",
|
||||
"miniflare": "3.20240405.2",
|
||||
"itty-time": "^1.0.6",
|
||||
"miniflare": "3.20241106.0",
|
||||
"nanoid": "^3.3.3",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"path-to-regexp": "^6.3.0",
|
||||
"resolve": "^1.22.8",
|
||||
"resolve.exports": "^2.0.2",
|
||||
"selfsigned": "^2.0.1",
|
||||
"source-map": "0.6.1",
|
||||
"ts-json-schema-generator": "^1.5.0",
|
||||
"source-map": "^0.6.1",
|
||||
"unenv": "npm:unenv-nightly@2.0.0-20241024-111401-d4156ac",
|
||||
"workerd": "1.20241106.1",
|
||||
"xxhash-wasm": "^1.0.1"
|
||||
},
|
||||
"bin": {
|
||||
@ -2917,7 +2985,7 @@
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240405.0"
|
||||
"@cloudflare/workers-types": "^4.20241106.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cloudflare/workers-types": {
|
||||
@ -2925,11 +2993,110 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/miniflare": {
|
||||
"version": "3.20240405.2",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20240405.2.tgz",
|
||||
"integrity": "sha512-n/V5m9GVMN37U5gWdrNXKx2d1icLXtcIKcxWtLslH4RTaebZJdSRmp12UHyuQsKlaSpTkNqyzLVtCEgt2bhRSA==",
|
||||
"node_modules/wrangler/node_modules/@cloudflare/kv-asset-handler": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz",
|
||||
"integrity": "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"mime": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.13"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-darwin-64": {
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-zxvaToi1m0qzAScrxFt7UvFVqU8DxrCO2CinM1yQkv5no7pA1HolpIrwZ0xOhR3ny64Is2s/J6BrRjpO5dM9Zw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-darwin-arm64": {
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-j3dg/42D/bPgfNP3cRUBxF+4waCKO/5YKwXNj+lnVOwHxDu+ne5pFw9TIkKYcWTcwn0ZUkbNZNM5rhJqRn4xbg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-linux-64": {
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-Ih+Ye8E1DMBXcKrJktGfGztFqHKaX1CeByqshmTbODnWKHt6O65ax3oTecUwyC0+abuyraOpAtdhHNpFMhUkmw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-linux-arm64": {
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-mdQFPk4+14Yywn7n1xIzI+6olWM8Ybz10R7H3h+rk0XulMumCWUCy1CzIDauOx6GyIcSgKIibYMssVHZR30ObA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/@cloudflare/workerd-windows-64": {
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20241106.1.tgz",
|
||||
"integrity": "sha512-4rtcss31E/Rb/PeFocZfr+B9i1MdrkhsTBWizh8siNR4KMmkslU2xs2wPaH1z8+ErxkOsHrKRa5EPLh5rIiFeg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/miniflare": {
|
||||
"version": "3.20241106.0",
|
||||
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20241106.0.tgz",
|
||||
"integrity": "sha512-PjOoJKjUUofCueQskfhXlGvvHxZj36UAJAp1DnquMK88MFF50zCULblh0KXMSNM+bXeQYA94Gj06a7kfmBGxPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "0.8.1",
|
||||
"acorn": "^8.8.0",
|
||||
@ -2938,11 +3105,11 @@
|
||||
"exit-hook": "^2.2.1",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"stoppable": "^1.1.0",
|
||||
"undici": "^5.28.2",
|
||||
"workerd": "1.20240405.0",
|
||||
"ws": "^8.11.0",
|
||||
"undici": "^5.28.4",
|
||||
"workerd": "1.20241106.1",
|
||||
"ws": "^8.18.0",
|
||||
"youch": "^3.2.2",
|
||||
"zod": "^3.20.6"
|
||||
"zod": "^3.22.3"
|
||||
},
|
||||
"bin": {
|
||||
"miniflare": "bootstrap.js"
|
||||
@ -2951,6 +3118,27 @@
|
||||
"node": ">=16.13"
|
||||
}
|
||||
},
|
||||
"node_modules/wrangler/node_modules/workerd": {
|
||||
"version": "1.20241106.1",
|
||||
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20241106.1.tgz",
|
||||
"integrity": "sha512-1GdKl0kDw8rrirr/ThcK66Kbl4/jd4h8uHx5g7YHBrnenY5SX1UPuop2cnCzYUxlg55kPjzIqqYslz1muRFgFw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"workerd": "bin/workerd"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@cloudflare/workerd-darwin-64": "1.20241106.1",
|
||||
"@cloudflare/workerd-darwin-arm64": "1.20241106.1",
|
||||
"@cloudflare/workerd-linux-64": "1.20241106.1",
|
||||
"@cloudflare/workerd-linux-arm64": "1.20241106.1",
|
||||
"@cloudflare/workerd-windows-64": "1.20241106.1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
@ -2958,10 +3146,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
|
||||
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
@ -11,10 +11,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
||||
"@cloudflare/workers-types": "^4.20240419.0",
|
||||
"@cloudflare/workers-types": "^4.20241106.0",
|
||||
"typescript": "^5.0.4",
|
||||
"vitest": "1.3.0",
|
||||
"wrangler": "^3.0.0"
|
||||
"wrangler": "^3.86.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"p-limit": "^6.1.0",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ExecutionContext, R2Bucket, Headers as CFHeaders } from "@cloudflare/workers-types"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface Env {
|
||||
@ -75,14 +76,13 @@ export default {
|
||||
if (obj === null) {
|
||||
return new Response(`${fileId} not found`, { status: 404 })
|
||||
}
|
||||
const headers = new Headers()
|
||||
const headers = new Headers() as unknown as CFHeaders
|
||||
headers.set("etag", obj.httpEtag)
|
||||
obj.writeHttpMetadata(headers)
|
||||
|
||||
const text = await obj.text()
|
||||
|
||||
return new Response(text, {
|
||||
headers,
|
||||
headers: Object.fromEntries(headers.entries()),
|
||||
})
|
||||
} else return invalidRequest
|
||||
} else if (method === "POST") {
|
||||
|
@ -34,7 +34,7 @@
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
"types": [
|
||||
"@cloudflare/workers-types/2023-07-01"
|
||||
"@cloudflare/workers-types"
|
||||
] /* Specify type package names to be included without being referenced in a source file. */,
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
"resolveJsonModule": true /* Enable importing .json files */,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Room } from "@/components/editor/live/room"
|
||||
// import { Room } from "@/components/editor/live/room"
|
||||
import Loading from "@/components/editor/loading"
|
||||
import Navbar from "@/components/editor/navbar"
|
||||
import { TerminalProvider } from "@/context/TerminalContext"
|
||||
@ -51,7 +51,7 @@ const getSharedUsers = async (usersToSandboxes: UsersToSandboxes[]) => {
|
||||
}
|
||||
)
|
||||
const userData: User = await userRes.json()
|
||||
return { id: userData.id, name: userData.name }
|
||||
return { id: userData.id, name: userData.name, avatarUrl: userData.avatarUrl }
|
||||
})
|
||||
)
|
||||
|
||||
@ -89,18 +89,18 @@ export default async function CodePage({ params }: { params: { id: string } }) {
|
||||
return (
|
||||
<>
|
||||
<div className="overflow-hidden overscroll-none w-screen flex flex-col h-screen bg-background">
|
||||
<Room id={sandboxId}>
|
||||
{/* <Room id={sandboxId}> */}
|
||||
<TerminalProvider>
|
||||
<Navbar
|
||||
userData={userData}
|
||||
sandboxData={sandboxData}
|
||||
shared={shared}
|
||||
shared={shared as { id: string; name: string; avatarUrl: string }[]}
|
||||
/>
|
||||
<div className="w-screen flex grow">
|
||||
<CodeEditor userData={userData} sandboxData={sandboxData} />
|
||||
</div>
|
||||
</TerminalProvider>
|
||||
</Room>
|
||||
{/* </Room> */}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -35,6 +35,7 @@ export default async function DashboardPage() {
|
||||
type: "react" | "node"
|
||||
author: string
|
||||
sharedOn: Date
|
||||
authorAvatarUrl: string
|
||||
}[]
|
||||
|
||||
return (
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { User } from "@/lib/types"
|
||||
import { currentUser } from "@clerk/nextjs"
|
||||
import { redirect } from "next/navigation"
|
||||
import { generateUniqueUsername } from "@/lib/username-generator";
|
||||
|
||||
export default async function AppAuthLayout({
|
||||
children,
|
||||
@ -24,6 +25,25 @@ export default async function AppAuthLayout({
|
||||
const dbUserJSON = (await dbUser.json()) as User
|
||||
|
||||
if (!dbUserJSON.id) {
|
||||
// Try to get GitHub username if available
|
||||
const githubUsername = user.externalAccounts.find(
|
||||
account => account.provider === "github"
|
||||
)?.username;
|
||||
|
||||
const username = githubUsername || await generateUniqueUsername(async (username) => {
|
||||
// Check if username exists in database
|
||||
const userCheck = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user/check-username?username=${username}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
const exists = await userCheck.json()
|
||||
return exists.exists
|
||||
});
|
||||
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`,
|
||||
{
|
||||
@ -36,9 +56,20 @@ export default async function AppAuthLayout({
|
||||
id: user.id,
|
||||
name: user.firstName + " " + user.lastName,
|
||||
email: user.emailAddresses[0].emailAddress,
|
||||
username: username,
|
||||
avatarUrl: user.imageUrl || null,
|
||||
createdAt: new Date().toISOString(),
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
console.error("Failed to create user:", error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
console.log("User created successfully:", data);
|
||||
}
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
|
@ -1,57 +1,61 @@
|
||||
import { colors } from "@/lib/colors"
|
||||
import { User } from "@/lib/types"
|
||||
// import { colors } from "@/lib/colors"
|
||||
// import { User } from "@/lib/types"
|
||||
import { currentUser } from "@clerk/nextjs"
|
||||
import { Liveblocks } from "@liveblocks/node"
|
||||
// import { Liveblocks } from "@liveblocks/node"
|
||||
import { NextRequest } from "next/server"
|
||||
|
||||
const API_KEY = process.env.LIVEBLOCKS_SECRET_KEY!
|
||||
// const API_KEY = process.env.LIVEBLOCKS_SECRET_KEY!
|
||||
|
||||
const liveblocks = new Liveblocks({
|
||||
secret: API_KEY!,
|
||||
})
|
||||
// const liveblocks = new Liveblocks({
|
||||
// secret: API_KEY!,
|
||||
// })
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const clerkUser = await currentUser()
|
||||
// Temporarily return unauthorized while Liveblocks is disabled
|
||||
return new Response("Liveblocks collaboration temporarily disabled", { status: 503 })
|
||||
|
||||
if (!clerkUser) {
|
||||
return new Response("Unauthorized", { status: 401 })
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${clerkUser.id}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
const user = (await res.json()) as User
|
||||
|
||||
const colorNames = Object.keys(colors)
|
||||
const randomColor = colorNames[
|
||||
Math.floor(Math.random() * colorNames.length)
|
||||
] as keyof typeof colors
|
||||
const code = colors[randomColor]
|
||||
|
||||
// Create a session for the current user
|
||||
// userInfo is made available in Liveblocks presence hooks, e.g. useOthers
|
||||
const session = liveblocks.prepareSession(user.id, {
|
||||
userInfo: {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
color: randomColor,
|
||||
},
|
||||
})
|
||||
|
||||
// Give the user access to the room
|
||||
user.sandbox.forEach((sandbox) => {
|
||||
session.allow(`${sandbox.id}`, session.FULL_ACCESS)
|
||||
})
|
||||
user.usersToSandboxes.forEach((userToSandbox) => {
|
||||
session.allow(`${userToSandbox.sandboxId}`, session.FULL_ACCESS)
|
||||
})
|
||||
|
||||
// Authorize the user and return the result
|
||||
const { body, status } = await session.authorize()
|
||||
return new Response(body, { status })
|
||||
// Original implementation commented out:
|
||||
// const clerkUser = await currentUser()
|
||||
//
|
||||
// if (!clerkUser) {
|
||||
// return new Response("Unauthorized", { status: 401 })
|
||||
// }
|
||||
//
|
||||
// const res = await fetch(
|
||||
// `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${clerkUser.id}`,
|
||||
// {
|
||||
// headers: {
|
||||
// Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`,
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// const user = (await res.json()) as User
|
||||
//
|
||||
// const colorNames = Object.keys(colors)
|
||||
// const randomColor = colorNames[
|
||||
// Math.floor(Math.random() * colorNames.length)
|
||||
// ] as keyof typeof colors
|
||||
// const code = colors[randomColor]
|
||||
//
|
||||
// // Create a session for the current user
|
||||
// // userInfo is made available in Liveblocks presence hooks, e.g. useOthers
|
||||
// const session = liveblocks.prepareSession(user.id, {
|
||||
// userInfo: {
|
||||
// name: user.name,
|
||||
// email: user.email,
|
||||
// color: randomColor,
|
||||
// },
|
||||
// })
|
||||
//
|
||||
// // Give the user access to the room
|
||||
// user.sandbox.forEach((sandbox) => {
|
||||
// session.allow(`${sandbox.id}`, session.FULL_ACCESS)
|
||||
// })
|
||||
// user.usersToSandboxes.forEach((userToSandbox) => {
|
||||
// session.allow(`${userToSandbox.sandboxId}`, session.FULL_ACCESS)
|
||||
// })
|
||||
//
|
||||
// // Authorize the user and return the result
|
||||
// const { body, status } = await session.authorize()
|
||||
// return new Response(body, { status })
|
||||
}
|
||||
|
@ -18,11 +18,38 @@ export default function AboutModal({
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>About this project</DialogTitle>
|
||||
<DialogTitle>Help & Support</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<div className="space-y-4">
|
||||
{/* <div className="text-sm text-muted-foreground">
|
||||
Sandbox is an open-source cloud-based code editing environment with
|
||||
custom AI code autocompletion and real-time collaboration.
|
||||
</div> */}
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Get help and support through our Discord community or by creating issues on GitHub:
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm">
|
||||
<a
|
||||
href="https://discord.gitwit.dev/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
Join our Discord community →
|
||||
</a>
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
<a
|
||||
href="https://github.com/jamesmurdza/sandbox/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
Report issues on GitHub →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
@ -25,6 +25,7 @@ export default function Dashboard({
|
||||
type: "react" | "node"
|
||||
author: string
|
||||
sharedOn: Date
|
||||
authorAvatarUrl?: string
|
||||
}[]
|
||||
}) {
|
||||
const [screen, setScreen] = useState<TScreen>("projects")
|
||||
@ -77,14 +78,14 @@ export default function Dashboard({
|
||||
<FolderDot className="w-4 h-4 mr-2" />
|
||||
My Projects
|
||||
</Button>
|
||||
<Button
|
||||
{/* <Button
|
||||
variant="ghost"
|
||||
onClick={() => setScreen("shared")}
|
||||
className={activeScreen("shared")}
|
||||
>
|
||||
<Users className="w-4 h-4 mr-2" />
|
||||
Shared With Me
|
||||
</Button>
|
||||
</Button> */}
|
||||
{/* <Button
|
||||
variant="ghost"
|
||||
onClick={() => setScreen("settings")}
|
||||
@ -110,7 +111,7 @@ export default function Dashboard({
|
||||
className="justify-start font-normal text-muted-foreground"
|
||||
>
|
||||
<HelpCircle className="w-4 h-4 mr-2" />
|
||||
About
|
||||
Help
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -121,7 +122,12 @@ export default function Dashboard({
|
||||
) : null}
|
||||
</>
|
||||
) : screen === "shared" ? (
|
||||
<DashboardSharedWithMe shared={shared} />
|
||||
<DashboardSharedWithMe
|
||||
shared={shared.map((item) => ({
|
||||
...item,
|
||||
authorAvatarUrl: item.authorAvatarUrl || "",
|
||||
}))}
|
||||
/>
|
||||
) : screen === "settings" ? null : null}
|
||||
</div>
|
||||
</>
|
||||
|
@ -11,6 +11,7 @@ import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import Avatar from "../ui/avatar"
|
||||
import Button from "../ui/customButton"
|
||||
import { projectTemplates } from "@/lib/data"
|
||||
|
||||
export default function DashboardSharedWithMe({
|
||||
shared,
|
||||
@ -18,8 +19,9 @@ export default function DashboardSharedWithMe({
|
||||
shared: {
|
||||
id: string
|
||||
name: string
|
||||
type: "react" | "node"
|
||||
type: string
|
||||
author: string
|
||||
authorAvatarUrl: string
|
||||
sharedOn: Date
|
||||
}[]
|
||||
}) {
|
||||
@ -45,9 +47,7 @@ export default function DashboardSharedWithMe({
|
||||
<Image
|
||||
alt=""
|
||||
src={
|
||||
sandbox.type === "react"
|
||||
? "/project-icons/react.svg"
|
||||
: "/project-icons/node.svg"
|
||||
projectTemplates.find((p) => p.id === sandbox.type)?.icon ?? "/project-icons/node.svg"
|
||||
}
|
||||
width={20}
|
||||
height={20}
|
||||
@ -58,7 +58,11 @@ export default function DashboardSharedWithMe({
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center">
|
||||
<Avatar name={sandbox.author} className="mr-2" />
|
||||
<Avatar
|
||||
name={sandbox.author}
|
||||
avatarUrl={sandbox.authorAvatarUrl}
|
||||
className="mr-2"
|
||||
/>
|
||||
{sandbox.author}
|
||||
</div>
|
||||
</TableCell>
|
||||
|
@ -7,11 +7,11 @@ import * as monaco from "monaco-editor"
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { TypedLiveblocksProvider, useRoom, useSelf } from "@/liveblocks.config"
|
||||
import LiveblocksProvider from "@liveblocks/yjs"
|
||||
import { MonacoBinding } from "y-monaco"
|
||||
import { Awareness } from "y-protocols/awareness"
|
||||
import * as Y from "yjs"
|
||||
// import { TypedLiveblocksProvider, useRoom, useSelf } from "@/liveblocks.config"
|
||||
// import LiveblocksProvider from "@liveblocks/yjs"
|
||||
// import { MonacoBinding } from "y-monaco"
|
||||
// import { Awareness } from "y-protocols/awareness"
|
||||
// import * as Y from "yjs"
|
||||
|
||||
import {
|
||||
ResizableHandle,
|
||||
@ -46,7 +46,7 @@ import { Button } from "../ui/button"
|
||||
import Tab from "../ui/tab"
|
||||
import AIChat from "./AIChat"
|
||||
import GenerateInput from "./generate"
|
||||
import { Cursors } from "./live/cursors"
|
||||
// import { Cursors } from "./live/cursors"
|
||||
import DisableAccessModal from "./live/disableModal"
|
||||
import Loading from "./loading"
|
||||
import PreviewWindow from "./preview"
|
||||
@ -147,20 +147,20 @@ export default function CodeEditor({
|
||||
const isOwner = sandboxData.userId === userData.id
|
||||
const clerk = useClerk()
|
||||
|
||||
// Liveblocks hooks
|
||||
const room = useRoom()
|
||||
const [provider, setProvider] = useState<TypedLiveblocksProvider>()
|
||||
const userInfo = useSelf((me) => me.info)
|
||||
// // Liveblocks hooks
|
||||
// const room = useRoom()
|
||||
// const [provider, setProvider] = useState<TypedLiveblocksProvider>()
|
||||
// const userInfo = useSelf((me) => me.info)
|
||||
|
||||
// Liveblocks providers map to prevent reinitializing providers
|
||||
type ProviderData = {
|
||||
provider: LiveblocksProvider<never, never, never, never>
|
||||
yDoc: Y.Doc
|
||||
yText: Y.Text
|
||||
binding?: MonacoBinding
|
||||
onSync: (isSynced: boolean) => void
|
||||
}
|
||||
const providersMap = useRef(new Map<string, ProviderData>())
|
||||
// // Liveblocks providers map to prevent reinitializing providers
|
||||
// type ProviderData = {
|
||||
// provider: LiveblocksProvider<never, never, never, never>
|
||||
// yDoc: Y.Doc
|
||||
// yText: Y.Text
|
||||
// binding?: MonacoBinding
|
||||
// onSync: (isSynced: boolean) => void
|
||||
// }
|
||||
// const providersMap = useRef(new Map<string, ProviderData>())
|
||||
|
||||
// Refs for libraries / features
|
||||
const editorContainerRef = useRef<HTMLDivElement>(null)
|
||||
@ -541,8 +541,6 @@ export default function CodeEditor({
|
||||
tab.id === activeFileId ? { ...tab, saved: true } : tab
|
||||
)
|
||||
)
|
||||
console.log(`Saving file...${activeFileId}`)
|
||||
console.log(`Saving file...${content}`)
|
||||
socket?.emit("saveFile", { fileId: activeFileId, body: content })
|
||||
}
|
||||
}, Number(process.env.FILE_SAVE_DEBOUNCE_DELAY) || 1000),
|
||||
@ -573,82 +571,82 @@ export default function CodeEditor({
|
||||
}
|
||||
}, [activeFileId, tabs, debouncedSaveData, setIsAIChatOpen, editorRef])
|
||||
|
||||
// Liveblocks live collaboration setup effect
|
||||
useEffect(() => {
|
||||
const tab = tabs.find((t) => t.id === activeFileId)
|
||||
const model = editorRef?.getModel()
|
||||
// // Liveblocks live collaboration setup effect
|
||||
// useEffect(() => {
|
||||
// const tab = tabs.find((t) => t.id === activeFileId)
|
||||
// const model = editorRef?.getModel()
|
||||
|
||||
if (!editorRef || !tab || !model) return
|
||||
// if (!editorRef || !tab || !model) return
|
||||
|
||||
let providerData: ProviderData
|
||||
// let providerData: ProviderData
|
||||
|
||||
// When a file is opened for the first time, create a new provider and store in providersMap.
|
||||
if (!providersMap.current.has(tab.id)) {
|
||||
const yDoc = new Y.Doc()
|
||||
const yText = yDoc.getText(tab.id)
|
||||
const yProvider = new LiveblocksProvider(room, yDoc)
|
||||
// // When a file is opened for the first time, create a new provider and store in providersMap.
|
||||
// if (!providersMap.current.has(tab.id)) {
|
||||
// const yDoc = new Y.Doc()
|
||||
// const yText = yDoc.getText(tab.id)
|
||||
// const yProvider = new LiveblocksProvider(room, yDoc)
|
||||
|
||||
// Inserts the file content into the editor once when the tab is changed.
|
||||
const onSync = (isSynced: boolean) => {
|
||||
if (isSynced) {
|
||||
const text = yText.toString()
|
||||
if (text === "") {
|
||||
if (activeFileContent) {
|
||||
yText.insert(0, activeFileContent)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
yText.insert(0, editorRef.getValue())
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Inserts the file content into the editor once when the tab is changed.
|
||||
// const onSync = (isSynced: boolean) => {
|
||||
// if (isSynced) {
|
||||
// const text = yText.toString()
|
||||
// if (text === "") {
|
||||
// if (activeFileContent) {
|
||||
// yText.insert(0, activeFileContent)
|
||||
// } else {
|
||||
// setTimeout(() => {
|
||||
// yText.insert(0, editorRef.getValue())
|
||||
// }, 0)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
yProvider.on("sync", onSync)
|
||||
// yProvider.on("sync", onSync)
|
||||
|
||||
// Save the provider to the map.
|
||||
providerData = { provider: yProvider, yDoc, yText, onSync }
|
||||
providersMap.current.set(tab.id, providerData)
|
||||
} else {
|
||||
// When a tab is opened that has been open before, reuse the existing provider.
|
||||
providerData = providersMap.current.get(tab.id)!
|
||||
}
|
||||
// // Save the provider to the map.
|
||||
// providerData = { provider: yProvider, yDoc, yText, onSync }
|
||||
// providersMap.current.set(tab.id, providerData)
|
||||
// } else {
|
||||
// // When a tab is opened that has been open before, reuse the existing provider.
|
||||
// providerData = providersMap.current.get(tab.id)!
|
||||
// }
|
||||
|
||||
const binding = new MonacoBinding(
|
||||
providerData.yText,
|
||||
model,
|
||||
new Set([editorRef]),
|
||||
providerData.provider.awareness as unknown as Awareness
|
||||
)
|
||||
// const binding = new MonacoBinding(
|
||||
// providerData.yText,
|
||||
// model,
|
||||
// new Set([editorRef]),
|
||||
// providerData.provider.awareness as unknown as Awareness
|
||||
// )
|
||||
|
||||
providerData.binding = binding
|
||||
setProvider(providerData.provider)
|
||||
// providerData.binding = binding
|
||||
// setProvider(providerData.provider)
|
||||
|
||||
return () => {
|
||||
// Cleanup logic
|
||||
if (binding) {
|
||||
binding.destroy()
|
||||
}
|
||||
if (providerData.binding) {
|
||||
providerData.binding = undefined
|
||||
}
|
||||
}
|
||||
}, [room, activeFileContent])
|
||||
// return () => {
|
||||
// // Cleanup logic
|
||||
// if (binding) {
|
||||
// binding.destroy()
|
||||
// }
|
||||
// if (providerData.binding) {
|
||||
// providerData.binding = undefined
|
||||
// }
|
||||
// }
|
||||
// }, [room, activeFileContent])
|
||||
|
||||
// Added this effect to clean up when the component unmounts
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Clean up all providers when the component unmounts
|
||||
providersMap.current.forEach((data) => {
|
||||
if (data.binding) {
|
||||
data.binding.destroy()
|
||||
}
|
||||
data.provider.disconnect()
|
||||
data.yDoc.destroy()
|
||||
})
|
||||
providersMap.current.clear()
|
||||
}
|
||||
}, [])
|
||||
// // Added this effect to clean up when the component unmounts
|
||||
// useEffect(() => {
|
||||
// return () => {
|
||||
// // Clean up all providers when the component unmounts
|
||||
// providersMap.current.forEach((data) => {
|
||||
// if (data.binding) {
|
||||
// data.binding.destroy()
|
||||
// }
|
||||
// data.provider.disconnect()
|
||||
// data.yDoc.destroy()
|
||||
// })
|
||||
// providersMap.current.clear()
|
||||
// }
|
||||
// }, [])
|
||||
|
||||
// Connection/disconnection effect
|
||||
useEffect(() => {
|
||||
@ -1090,9 +1088,9 @@ export default function CodeEditor({
|
||||
) : // Note clerk.loaded is required here due to a bug: https://github.com/clerk/javascript/issues/1643
|
||||
clerk.loaded ? (
|
||||
<>
|
||||
{provider && userInfo ? (
|
||||
{/* {provider && userInfo ? (
|
||||
<Cursors yProvider={provider} userInfo={userInfo} />
|
||||
) : null}
|
||||
) : null} */}
|
||||
<Editor
|
||||
height="100%"
|
||||
language={editorLanguage}
|
||||
|
@ -9,7 +9,7 @@ import { Pencil, Users } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { useState } from "react"
|
||||
import { Avatars } from "../live/avatars"
|
||||
// import { Avatars } from "../live/avatars"
|
||||
import DeployButtonModal from "./deploy"
|
||||
import EditSandboxModal from "./edit"
|
||||
import RunButtonModal from "./run"
|
||||
@ -23,7 +23,7 @@ export default function Navbar({
|
||||
}: {
|
||||
userData: User
|
||||
sandboxData: Sandbox
|
||||
shared: { id: string; name: string }[]
|
||||
shared: { id: string; name: string; avatarUrl: string }[]
|
||||
}) {
|
||||
const [isEditOpen, setIsEditOpen] = useState(false)
|
||||
const [isShareOpen, setIsShareOpen] = useState(false)
|
||||
@ -70,15 +70,15 @@ export default function Navbar({
|
||||
sandboxData={sandboxData}
|
||||
/>
|
||||
<div className="flex items-center h-full space-x-4">
|
||||
<Avatars />
|
||||
{/* <Avatars /> */}
|
||||
|
||||
{isOwner ? (
|
||||
<>
|
||||
<DeployButtonModal data={sandboxData} userData={userData} />
|
||||
<Button variant="outline" onClick={() => setIsShareOpen(true)}>
|
||||
{/* <Button variant="outline" onClick={() => setIsShareOpen(true)}>
|
||||
<Users className="w-4 h-4 mr-2" />
|
||||
Share
|
||||
</Button>
|
||||
</Button> */}
|
||||
<DownloadButton name={sandboxData.name} /></>
|
||||
) : null}
|
||||
<ThemeSwitcher />
|
||||
|
@ -43,6 +43,7 @@ export default function ShareSandboxModal({
|
||||
shared: {
|
||||
id: string
|
||||
name: string
|
||||
avatarUrl: string
|
||||
}[]
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false)
|
||||
@ -142,7 +143,11 @@ export default function ShareSandboxModal({
|
||||
</DialogHeader>
|
||||
<div className="space-y-2">
|
||||
{shared.map((user) => (
|
||||
<SharedUser key={user.id} user={user} sandboxId={data.id} />
|
||||
<SharedUser
|
||||
key={user.id}
|
||||
user={user}
|
||||
sandboxId={data.id}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@ export default function SharedUser({
|
||||
user,
|
||||
sandboxId,
|
||||
}: {
|
||||
user: { id: string; name: string }
|
||||
user: { id: string; name: string; avatarUrl: string }
|
||||
sandboxId: string
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false)
|
||||
@ -24,7 +24,7 @@ export default function SharedUser({
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<Avatar name={user.name} className="mr-2" />
|
||||
<Avatar name={user.name} avatarUrl={user.avatarUrl} className="mr-2" />
|
||||
{user.name}
|
||||
</div>
|
||||
<Button
|
||||
|
@ -45,9 +45,14 @@ export default function Landing() {
|
||||
<h1 className="text-2xl font-medium text-center mt-16">
|
||||
A Collaborative + AI-Powered Code Environment
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-4 text-center ">
|
||||
{/* <p className="text-muted-foreground mt-4 text-center ">
|
||||
Sandbox is an open-source cloud-based code editing environment with
|
||||
custom AI code autocompletion and real-time collaboration.
|
||||
</p> */}
|
||||
<p className="text-muted-foreground mt-4 text-center ">
|
||||
A cloud-based code editor featuring real-time collaboration,
|
||||
intelligent code autocompletion, and an AI assistant to help you code
|
||||
faster and smarter.
|
||||
</p>
|
||||
<div className="mt-8 flex space-x-4">
|
||||
<Link href="/sign-up">
|
||||
|
59
frontend/components/ui/alert.tsx
Normal file
59
frontend/components/ui/alert.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDescription.displayName = "AlertDescription"
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
@ -1,23 +1,42 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
import Image from "next/image"
|
||||
|
||||
export default function Avatar({
|
||||
name,
|
||||
avatarUrl,
|
||||
className,
|
||||
}: {
|
||||
name: string
|
||||
avatarUrl?: string | null
|
||||
className?: string
|
||||
}) {
|
||||
// Generate initials from name if no avatarUrl is provided
|
||||
const initials = name
|
||||
? name
|
||||
.split(" ")
|
||||
.slice(0, 2)
|
||||
.map((letter) => letter[0].toUpperCase())
|
||||
.join("")
|
||||
: "?"
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
"w-5 h-5 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-[0.5rem] font-medium"
|
||||
"w-9 h-9 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-sm font-medium"
|
||||
)}
|
||||
>
|
||||
{name
|
||||
.split(" ")
|
||||
.slice(0, 2)
|
||||
.map((letter) => letter[0].toUpperCase())}
|
||||
{avatarUrl ? (
|
||||
<Image
|
||||
src={avatarUrl}
|
||||
alt={name || "User"}
|
||||
width={20}
|
||||
height={20}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
initials
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
28
frontend/components/ui/progress.tsx
Normal file
28
frontend/components/ui/progress.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Progress = React.forwardRef<
|
||||
React.ElementRef<typeof ProgressPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
||||
>(({ className, value, ...props }, ref) => (
|
||||
<ProgressPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
className="h-full w-full flex-1 bg-primary transition-all"
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
))
|
||||
Progress.displayName = ProgressPrimitive.Root.displayName
|
||||
|
||||
export { Progress }
|
32
frontend/components/ui/tooltip.tsx
Normal file
32
frontend/components/ui/tooltip.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</TooltipPrimitive.Portal>
|
||||
))
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
@ -11,6 +11,7 @@ import { User } from "@/lib/types"
|
||||
import { useClerk } from "@clerk/nextjs"
|
||||
import { LogOut, Sparkles } from "lucide-react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import Avatar from "./avatar"
|
||||
|
||||
export default function UserButton({ userData }: { userData: User }) {
|
||||
if (!userData) return null
|
||||
@ -21,13 +22,7 @@ export default function UserButton({ userData }: { userData: User }) {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<div className="w-9 h-9 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-sm font-medium">
|
||||
{userData.name &&
|
||||
userData.name
|
||||
.split(" ")
|
||||
.slice(0, 2)
|
||||
.map((name) => name[0].toUpperCase())}
|
||||
</div>
|
||||
<Avatar name={userData.name} avatarUrl={userData.avatarUrl} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-48" align="end">
|
||||
<div className="py-1.5 px-2 w-full">
|
||||
|
@ -16,7 +16,7 @@ export const projectTemplates: {
|
||||
id: "vanillajs",
|
||||
name: "HTML/JS",
|
||||
icon: "/project-icons/more.svg",
|
||||
description: "More coming soon, feel free to contribute on GitHub",
|
||||
description: "A simple HTML/JS project for building web apps",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
|
@ -4,6 +4,9 @@ export type User = {
|
||||
id: string
|
||||
name: string
|
||||
email: string
|
||||
username: string
|
||||
avatarUrl: string | null
|
||||
createdAt: Date
|
||||
generations: number
|
||||
sandbox: Sandbox[]
|
||||
usersToSandboxes: UsersToSandboxes[]
|
||||
|
82
frontend/lib/username-generator.ts
Normal file
82
frontend/lib/username-generator.ts
Normal file
@ -0,0 +1,82 @@
|
||||
// Constants for username generation
|
||||
const WORDS = {
|
||||
adjectives: [
|
||||
"azure", "crimson", "golden", "silver", "violet", "emerald", "cobalt", "amber", "coral", "jade",
|
||||
"cyber", "digital", "quantum", "neural", "binary", "cosmic", "stellar", "atomic", "crypto", "nano",
|
||||
"swift", "brave", "clever", "wise", "noble", "rapid", "bright", "sharp", "keen", "bold",
|
||||
"dynamic", "epic", "mega", "ultra", "hyper", "super", "prime", "elite", "alpha", "omega",
|
||||
"pixel", "vector", "sonic", "laser", "matrix", "nexus", "proxy", "cloud", "data", "tech",
|
||||
],
|
||||
nouns: [
|
||||
"coder", "hacker", "dev", "ninja", "guru", "wizard", "admin", "mod", "chief", "boss",
|
||||
"wolf", "eagle", "phoenix", "dragon", "tiger", "falcon", "shark", "lion", "hawk", "bear",
|
||||
"byte", "bit", "node", "stack", "cache", "chip", "core", "net", "web", "app",
|
||||
"star", "nova", "pulsar", "comet", "nebula", "quasar", "cosmos", "orbit", "astro", "solar",
|
||||
"mind", "soul", "spark", "pulse", "force", "power", "wave", "storm", "flash", "surge",
|
||||
],
|
||||
prefixes: [
|
||||
"the", "mr", "ms", "dr", "pro", "master", "lord", "captain", "chief", "agent",
|
||||
],
|
||||
} as const;
|
||||
|
||||
// Helper function to get random element from array
|
||||
const getRandomElement = <T>(array: readonly T[]): T => {
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
};
|
||||
|
||||
// Username pattern generators
|
||||
const usernamePatterns = {
|
||||
basic: (): string => {
|
||||
const adjective = getRandomElement(WORDS.adjectives);
|
||||
const noun = getRandomElement(WORDS.nouns);
|
||||
const number = Math.floor(Math.random() * 10000);
|
||||
return `${adjective}${noun}${number}`;
|
||||
},
|
||||
|
||||
prefixed: (): string => {
|
||||
const prefix = getRandomElement(WORDS.prefixes);
|
||||
const noun = getRandomElement(WORDS.nouns);
|
||||
const number = Math.floor(Math.random() * 100);
|
||||
return `${prefix}${noun}${number}`;
|
||||
},
|
||||
|
||||
doubleAdjective: (): string => {
|
||||
const adj1 = getRandomElement(WORDS.adjectives);
|
||||
const adj2 = getRandomElement(WORDS.adjectives);
|
||||
const noun = getRandomElement(WORDS.nouns);
|
||||
return `${adj1}${adj2}${noun}`;
|
||||
},
|
||||
|
||||
doubleNoun: (): string => {
|
||||
const noun1 = getRandomElement(WORDS.nouns);
|
||||
const noun2 = getRandomElement(WORDS.nouns);
|
||||
const number = Math.floor(Math.random() * 100);
|
||||
return `${noun1}${number}${noun2}`;
|
||||
},
|
||||
};
|
||||
|
||||
export function generateUsername(): string {
|
||||
const patterns = Object.values(usernamePatterns);
|
||||
const selectedPattern = getRandomElement(patterns);
|
||||
return selectedPattern();
|
||||
}
|
||||
|
||||
export async function generateUniqueUsername(
|
||||
checkExists: (username: string) => Promise<boolean>
|
||||
): Promise<string> {
|
||||
const MAX_ATTEMPTS = 10;
|
||||
let attempts = 0;
|
||||
let username = generateUsername();
|
||||
|
||||
while (await checkExists(username) && attempts < MAX_ATTEMPTS) {
|
||||
username = generateUsername();
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (attempts >= MAX_ATTEMPTS) {
|
||||
// Add a large random number to ensure uniqueness
|
||||
username = generateUsername() + Math.floor(Math.random() * 1000000);
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
@ -5,6 +5,12 @@ const nextConfig = {
|
||||
{
|
||||
hostname: "cdn.simpleicons.org",
|
||||
},
|
||||
{
|
||||
hostname: "img.clerk.com",
|
||||
},
|
||||
{
|
||||
hostname: "images.clerk.dev",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
1987
frontend/package-lock.json
generated
1987
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,9 +27,11 @@
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.1.1",
|
||||
"@radix-ui/react-progress": "^1.1.0",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@react-three/fiber": "^8.16.6",
|
||||
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
||||
"@uiw/react-codemirror": "^4.23.5",
|
||||
@ -57,6 +59,7 @@
|
||||
"react-resizable-panels": "^2.0.16",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"shadcn": "^2.1.6",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"sonner": "^1.4.41",
|
||||
"tailwind-merge": "^2.3.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user