work heavily on all server stuffs
This commit is contained in:
parent
d9f5919b60
commit
365c43c501
17 changed files with 2726 additions and 531 deletions
1
drizzle/0002_last_sleepwalker.sql
Normal file
1
drizzle/0002_last_sleepwalker.sql
Normal file
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `user` ADD `invites` text DEFAULT '[]' NOT NULL;
|
||||
2
drizzle/0003_sleepy_risque.sql
Normal file
2
drizzle/0003_sleepy_risque.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE `server` ADD `invites` text DEFAULT '[]' NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `user` DROP COLUMN `invites`;
|
||||
4
drizzle/0004_minor_wildside.sql
Normal file
4
drizzle/0004_minor_wildside.sql
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
ALTER TABLE `invite` ADD `creator_id` text NOT NULL REFERENCES user(id);--> statement-breakpoint
|
||||
ALTER TABLE `invite` ADD `expires_at` integer NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `invite` ADD `uses` text DEFAULT '[]' NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `invite` ADD `max_uses` integer;
|
||||
564
drizzle/meta/0002_snapshot.json
Normal file
564
drizzle/meta/0002_snapshot.json
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "37c8a92f-0590-4c8c-870d-ad145966dadb",
|
||||
"prevId": "e21369b9-d475-4c0f-8a6d-1c5f92ab8948",
|
||||
"tables": {
|
||||
"channel": {
|
||||
"name": "channel",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_id": {
|
||||
"name": "server_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"channel_server_id_server_id_fk": {
|
||||
"name": "channel_server_id_server_id_fk",
|
||||
"tableFrom": "channel",
|
||||
"tableTo": "server",
|
||||
"columnsFrom": [
|
||||
"server_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"directMessage": {
|
||||
"name": "directMessage",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"first_member": {
|
||||
"name": "first_member",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"second_member": {
|
||||
"name": "second_member",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"directMessage_first_member_user_id_fk": {
|
||||
"name": "directMessage_first_member_user_id_fk",
|
||||
"tableFrom": "directMessage",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"first_member"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"directMessage_second_member_user_id_fk": {
|
||||
"name": "directMessage_second_member_user_id_fk",
|
||||
"tableFrom": "directMessage",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"second_member"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"friendRequest": {
|
||||
"name": "friendRequest",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"from_user": {
|
||||
"name": "from_user",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"from_username": {
|
||||
"name": "from_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"to_username": {
|
||||
"name": "to_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"to_user": {
|
||||
"name": "to_user",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"friendRequest_from_user_user_id_fk": {
|
||||
"name": "friendRequest_from_user_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"from_user"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_from_username_user_id_fk": {
|
||||
"name": "friendRequest_from_username_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"from_username"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_to_username_user_id_fk": {
|
||||
"name": "friendRequest_to_username_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"to_username"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_to_user_user_id_fk": {
|
||||
"name": "friendRequest_to_user_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"to_user"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"group": {
|
||||
"name": "group",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner": {
|
||||
"name": "owner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"change_title": {
|
||||
"name": "change_title",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"add_members": {
|
||||
"name": "add_members",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"remove_members": {
|
||||
"name": "remove_members",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"members": {
|
||||
"name": "members",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"group_owner_user_id_fk": {
|
||||
"name": "group_owner_user_id_fk",
|
||||
"tableFrom": "group",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"owner"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"invite": {
|
||||
"name": "invite",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_id": {
|
||||
"name": "server_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"code": {
|
||||
"name": "code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"invite_server_id_server_id_fk": {
|
||||
"name": "invite_server_id_server_id_fk",
|
||||
"tableFrom": "invite",
|
||||
"tableTo": "server",
|
||||
"columnsFrom": [
|
||||
"server_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"server": {
|
||||
"name": "server",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner": {
|
||||
"name": "owner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"members": {
|
||||
"name": "members",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"channels": {
|
||||
"name": "channels",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"server_owner_user_id_fk": {
|
||||
"name": "server_owner_user_id_fk",
|
||||
"tableFrom": "server",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"owner"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"session": {
|
||||
"name": "session",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_user_id_user_id_fk": {
|
||||
"name": "session_user_id_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"password_hash": {
|
||||
"name": "password_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status_overwrite": {
|
||||
"name": "status_overwrite",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 3
|
||||
},
|
||||
"friends": {
|
||||
"name": "friends",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"invites": {
|
||||
"name": "invites",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"servers": {
|
||||
"name": "servers",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"groups": {
|
||||
"name": "groups",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_username_unique": {
|
||||
"name": "user_username_unique",
|
||||
"columns": [
|
||||
"username"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email_unique": {
|
||||
"name": "user_email_unique",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
564
drizzle/meta/0003_snapshot.json
Normal file
564
drizzle/meta/0003_snapshot.json
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "6112aa0b-60eb-4835-86a9-b137b02ce152",
|
||||
"prevId": "37c8a92f-0590-4c8c-870d-ad145966dadb",
|
||||
"tables": {
|
||||
"channel": {
|
||||
"name": "channel",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_id": {
|
||||
"name": "server_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"channel_server_id_server_id_fk": {
|
||||
"name": "channel_server_id_server_id_fk",
|
||||
"tableFrom": "channel",
|
||||
"tableTo": "server",
|
||||
"columnsFrom": [
|
||||
"server_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"directMessage": {
|
||||
"name": "directMessage",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"first_member": {
|
||||
"name": "first_member",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"second_member": {
|
||||
"name": "second_member",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"directMessage_first_member_user_id_fk": {
|
||||
"name": "directMessage_first_member_user_id_fk",
|
||||
"tableFrom": "directMessage",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"first_member"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"directMessage_second_member_user_id_fk": {
|
||||
"name": "directMessage_second_member_user_id_fk",
|
||||
"tableFrom": "directMessage",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"second_member"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"friendRequest": {
|
||||
"name": "friendRequest",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"from_user": {
|
||||
"name": "from_user",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"from_username": {
|
||||
"name": "from_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"to_username": {
|
||||
"name": "to_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"to_user": {
|
||||
"name": "to_user",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"friendRequest_from_user_user_id_fk": {
|
||||
"name": "friendRequest_from_user_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"from_user"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_from_username_user_id_fk": {
|
||||
"name": "friendRequest_from_username_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"from_username"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_to_username_user_id_fk": {
|
||||
"name": "friendRequest_to_username_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"to_username"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_to_user_user_id_fk": {
|
||||
"name": "friendRequest_to_user_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"to_user"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"group": {
|
||||
"name": "group",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner": {
|
||||
"name": "owner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"change_title": {
|
||||
"name": "change_title",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"add_members": {
|
||||
"name": "add_members",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"remove_members": {
|
||||
"name": "remove_members",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"members": {
|
||||
"name": "members",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"group_owner_user_id_fk": {
|
||||
"name": "group_owner_user_id_fk",
|
||||
"tableFrom": "group",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"owner"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"invite": {
|
||||
"name": "invite",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_id": {
|
||||
"name": "server_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"code": {
|
||||
"name": "code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"invite_server_id_server_id_fk": {
|
||||
"name": "invite_server_id_server_id_fk",
|
||||
"tableFrom": "invite",
|
||||
"tableTo": "server",
|
||||
"columnsFrom": [
|
||||
"server_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"server": {
|
||||
"name": "server",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner": {
|
||||
"name": "owner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"members": {
|
||||
"name": "members",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"channels": {
|
||||
"name": "channels",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"invites": {
|
||||
"name": "invites",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"server_owner_user_id_fk": {
|
||||
"name": "server_owner_user_id_fk",
|
||||
"tableFrom": "server",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"owner"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"session": {
|
||||
"name": "session",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_user_id_user_id_fk": {
|
||||
"name": "session_user_id_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"password_hash": {
|
||||
"name": "password_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status_overwrite": {
|
||||
"name": "status_overwrite",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 3
|
||||
},
|
||||
"friends": {
|
||||
"name": "friends",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"servers": {
|
||||
"name": "servers",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"groups": {
|
||||
"name": "groups",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_username_unique": {
|
||||
"name": "user_username_unique",
|
||||
"columns": [
|
||||
"username"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email_unique": {
|
||||
"name": "user_email_unique",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
606
drizzle/meta/0004_snapshot.json
Normal file
606
drizzle/meta/0004_snapshot.json
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "7f91f3dc-746e-40eb-b1d5-8ca20e04dd5f",
|
||||
"prevId": "6112aa0b-60eb-4835-86a9-b137b02ce152",
|
||||
"tables": {
|
||||
"channel": {
|
||||
"name": "channel",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_id": {
|
||||
"name": "server_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"channel_server_id_server_id_fk": {
|
||||
"name": "channel_server_id_server_id_fk",
|
||||
"tableFrom": "channel",
|
||||
"tableTo": "server",
|
||||
"columnsFrom": [
|
||||
"server_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"directMessage": {
|
||||
"name": "directMessage",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"first_member": {
|
||||
"name": "first_member",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"second_member": {
|
||||
"name": "second_member",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"directMessage_first_member_user_id_fk": {
|
||||
"name": "directMessage_first_member_user_id_fk",
|
||||
"tableFrom": "directMessage",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"first_member"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"directMessage_second_member_user_id_fk": {
|
||||
"name": "directMessage_second_member_user_id_fk",
|
||||
"tableFrom": "directMessage",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"second_member"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"friendRequest": {
|
||||
"name": "friendRequest",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"from_user": {
|
||||
"name": "from_user",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"from_username": {
|
||||
"name": "from_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"to_username": {
|
||||
"name": "to_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"to_user": {
|
||||
"name": "to_user",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"friendRequest_from_user_user_id_fk": {
|
||||
"name": "friendRequest_from_user_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"from_user"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_from_username_user_id_fk": {
|
||||
"name": "friendRequest_from_username_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"from_username"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_to_username_user_id_fk": {
|
||||
"name": "friendRequest_to_username_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"to_username"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"friendRequest_to_user_user_id_fk": {
|
||||
"name": "friendRequest_to_user_user_id_fk",
|
||||
"tableFrom": "friendRequest",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"to_user"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"group": {
|
||||
"name": "group",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner": {
|
||||
"name": "owner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"change_title": {
|
||||
"name": "change_title",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"add_members": {
|
||||
"name": "add_members",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 1
|
||||
},
|
||||
"remove_members": {
|
||||
"name": "remove_members",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"members": {
|
||||
"name": "members",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"messages": {
|
||||
"name": "messages",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"group_owner_user_id_fk": {
|
||||
"name": "group_owner_user_id_fk",
|
||||
"tableFrom": "group",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"owner"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"invite": {
|
||||
"name": "invite",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_id": {
|
||||
"name": "server_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"code": {
|
||||
"name": "code",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"creator_id": {
|
||||
"name": "creator_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"uses": {
|
||||
"name": "uses",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"max_uses": {
|
||||
"name": "max_uses",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"invite_server_id_server_id_fk": {
|
||||
"name": "invite_server_id_server_id_fk",
|
||||
"tableFrom": "invite",
|
||||
"tableTo": "server",
|
||||
"columnsFrom": [
|
||||
"server_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"invite_creator_id_user_id_fk": {
|
||||
"name": "invite_creator_id_user_id_fk",
|
||||
"tableFrom": "invite",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"creator_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"server": {
|
||||
"name": "server",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"owner": {
|
||||
"name": "owner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"members": {
|
||||
"name": "members",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"channels": {
|
||||
"name": "channels",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"invites": {
|
||||
"name": "invites",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"server_owner_user_id_fk": {
|
||||
"name": "server_owner_user_id_fk",
|
||||
"tableFrom": "server",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"owner"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"session": {
|
||||
"name": "session",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_user_id_user_id_fk": {
|
||||
"name": "session_user_id_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"username": {
|
||||
"name": "username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"password_hash": {
|
||||
"name": "password_hash",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status_overwrite": {
|
||||
"name": "status_overwrite",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 3
|
||||
},
|
||||
"friends": {
|
||||
"name": "friends",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"servers": {
|
||||
"name": "servers",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
},
|
||||
"groups": {
|
||||
"name": "groups",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'[]'"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_username_unique": {
|
||||
"name": "user_username_unique",
|
||||
"columns": [
|
||||
"username"
|
||||
],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email_unique": {
|
||||
"name": "user_email_unique",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,27 @@
|
|||
"when": 1767860870240,
|
||||
"tag": "0001_tiresome_stature",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "6",
|
||||
"when": 1768471463663,
|
||||
"tag": "0002_last_sleepwalker",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "6",
|
||||
"when": 1768471566545,
|
||||
"tag": "0003_sleepy_risque",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "6",
|
||||
"when": 1768584253382,
|
||||
"tag": "0004_minor_wildside",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
7
src/lib/components/ui/switch/index.ts
Normal file
7
src/lib/components/ui/switch/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import Root from "./switch.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Switch,
|
||||
};
|
||||
29
src/lib/components/ui/switch/switch.svelte
Normal file
29
src/lib/components/ui/switch/switch.svelte
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<script lang="ts">
|
||||
import { Switch as SwitchPrimitive } from "bits-ui";
|
||||
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
checked = $bindable(false),
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<SwitchPrimitive.Root
|
||||
bind:ref
|
||||
bind:checked
|
||||
data-slot="switch"
|
||||
class={cn(
|
||||
"data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
class={cn(
|
||||
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
|
|
@ -5,6 +5,7 @@ export const UserID = definePrefix('user');
|
|||
export const GroupID = definePrefix('group');
|
||||
export const ServerID = definePrefix('srv');
|
||||
export const ChannelID = definePrefix('ch');
|
||||
export const InviteID = definePrefix('inv');
|
||||
|
||||
export const FriendRequestID = definePrefix('frq');
|
||||
export const DirectMessageID = definePrefix('dmid');
|
||||
|
|
@ -14,6 +15,7 @@ export type GroupId = Puuid<'group'>;
|
|||
export type ServerId = Puuid<'srv'>;
|
||||
export type DirectMessageId = Puuid<'dmid'>;
|
||||
export type ChannelId = Puuid<'ch'>;
|
||||
export type InviteId = Puuid<'inv'>;
|
||||
|
||||
export interface Status {
|
||||
statusMessage: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { boolean } from 'drizzle-orm/singlestore-core';
|
||||
import { Status } from '../../index.ts';
|
||||
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
|
||||
|
||||
|
|
@ -9,7 +8,6 @@ export const user = sqliteTable('user', {
|
|||
passwordHash: text('password_hash').notNull(),
|
||||
statusOverwrite: integer('status_overwrite').default(Status.ONLINE).notNull(),
|
||||
friends: text('friends', { mode: 'json' }).default([]).notNull(),
|
||||
|
||||
servers: text('servers', { mode: 'json' }).default([]).notNull(), // string[] of ServerIDs
|
||||
groups: text('groups', { mode: 'json' }).default([]).notNull() // string[] of GroupIDs
|
||||
});
|
||||
|
|
@ -29,7 +27,8 @@ export const server = sqliteTable('server', {
|
|||
.notNull()
|
||||
.references(() => user.id),
|
||||
members: text('members', { mode: 'json' }).default([]).notNull(),
|
||||
channels: text('channels', { mode: 'json' }).default([]).notNull() // string[] of ChannelIDs
|
||||
channels: text('channels', { mode: 'json' }).default([]).notNull(), // string[] of ChannelIDs
|
||||
invites: text('invites', { mode: 'json' }).default([]).notNull() // string[] of InviteIDs
|
||||
});
|
||||
|
||||
export const group = sqliteTable('group', {
|
||||
|
|
@ -86,7 +85,13 @@ export const invite = sqliteTable('invite', {
|
|||
serverId: text('server_id')
|
||||
.notNull()
|
||||
.references(() => server.id),
|
||||
code: text('code').notNull()
|
||||
code: text('code').notNull(),
|
||||
creatorId: text('creator_id')
|
||||
.notNull()
|
||||
.references(() => user.id),
|
||||
createdAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
|
||||
uses: text('uses', { mode: 'json' }).default([]).notNull(), // users who used the invite. can repeat,
|
||||
maxUses: integer('max_uses')
|
||||
});
|
||||
|
||||
export type Session = typeof session.$inferSelect;
|
||||
|
|
|
|||
|
|
@ -57,12 +57,12 @@ export async function GET({ locals, request }) {
|
|||
|
||||
controller.enqueue(`data: ${JSON.stringify({ type: 'connected', sessionId })}\n\n`);
|
||||
|
||||
if (overwrite === Status.DND) {
|
||||
kvStore.set(`user-${userId}-state`, Status.DND);
|
||||
if (overwrite === Status.DND || overwrite == Status.ONLINE) {
|
||||
kvStore.set(`user-${userId}-state`, overwrite);
|
||||
_sendToSubscribers(userId, {
|
||||
type: 'status',
|
||||
id: userId,
|
||||
status: Status.DND,
|
||||
status: overwrite,
|
||||
statusMessage:
|
||||
kvStore.get('user-' + userId + '-state') != Status.OFFLINE
|
||||
? kvStore.get('user-' + userId + '-message')
|
||||
|
|
|
|||
|
|
@ -3,200 +3,20 @@ import { getRequestEvent } from '$app/server';
|
|||
import type { Actions, PageServerLoad } from './$types';
|
||||
import { db, kvStore } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { DirectMessageID, FriendRequestID, GroupID, ServerID } from '$lib';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { and } from 'drizzle-orm';
|
||||
import { type User } from '$lib/server/db/schema';
|
||||
import { _sendToSubscribers, _sendToUser } from '../api/updates/+server';
|
||||
import { _sendToSubscribers } from '../api/updates/+server';
|
||||
import { validateUsername } from '$lib/server/auth';
|
||||
|
||||
import FriendActions from './actions/friend';
|
||||
import GroupActions from './actions/group';
|
||||
import ServerActions from './actions/server';
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
const user = requireLogin();
|
||||
return { user };
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
addFriend: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const username = data.get('username');
|
||||
const userId = data.get('userId');
|
||||
|
||||
if (username && !userId) {
|
||||
if (typeof username !== 'string' || username.length < 3) {
|
||||
return fail(400, { error: 'Invalid username' });
|
||||
}
|
||||
}
|
||||
|
||||
let user: User[];
|
||||
if (username) {
|
||||
user = await db
|
||||
.select()
|
||||
.from(table.user)
|
||||
.where(eq(table.user.username, username.toString()))
|
||||
.limit(1);
|
||||
} else if (userId) {
|
||||
user = await db
|
||||
.select()
|
||||
.from(table.user)
|
||||
.where(eq(table.user.id, userId.toString()))
|
||||
.limit(1);
|
||||
} else {
|
||||
return fail(400, { error: 'Missing username or userId' });
|
||||
}
|
||||
|
||||
if (locals.user?.friends.find((z) => z.id == user[0].id)) {
|
||||
return fail(400, { error: 'Already friends' });
|
||||
}
|
||||
|
||||
if (user?.length == 0) return fail(400, { error: 'User not found' });
|
||||
|
||||
const friendRequest = await db
|
||||
.select()
|
||||
.from(table.friendRequest)
|
||||
.where(
|
||||
and(
|
||||
eq(table.friendRequest.fromUser, user[0].id),
|
||||
eq(table.friendRequest.toUser, locals.user!.id)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
// user has already sent a request to us
|
||||
// means we want to accept it
|
||||
//
|
||||
if (friendRequest?.length != 0) {
|
||||
await db
|
||||
.delete(table.friendRequest)
|
||||
.where(
|
||||
and(
|
||||
eq(table.friendRequest.fromUser, user[0].id),
|
||||
eq(table.friendRequest.toUser, locals.user!.id)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
await db.insert(table.directMessage).values({
|
||||
id: DirectMessageID.newV4(),
|
||||
firstMember: locals.user!.id,
|
||||
secondMember: user[0].id,
|
||||
messages: []
|
||||
});
|
||||
|
||||
// add other guy to us
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ friends: locals.user?.friends.map((z) => z.id).concat(user[0].id) })
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ friends: (user[0].friends as string[]).concat(locals.user!.id) })
|
||||
.where(eq(table.user.id, user[0].id));
|
||||
});
|
||||
|
||||
_sendToSubscribers(locals.user!.id, { type: 'friends', status: 'accepted' });
|
||||
_sendToSubscribers(user[0].id, { type: 'friends', status: 'accepted' });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// a request from us has already been sent to user
|
||||
if (
|
||||
locals.user?.friendRequests.find(
|
||||
(z) => z.toUser == user[0].id && z.fromUser == locals.user!.id
|
||||
)
|
||||
)
|
||||
return fail(400, { error: 'Already sent request' });
|
||||
|
||||
await db.insert(table.friendRequest).values({
|
||||
id: FriendRequestID.newV4(),
|
||||
fromUser: locals.user!.id,
|
||||
toUser: user[0].id,
|
||||
toUsername: user[0].username,
|
||||
fromUsername: locals.user!.username
|
||||
});
|
||||
|
||||
_sendToSubscribers(locals.user!.id, { type: 'friends', status: 'sent-request' });
|
||||
_sendToSubscribers(user[0].id, { type: 'friends', status: 'new-request' });
|
||||
return { success: true };
|
||||
},
|
||||
removeFriend: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const userId = data.get('userId');
|
||||
|
||||
if (typeof userId !== 'string') {
|
||||
return fail(400, { error: 'Invalid user ID' });
|
||||
}
|
||||
|
||||
// verify we are actually friends
|
||||
if (!locals.user?.friends.find((z) => z.id === userId)) {
|
||||
return fail(400, { error: 'Not in friends list' });
|
||||
}
|
||||
|
||||
// fetch the target user
|
||||
const user = await db.select().from(table.user).where(eq(table.user.id, userId)).limit(1);
|
||||
|
||||
if (!user?.length) {
|
||||
return fail(404, { error: 'User not found' });
|
||||
}
|
||||
|
||||
const target = user[0];
|
||||
|
||||
// remove each other from friends lists
|
||||
await db.transaction(async (tx) => {
|
||||
// update current user – filter out removed friend
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
friends: locals.user!.friends.map((z) => z.id).filter((id) => id !== userId)
|
||||
})
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
// update target user – filter out us
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
friends: (target.friends as string[]).filter((id) => id !== locals.user!.id)
|
||||
})
|
||||
.where(eq(table.user.id, target.id));
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
cancelFriendRequest: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const requestId = data.get('requestId');
|
||||
|
||||
if (typeof requestId !== 'string') {
|
||||
return fail(400, { error: 'Invalid request ID' });
|
||||
}
|
||||
|
||||
// fetch the friend request
|
||||
const friendRequest = await db
|
||||
.select()
|
||||
.from(table.friendRequest)
|
||||
.where(eq(table.friendRequest.id, requestId))
|
||||
.limit(1);
|
||||
|
||||
if (!friendRequest?.length) {
|
||||
return fail(404, { error: 'Friend request not found' });
|
||||
}
|
||||
|
||||
const fr = friendRequest[0];
|
||||
|
||||
// only allow cancelling if it's related to current user
|
||||
if (fr.fromUser !== locals.user!.id && fr.toUser !== locals.user!.id) {
|
||||
return fail(403, { error: 'Not allowed' });
|
||||
}
|
||||
|
||||
// delete the request
|
||||
await db.delete(table.friendRequest).where(eq(table.friendRequest.id, requestId)).limit(1);
|
||||
|
||||
_sendToSubscribers(fr.fromUser, { type: 'friends', status: 'request-cancelled' });
|
||||
_sendToSubscribers(fr.toUser, { type: 'friends', status: 'request-cancelled' });
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
updateProfile: async ({ request, locals }) => {
|
||||
const user = locals.user;
|
||||
if (!user) return fail(401);
|
||||
|
|
@ -232,322 +52,9 @@ export const actions = {
|
|||
|
||||
return { success: true };
|
||||
},
|
||||
createGroup: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const members = data.getAll('member').map((z) => z.toString());
|
||||
|
||||
if (!members.length) {
|
||||
return fail(400, { error: 'No members selected' });
|
||||
}
|
||||
if (members.includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You cannot add yourself to a group.' });
|
||||
}
|
||||
|
||||
if (members.length > 9) {
|
||||
return fail(400, { error: 'Too many members' });
|
||||
}
|
||||
if (members.length < 2) {
|
||||
return fail(400, { error: 'Not enough members' });
|
||||
}
|
||||
for (const member of members) {
|
||||
if (!locals.user!.friends.find((z) => z.id == member)) {
|
||||
return fail(403, { error: 'A member is not your friend.' });
|
||||
}
|
||||
}
|
||||
const nameArray = [];
|
||||
|
||||
for await (const member of members) {
|
||||
const dbUser = await db.select().from(table.user).where(eq(table.user.id, member)).limit(1);
|
||||
nameArray.push(dbUser[0].username);
|
||||
|
||||
if (!dbUser.length) {
|
||||
return fail(400, { error: 'Invalid member' });
|
||||
}
|
||||
}
|
||||
|
||||
members.push(locals.user!.id);
|
||||
nameArray.push(locals.user!.username);
|
||||
|
||||
const group = await db
|
||||
.insert(table.group)
|
||||
.values({
|
||||
id: GroupID.newV4(),
|
||||
name: nameArray.join(', '),
|
||||
owner: locals.user!.id,
|
||||
members: members,
|
||||
messages: []
|
||||
})
|
||||
.returning();
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
for await (const member of members) {
|
||||
_sendToUser(member, { type: 'group', status: 'added-to-group' });
|
||||
const user = await tx.select().from(table.user).where(eq(table.user.id, member)).limit(1);
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ groups: (user[0].groups as string[]).concat(group[0].id) })
|
||||
.where(eq(table.user.id, member));
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true, group };
|
||||
},
|
||||
|
||||
joinServer: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const invite = data.get('invite');
|
||||
|
||||
if (typeof invite !== 'string') {
|
||||
return fail(400, { error: 'Invalid invite' });
|
||||
}
|
||||
|
||||
const inv = await db.select().from(table.invite).where(eq(table.invite.code, invite)).limit(1);
|
||||
|
||||
if (inv?.length == 0) return fail(400, { error: 'Invalid invite' });
|
||||
|
||||
const server = await db
|
||||
.select()
|
||||
.from(table.server)
|
||||
.where(eq(table.server.id, inv[0].serverId))
|
||||
.limit(1);
|
||||
|
||||
if (server?.length == 0) return fail(400, { error: 'Invalid server' });
|
||||
|
||||
if (locals.user!.servers.some((z) => z.id == server[0].id))
|
||||
return fail(400, { error: 'Already in server' });
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ servers: locals.user!.servers.map((z) => z.id).concat([server[0].id]) })
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
await tx
|
||||
.update(table.server)
|
||||
.set({ members: (server[0].members as string[]).concat([locals.user!.id]) })
|
||||
.where(eq(table.server.id, server[0].id));
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
addMembers: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
const memberIds = data.getAll('memberIds').map(String);
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
if (!memberIds.length) {
|
||||
return fail(400, { error: 'No members selected' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
const g = group[0];
|
||||
|
||||
if (!(g.members as string[]).includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You do not have permission to act on this group.' });
|
||||
}
|
||||
|
||||
const isOwner = g.owner === locals.user!.id;
|
||||
if (!isOwner && !g.addMembers) {
|
||||
return fail(403, { error: 'No permission to add members' });
|
||||
}
|
||||
|
||||
for (const id of memberIds) {
|
||||
if (!locals.user!.friends.find((f) => f.id === id)) {
|
||||
return fail(403, { error: 'Can only add friends' });
|
||||
}
|
||||
}
|
||||
|
||||
const newMembers = [...new Set([...(g.members as string[]), ...memberIds])];
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx.update(table.group).set({ members: newMembers }).where(eq(table.group.id, groupId));
|
||||
|
||||
for (const id of memberIds) {
|
||||
const user = await tx.select().from(table.user).where(eq(table.user.id, id)).limit(1);
|
||||
if (!user.length) continue;
|
||||
if (newMembers.includes(id)) {
|
||||
_sendToUser(id, { type: 'group', status: 'added-to-group' });
|
||||
} else {
|
||||
_sendToUser(id, { type: 'group', status: 'member-added-to-group' });
|
||||
}
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ groups: (user[0].groups as string[]).concat(groupId) })
|
||||
.where(eq(table.user.id, id));
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
removeMembers: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
const memberIds = data.getAll('memberIds').map(String);
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
const g = group[0];
|
||||
|
||||
if (!(g.members as string[]).includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You do not have permission to act on this group.' });
|
||||
}
|
||||
const isOwner = g.owner === locals.user!.id;
|
||||
if (!isOwner && !g.removeMembers) {
|
||||
return fail(403, { error: 'No permission to remove members' });
|
||||
}
|
||||
|
||||
if (memberIds.includes(g.owner)) {
|
||||
return fail(400, { error: 'Cannot remove group owner' });
|
||||
}
|
||||
|
||||
const remaining = (g.members as string[]).filter((id) => !memberIds.includes(id));
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx.update(table.group).set({ members: remaining }).where(eq(table.group.id, groupId));
|
||||
|
||||
for (const id of remaining) {
|
||||
_sendToUser(id, { type: 'group', status: 'someone-was-removed' });
|
||||
}
|
||||
|
||||
for (const id of memberIds) {
|
||||
_sendToUser(id, { type: 'group', status: 'removed-from-group' });
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
groups: locals.user!.groups.map((z) => z.id).filter((g) => g !== groupId)
|
||||
})
|
||||
.where(eq(table.user.id, id));
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
changeTitle: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
const title = data.get('title');
|
||||
|
||||
if (typeof groupId !== 'string' || typeof title !== 'string' || title.length < 1) {
|
||||
return fail(400, { error: 'Invalid input' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
const g = group[0];
|
||||
if (!(g.members as string[]).includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You do not have permission to act on this group.' });
|
||||
}
|
||||
const isOwner = g.owner === locals.user!.id;
|
||||
|
||||
if (!isOwner && !g.changeTitle) {
|
||||
return fail(403, { error: 'No permission to change title' });
|
||||
}
|
||||
|
||||
await db.update(table.group).set({ name: title }).where(eq(table.group.id, groupId));
|
||||
//@TODO if a user isn't in the group screen this doesnt get propogated
|
||||
_sendToSubscribers(groupId, { type: 'group', status: 'name-changed' });
|
||||
return { success: true };
|
||||
},
|
||||
configureGroup: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
if (group[0].owner !== locals.user!.id) {
|
||||
return fail(403, { error: 'Only owner can configure group' });
|
||||
}
|
||||
await db
|
||||
.update(table.group)
|
||||
.set({
|
||||
addMembers: data.has('addMembers') ? 1 : 0,
|
||||
removeMembers: data.has('removeMembers') ? 1 : 0,
|
||||
changeTitle: data.has('changeTitle') ? 1 : 0
|
||||
})
|
||||
.where(eq(table.group.id, groupId));
|
||||
_sendToSubscribers(group[0].id, { type: 'group', status: 'permission-change' });
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
deleteGroup: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
|
||||
if (!group.length) {
|
||||
return fail(404, { error: 'Group not found' });
|
||||
}
|
||||
|
||||
const g = group[0];
|
||||
|
||||
if (g.owner !== locals.user!.id) {
|
||||
return fail(403, { error: 'Not allowed' });
|
||||
}
|
||||
|
||||
if ((g.members as string[]).length !== 1 || (g.members as string[])[0] !== locals.user!.id) {
|
||||
return fail(400, { error: 'Group still has members' });
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
groups: locals.user!.groups.map((z) => z.id).filter((id) => id !== groupId)
|
||||
})
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
await tx.delete(table.group).where(eq(table.group.id, groupId));
|
||||
});
|
||||
_sendToUser(locals.user!.id, { type: 'group', status: 'removed-from-group' });
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
createServer: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const name = data.get('name');
|
||||
|
||||
if (typeof name !== 'string' || name.length < 3) {
|
||||
return fail(400, { error: 'Server name too short' });
|
||||
}
|
||||
const serverId = ServerID.newV4();
|
||||
|
||||
await db
|
||||
.insert(table.server)
|
||||
.values({ id: serverId, name, owner: locals.user!.id, members: [locals.user!.id] });
|
||||
|
||||
await db
|
||||
.update(table.user)
|
||||
.set({ servers: locals.user!.servers.map((z) => z.id).concat([serverId]) })
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
_sendToUser(locals.user!.id, { type: 'server', status: 'server-created' });
|
||||
return { success: true };
|
||||
}
|
||||
...FriendActions,
|
||||
...GroupActions,
|
||||
...ServerActions
|
||||
} satisfies Actions;
|
||||
|
||||
function requireLogin() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import {
|
||||
Status,
|
||||
type OverviewData,
|
||||
GroupID,
|
||||
UserID,
|
||||
|
|
@ -20,19 +19,22 @@
|
|||
import type { PageServerData } from './$types';
|
||||
import AppSidebar from '$lib/components/app-sidebar.svelte';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import * as AlertDialog from '$lib/components/ui/alert-dialog/index.js';
|
||||
import { enhance } from '$app/forms';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import type { ActionData } from './$types';
|
||||
import { formatTimestamp } from '$lib/utils';
|
||||
import Input from '$lib/components/ui/input/input.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
|
||||
import * as Tabs from '$lib/components/ui/tabs/index.js';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
import { Switch } from '$lib/components/ui/switch/index.js';
|
||||
import SendHorizontal from '@lucide/svelte/icons/send-horizontal';
|
||||
import PersonStanding from '@lucide/svelte/icons/person-standing';
|
||||
import MemberSidebar from '$lib/components/member-sidebar.svelte';
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { toast } from 'svelte-sonner';
|
||||
let errorOpen = $state(true);
|
||||
|
||||
let { form, data }: { form: ActionData; data: PageServerData } = $props();
|
||||
let currentPageID: (UserId | GroupId | ServerId) | null = $state(null);
|
||||
|
|
@ -40,6 +42,8 @@
|
|||
let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state();
|
||||
let currentSubPage: Channel | null = $state(null);
|
||||
|
||||
let inviteCode = $state();
|
||||
|
||||
let sse: EventSource | undefined;
|
||||
let messagesElement: HTMLDivElement | undefined = $state();
|
||||
let isMembersTabOpen = $state(true);
|
||||
|
|
@ -146,7 +150,7 @@
|
|||
|
||||
async function getMessages() {
|
||||
let path = '';
|
||||
let subscribe = '';
|
||||
let subscribe: string | null | undefined = '';
|
||||
|
||||
if (ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) {
|
||||
path = '/api/messages/' + currentPageID + '/' + currentSubPageID;
|
||||
|
|
@ -156,6 +160,7 @@
|
|||
path = '/api/messages/' + subscribe;
|
||||
}
|
||||
|
||||
if (!subscribe) return;
|
||||
const req = await fetch(path);
|
||||
const data = await req.json();
|
||||
|
||||
|
|
@ -184,6 +189,14 @@
|
|||
onMount(() => {
|
||||
async function run() {
|
||||
if (form) {
|
||||
if (form.success) {
|
||||
toast.success('Action succeeded.');
|
||||
}
|
||||
|
||||
if (form.error) {
|
||||
toast.error('Action had a error: ' + form.error);
|
||||
}
|
||||
|
||||
await invalidateAll();
|
||||
}
|
||||
await fill_overview_data();
|
||||
|
|
@ -261,23 +274,81 @@
|
|||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if form}
|
||||
<AlertDialog.Root bind:open={errorOpen}>
|
||||
<AlertDialog.Content>
|
||||
<AlertDialog.Header>
|
||||
<AlertDialog.Title>{form?.error ? 'Ran into an error.' : 'Success!'}</AlertDialog.Title>
|
||||
<AlertDialog.Description>
|
||||
{form?.error || 'Action completed succesfully.'}
|
||||
</AlertDialog.Description>
|
||||
</AlertDialog.Header>
|
||||
<AlertDialog.Footer>
|
||||
<AlertDialog.Cancel>Close</AlertDialog.Cancel>
|
||||
</AlertDialog.Footer>
|
||||
</AlertDialog.Content>
|
||||
</AlertDialog.Root>
|
||||
{/if}
|
||||
type FakePermission =
|
||||
| 'changeChannelName'
|
||||
| 'changeServerName'
|
||||
| 'createInvite'
|
||||
| 'createChannels'
|
||||
| 'deleteChannels'
|
||||
| 'deleteInvites'
|
||||
| 'addRoles'
|
||||
| 'deleteRoles'
|
||||
| 'kickPeople'
|
||||
| 'banPeople';
|
||||
|
||||
type FakeRole = {
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: Record<FakePermission, boolean>;
|
||||
};
|
||||
let newRoleName = $state('');
|
||||
|
||||
let fakeRoles = $state<FakeRole[]>([
|
||||
{
|
||||
id: 'admin',
|
||||
name: 'Admin',
|
||||
permissions: {
|
||||
changeChannelName: true,
|
||||
changeServerName: true,
|
||||
createInvite: true,
|
||||
createChannels: true,
|
||||
deleteChannels: true,
|
||||
deleteInvites: true,
|
||||
addRoles: true,
|
||||
deleteRoles: true,
|
||||
kickPeople: true,
|
||||
banPeople: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'moderator',
|
||||
name: 'Moderator',
|
||||
permissions: {
|
||||
changeChannelName: true,
|
||||
changeServerName: false,
|
||||
createInvite: true,
|
||||
createChannels: false,
|
||||
deleteChannels: true,
|
||||
deleteInvites: true,
|
||||
addRoles: true,
|
||||
deleteRoles: true,
|
||||
kickPeople: true,
|
||||
banPeople: false
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'member',
|
||||
name: 'Member',
|
||||
permissions: {
|
||||
changeChannelName: false,
|
||||
changeServerName: false,
|
||||
createInvite: true,
|
||||
createChannels: false,
|
||||
deleteChannels: false,
|
||||
deleteInvites: false,
|
||||
addRoles: false,
|
||||
deleteRoles: false,
|
||||
kickPeople: false,
|
||||
banPeople: false
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
let selectedRoleId = $state<string | null>(null);
|
||||
|
||||
let selectedRole = $derived(fakeRoles.find((r) => r.id === selectedRoleId));
|
||||
</script>
|
||||
|
||||
<Sidebar.Provider>
|
||||
<AppSidebar
|
||||
|
|
@ -331,7 +402,205 @@
|
|||
</header>
|
||||
|
||||
{#if currentPageID && currentPage && ServerID.is(currentPageID) && !currentSubPageID}
|
||||
<h1>add invite creation, role creation, moderation, et cetera to this page.</h1>
|
||||
{@const server = currentPage as OverviewServer}
|
||||
|
||||
<Tabs.Root value="roles" class="max-w-[800px] p-2">
|
||||
<Tabs.List>
|
||||
<Tabs.Trigger value="roles">Roles</Tabs.Trigger>
|
||||
<Tabs.Trigger value="general">General</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.Content value="roles" class="space-y-6 p-4">
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-medium">Roles (fake)</h2>
|
||||
<div class="flex gap-2">
|
||||
<Input placeholder="New role name" class="h-8 w-40" bind:value={newRoleName} />
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
if (!newRoleName) return;
|
||||
const newRole: FakeRole = {
|
||||
id: crypto.randomUUID(),
|
||||
name: newRoleName,
|
||||
permissions: {
|
||||
changeChannelName: false,
|
||||
changeServerName: false,
|
||||
createInvite: false,
|
||||
createChannels: false,
|
||||
deleteChannels: false,
|
||||
deleteInvites: false,
|
||||
addRoles: false,
|
||||
deleteRoles: false,
|
||||
kickPeople: false,
|
||||
banPeople: false
|
||||
}
|
||||
};
|
||||
fakeRoles = [...fakeRoles, newRole];
|
||||
selectedRoleId = newRole.id;
|
||||
newRoleName = '';
|
||||
}}
|
||||
>
|
||||
Create Role
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each fakeRoles as role (role.id)}
|
||||
<Button
|
||||
type="button"
|
||||
variant={selectedRoleId === role.id ? 'secondary' : 'ghost'}
|
||||
size="sm"
|
||||
class="gap-1"
|
||||
onclick={() => (selectedRoleId = role.id)}
|
||||
>
|
||||
{role.name}
|
||||
{#if role.id !== 'member'}
|
||||
<button
|
||||
type="button"
|
||||
class="text-destructive hover:text-destructive/80"
|
||||
onclick={(e) => {
|
||||
e.stopPropagation();
|
||||
fakeRoles = fakeRoles.filter((r) => r.id !== role.id);
|
||||
if (selectedRoleId === role.id) selectedRoleId = null;
|
||||
}}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
{/if}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if selectedRole}
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>{selectedRole.name} permissions</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-4">
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
{#each Object.entries(selectedRole.permissions) as [perm, enabled] (perm)}
|
||||
<div class="flex items-center justify-between">
|
||||
<Label>
|
||||
{#if perm === 'changeChannelName'}Change channel name{/if}
|
||||
{#if perm === 'changeServerName'}Change server name{/if}
|
||||
{#if perm === 'createInvite'}Create invite{/if}
|
||||
{#if perm === 'createChannels'}Create channels{/if}
|
||||
{#if perm === 'deleteChannels'}Delete channels{/if}
|
||||
{#if perm === 'deleteInvites'}Delete invites{/if}
|
||||
{#if perm === 'addRoles'}Add roles{/if}
|
||||
{#if perm === 'deleteRoles'}Delete roles{/if}
|
||||
{#if perm === 'kickPeople'}Kick members{/if}
|
||||
{#if perm === 'banPeople'}Ban members{/if}
|
||||
</Label>
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onchange={(e) => {
|
||||
selectedRole.permissions[perm as FakePermission] = e.detail;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Changes are local only (no backend yet)
|
||||
</p>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
{/if}
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="general" class="space-y-6 p-4">
|
||||
<div class="space-y-2">
|
||||
<h2 class="text-lg font-medium">Server Settings</h2>
|
||||
<div class="flex items-center gap-4">
|
||||
<Input
|
||||
placeholder="New server name"
|
||||
class="max-w-xs"
|
||||
value={server?.name}
|
||||
oninput={(e) => {
|
||||
server.name = e.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => toast.info('Server name change would be saved (no backend yet)')}
|
||||
>
|
||||
Save Name
|
||||
</Button>
|
||||
</div>
|
||||
<p class="text-xs text-muted-foreground">Changes are local only (no backend yet)</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<h2 class="text-lg font-medium">Invites</h2>
|
||||
<form
|
||||
method="POST"
|
||||
action="?/createInvite"
|
||||
use:enhance={() => {
|
||||
return async ({ result }) => {
|
||||
if (result.type === 'success') {
|
||||
toast.success('Invite created successfully');
|
||||
inviteCode = location.origin + '/invite/' + result.data!.code;
|
||||
await invalidateAll();
|
||||
await fill_overview_data();
|
||||
} else if (result.type === 'failure') {
|
||||
toast.error('Failed to create invite: ' + result.data?.error);
|
||||
}
|
||||
};
|
||||
}}
|
||||
class="flex gap-2"
|
||||
>
|
||||
<input type="hidden" name="serverId" value={currentPageID} />
|
||||
<div class="flex flex-1 gap-2">
|
||||
<Input placeholder="Create new invite" class="flex-1" readonly value={inviteCode} />
|
||||
<div class="flex items-center gap-2">
|
||||
<Label class="text-sm">Max uses:</Label>
|
||||
<Input type="number" name="maxUses" min="0" class="w-20" placeholder="10" />
|
||||
</div>
|
||||
</div>
|
||||
<Button type="submit">Create Invite</Button>
|
||||
</form>
|
||||
|
||||
<div class="space-y-2 pt-4">
|
||||
<h3 class="font-medium">Active Invites</h3>
|
||||
<div class="space-y-2 rounded border p-3">
|
||||
{#each server.invites || [] as invite (invite.code)}
|
||||
<div class="flex items-center justify-between rounded p-2 hover:bg-muted/50">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="font-mono">{invite.code}</div>
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{invite.uses}/{invite.maxUses || '∞'} uses • Created by {invite.creator} on {formatTimestamp(
|
||||
invite.createdAt
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onclick={async () => {
|
||||
const response = await fetch(`/api/invites/${invite.code}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
if (response.ok) {
|
||||
toast.success('Invite deleted successfully');
|
||||
await invalidateAll();
|
||||
await fill_overview_data();
|
||||
} else {
|
||||
toast.error('Failed to delete invite');
|
||||
}
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
{/if}
|
||||
{#if currentPageID && currentPage && ((ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) || UserID.is(currentPageID) || GroupID.is(currentPageID))}
|
||||
<div class="h-min shrink overflow-y-scroll" bind:this={messagesElement}>
|
||||
|
|
@ -425,7 +694,7 @@
|
|||
user={data.user}
|
||||
data={overview_data}
|
||||
{members}
|
||||
currentEntity={currentPage}
|
||||
currentEntity={currentPage as OverviewGroup | OverviewServer}
|
||||
currentEntityId={currentPageID}
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
|||
193
src/routes/app/actions/friend.ts
Normal file
193
src/routes/app/actions/friend.ts
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import type { Actions } from '../$types';
|
||||
import { fail } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { DirectMessageID, FriendRequestID } from '$lib';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { and } from 'drizzle-orm';
|
||||
import { type User } from '$lib/server/db/schema';
|
||||
import { _sendToSubscribers } from '../../api/updates/+server';
|
||||
|
||||
export default {
|
||||
addFriend: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const username = data.get('username');
|
||||
const userId = data.get('userId');
|
||||
|
||||
if (username && !userId) {
|
||||
if (typeof username !== 'string' || username.length < 3) {
|
||||
return fail(400, { error: 'Invalid username' });
|
||||
}
|
||||
}
|
||||
|
||||
let user: User[];
|
||||
if (username) {
|
||||
user = await db
|
||||
.select()
|
||||
.from(table.user)
|
||||
.where(eq(table.user.username, username.toString()))
|
||||
.limit(1);
|
||||
} else if (userId) {
|
||||
user = await db
|
||||
.select()
|
||||
.from(table.user)
|
||||
.where(eq(table.user.id, userId.toString()))
|
||||
.limit(1);
|
||||
} else {
|
||||
return fail(400, { error: 'Missing username or userId' });
|
||||
}
|
||||
|
||||
if (locals.user?.friends.find((z) => z.id == user[0].id)) {
|
||||
return fail(400, { error: 'Already friends' });
|
||||
}
|
||||
|
||||
if (user?.length == 0) return fail(400, { error: 'User not found' });
|
||||
|
||||
const friendRequest = await db
|
||||
.select()
|
||||
.from(table.friendRequest)
|
||||
.where(
|
||||
and(
|
||||
eq(table.friendRequest.fromUser, user[0].id),
|
||||
eq(table.friendRequest.toUser, locals.user!.id)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
// user has already sent a request to us
|
||||
// means we want to accept it
|
||||
//
|
||||
if (friendRequest?.length != 0) {
|
||||
await db
|
||||
.delete(table.friendRequest)
|
||||
.where(
|
||||
and(
|
||||
eq(table.friendRequest.fromUser, user[0].id),
|
||||
eq(table.friendRequest.toUser, locals.user!.id)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
await db.insert(table.directMessage).values({
|
||||
id: DirectMessageID.newV4(),
|
||||
firstMember: locals.user!.id,
|
||||
secondMember: user[0].id,
|
||||
messages: []
|
||||
});
|
||||
|
||||
// add other guy to us
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ friends: locals.user?.friends.map((z) => z.id).concat(user[0].id) })
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ friends: (user[0].friends as string[]).concat(locals.user!.id) })
|
||||
.where(eq(table.user.id, user[0].id));
|
||||
});
|
||||
|
||||
_sendToSubscribers(locals.user!.id, { type: 'friends', status: 'accepted' });
|
||||
_sendToSubscribers(user[0].id, { type: 'friends', status: 'accepted' });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// a request from us has already been sent to user
|
||||
if (
|
||||
locals.user?.friendRequests.find(
|
||||
(z) => z.toUser == user[0].id && z.fromUser == locals.user!.id
|
||||
)
|
||||
)
|
||||
return fail(400, { error: 'Already sent request' });
|
||||
|
||||
await db.insert(table.friendRequest).values({
|
||||
id: FriendRequestID.newV4(),
|
||||
fromUser: locals.user!.id,
|
||||
toUser: user[0].id,
|
||||
toUsername: user[0].username,
|
||||
fromUsername: locals.user!.username
|
||||
});
|
||||
|
||||
_sendToSubscribers(locals.user!.id, { type: 'friends', status: 'sent-request' });
|
||||
_sendToSubscribers(user[0].id, { type: 'friends', status: 'new-request' });
|
||||
return { success: true };
|
||||
},
|
||||
removeFriend: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const userId = data.get('userId');
|
||||
|
||||
if (typeof userId !== 'string') {
|
||||
return fail(400, { error: 'Invalid user ID' });
|
||||
}
|
||||
|
||||
// verify we are actually friends
|
||||
if (!locals.user?.friends.find((z) => z.id === userId)) {
|
||||
return fail(400, { error: 'Not in friends list' });
|
||||
}
|
||||
|
||||
// fetch the target user
|
||||
const user = await db.select().from(table.user).where(eq(table.user.id, userId)).limit(1);
|
||||
|
||||
if (!user?.length) {
|
||||
return fail(404, { error: 'User not found' });
|
||||
}
|
||||
|
||||
const target = user[0];
|
||||
|
||||
// remove each other from friends lists
|
||||
await db.transaction(async (tx) => {
|
||||
// update current user – filter out removed friend
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
friends: locals.user!.friends.map((z) => z.id).filter((id) => id !== userId)
|
||||
})
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
// update target user – filter out us
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
friends: (target.friends as string[]).filter((id) => id !== locals.user!.id)
|
||||
})
|
||||
.where(eq(table.user.id, target.id));
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
cancelFriendRequest: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const requestId = data.get('requestId');
|
||||
|
||||
if (typeof requestId !== 'string') {
|
||||
return fail(400, { error: 'Invalid request ID' });
|
||||
}
|
||||
|
||||
// fetch the friend request
|
||||
const friendRequest = await db
|
||||
.select()
|
||||
.from(table.friendRequest)
|
||||
.where(eq(table.friendRequest.id, requestId))
|
||||
.limit(1);
|
||||
|
||||
if (!friendRequest?.length) {
|
||||
return fail(404, { error: 'Friend request not found' });
|
||||
}
|
||||
|
||||
const fr = friendRequest[0];
|
||||
|
||||
// only allow cancelling if it's related to current user
|
||||
if (fr.fromUser !== locals.user!.id && fr.toUser !== locals.user!.id) {
|
||||
return fail(403, { error: 'Not allowed' });
|
||||
}
|
||||
|
||||
// delete the request
|
||||
await db.delete(table.friendRequest).where(eq(table.friendRequest.id, requestId)).limit(1);
|
||||
|
||||
_sendToSubscribers(fr.fromUser, { type: 'friends', status: 'request-cancelled' });
|
||||
_sendToSubscribers(fr.toUser, { type: 'friends', status: 'request-cancelled' });
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
} satisfies Actions;
|
||||
265
src/routes/app/actions/group.ts
Normal file
265
src/routes/app/actions/group.ts
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
import type { Actions } from '../$types';
|
||||
import { fail } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { GroupID } from '$lib';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { _sendToSubscribers, _sendToUser } from '../../api/updates/+server';
|
||||
export default {
|
||||
addMembers: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
const memberIds = data.getAll('memberIds').map(String);
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
if (!memberIds.length) {
|
||||
return fail(400, { error: 'No members selected' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
const g = group[0];
|
||||
|
||||
if (!(g.members as string[]).includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You do not have permission to act on this group.' });
|
||||
}
|
||||
|
||||
const isOwner = g.owner === locals.user!.id;
|
||||
if (!isOwner && !g.addMembers) {
|
||||
return fail(403, { error: 'No permission to add members' });
|
||||
}
|
||||
|
||||
for (const id of memberIds) {
|
||||
if (!locals.user!.friends.find((f) => f.id === id)) {
|
||||
return fail(403, { error: 'Can only add friends' });
|
||||
}
|
||||
}
|
||||
|
||||
const newMembers = [...new Set([...(g.members as string[]), ...memberIds])];
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx.update(table.group).set({ members: newMembers }).where(eq(table.group.id, groupId));
|
||||
|
||||
for (const id of memberIds) {
|
||||
const user = await tx.select().from(table.user).where(eq(table.user.id, id)).limit(1);
|
||||
if (!user.length) continue;
|
||||
if (newMembers.includes(id)) {
|
||||
_sendToUser(id, { type: 'group', status: 'added-to-group' });
|
||||
} else {
|
||||
_sendToUser(id, { type: 'group', status: 'member-added-to-group' });
|
||||
}
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ groups: (user[0].groups as string[]).concat(groupId) })
|
||||
.where(eq(table.user.id, id));
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
removeMembers: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
const memberIds = data.getAll('memberIds').map(String);
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
const g = group[0];
|
||||
|
||||
if (!(g.members as string[]).includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You do not have permission to act on this group.' });
|
||||
}
|
||||
const isOwner = g.owner === locals.user!.id;
|
||||
if (!isOwner && !g.removeMembers) {
|
||||
return fail(403, { error: 'No permission to remove members' });
|
||||
}
|
||||
|
||||
if (memberIds.includes(g.owner)) {
|
||||
return fail(400, { error: 'Cannot remove group owner' });
|
||||
}
|
||||
|
||||
const remaining = (g.members as string[]).filter((id) => !memberIds.includes(id));
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx.update(table.group).set({ members: remaining }).where(eq(table.group.id, groupId));
|
||||
|
||||
for (const id of remaining) {
|
||||
_sendToUser(id, { type: 'group', status: 'someone-was-removed' });
|
||||
}
|
||||
|
||||
for (const id of memberIds) {
|
||||
_sendToUser(id, { type: 'group', status: 'removed-from-group' });
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
groups: locals.user!.groups.map((z) => z.id).filter((g) => g !== groupId)
|
||||
})
|
||||
.where(eq(table.user.id, id));
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
changeTitle: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
const title = data.get('title');
|
||||
|
||||
if (typeof groupId !== 'string' || typeof title !== 'string' || title.length < 1) {
|
||||
return fail(400, { error: 'Invalid input' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
const g = group[0];
|
||||
if (!(g.members as string[]).includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You do not have permission to act on this group.' });
|
||||
}
|
||||
const isOwner = g.owner === locals.user!.id;
|
||||
|
||||
if (!isOwner && !g.changeTitle) {
|
||||
return fail(403, { error: 'No permission to change title' });
|
||||
}
|
||||
|
||||
await db.update(table.group).set({ name: title }).where(eq(table.group.id, groupId));
|
||||
//@TODO if a user isn't in the group screen this doesnt get propogated
|
||||
_sendToSubscribers(groupId, { type: 'group', status: 'name-changed' });
|
||||
return { success: true };
|
||||
},
|
||||
configureGroup: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
if (!group.length) return fail(404, { error: 'Group not found' });
|
||||
|
||||
if (group[0].owner !== locals.user!.id) {
|
||||
return fail(403, { error: 'Only owner can configure group' });
|
||||
}
|
||||
await db
|
||||
.update(table.group)
|
||||
.set({
|
||||
addMembers: data.has('addMembers') ? 1 : 0,
|
||||
removeMembers: data.has('removeMembers') ? 1 : 0,
|
||||
changeTitle: data.has('changeTitle') ? 1 : 0
|
||||
})
|
||||
.where(eq(table.group.id, groupId));
|
||||
_sendToSubscribers(group[0].id, { type: 'group', status: 'permission-change' });
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
deleteGroup: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const groupId = data.get('groupId');
|
||||
|
||||
if (typeof groupId !== 'string') {
|
||||
return fail(400, { error: 'Invalid group ID' });
|
||||
}
|
||||
|
||||
const group = await db.select().from(table.group).where(eq(table.group.id, groupId)).limit(1);
|
||||
|
||||
if (!group.length) {
|
||||
return fail(404, { error: 'Group not found' });
|
||||
}
|
||||
|
||||
const g = group[0];
|
||||
|
||||
if (g.owner !== locals.user!.id) {
|
||||
return fail(403, { error: 'Not allowed' });
|
||||
}
|
||||
|
||||
if ((g.members as string[]).length !== 1 || (g.members as string[])[0] !== locals.user!.id) {
|
||||
return fail(400, { error: 'Group still has members' });
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({
|
||||
groups: locals.user!.groups.map((z) => z.id).filter((id) => id !== groupId)
|
||||
})
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
await tx.delete(table.group).where(eq(table.group.id, groupId));
|
||||
});
|
||||
_sendToUser(locals.user!.id, { type: 'group', status: 'removed-from-group' });
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
createGroup: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const members = data.getAll('member').map((z) => z.toString());
|
||||
|
||||
if (!members.length) {
|
||||
return fail(400, { error: 'No members selected' });
|
||||
}
|
||||
if (members.includes(locals.user!.id)) {
|
||||
return fail(403, { error: 'You cannot add yourself to a group.' });
|
||||
}
|
||||
|
||||
if (members.length > 9) {
|
||||
return fail(400, { error: 'Too many members' });
|
||||
}
|
||||
if (members.length < 2) {
|
||||
return fail(400, { error: 'Not enough members' });
|
||||
}
|
||||
for (const member of members) {
|
||||
if (!locals.user!.friends.find((z) => z.id == member)) {
|
||||
return fail(403, { error: 'A member is not your friend.' });
|
||||
}
|
||||
}
|
||||
const nameArray = [];
|
||||
|
||||
for await (const member of members) {
|
||||
const dbUser = await db.select().from(table.user).where(eq(table.user.id, member)).limit(1);
|
||||
nameArray.push(dbUser[0].username);
|
||||
|
||||
if (!dbUser.length) {
|
||||
return fail(400, { error: 'Invalid member' });
|
||||
}
|
||||
}
|
||||
|
||||
members.push(locals.user!.id);
|
||||
nameArray.push(locals.user!.username);
|
||||
|
||||
const group = await db
|
||||
.insert(table.group)
|
||||
.values({
|
||||
id: GroupID.newV4(),
|
||||
name: nameArray.join(', '),
|
||||
owner: locals.user!.id,
|
||||
members: members,
|
||||
messages: []
|
||||
})
|
||||
.returning();
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
for await (const member of members) {
|
||||
_sendToUser(member, { type: 'group', status: 'added-to-group' });
|
||||
const user = await tx.select().from(table.user).where(eq(table.user.id, member)).limit(1);
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ groups: (user[0].groups as string[]).concat(group[0].id) })
|
||||
.where(eq(table.user.id, member));
|
||||
}
|
||||
});
|
||||
|
||||
return { success: true, group };
|
||||
}
|
||||
} satisfies Actions;
|
||||
156
src/routes/app/actions/server.ts
Normal file
156
src/routes/app/actions/server.ts
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import { fail } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import * as table from '$lib/server/db/schema';
|
||||
import { InviteID, ServerID } from '$lib';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { _sendToUser } from '../../api/updates/+server';
|
||||
import type { Actions } from '../$types';
|
||||
|
||||
export default {
|
||||
joinServer: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const inviteOrUrl = data.get('invite');
|
||||
|
||||
if (typeof inviteOrUrl !== 'string') {
|
||||
return fail(400, { error: 'Invalid invite' });
|
||||
}
|
||||
|
||||
let isUrl: URL | undefined;
|
||||
|
||||
try {
|
||||
isUrl = new URL(inviteOrUrl); // http(s|)://[^/]*/invite/(.*)
|
||||
} catch {
|
||||
// IsURL stays undefined if url is not able to be parsed.
|
||||
}
|
||||
|
||||
const code = isUrl ? decodeURIComponent(isUrl.pathname.split('/').at(-1)!) : inviteOrUrl;
|
||||
|
||||
const invRaw = await db.select().from(table.invite).where(eq(table.invite.code, code)).limit(1);
|
||||
|
||||
const invite = invRaw[0];
|
||||
|
||||
if (!invite) return fail(400, { error: 'Invalid invite' });
|
||||
|
||||
const server = await db
|
||||
.select()
|
||||
.from(table.server)
|
||||
.where(eq(table.server.id, invite.serverId))
|
||||
.limit(1);
|
||||
|
||||
if (server?.length == 0) return fail(400, { error: 'Invalid server' });
|
||||
|
||||
if (locals.user!.servers.some((z) => z.id == server[0].id))
|
||||
return fail(400, { error: 'Already in server' });
|
||||
|
||||
// @TODO check if maybe i'm trippin here and this is not how you do it
|
||||
if (invite.maxUses && (invite.uses as string[]).length >= invite.maxUses) {
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(table.server)
|
||||
.set({
|
||||
invites: (server[0].invites as string[]).filter((id) => id !== invite.id)
|
||||
})
|
||||
.where(eq(table.server.id, server[0].id));
|
||||
|
||||
await tx.delete(table.invite).where(eq(table.invite.id, invite.id));
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ servers: locals.user!.servers.map((z) => z.id).concat([server[0].id]) })
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
await tx
|
||||
.update(table.server)
|
||||
.set({ members: (server[0].members as string[]).concat([locals.user!.id]) })
|
||||
.where(eq(table.server.id, server[0].id));
|
||||
});
|
||||
} else {
|
||||
// Normal case - just update the invite uses
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(table.invite)
|
||||
.set({ uses: (invite.uses as string[]).concat([locals.user!.id]) })
|
||||
.where(eq(table.invite.id, invite.id));
|
||||
|
||||
await tx
|
||||
.update(table.user)
|
||||
.set({ servers: locals.user!.servers.map((z) => z.id).concat([server[0].id]) })
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
await tx
|
||||
.update(table.server)
|
||||
.set({ members: (server[0].members as string[]).concat([locals.user!.id]) })
|
||||
.where(eq(table.server.id, server[0].id));
|
||||
});
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
createInvite: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const serverId = data.get('serverId'); // hidden in frontend
|
||||
const maxUses = data.has('maxUses') ? parseInt(data.get('maxUses')!.toString()) : 10;
|
||||
|
||||
if (typeof serverId !== 'string') {
|
||||
return fail(400, { error: 'Server ID incorrect' });
|
||||
}
|
||||
|
||||
const server = await db.select().from(table.server).where(eq(table.server.id, serverId));
|
||||
if (!server || server.length == 0) {
|
||||
return fail(400, { error: 'Server ID invalid' });
|
||||
}
|
||||
|
||||
// @TODO check permissions here (if owner ignore, then check roles,if any role has createInvite then let create invite)
|
||||
const inviteId = InviteID.newV4();
|
||||
const code = new Array(5)
|
||||
.fill('')
|
||||
.map(() =>
|
||||
String.fromCodePoint(
|
||||
Math.random() < 0.5
|
||||
? (0x1f600 + Math.random() * 0x80) | 0
|
||||
: (0x1f300 + Math.random() * 0x500) | 0
|
||||
)
|
||||
)
|
||||
.join('');
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx.insert(table.invite).values({
|
||||
id: inviteId,
|
||||
serverId: serverId,
|
||||
code,
|
||||
creatorId: locals.user!.id,
|
||||
createdAt: new Date(),
|
||||
maxUses: maxUses <= 0 ? undefined : maxUses // if maxUses is not undefined, there are infnite uses
|
||||
});
|
||||
|
||||
await tx
|
||||
.update(table.server)
|
||||
.set({
|
||||
invites: (server[0].invites as string[]).concat([inviteId])
|
||||
})
|
||||
.where(eq(table.server.id, serverId));
|
||||
});
|
||||
return { success: true, code };
|
||||
},
|
||||
createServer: async ({ request, locals }) => {
|
||||
const data = await request.formData();
|
||||
const name = data.get('name');
|
||||
|
||||
if (typeof name !== 'string' || name.length < 3) {
|
||||
return fail(400, { error: 'Server name too short' });
|
||||
}
|
||||
const serverId = ServerID.newV4();
|
||||
|
||||
await db
|
||||
.insert(table.server)
|
||||
.values({ id: serverId, name, owner: locals.user!.id, members: [locals.user!.id] });
|
||||
|
||||
await db
|
||||
.update(table.user)
|
||||
.set({ servers: locals.user!.servers.map((z) => z.id).concat([serverId]) })
|
||||
.where(eq(table.user.id, locals.user!.id));
|
||||
|
||||
_sendToUser(locals.user!.id, { type: 'server', status: 'server-created' });
|
||||
return { success: true };
|
||||
}
|
||||
} satisfies Actions;
|
||||
Loading…
Add table
Add a link
Reference in a new issue