From 0a8a9236b1053dbbc70c024ad98e76f3c08f4207 Mon Sep 17 00:00:00 2001 From: fucksophie Date: Sat, 17 Jan 2026 20:58:32 +0200 Subject: [PATCH] start roles --- drizzle/0005_round_gwen_stacy.sql | 15 + drizzle/0006_condemned_lockjaw.sql | 1 + drizzle/meta/0005_snapshot.json | 712 ++++++++++++++++++++++ drizzle/meta/0006_snapshot.json | 718 +++++++++++++++++++++++ drizzle/meta/_journal.json | 14 + src/lib/components/main-sidebar.svelte | 38 +- src/lib/components/member-sidebar.svelte | 20 +- src/lib/index.ts | 57 +- src/lib/server/auth.ts | 42 +- src/lib/server/db/schema.ts | 19 +- src/lib/state.svelte.ts | 6 +- src/routes/app/ServerDashboard.svelte | 318 +++++----- src/routes/app/actions/server.ts | 40 +- 13 files changed, 1809 insertions(+), 191 deletions(-) create mode 100644 drizzle/0005_round_gwen_stacy.sql create mode 100644 drizzle/0006_condemned_lockjaw.sql create mode 100644 drizzle/meta/0005_snapshot.json create mode 100644 drizzle/meta/0006_snapshot.json diff --git a/drizzle/0005_round_gwen_stacy.sql b/drizzle/0005_round_gwen_stacy.sql new file mode 100644 index 0000000..722b883 --- /dev/null +++ b/drizzle/0005_round_gwen_stacy.sql @@ -0,0 +1,15 @@ +ALTER TABLE `invite` RENAME COLUMN "expires_at" TO "created_at";--> statement-breakpoint +CREATE TABLE `role` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `server_id` text NOT NULL, + `users` text DEFAULT '[]' NOT NULL, + `permissions` text DEFAULT '{}' NOT NULL, + `created_at` integer NOT NULL, + `created_by` text NOT NULL, + FOREIGN KEY (`server_id`) REFERENCES `server`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`created_by`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +ALTER TABLE `channel` ADD `role_overwrites` text DEFAULT '{}' NOT NULL;--> statement-breakpoint +ALTER TABLE `server` ADD `roles` text DEFAULT '[]' NOT NULL; \ No newline at end of file diff --git a/drizzle/0006_condemned_lockjaw.sql b/drizzle/0006_condemned_lockjaw.sql new file mode 100644 index 0000000..89113f0 --- /dev/null +++ b/drizzle/0006_condemned_lockjaw.sql @@ -0,0 +1 @@ +ALTER TABLE `role` ADD `can_be_deleted` integer DEFAULT 1 NOT NULL; \ No newline at end of file diff --git a/drizzle/meta/0005_snapshot.json b/drizzle/meta/0005_snapshot.json new file mode 100644 index 0000000..64b3f88 --- /dev/null +++ b/drizzle/meta/0005_snapshot.json @@ -0,0 +1,712 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "a5e4235b-e796-4438-b08b-35776d7ec93b", + "prevId": "7f91f3dc-746e-40eb-b1d5-8ca20e04dd5f", + "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 + }, + "role_overwrites": { + "name": "role_overwrites", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "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 + }, + "created_at": { + "name": "created_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": {} + }, + "role": { + "name": "role", + "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 + }, + "users": { + "name": "users", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "role_server_id_server_id_fk": { + "name": "role_server_id_server_id_fk", + "tableFrom": "role", + "tableTo": "server", + "columnsFrom": [ + "server_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "role_created_by_user_id_fk": { + "name": "role_created_by_user_id_fk", + "tableFrom": "role", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "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": "'[]'" + }, + "roles": { + "name": "roles", + "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": { + "\"invite\".\"expires_at\"": "\"invite\".\"created_at\"" + } + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0006_snapshot.json b/drizzle/meta/0006_snapshot.json new file mode 100644 index 0000000..ac46d7a --- /dev/null +++ b/drizzle/meta/0006_snapshot.json @@ -0,0 +1,718 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "85b0b83b-e18d-4ed7-82a7-855342d5eddf", + "prevId": "a5e4235b-e796-4438-b08b-35776d7ec93b", + "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 + }, + "role_overwrites": { + "name": "role_overwrites", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "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 + }, + "created_at": { + "name": "created_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": {} + }, + "role": { + "name": "role", + "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 + }, + "users": { + "name": "users", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "can_be_deleted": { + "name": "can_be_deleted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "role_server_id_server_id_fk": { + "name": "role_server_id_server_id_fk", + "tableFrom": "role", + "tableTo": "server", + "columnsFrom": [ + "server_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "role_created_by_user_id_fk": { + "name": "role_created_by_user_id_fk", + "tableFrom": "role", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "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": "'[]'" + }, + "roles": { + "name": "roles", + "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": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index b8517fe..3529284 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -36,6 +36,20 @@ "when": 1768584253382, "tag": "0004_minor_wildside", "breakpoints": true + }, + { + "idx": 5, + "version": "6", + "when": 1768661620588, + "tag": "0005_round_gwen_stacy", + "breakpoints": true + }, + { + "idx": 6, + "version": "6", + "when": 1768662920723, + "tag": "0006_condemned_lockjaw", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/lib/components/main-sidebar.svelte b/src/lib/components/main-sidebar.svelte index 88b444f..c90a6ba 100644 --- a/src/lib/components/main-sidebar.svelte +++ b/src/lib/components/main-sidebar.svelte @@ -87,7 +87,7 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not send friend request: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' ? result.error.message : result.data?.error) ); } @@ -135,7 +135,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not cancel friend request: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -178,7 +180,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not accept friend request: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -203,7 +207,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not cancel friend request: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -243,7 +249,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not remove friend: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -286,7 +294,7 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not create group: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' ? result.error.message : result.data?.error) ); } @@ -336,7 +344,7 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not join server: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' ? result.error.message : result.data?.error) ); } @@ -380,7 +388,7 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not create server: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' ? result.error.message : result.data?.error) ); } @@ -537,7 +545,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not create channel: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -589,7 +599,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not leave server: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -653,7 +665,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not delete channel: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -710,7 +724,7 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not edit profile: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' ? result.error.message : result.data?.error) ); } diff --git a/src/lib/components/member-sidebar.svelte b/src/lib/components/member-sidebar.svelte index a0ffcf8..664641b 100644 --- a/src/lib/components/member-sidebar.svelte +++ b/src/lib/components/member-sidebar.svelte @@ -82,7 +82,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not configure group successfully: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -143,7 +145,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not delete group successfully: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -182,7 +186,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not add users successfully: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -222,7 +228,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not change title successfully: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } @@ -256,7 +264,9 @@ if (result.type == 'error' || result.type == 'failure') { toast.error( 'Could not remove users successfully: ' + - (result.type === 'error' ? result.error : result.data?.error) + (result.type === 'error' + ? result.error.message + : result.data?.error) ); } diff --git a/src/lib/index.ts b/src/lib/index.ts index 5ef1141..6c68886 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -6,6 +6,7 @@ export const GroupID = definePrefix('group'); export const ServerID = definePrefix('srv'); export const ChannelID = definePrefix('ch'); export const InviteID = definePrefix('inv'); +export const RoleID = definePrefix('role'); export const FriendRequestID = definePrefix('frq'); export const DirectMessageID = definePrefix('dmid'); @@ -16,11 +17,46 @@ export type ServerId = Puuid<'srv'>; export type DirectMessageId = Puuid<'dmid'>; export type ChannelId = Puuid<'ch'>; export type InviteId = Puuid<'inv'>; +export type RoleId = Puuid<'role'>; export interface Status { statusMessage: string; status: 1 | 2 | 3; } + +export type Permission = + | 'changeChannelName' + | 'changeServerName' + | 'createInvite' + | 'createChannels' + | 'deleteChannels' + | 'deleteInvites' + | 'addRoles' + | 'deleteRoles' + | 'kickPeople' + | 'banPeople' + | 'sendMessage' + | 'viewChannels' + | 'viewInvites' + | 'deleteServer'; + +export const PERMISSION_LABELS: Record = { + changeChannelName: 'Change channel name', + changeServerName: 'Change server name', + createInvite: 'Create invite', + createChannels: 'Create channels', + deleteChannels: 'Delete channels', + deleteInvites: 'Delete invites', + addRoles: 'Add roles', + deleteRoles: 'Delete roles', + kickPeople: 'Kick members', + banPeople: 'Ban members', + sendMessage: 'Send messages', + viewChannels: 'View channels', + viewInvites: 'View invites', + deleteServer: 'Delete server' +}; + export type AppPSD = { user: { servers: { @@ -29,6 +65,7 @@ export type AppPSD = { ownerId: string; channels: unknown; invites: unknown; + roles: unknown; }[]; friends: { dmId: string | undefined; @@ -84,7 +121,7 @@ export interface Channel { } export interface Invite { - code: 'πŸŽˆπŸπŸŽπŸ˜ΊπŸ™•'; + code: string; createdAt: Date; createdBy: { username: string; @@ -95,6 +132,23 @@ export interface Invite { uses: number; } + +export interface Role { + name: string; + id: string; + canBeDeleted: number; + permissions: Record; + createdAt: string; + users: { + username: string; + id: string; + }[]; + createdBy: { + username: string; + id: string; + }; +} + export interface Message { id: string; authorId: string; @@ -116,6 +170,7 @@ export type OverviewServer = { image: string; channels: Channel[]; invites: Invite[]; + roles: Role[]; }; export type OverviewGroup = { id: string; diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 212ff24..d62fb4d 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -91,7 +91,8 @@ export async function validateSessionToken(token: string) { name: table.server.name, ownerId: table.server.owner, channels: table.server.channels, - invites: table.server.invites + invites: table.server.invites, + roles: table.server.roles }) .from(table.server) .where(inArray(table.server.id, user.servers as string[])) @@ -114,6 +115,45 @@ export async function validateSessionToken(token: string) { }) ) ).filter(Boolean), + //@TODO technically all users should be able to see all roles, but idk, inspect this later + roles: ( + await Promise.all( + (z.roles as string[]).map(async (m) => { + const role = await db.select().from(table.role).where(eq(table.role.id, m)); + if (!role || role.length == 0) return; + + return { + name: role[0].name, + id: role[0].id, + canBeDeleted: role[0].canBeDeleted, + permissions: role[0].permissions, + createdAt: role[0].createdAt, + users: await db + .select() + .from(table.user) + .where(inArray(table.user.id, role[0].users as string[])) + .then((users) => { + return users.map((user) => { + return { + username: user.username, + id: user.id + }; + }); + }), + createdBy: await db + .select() + .from(table.user) + .where(eq(table.user.id, role[0].createdBy)) + .then((user) => { + return { + username: user[0].username, + id: user[0].id + }; + }) + }; + }) + ) + ).filter(Boolean), //@TODO check if user can view all invites (limit only to user's own invites if you can't) invites: ( await Promise.all( diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 4f8db8e..ca9e63e 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -28,7 +28,8 @@ export const server = sqliteTable('server', { .references(() => user.id), members: text('members', { mode: 'json' }).default([]).notNull(), channels: text('channels', { mode: 'json' }).default([]).notNull(), // string[] of ChannelIDs - invites: text('invites', { mode: 'json' }).default([]).notNull() // string[] of InviteIDs + invites: text('invites', { mode: 'json' }).default([]).notNull(), // string[] of InviteIDs + roles: text('roles', { mode: 'json' }).default([]).notNull() // string[] of RoleIDs }); export const group = sqliteTable('group', { @@ -50,6 +51,7 @@ export const channel = sqliteTable('channel', { serverId: text('server_id') .notNull() .references(() => server.id), + roleOverwrites: text('role_overwrites', { mode: 'json' }).default({}).notNull(), // Record> messages: text('messages', { mode: 'json' }).default([]).notNull() }); @@ -80,6 +82,21 @@ export const friendRequest = sqliteTable('friendRequest', { .references(() => user.id) }); +export const role = sqliteTable('role', { + id: text('id').primaryKey(), + name: text('name').notNull(), + serverId: text('server_id') + .notNull() + .references(() => server.id), + users: text('users', { mode: 'json' }).default([]).notNull(), + permissions: text('permissions', { mode: 'json' }).default({}).notNull(), // Record + createdAt: integer('created_at', { mode: 'timestamp' }).notNull(), + canBeDeleted: integer('can_be_deleted').default(1).notNull(), + createdBy: text('created_by') + .notNull() + .references(() => user.id) +}); + export const invite = sqliteTable('invite', { id: text('id').primaryKey(), serverId: text('server_id') diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts index a7bbcaf..5dca3d6 100644 --- a/src/lib/state.svelte.ts +++ b/src/lib/state.svelte.ts @@ -5,7 +5,8 @@ import { UserID, type Channel, type Invite, - type OverviewData + type OverviewData, + type Role } from '$lib'; import type { PageServerData } from '../routes/app/$types'; @@ -23,6 +24,7 @@ export async function fill_overview_data(data: PageServerData) { ownerId: z.ownerId, channels: z.channels as Channel[], invites: z.invites as Invite[], + roles: z.roles as Role[], image: 'https://api.dicebear.com/9.x/glass/svg?seed=' + z.name }; }); @@ -55,7 +57,7 @@ export async function fill_overview_data(data: PageServerData) { id: UserID.parse(friend.id), username: friend.username, dmId: friend.dmId, - image: 'https://api.dicebear.com/9.x/glass/svg/svg?seed=' + friend.username + image: 'https://api.dicebear.com/9.x/glass/svg?seed=' + friend.username }; }) ); diff --git a/src/routes/app/ServerDashboard.svelte b/src/routes/app/ServerDashboard.svelte index feda057..6748743 100644 --- a/src/routes/app/ServerDashboard.svelte +++ b/src/routes/app/ServerDashboard.svelte @@ -1,5 +1,5 @@ @@ -140,104 +71,148 @@
-
-

Roles (fake)

-
- - + + + +
-
-
- {#each fakeRoles as role (role.id)} - + + {/if} + + {/each} +
+ + {#if selectedRoleId} + + + {selectedRole?.name} permissions + + +
{ + return async ({ result }) => { + if (result.type === 'success') { + toast.success('Role updated successfully'); + await invalidateAll(); + await fill_overview_data(psd); + } else if (result.type == 'error' || result.type == 'failure') { + toast.error( + 'Failed to change role: ' + + (result.type === 'error' ? result.error.message : result.data?.error) + ); + } + }; }} + class="space-y-4" > - Γ— - - {/if} - - {/each} -
- - {#if selectedRole} - - - {selectedRole.name} permissions - - -
- {#each Object.entries(selectedRole.permissions) as [perm, enabled] (perm)} -
- - { - selectedRole.permissions[perm as FakePermission] = e.detail; - }} - /> + + +
+ {#each Object.entries(selectedRole?.permissions || {}) as [perm, enabled] (perm)} +
+ + +
+ {/each}
- {/each} -
-

Changes are local only (no backend yet)

- - - {/if} -
-
+
+

Changes will be saved to the server

+ +
+ + + +
+

Role Information

+
+

+ Created by: {selectedRole?.createdBy.username} +

+

+ Created at: {formatTimestamp(selectedRole?.createdAt!)} +

+

+ Members: {selectedRole?.users.length || 0} +

+
+
+
+ + {/if} + +

Server Settings

@@ -250,8 +225,11 @@ toast.success('Server name updated successfully'); await invalidateAll(); await fill_overview_data(psd); - } else if (result.type === 'failure') { - toast.error('Failed to update server name: ' + result.data?.error); + } else if (result.type == 'error' || result.type == 'failure') { + toast.error( + 'Failed to update server name: ' + + (result.type === 'error' ? result.error.message : result.data?.error) + ); } }; }} @@ -275,8 +253,11 @@ inviteCode = location.origin + '/invite/' + result.data!.code; await invalidateAll(); await fill_overview_data(psd); - } else if (result.type === 'failure') { - toast.error('Failed to create invite: ' + result.data?.error); + } else if (result.type == 'error' || result.type == 'failure') { + toast.error( + 'Failed to create invite: ' + + (result.type === 'error' ? result.error.message : result.data?.error) + ); } }; }} @@ -314,8 +295,11 @@ toast.success('Invite deleted successfully'); await invalidateAll(); await fill_overview_data(psd); - } else if (result.type === 'failure') { - toast.error('Failed to delete invite: ' + result.data?.error); + } else if (result.type == 'error' || result.type == 'failure') { + toast.error( + 'Failed to delete invite: ' + + (result.type === 'error' ? result.error.message : result.data?.error) + ); } }; }} diff --git a/src/routes/app/actions/server.ts b/src/routes/app/actions/server.ts index df800cb..6921b1b 100644 --- a/src/routes/app/actions/server.ts +++ b/src/routes/app/actions/server.ts @@ -1,7 +1,7 @@ import { fail } from '@sveltejs/kit'; import { db } from '$lib/server/db'; import * as table from '$lib/server/db/schema'; -import { ChannelID, InviteID, ServerID } from '$lib'; +import { ChannelID, InviteID, RoleID, ServerID } from '$lib'; import { eq } from 'drizzle-orm'; import { _sendToSubscribers } from '../../api/updates/+server'; import type { Actions } from '../$types'; @@ -181,9 +181,43 @@ export default { } const serverId = ServerID.newV4(); + const everyoneRoleId = RoleID.newV4(); + + await db.insert(table.role).values({ + id: everyoneRoleId, + serverId, + name: 'Everyone', + canBeDeleted: 0, // cannot be deleted + users: [locals.user!.id], + permissions: { + changeChannelName: false, + changeServerName: false, + createInvite: true, + createChannels: false, + deleteChannels: false, + deleteInvites: false, + addRoles: false, + deleteRoles: false, + kickPeople: false, + banPeople: false, + sendMessage: true, + viewChannels: true, + viewInvites: false, + deleteServer: false + }, + createdAt: new Date(), + createdBy: locals.user!.id + }); + await db .insert(table.server) - .values({ id: serverId, name, owner: locals.user!.id, members: [locals.user!.id] }); + .values({ + id: serverId, + name, + owner: locals.user!.id, + members: [locals.user!.id], + roles: [everyoneRoleId] + }); await db .update(table.user) @@ -327,6 +361,8 @@ export default { await tx.delete(table.invite).where(eq(table.invite.serverId, serverId)); + await tx.delete(table.role).where(eq(table.role.serverId, serverId)); + await tx.delete(table.channel).where(eq(table.channel.serverId, serverId)); await tx.delete(table.server).where(eq(table.server.id, serverId));