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,
|
"when": 1767860870240,
|
||||||
"tag": "0001_tiresome_stature",
|
"tag": "0001_tiresome_stature",
|
||||||
"breakpoints": true
|
"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 GroupID = definePrefix('group');
|
||||||
export const ServerID = definePrefix('srv');
|
export const ServerID = definePrefix('srv');
|
||||||
export const ChannelID = definePrefix('ch');
|
export const ChannelID = definePrefix('ch');
|
||||||
|
export const InviteID = definePrefix('inv');
|
||||||
|
|
||||||
export const FriendRequestID = definePrefix('frq');
|
export const FriendRequestID = definePrefix('frq');
|
||||||
export const DirectMessageID = definePrefix('dmid');
|
export const DirectMessageID = definePrefix('dmid');
|
||||||
|
|
@ -14,6 +15,7 @@ export type GroupId = Puuid<'group'>;
|
||||||
export type ServerId = Puuid<'srv'>;
|
export type ServerId = Puuid<'srv'>;
|
||||||
export type DirectMessageId = Puuid<'dmid'>;
|
export type DirectMessageId = Puuid<'dmid'>;
|
||||||
export type ChannelId = Puuid<'ch'>;
|
export type ChannelId = Puuid<'ch'>;
|
||||||
|
export type InviteId = Puuid<'inv'>;
|
||||||
|
|
||||||
export interface Status {
|
export interface Status {
|
||||||
statusMessage: string;
|
statusMessage: string;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { boolean } from 'drizzle-orm/singlestore-core';
|
|
||||||
import { Status } from '../../index.ts';
|
import { Status } from '../../index.ts';
|
||||||
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
|
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
|
||||||
|
|
||||||
|
|
@ -9,7 +8,6 @@ export const user = sqliteTable('user', {
|
||||||
passwordHash: text('password_hash').notNull(),
|
passwordHash: text('password_hash').notNull(),
|
||||||
statusOverwrite: integer('status_overwrite').default(Status.ONLINE).notNull(),
|
statusOverwrite: integer('status_overwrite').default(Status.ONLINE).notNull(),
|
||||||
friends: text('friends', { mode: 'json' }).default([]).notNull(),
|
friends: text('friends', { mode: 'json' }).default([]).notNull(),
|
||||||
|
|
||||||
servers: text('servers', { mode: 'json' }).default([]).notNull(), // string[] of ServerIDs
|
servers: text('servers', { mode: 'json' }).default([]).notNull(), // string[] of ServerIDs
|
||||||
groups: text('groups', { mode: 'json' }).default([]).notNull() // string[] of GroupIDs
|
groups: text('groups', { mode: 'json' }).default([]).notNull() // string[] of GroupIDs
|
||||||
});
|
});
|
||||||
|
|
@ -29,7 +27,8 @@ export const server = sqliteTable('server', {
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => user.id),
|
.references(() => user.id),
|
||||||
members: text('members', { mode: 'json' }).default([]).notNull(),
|
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', {
|
export const group = sqliteTable('group', {
|
||||||
|
|
@ -86,7 +85,13 @@ export const invite = sqliteTable('invite', {
|
||||||
serverId: text('server_id')
|
serverId: text('server_id')
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => server.id),
|
.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;
|
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`);
|
controller.enqueue(`data: ${JSON.stringify({ type: 'connected', sessionId })}\n\n`);
|
||||||
|
|
||||||
if (overwrite === Status.DND) {
|
if (overwrite === Status.DND || overwrite == Status.ONLINE) {
|
||||||
kvStore.set(`user-${userId}-state`, Status.DND);
|
kvStore.set(`user-${userId}-state`, overwrite);
|
||||||
_sendToSubscribers(userId, {
|
_sendToSubscribers(userId, {
|
||||||
type: 'status',
|
type: 'status',
|
||||||
id: userId,
|
id: userId,
|
||||||
status: Status.DND,
|
status: overwrite,
|
||||||
statusMessage:
|
statusMessage:
|
||||||
kvStore.get('user-' + userId + '-state') != Status.OFFLINE
|
kvStore.get('user-' + userId + '-state') != Status.OFFLINE
|
||||||
? kvStore.get('user-' + userId + '-message')
|
? kvStore.get('user-' + userId + '-message')
|
||||||
|
|
|
||||||
|
|
@ -3,200 +3,20 @@ import { getRequestEvent } from '$app/server';
|
||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
import { db, kvStore } from '$lib/server/db';
|
import { db, kvStore } from '$lib/server/db';
|
||||||
import * as table from '$lib/server/db/schema';
|
import * as table from '$lib/server/db/schema';
|
||||||
import { DirectMessageID, FriendRequestID, GroupID, ServerID } from '$lib';
|
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import { and } from 'drizzle-orm';
|
import { _sendToSubscribers } from '../api/updates/+server';
|
||||||
import { type User } from '$lib/server/db/schema';
|
|
||||||
import { _sendToSubscribers, _sendToUser } from '../api/updates/+server';
|
|
||||||
import { validateUsername } from '$lib/server/auth';
|
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 () => {
|
export const load: PageServerLoad = async () => {
|
||||||
const user = requireLogin();
|
const user = requireLogin();
|
||||||
return { user };
|
return { user };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actions = {
|
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 }) => {
|
updateProfile: async ({ request, locals }) => {
|
||||||
const user = locals.user;
|
const user = locals.user;
|
||||||
if (!user) return fail(401);
|
if (!user) return fail(401);
|
||||||
|
|
@ -232,322 +52,9 @@ export const actions = {
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
createGroup: async ({ request, locals }) => {
|
...FriendActions,
|
||||||
const data = await request.formData();
|
...GroupActions,
|
||||||
const members = data.getAll('member').map((z) => z.toString());
|
...ServerActions
|
||||||
|
|
||||||
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 };
|
|
||||||
}
|
|
||||||
} satisfies Actions;
|
} satisfies Actions;
|
||||||
|
|
||||||
function requireLogin() {
|
function requireLogin() {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import {
|
||||||
Status,
|
|
||||||
type OverviewData,
|
type OverviewData,
|
||||||
GroupID,
|
GroupID,
|
||||||
UserID,
|
UserID,
|
||||||
|
|
@ -20,19 +19,22 @@
|
||||||
import type { PageServerData } from './$types';
|
import type { PageServerData } from './$types';
|
||||||
import AppSidebar from '$lib/components/app-sidebar.svelte';
|
import AppSidebar from '$lib/components/app-sidebar.svelte';
|
||||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
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 { onMount } from 'svelte';
|
||||||
import type { ActionData } from './$types';
|
import type { ActionData } from './$types';
|
||||||
import { formatTimestamp } from '$lib/utils';
|
import { formatTimestamp } from '$lib/utils';
|
||||||
import Input from '$lib/components/ui/input/input.svelte';
|
import Input from '$lib/components/ui/input/input.svelte';
|
||||||
import { Button } from '$lib/components/ui/button';
|
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 SendHorizontal from '@lucide/svelte/icons/send-horizontal';
|
||||||
import PersonStanding from '@lucide/svelte/icons/person-standing';
|
import PersonStanding from '@lucide/svelte/icons/person-standing';
|
||||||
import MemberSidebar from '$lib/components/member-sidebar.svelte';
|
import MemberSidebar from '$lib/components/member-sidebar.svelte';
|
||||||
import { invalidateAll } from '$app/navigation';
|
import { invalidateAll } from '$app/navigation';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
let errorOpen = $state(true);
|
|
||||||
|
|
||||||
let { form, data }: { form: ActionData; data: PageServerData } = $props();
|
let { form, data }: { form: ActionData; data: PageServerData } = $props();
|
||||||
let currentPageID: (UserId | GroupId | ServerId) | null = $state(null);
|
let currentPageID: (UserId | GroupId | ServerId) | null = $state(null);
|
||||||
|
|
@ -40,6 +42,8 @@
|
||||||
let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state();
|
let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state();
|
||||||
let currentSubPage: Channel | null = $state(null);
|
let currentSubPage: Channel | null = $state(null);
|
||||||
|
|
||||||
|
let inviteCode = $state();
|
||||||
|
|
||||||
let sse: EventSource | undefined;
|
let sse: EventSource | undefined;
|
||||||
let messagesElement: HTMLDivElement | undefined = $state();
|
let messagesElement: HTMLDivElement | undefined = $state();
|
||||||
let isMembersTabOpen = $state(true);
|
let isMembersTabOpen = $state(true);
|
||||||
|
|
@ -146,7 +150,7 @@
|
||||||
|
|
||||||
async function getMessages() {
|
async function getMessages() {
|
||||||
let path = '';
|
let path = '';
|
||||||
let subscribe = '';
|
let subscribe: string | null | undefined = '';
|
||||||
|
|
||||||
if (ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) {
|
if (ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) {
|
||||||
path = '/api/messages/' + currentPageID + '/' + currentSubPageID;
|
path = '/api/messages/' + currentPageID + '/' + currentSubPageID;
|
||||||
|
|
@ -156,6 +160,7 @@
|
||||||
path = '/api/messages/' + subscribe;
|
path = '/api/messages/' + subscribe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!subscribe) return;
|
||||||
const req = await fetch(path);
|
const req = await fetch(path);
|
||||||
const data = await req.json();
|
const data = await req.json();
|
||||||
|
|
||||||
|
|
@ -184,6 +189,14 @@
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
async function run() {
|
async function run() {
|
||||||
if (form) {
|
if (form) {
|
||||||
|
if (form.success) {
|
||||||
|
toast.success('Action succeeded.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form.error) {
|
||||||
|
toast.error('Action had a error: ' + form.error);
|
||||||
|
}
|
||||||
|
|
||||||
await invalidateAll();
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
await fill_overview_data();
|
await fill_overview_data();
|
||||||
|
|
@ -261,23 +274,81 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if form}
|
type FakePermission =
|
||||||
<AlertDialog.Root bind:open={errorOpen}>
|
| 'changeChannelName'
|
||||||
<AlertDialog.Content>
|
| 'changeServerName'
|
||||||
<AlertDialog.Header>
|
| 'createInvite'
|
||||||
<AlertDialog.Title>{form?.error ? 'Ran into an error.' : 'Success!'}</AlertDialog.Title>
|
| 'createChannels'
|
||||||
<AlertDialog.Description>
|
| 'deleteChannels'
|
||||||
{form?.error || 'Action completed succesfully.'}
|
| 'deleteInvites'
|
||||||
</AlertDialog.Description>
|
| 'addRoles'
|
||||||
</AlertDialog.Header>
|
| 'deleteRoles'
|
||||||
<AlertDialog.Footer>
|
| 'kickPeople'
|
||||||
<AlertDialog.Cancel>Close</AlertDialog.Cancel>
|
| 'banPeople';
|
||||||
</AlertDialog.Footer>
|
|
||||||
</AlertDialog.Content>
|
type FakeRole = {
|
||||||
</AlertDialog.Root>
|
id: string;
|
||||||
{/if}
|
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>
|
<Sidebar.Provider>
|
||||||
<AppSidebar
|
<AppSidebar
|
||||||
|
|
@ -331,7 +402,205 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{#if currentPageID && currentPage && ServerID.is(currentPageID) && !currentSubPageID}
|
{#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}
|
||||||
{#if currentPageID && currentPage && ((ServerID.is(currentPageID) && ChannelID.is(currentSubPageID)) || UserID.is(currentPageID) || GroupID.is(currentPageID))}
|
{#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}>
|
<div class="h-min shrink overflow-y-scroll" bind:this={messagesElement}>
|
||||||
|
|
@ -425,7 +694,7 @@
|
||||||
user={data.user}
|
user={data.user}
|
||||||
data={overview_data}
|
data={overview_data}
|
||||||
{members}
|
{members}
|
||||||
currentEntity={currentPage}
|
currentEntity={currentPage as OverviewGroup | OverviewServer}
|
||||||
currentEntityId={currentPageID}
|
currentEntityId={currentPageID}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/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