reset drizzle, get DMs working (partly, usernames aren't resolved)

This commit is contained in:
Soph :3 2026-01-04 23:13:39 +02:00
parent 3a0f096ade
commit bf679f9ee0
32 changed files with 1150 additions and 2867 deletions

View file

@ -0,0 +1,74 @@
CREATE TABLE `channel` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`server_id` text NOT NULL,
`messages` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`server_id`) REFERENCES `server`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `directMessage` (
`id` text PRIMARY KEY NOT NULL,
`first_member` text NOT NULL,
`second_member` text NOT NULL,
`messages` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`first_member`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`second_member`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `friendRequest` (
`id` text PRIMARY KEY NOT NULL,
`from_user` text NOT NULL,
`from_username` text NOT NULL,
`to_username` text NOT NULL,
`to_user` text NOT NULL,
FOREIGN KEY (`from_user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`from_username`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`to_username`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`to_user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `group` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`owner` text NOT NULL,
`members` text DEFAULT '[]' NOT NULL,
`messages` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`owner`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `invite` (
`id` text PRIMARY KEY NOT NULL,
`server_id` text NOT NULL,
`code` text NOT NULL,
FOREIGN KEY (`server_id`) REFERENCES `server`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `server` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`owner` text NOT NULL,
`members` text DEFAULT '[]' NOT NULL,
`channels` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`owner`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `session` (
`id` text PRIMARY KEY NOT NULL,
`user_id` text NOT NULL,
`expires_at` integer NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `user` (
`id` text PRIMARY KEY NOT NULL,
`username` text NOT NULL,
`email` text NOT NULL,
`password_hash` text NOT NULL,
`status_overwrite` integer DEFAULT 3 NOT NULL,
`friends` text DEFAULT '[]' NOT NULL,
`servers` text DEFAULT '[]' NOT NULL,
`groups` text DEFAULT '[]' NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `user_username_unique` ON `user` (`username`);--> statement-breakpoint
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);

View file

@ -1,15 +0,0 @@
CREATE TABLE `session` (
`id` text PRIMARY KEY NOT NULL,
`user_id` text NOT NULL,
`expires_at` integer NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `user` (
`id` text PRIMARY KEY NOT NULL,
`age` integer,
`username` text NOT NULL,
`password_hash` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `user_username_unique` ON `user` (`username`);

View file

@ -1,2 +0,0 @@
ALTER TABLE `user` ADD `email` text NOT NULL;--> statement-breakpoint
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);

View file

@ -1 +0,0 @@
ALTER TABLE `user` ADD `friends` text DEFAULT '[]' NOT NULL;

View file

@ -1,18 +0,0 @@
CREATE TABLE `group` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`owner` text NOT NULL,
`members` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`owner`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `server` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`owner` text NOT NULL,
`members` text DEFAULT '[]' NOT NULL,
`channels` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`owner`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
ALTER TABLE `user` DROP COLUMN `age`;

View file

@ -1,2 +0,0 @@
ALTER TABLE `user` ADD `servers` text DEFAULT '[]' NOT NULL;--> statement-breakpoint
ALTER TABLE `user` ADD `groups` text DEFAULT '[]' NOT NULL;

View file

@ -1,7 +0,0 @@
CREATE TABLE `channel` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`server_id` text NOT NULL,
`messages` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`server_id`) REFERENCES `server`(`id`) ON UPDATE no action ON DELETE no action
);

View file

@ -1,7 +0,0 @@
CREATE TABLE `friendRequest` (
`id` text PRIMARY KEY NOT NULL,
`from_user` text NOT NULL,
`to_user` text NOT NULL,
FOREIGN KEY (`from_user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`to_user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);

View file

@ -1,9 +0,0 @@
CREATE TABLE `invite` (
`id` text PRIMARY KEY NOT NULL,
`server_id` text NOT NULL,
`code` text NOT NULL,
FOREIGN KEY (`server_id`) REFERENCES `server`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
ALTER TABLE `friendRequest` ADD `from_username` text NOT NULL REFERENCES user(id);--> statement-breakpoint
ALTER TABLE `friendRequest` ADD `to_username` text NOT NULL REFERENCES user(id);

View file

@ -1,9 +0,0 @@
CREATE TABLE `directMessage` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`server_id` text NOT NULL,
`messages` text DEFAULT '[]' NOT NULL,
FOREIGN KEY (`server_id`) REFERENCES `server`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
ALTER TABLE `user` ADD `status_overwrite` integer DEFAULT 3 NOT NULL;

View file

@ -1,9 +1,393 @@
{ {
"version": "6", "version": "6",
"dialect": "sqlite", "dialect": "sqlite",
"id": "053c418c-34da-4776-88a4-2e048c6a4637", "id": "bce65872-fa2f-4adc-b86f-af9880038bc8",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"tables": { "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
},
"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": { "session": {
"name": "session", "name": "session",
"columns": { "columns": {
@ -59,13 +443,6 @@
"notNull": true, "notNull": true,
"autoincrement": false "autoincrement": false
}, },
"age": {
"name": "age",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"username": { "username": {
"name": "username", "name": "username",
"type": "text", "type": "text",
@ -73,12 +450,51 @@
"notNull": true, "notNull": true,
"autoincrement": false "autoincrement": false
}, },
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"password_hash": { "password_hash": {
"name": "password_hash", "name": "password_hash",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true,
"autoincrement": false "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": { "indexes": {
@ -88,6 +504,13 @@
"username" "username"
], ],
"isUnique": true "isUnique": true
},
"user_email_unique": {
"name": "user_email_unique",
"columns": [
"email"
],
"isUnique": true
} }
}, },
"foreignKeys": {}, "foreignKeys": {},

View file

@ -1,123 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "8eb771ed-36ad-43c2-a76a-3c704801326a",
"prevId": "053c418c-34da-4776-88a4-2e048c6a4637",
"tables": {
"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
},
"age": {
"name": "age",
"type": "integer",
"primaryKey": false,
"notNull": false,
"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
}
},
"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": {}
}
}

View file

@ -1,131 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "6f6aa2cb-1b39-4cff-a1c6-915735fab505",
"prevId": "8eb771ed-36ad-43c2-a76a-3c704801326a",
"tables": {
"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
},
"age": {
"name": "age",
"type": "integer",
"primaryKey": false,
"notNull": false,
"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
},
"friends": {
"name": "friends",
"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": {}
}
}

View file

@ -1,238 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "218a80a3-4754-48c7-8173-099613497b99",
"prevId": "6f6aa2cb-1b39-4cff-a1c6-915735fab505",
"tables": {
"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
},
"members": {
"name": "members",
"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": {}
},
"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
},
"friends": {
"name": "friends",
"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": {}
}
}

View file

@ -1,254 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "8592fa88-aea8-41f9-a183-8608ec4c4323",
"prevId": "218a80a3-4754-48c7-8173-099613497b99",
"tables": {
"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
},
"members": {
"name": "members",
"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": {}
},
"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
},
"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": {}
}
}

View file

@ -1,307 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "69be2f8f-70ca-4016-b10f-60f64a99af73",
"prevId": "8592fa88-aea8-41f9-a183-8608ec4c4323",
"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": {}
},
"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
},
"members": {
"name": "members",
"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": {}
},
"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
},
"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": {}
}
}

View file

@ -1,365 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "e809c266-891b-4355-a87c-facd55a5293a",
"prevId": "69be2f8f-70ca-4016-b10f-60f64a99af73",
"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": {}
},
"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
},
"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_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
},
"members": {
"name": "members",
"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": {}
},
"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
},
"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": {}
}
}

View file

@ -1,450 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "c0ac7678-4e2c-433f-ac26-73c7b88cc38b",
"prevId": "e809c266-891b-4355-a87c-facd55a5293a",
"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": {}
},
"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
},
"members": {
"name": "members",
"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
},
"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": {}
}
}

View file

@ -1,511 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "bd16f3db-5965-4d36-89b7-d259cbd21149",
"prevId": "c0ac7678-4e2c-433f-ac26-73c7b88cc38b",
"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
},
"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": {
"directMessage_server_id_server_id_fk": {
"name": "directMessage_server_id_server_id_fk",
"tableFrom": "directMessage",
"tableTo": "server",
"columnsFrom": [
"server_id"
],
"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
},
"members": {
"name": "members",
"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": "'[]'"
},
"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": {}
}
}

View file

@ -5,64 +5,8 @@
{ {
"idx": 0, "idx": 0,
"version": "6", "version": "6",
"when": 1767358712855, "when": 1767559403688,
"tag": "0000_hard_thaddeus_ross", "tag": "0000_amusing_shatterstar",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1767361487418,
"tag": "0001_melted_goliath",
"breakpoints": true
},
{
"idx": 2,
"version": "6",
"when": 1767530520182,
"tag": "0002_fair_sasquatch",
"breakpoints": true
},
{
"idx": 3,
"version": "6",
"when": 1767530666249,
"tag": "0003_wise_lucky_pierre",
"breakpoints": true
},
{
"idx": 4,
"version": "6",
"when": 1767530823274,
"tag": "0004_fair_stature",
"breakpoints": true
},
{
"idx": 5,
"version": "6",
"when": 1767531680914,
"tag": "0005_mute_mephisto",
"breakpoints": true
},
{
"idx": 6,
"version": "6",
"when": 1767537153441,
"tag": "0006_gifted_machine_man",
"breakpoints": true
},
{
"idx": 7,
"version": "6",
"when": 1767543898231,
"tag": "0007_outstanding_punisher",
"breakpoints": true
},
{
"idx": 8,
"version": "6",
"when": 1767549353944,
"tag": "0008_mute_lilandra",
"breakpoints": true "breakpoints": true
} }
] ]

View file

@ -1,8 +1,5 @@
import type { Handle } from '@sveltejs/kit'; import type { Handle } from '@sveltejs/kit';
import * as auth from '$lib/server/auth'; import * as auth from '$lib/server/auth';
import { kvStore } from '$lib/server/db';
import { _isUserConnected, _sendToSubscribers } from './routes/api/updates/+server';
import { Status } from '$lib';
const handleAuth: Handle = async ({ event, resolve }) => { const handleAuth: Handle = async ({ event, resolve }) => {
const sessionToken = event.cookies.get(auth.sessionCookieName); const sessionToken = event.cookies.get(auth.sessionCookieName);

View file

@ -1,312 +1,311 @@
<script lang="ts"> <script lang="ts">
import { type Data, type OverviewUser } from '$lib'; import * as Collapsible from '$lib/components/ui/collapsible/index.js';
import * as Collapsible from '$lib/components/ui/collapsible/index.js'; import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Tabs from '$lib/components/ui/tabs/index.js';
import * as Tabs from '$lib/components/ui/tabs/index.js'; import * as Card from '$lib/components/ui/card/index.js';
import * as Card from '$lib/components/ui/card/index.js'; import MessagesSquare from '@lucide/svelte/icons/messages-square';
import MessagesSquare from '@lucide/svelte/icons/messages-square'; import MinusIcon from '@lucide/svelte/icons/minus';
import MinusIcon from '@lucide/svelte/icons/minus'; import PlusIcon from '@lucide/svelte/icons/plus';
import PlusIcon from '@lucide/svelte/icons/plus'; import UserRoundPlus from '@lucide/svelte/icons/user-round-plus';
import UserRoundPlus from '@lucide/svelte/icons/user-round-plus'; import UsersRound from '@lucide/svelte/icons/users-round';
import UsersRound from '@lucide/svelte/icons/users-round'; import CirclePlus from '@lucide/svelte/icons/circle-plus';
import CirclePlus from '@lucide/svelte/icons/circle-plus'; import Input from './ui/input/input.svelte';
import Input from './ui/input/input.svelte'; import Button, { buttonVariants } from './ui/button/button.svelte';
import Button, { buttonVariants } from './ui/button/button.svelte'; import User from './extra/User.svelte';
import User from './extra/User.svelte'; import type { SessionValidationResult } from '$lib/server/auth';
import type { SessionValidationResult } from '$lib/server/auth';
let { let {
currentPage = $bindable<string | null>(), currentPage = $bindable<string | null>(),
data, data,
user, user,
...restProps ...restProps
}: { currentPage: string | null; data: Data; user: SessionValidationResult['user'] } = $props(); }: { currentPage: string | null; data: Data; user: SessionValidationResult['user'] } = $props();
</script> </script>
<Sidebar.Root {...restProps}> <Sidebar.Root {...restProps}>
<Sidebar.Header> <Sidebar.Header>
<Sidebar.Menu> <Sidebar.Menu>
<Sidebar.MenuItem> <Sidebar.MenuItem>
<Sidebar.MenuButton size="lg"> <Sidebar.MenuButton size="lg">
<div <div
class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground" class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"
> >
<MessagesSquare class="size-4" /> <MessagesSquare class="size-4" />
</div> </div>
<div class="flex flex-col gap-0.5 leading-none"> <div class="flex flex-col gap-0.5 leading-none">
<span class="font-medium">chat.sad.ovh</span> <span class="font-medium">chat.sad.ovh</span>
</div> </div>
</Sidebar.MenuButton> </Sidebar.MenuButton>
<div class="flex w-full justify-center gap-2"> <div class="flex w-full justify-center gap-2">
<Dialog.Root> <Dialog.Root>
<Dialog.Trigger> <Dialog.Trigger>
<Button <Button
variant={user!.friendRequests.length > 0 ? 'destructive' : 'outline'} variant={user!.friendRequests.length > 0 ? 'destructive' : 'outline'}
size="icon" size="icon"
> >
<UserRoundPlus /> <UserRoundPlus />
</Button> </Button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Content class="sm:max-w-[425px]"> <Dialog.Content class="sm:max-w-[425px]">
<Dialog.Header> <Dialog.Header>
<Dialog.Title>Add a friend</Dialog.Title> <Dialog.Title>Add a friend</Dialog.Title>
<Dialog.Description> <Dialog.Description>
Add a friend using their username or manage pending requests. Add a friend using their username or manage pending requests.
</Dialog.Description> </Dialog.Description>
</Dialog.Header> </Dialog.Header>
<!-- input to add a new friend --> <!-- input to add a new friend -->
<form method="POST" action="?/addFriend" class="mb-4"> <form method="POST" action="?/addFriend" class="mb-4">
<Input name="username" placeholder="username" required class="mb-2" /> <Input name="username" placeholder="username" required class="mb-2" />
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close> <Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close>
<Button type="submit">Send request</Button> <Button type="submit">Send request</Button>
</Dialog.Footer> </Dialog.Footer>
</form> </form>
<!-- Tabs for Friend Requests --> <!-- Tabs for Friend Requests -->
<Tabs.Root value="outgoing"> <Tabs.Root value="outgoing">
<Tabs.List> <Tabs.List>
<Tabs.Trigger value="outgoing">Outgoing</Tabs.Trigger> <Tabs.Trigger value="outgoing">Outgoing</Tabs.Trigger>
<Tabs.Trigger value="incoming">Incoming</Tabs.Trigger> <Tabs.Trigger value="incoming">Incoming</Tabs.Trigger>
</Tabs.List> </Tabs.List>
<!-- Outgoing Requests --> <!-- Outgoing Requests -->
<Tabs.Content value="outgoing"> <Tabs.Content value="outgoing">
{#if user.friendRequests.filter((r) => r.fromUser === user.id).length === 0} {#if user!.friendRequests.filter((r) => r.fromUser === user!.id).length === 0}
<p class="text-sm text-muted-foreground">No outgoing requests</p> <p class="text-sm text-muted-foreground">No outgoing requests</p>
{:else} {:else}
{#each user.friendRequests.filter((r) => r.fromUser === user.id) as request (request.id)} {#each user!.friendRequests.filter((r) => r.fromUser === user!.id) as request (request.id)}
<Card.Root class="mb-2"> <Card.Root class="mb-2">
<Card.Header> <Card.Header>
<Card.Title>{request.username}</Card.Title> <Card.Title>{request.toUsername}</Card.Title>
<Card.Description>Request sent</Card.Description> <Card.Description>Request sent</Card.Description>
</Card.Header> </Card.Header>
<Card.Footer> <Card.Footer>
<form method="POST" action="?/cancelFriendRequest"> <form method="POST" action="?/cancelFriendRequest">
<input type="hidden" name="requestId" value={request.id} /> <input type="hidden" name="requestId" value={request.id} />
<Button type="submit" variant="outline" size="sm">Cancel</Button> <Button type="submit" variant="outline" size="sm">Cancel</Button>
</form> </form>
</Card.Footer> </Card.Footer>
</Card.Root> </Card.Root>
{/each} {/each}
{/if} {/if}
</Tabs.Content> </Tabs.Content>
<!-- Incoming Requests --> <!-- Incoming Requests -->
<Tabs.Content value="incoming"> <Tabs.Content value="incoming">
{#if user.friendRequests.filter((r) => r.toUser === user.id).length === 0} {#if user!.friendRequests.filter((r) => r.toUser === user!.id).length === 0}
<p class="text-sm text-muted-foreground">No incoming requests</p> <p class="text-sm text-muted-foreground">No incoming requests</p>
{:else} {:else}
{#each user.friendRequests.filter((r) => r.toUser === user.id) as request (request.id)} {#each user!.friendRequests.filter((r) => r.toUser === user!.id) as request (request.id)}
<Card.Root class="mb-2"> <Card.Root class="mb-2">
<Card.Header> <Card.Header>
<Card.Title>{request.username}</Card.Title> <Card.Title>{request.fromUsername}</Card.Title>
<Card.Description>Sent you a friend request</Card.Description> <Card.Description>Sent you a friend request</Card.Description>
</Card.Header> </Card.Header>
<Card.Footer class="flex gap-2"> <Card.Footer class="flex gap-2">
<!-- accept friend --> <!-- accept friend -->
<form method="POST" action="?/addFriend"> <form method="POST" action="?/addFriend">
<input type="hidden" name="userId" value={request.fromUser} /> <input type="hidden" name="userId" value={request.fromUser} />
<Button type="submit" size="sm">Accept</Button> <Button type="submit" size="sm">Accept</Button>
</form> </form>
<!-- decline friend --> <!-- decline friend -->
<form method="POST" action="?/cancelFriendRequest"> <form method="POST" action="?/cancelFriendRequest">
<input type="hidden" name="requestId" value={request.id} /> <input type="hidden" name="requestId" value={request.id} />
<Button type="submit" variant="outline" size="sm">Decline</Button> <Button type="submit" variant="outline" size="sm">Decline</Button>
</form> </form>
</Card.Footer> </Card.Footer>
</Card.Root> </Card.Root>
{/each} {/each}
{/if} {/if}
</Tabs.Content> </Tabs.Content>
</Tabs.Root> </Tabs.Root>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
<Dialog.Root> <Dialog.Root>
<Dialog.Trigger> <Dialog.Trigger>
<Button variant="outline" size="icon"> <Button variant="outline" size="icon">
<UsersRound /> <UsersRound />
</Button> </Button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Content class="sm:max-w-[425px]"> <Dialog.Content class="sm:max-w-[425px]">
<form method="POST" action="?/createGroup"> <form method="POST" action="?/createGroup">
<Dialog.Header> <Dialog.Header>
<Dialog.Title>Create a group</Dialog.Title> <Dialog.Title>Create a group</Dialog.Title>
<Dialog.Description>Add friends into your group!</Dialog.Description> <Dialog.Description>Add friends into your group!</Dialog.Description>
</Dialog.Header> </Dialog.Header>
{#each data.friends as friend (friend.id)} {#each data.friends as friend (friend.id)}
<label class="flex items-center gap-2"> <label class="flex items-center gap-2">
<input type="checkbox" name="member" value={friend.id} /> <input type="checkbox" name="member" value={friend.id} />
<User user={friend} /> <User user={friend} />
</label> </label>
{/each} {/each}
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close> <Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close>
<Button type="submit">Create group</Button> <Button type="submit">Create group</Button>
</Dialog.Footer> </Dialog.Footer>
</form> </form>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
<Dialog.Root> <Dialog.Root>
<Dialog.Trigger> <Dialog.Trigger>
<Button variant="outline" size="icon"> <Button variant="outline" size="icon">
<CirclePlus /> <CirclePlus />
</Button> </Button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Content class="sm:max-w-[425px]"> <Dialog.Content class="sm:max-w-[425px]">
<form method="POST" action="?/joinServer"> <form method="POST" action="?/joinServer">
<Dialog.Header> <Dialog.Header>
<Dialog.Title>Join a server</Dialog.Title> <Dialog.Title>Join a server</Dialog.Title>
<Dialog.Description>Enter an invite link.</Dialog.Description> <Dialog.Description>Enter an invite link.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<Input name="invite" placeholder="invite link" required /> <Input name="invite" placeholder="invite link" required />
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close> <Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close>
<Button type="submit">Join</Button> <Button type="submit">Join</Button>
</Dialog.Footer> </Dialog.Footer>
</form> </form>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
<Dialog.Root> <Dialog.Root>
<Dialog.Trigger> <Dialog.Trigger>
<Button variant="outline" size="icon"> <Button variant="outline" size="icon">
<PlusIcon /> <PlusIcon />
</Button> </Button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Content class="sm:max-w-[425px]"> <Dialog.Content class="sm:max-w-[425px]">
<form method="POST" action="?/createServer"> <form method="POST" action="?/createServer">
<Dialog.Header> <Dialog.Header>
<Dialog.Title>Create a server</Dialog.Title> <Dialog.Title>Create a server</Dialog.Title>
<Dialog.Description>Name your new server.</Dialog.Description> <Dialog.Description>Name your new server.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<Input name="name" placeholder="Server name" required /> <Input name="name" placeholder="Server name" required />
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close> <Dialog.Close class={buttonVariants({ variant: 'outline' })}>Cancel</Dialog.Close>
<Button type="submit">Create</Button> <Button type="submit">Create</Button>
</Dialog.Footer> </Dialog.Footer>
</form> </form>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
</div> </div>
</Sidebar.MenuItem> </Sidebar.MenuItem>
</Sidebar.Menu> </Sidebar.Menu>
</Sidebar.Header> </Sidebar.Header>
<Sidebar.Content> <Sidebar.Content>
<Sidebar.Group> <Sidebar.Group>
<Sidebar.Menu> <Sidebar.Menu>
<Collapsible.Root open={true} class="group/collapsible"> <Collapsible.Root open={true} class="group/collapsible">
<Sidebar.MenuItem> <Sidebar.MenuItem>
<Collapsible.Trigger> <Collapsible.Trigger>
<Sidebar.MenuButton> <Sidebar.MenuButton>
Friends Friends
<PlusIcon class="ms-auto group-data-[state=open]/collapsible:hidden" /> <PlusIcon class="ms-auto group-data-[state=open]/collapsible:hidden" />
<MinusIcon class="ms-auto group-data-[state=closed]/collapsible:hidden" /> <MinusIcon class="ms-auto group-data-[state=closed]/collapsible:hidden" />
</Sidebar.MenuButton> </Sidebar.MenuButton>
</Collapsible.Trigger> </Collapsible.Trigger>
<Collapsible.Content> <Collapsible.Content>
<Sidebar.MenuSub> <Sidebar.MenuSub>
{#each data.friends as friend (friend.id)} {#each data.friends as friend (friend.id)}
<Sidebar.MenuSubItem> <Sidebar.MenuSubItem>
<Sidebar.MenuSubButton> <Sidebar.MenuSubButton>
<User <User
onclick={(e) => { onclick={(e) => {
e.preventDefault(); e.preventDefault();
currentPage = friend.id; currentPage = friend.id;
}} }}
user={friend} user={friend}
></User> ></User>
</Sidebar.MenuSubButton> </Sidebar.MenuSubButton>
</Sidebar.MenuSubItem> </Sidebar.MenuSubItem>
{/each} {/each}
</Sidebar.MenuSub> </Sidebar.MenuSub>
</Collapsible.Content> </Collapsible.Content>
</Sidebar.MenuItem> </Sidebar.MenuItem>
</Collapsible.Root> </Collapsible.Root>
<Collapsible.Root open={true} class="group/collapsible"> <Collapsible.Root open={true} class="group/collapsible">
<Sidebar.MenuItem> <Sidebar.MenuItem>
<Collapsible.Trigger> <Collapsible.Trigger>
<Sidebar.MenuButton> <Sidebar.MenuButton>
Groups Groups
<PlusIcon class="ms-auto group-data-[state=open]/collapsible:hidden" /> <PlusIcon class="ms-auto group-data-[state=open]/collapsible:hidden" />
<MinusIcon class="ms-auto group-data-[state=closed]/collapsible:hidden" /> <MinusIcon class="ms-auto group-data-[state=closed]/collapsible:hidden" />
</Sidebar.MenuButton> </Sidebar.MenuButton>
</Collapsible.Trigger> </Collapsible.Trigger>
<Collapsible.Content> <Collapsible.Content>
<Sidebar.MenuSub> <Sidebar.MenuSub>
{#each data.groups as group (group.id)} {#each data.groups as group (group.id)}
<Sidebar.MenuSubItem> <Sidebar.MenuSubItem>
<Sidebar.MenuSubButton> <Sidebar.MenuSubButton>
<a <a
onclick={(e) => { onclick={(e) => {
e.preventDefault(); e.preventDefault();
currentPage = group.id; currentPage = group.id;
}} }}
href="##" href="##"
> >
{group.name} ({group.members} members) {group.name} ({group.members} members)
</a> </a>
</Sidebar.MenuSubButton> </Sidebar.MenuSubButton>
</Sidebar.MenuSubItem> </Sidebar.MenuSubItem>
{/each} {/each}
</Sidebar.MenuSub> </Sidebar.MenuSub>
</Collapsible.Content> </Collapsible.Content>
</Sidebar.MenuItem> </Sidebar.MenuItem>
</Collapsible.Root> </Collapsible.Root>
<Collapsible.Root open={true} class="group/collapsible"> <Collapsible.Root open={true} class="group/collapsible">
<Sidebar.MenuItem> <Sidebar.MenuItem>
<Collapsible.Trigger> <Collapsible.Trigger>
<Sidebar.MenuButton> <Sidebar.MenuButton>
Servers Servers
<PlusIcon class="ms-auto group-data-[state=open]/collapsible:hidden" /> <PlusIcon class="ms-auto group-data-[state=open]/collapsible:hidden" />
<MinusIcon class="ms-auto group-data-[state=closed]/collapsible:hidden" /> <MinusIcon class="ms-auto group-data-[state=closed]/collapsible:hidden" />
</Sidebar.MenuButton> </Sidebar.MenuButton>
</Collapsible.Trigger> </Collapsible.Trigger>
<Collapsible.Content> <Collapsible.Content>
<Sidebar.MenuSub> <Sidebar.MenuSub>
{#each data.servers as server (server.id)} {#each data.servers as server (server.id)}
<Sidebar.MenuSubItem> <Sidebar.MenuSubItem>
<Sidebar.MenuSubButton> <Sidebar.MenuSubButton>
<a <a
onclick={(e) => { onclick={(e) => {
e.preventDefault(); e.preventDefault();
currentPage = server.id; currentPage = server.id;
}} }}
href="##" href="##"
class="flex items-center gap-2" class="flex items-center gap-2"
> >
<img <img
src={'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + server.name} src={'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + server.name}
alt={server.name} alt={server.name}
class="size-6 rounded-full" class="size-6 rounded-full"
/> />
{server.name} {server.name}
</a> </a>
</Sidebar.MenuSubButton> </Sidebar.MenuSubButton>
</Sidebar.MenuSubItem> </Sidebar.MenuSubItem>
{/each} {/each}
</Sidebar.MenuSub> </Sidebar.MenuSub>
</Collapsible.Content> </Collapsible.Content>
</Sidebar.MenuItem> </Sidebar.MenuItem>
</Collapsible.Root> </Collapsible.Root>
</Sidebar.Menu> </Sidebar.Menu>
</Sidebar.Group> </Sidebar.Group>
</Sidebar.Content> </Sidebar.Content>
<Sidebar.Rail /> <Sidebar.Rail />
</Sidebar.Root> </Sidebar.Root>

View file

@ -1,9 +0,0 @@
import { MediaQuery } from "svelte/reactivity";
const DEFAULT_MOBILE_BREAKPOINT = 768;
export class IsMobile extends MediaQuery {
constructor(breakpoint: number = DEFAULT_MOBILE_BREAKPOINT) {
super(`max-width: ${breakpoint - 1}px`);
}
}

View file

@ -4,11 +4,12 @@ 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 FriendRequestID = definePrefix('frq'); export const FriendRequestID = definePrefix('frq');
export const DirectMessageID = definePrefix('dmid');
export type UserId = Puuid<'user'>; export type UserId = Puuid<'user'>;
export type GroupId = Puuid<'group'>; export type GroupId = Puuid<'group'>;
export type ServerId = Puuid<'srv'>; export type ServerId = Puuid<'srv'>;
export type FriendRequestID = Puuid<'frq'>; export type DirectMessageId = Puuid<'dmid'>;
export const Status: Record<string, 1 | 2 | 3> = { export const Status: Record<string, 1 | 2 | 3> = {
OFFLINE: 1, OFFLINE: 1,
@ -20,6 +21,7 @@ export type OverviewUser = {
id: string; id: string;
username: string; username: string;
image: string; image: string;
dmId?: string;
}; };
export type OverviewServer = { export type OverviewServer = {

View file

@ -4,6 +4,7 @@ import { sha256 } from '@oslojs/crypto/sha2';
import { encodeBase64url, encodeHexLowerCase } from '@oslojs/encoding'; import { encodeBase64url, encodeHexLowerCase } from '@oslojs/encoding';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import * as table from '$lib/server/db/schema'; import * as table from '$lib/server/db/schema';
import { _findDmId } from '../../routes/api/messages/[[grp_srv_dm]]/[[channelId]]/[[channelId]]/+server';
const DAY_IN_MS = 1000 * 60 * 60 * 24; const DAY_IN_MS = 1000 * 60 * 60 * 24;
@ -63,15 +64,25 @@ export async function validateSessionToken(token: string) {
.set({ expiresAt: session.expiresAt }) .set({ expiresAt: session.expiresAt })
.where(eq(table.session.id, session.id)); .where(eq(table.session.id, session.id));
} }
const friends = (user.friends as string[]).length const friends = await Promise.all(
? await db ((user.friends as string[]).length
.select({ ? await db
id: table.user.id, .select({
username: table.user.username id: table.user.id,
}) username: table.user.username
.from(table.user) })
.where(inArray(table.user.id, user.friends as string[])) .from(table.user)
: []; .where(inArray(table.user.id, user.friends as string[]))
: []
).map(async (z) => {
const dmid = await _findDmId(z.id, user.id);
return {
...z,
dmId: dmid
};
})
);
const servers = (user.servers as string[]).length const servers = (user.servers as string[]).length
? await db ? await db
@ -99,6 +110,8 @@ export async function validateSessionToken(token: string) {
.select({ .select({
id: table.friendRequest.id, id: table.friendRequest.id,
fromUser: table.friendRequest.fromUser, fromUser: table.friendRequest.fromUser,
fromUsername: table.friendRequest.fromUsername,
toUsername: table.friendRequest.toUsername,
toUser: table.friendRequest.toUser toUser: table.friendRequest.toUser
}) })
.from(table.friendRequest) .from(table.friendRequest)

View file

@ -37,7 +37,8 @@ export const group = sqliteTable('group', {
owner: text('owner') owner: text('owner')
.notNull() .notNull()
.references(() => user.id), .references(() => user.id),
members: text('members', { mode: 'json' }).default([]).notNull() members: text('members', { mode: 'json' }).default([]).notNull(),
messages: text('messages', { mode: 'json' }).default([]).notNull()
}); });
export const channel = sqliteTable('channel', { export const channel = sqliteTable('channel', {
@ -51,10 +52,12 @@ export const channel = sqliteTable('channel', {
export const directMessage = sqliteTable('directMessage', { export const directMessage = sqliteTable('directMessage', {
id: text('id').primaryKey(), id: text('id').primaryKey(),
name: text('name').notNull(), firstMember: text('first_member')
serverId: text('server_id')
.notNull() .notNull()
.references(() => server.id), .references(() => user.id),
secondMember: text('second_member')
.notNull()
.references(() => user.id),
messages: text('messages', { mode: 'json' }).default([]).notNull() messages: text('messages', { mode: 'json' }).default([]).notNull()
}); });

View file

@ -4,6 +4,7 @@ import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
} }
export function formatTimestamp(dateString: string) { export function formatTimestamp(dateString: string) {
const date = new Date(dateString); const date = new Date(dateString);
const now = new Date(); const now = new Date();

View file

@ -1,7 +1,59 @@
import { fail, json } from '@sveltejs/kit'; import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types'; import type { RequestHandler } from './$types';
import { GroupID, ServerID, UserID } from '$lib'; import { db } from '$lib/server/db';
import * as table from '$lib/server/db/schema';
import { DirectMessageID, GroupID, ServerID } from '$lib';
import { eq, or } from 'drizzle-orm';
import { _sendToSubscribers } from '../../../../updates/+server';
import { and } from 'drizzle-orm';
interface Message {
id: string;
authorId: string;
content: string;
timestamp: number;
}
export async function _findDm(member_one: string, member_two: string) {
return await db
.select()
.from(table.directMessage)
.where(
or(
and(
eq(table.directMessage.secondMember, member_two),
eq(table.directMessage.firstMember, member_one)
),
and(
eq(table.directMessage.firstMember, member_two),
eq(table.directMessage.secondMember, member_one)
)
)
);
}
export async function _findDmId(
member_one: string,
member_two: string
): Promise<string | undefined> {
return (
await db
.select({ id: table.directMessage.id })
.from(table.directMessage)
.where(
or(
and(
eq(table.directMessage.secondMember, member_two),
eq(table.directMessage.firstMember, member_one)
),
and(
eq(table.directMessage.firstMember, member_two),
eq(table.directMessage.secondMember, member_one)
)
)
)
.limit(1)
)?.[0]?.id;
}
export const GET: RequestHandler = async ({ params, locals }) => { export const GET: RequestHandler = async ({ params, locals }) => {
if (!locals.user) { if (!locals.user) {
return new Response('No authentication', { status: 401 }); return new Response('No authentication', { status: 401 });
@ -11,38 +63,32 @@ export const GET: RequestHandler = async ({ params, locals }) => {
if (!grp_srv_dm) { if (!grp_srv_dm) {
return new Response('Missing group, server, or DM ID.', { status: 400 }); return new Response('Missing group, server, or DM ID.', { status: 400 });
} }
let messages = [];
let messages: Message[] = [];
let type = ''; let type = '';
if (GroupID.is(grp_srv_dm)) { if (GroupID.is(grp_srv_dm)) {
type = 'group'; type = 'group';
messages = Array.from({ length: 5 }, (_, i) => ({ const g = (await db.select().from(table.group).where(eq(table.group.id, grp_srv_dm)))[0];
id: crypto.randomUUID(), if (!g) return new Response('Group not found.', { status: 404 });
authorId: `user_${Math.floor(Math.random() * 10)}`,
content: 'group message ' + (i + 1), messages = g.messages ?? [];
timestamp: Date.now() - Math.floor(Math.random() * 100000)
}));
} else if (ServerID.is(grp_srv_dm)) { } else if (ServerID.is(grp_srv_dm)) {
type = 'server'; type = 'server';
if (!channelId) return new Response('Missing channel ID.', { status: 400 });
const c = (await db.select().from(table.channel).where(eq(table.channel.id, channelId)))[0];
if (!c) return new Response('Channel not found.', { status: 404 });
if (!channelId) { messages = c.messages;
return new Response('Missing channel ID.', { status: 400 }); } else if (DirectMessageID.is(grp_srv_dm)) {
}
messages = Array.from({ length: 5 }, (_, i) => ({
id: crypto.randomUUID(),
authorId: `user_${Math.floor(Math.random() * 10)}`,
content: 'server message ' + (i + 1),
timestamp: Date.now() - Math.floor(Math.random() * 100000)
}));
} else if (UserID.is(grp_srv_dm)) {
type = 'dms'; type = 'dms';
messages = Array.from({ length: 5 }, (_, i) => ({ const dm = (
id: crypto.randomUUID(), await db.select().from(table.directMessage).where(eq(table.directMessage.id, grp_srv_dm))
authorId: Math.random() > 0.5 ? locals.user.id : grp_srv_dm, )[0];
content: 'dm message ' + (i + 1),
timestamp: Date.now() - Math.floor(Math.random() * 100000) if (!dm) return new Response('DM not found.', { status: 404 });
}));
messages = dm.messages;
} }
return json({ return json({
@ -51,3 +97,67 @@ export const GET: RequestHandler = async ({ params, locals }) => {
messages messages
}); });
}; };
export const POST: RequestHandler = async ({ params, request, locals }) => {
if (!locals.user) {
return new Response('Unauthorized', { status: 401 });
}
let { grp_srv_dm, channelId } = params;
if (!grp_srv_dm) return new Response('Missing Group/Server/DM id.', { status: 400 });
const data = await request.json();
const { content } = data;
if (!content) return new Response('Missing message content.', { status: 400 });
let messages: Message[] = [];
let type = '';
const message = {
id: crypto.randomUUID(),
authorId: locals.user.id,
content,
timestamp: Date.now()
};
if (GroupID.is(grp_srv_dm)) {
type = 'group';
const g = (await db.select().from(table.group).where(eq(table.group.id, grp_srv_dm)))[0];
if (!g) return new Response('Group not found.', { status: 404 });
messages = g.messages ?? [];
messages.push(message);
await db.update(table.group).set({ messages }).where(eq(table.group.id, grp_srv_dm));
} else if (ServerID.is(grp_srv_dm)) {
type = 'server';
if (!channelId) return new Response('Missing channel ID.', { status: 400 });
const c = (await db.select().from(table.channel).where(eq(table.channel.id, channelId)))[0];
if (!c) return new Response('Channel not found.', { status: 404 });
messages = c.messages ?? [];
messages.push(message);
await db.update(table.channel).set({ messages }).where(eq(table.channel.id, channelId));
} else if (DirectMessageID.is(grp_srv_dm)) {
type = 'dms';
const dm = (
await db.select().from(table.directMessage).where(eq(table.directMessage.id, grp_srv_dm))
)[0];
if (!dm) return new Response('DM not found.', { status: 404 });
messages = dm.messages ?? [];
messages.push(message);
await db.update(table.directMessage).set({ messages }).where(eq(table.directMessage.id, dm.id));
_sendToSubscribers(dm.id, { type: 'message', id: dm.id, message });
return json({ type, id: dm.id, messages });
}
_sendToSubscribers(grp_srv_dm, { type: 'message', id: grp_srv_dm, message });
return json({ type, id: grp_srv_dm, messages });
};

View file

@ -1,17 +1,18 @@
import { Status } from '$lib'; import { Status } from '$lib';
import { kvStore } from '$lib/server/db/index.js'; import { kvStore } from '$lib/server/db/index.js';
import { json } from '@sveltejs/kit';
interface SubscribedTo { interface SubscribedTo {
subscribed: string[]; subscribed: string[]; // any ID (including channels, dms, et cetera can be added in here, to send SSE data to any group of users)
userId: string; userId: string;
controller: ReadableStreamDefaultController; controller: ReadableStreamDefaultController;
} }
export const _clients = new Map<string, SubscribedTo>(); export const _clients = new Map<string, SubscribedTo>();
export function _sendToSubscribers(userId: string, payload: unknown) { export function _sendToSubscribers(id: string, payload: unknown) {
for (const [key, client] of _clients) { for (const [key, client] of _clients) {
if (client.subscribed.includes(userId)) { if (client.subscribed.includes(id)) {
try { try {
client.controller.enqueue(`data: ${JSON.stringify(payload)}\n\n`); client.controller.enqueue(`data: ${JSON.stringify(payload)}\n\n`);
} catch { } catch {
@ -30,7 +31,7 @@ export function _isUserConnected(userId: string): boolean {
export async function GET({ locals, request }) { export async function GET({ locals, request }) {
if (!locals.user) { if (!locals.user) {
return new Response('No authentication', { status: 401 }); return json({ error: 'Unauthorized' }, { status: 401 });
} }
const userId = locals.user.id; const userId = locals.user.id;
@ -38,14 +39,14 @@ export async function GET({ locals, request }) {
const subscribed = locals.user.friends.map((f) => f.id); const subscribed = locals.user.friends.map((f) => f.id);
const overwrite = locals.user.statusOverwrite; const overwrite = locals.user.statusOverwrite;
const reqId = crypto.randomUUID(); const sessionId = crypto.randomUUID();
const stream = new ReadableStream({ const stream = new ReadableStream({
start(controller) { start(controller) {
_clients.set(reqId, { subscribed, userId, controller }); _clients.set(sessionId, { subscribed, userId, controller });
console.log(`SSE Client opened. total: ${_clients.size}`); console.log(`SSE Client opened. total: ${_clients.size}`);
controller.enqueue(`data: ${JSON.stringify({ type: 'connected' })}\n\n`); controller.enqueue(`data: ${JSON.stringify({ type: 'connected', sessionId })}\n\n`);
if (overwrite === Status.DND) { if (overwrite === Status.DND) {
kvStore.set(`user-${userId}-state`, Status.DND); kvStore.set(`user-${userId}-state`, Status.DND);
@ -56,7 +57,7 @@ export async function GET({ locals, request }) {
} }
request.signal.addEventListener('abort', () => { request.signal.addEventListener('abort', () => {
_clients.delete(reqId); _clients.delete(sessionId);
console.log(`SSE Client aborted. total: ${_clients.size}`); console.log(`SSE Client aborted. total: ${_clients.size}`);
if (_isUserConnected(userId)) return; if (_isUserConnected(userId)) return;

View file

@ -0,0 +1,111 @@
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { _clients } from '../+server';
import { DirectMessageID } from '$lib';
import { db } from '$lib/server/db';
import * as table from '$lib/server/db/schema';
import { or } from 'drizzle-orm';
import { eq } from 'drizzle-orm';
export const POST: RequestHandler = async ({ params, request, locals }) => {
if (!locals.user) {
return json({ error: 'Unauthorized' }, { status: 401 });
}
const { sessionId } = params;
if (!sessionId) {
return json({ error: 'Missing sessionId' }, { status: 400 });
}
const client = _clients.get(sessionId);
if (!client) {
return json({ error: 'Client not found' }, { status: 404 });
}
if (client.userId !== locals.user.id) {
return json({ error: 'Unauthorized sessionID access' }, { status: 401 });
}
const body = await request.json();
const { subscribeTo } = body;
if (typeof subscribeTo !== 'string' || !subscribeTo.trim()) {
return json({ error: 'Invalid subscribeTo value' }, { status: 400 });
}
let isValidDmid = false;
if (DirectMessageID.is(subscribeTo)) {
const find = await db
.select()
.from(table.directMessage)
.where(
or(
eq(table.directMessage.firstMember, locals.user.id),
eq(table.directMessage.secondMember, locals.user.id)
)
);
if (find?.length != 0) {
isValidDmid = true;
}
}
if (
!(
isValidDmid ||
locals.user.groups.find((z) => z.id === subscribeTo) ||
locals.user.servers.find((z) => z.id === subscribeTo)
)
) {
return json({ error: 'Invalid subscription' }, { status: 401 });
}
if (!client.subscribed.includes(subscribeTo)) {
client.subscribed.push(subscribeTo);
}
return json({
message: `Added ${subscribeTo} to client subscriptions.`,
subscribed: client.subscribed
});
};
export const DELETE: RequestHandler = async ({ params, request, locals }) => {
if (!locals.user) {
return json({ error: 'Unauthorized' }, { status: 401 });
}
const { sessionId } = params;
if (!sessionId) {
return json({ error: 'Missing sessionId' }, { status: 400 });
}
const client = _clients.get(sessionId);
if (!client) {
return json({ error: 'Client not found' }, { status: 404 });
}
if (client.userId !== locals.user.id) {
return json({ error: 'Unauthorized sessionID access' }, { status: 401 });
}
const body = await request.json();
const { subscribeTo } = body;
if (typeof subscribeTo !== 'string' || !subscribeTo.trim()) {
return json({ error: 'Invalid subscribeTo value' }, { status: 400 });
}
const index = client.subscribed.indexOf(subscribeTo);
if (index !== -1) {
client.subscribed.splice(index, 1);
} else {
return json({ error: 'Subscription not found' }, { status: 404 });
}
return json({
message: `Removed ${subscribeTo} from client subscriptions.`,
subscribed: client.subscribed
});
};

View file

@ -3,7 +3,7 @@ import { getRequestEvent } from '$app/server';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import * as table from '$lib/server/db/schema'; import * as table from '$lib/server/db/schema';
import { FriendRequestID, ServerID } from '$lib'; import { DirectMessageID, FriendRequestID, ServerID } from '$lib';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { and } from 'drizzle-orm'; import { and } from 'drizzle-orm';
import { type User } from '$lib/server/db/schema'; import { type User } from '$lib/server/db/schema';
@ -72,6 +72,13 @@ export const actions = {
) )
.limit(1); .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 // add other guy to us
await db.transaction(async (tx) => { await db.transaction(async (tx) => {
await tx await tx
@ -99,7 +106,9 @@ export const actions = {
await db.insert(table.friendRequest).values({ await db.insert(table.friendRequest).values({
id: FriendRequestID.newV4(), id: FriendRequestID.newV4(),
fromUser: locals.user!.id, fromUser: locals.user!.id,
toUser: user[0].id toUser: user[0].id,
toUsername: user[0].username,
fromUsername: locals.user!.username
}); });
return { success: true }; return { success: true };

View file

@ -20,6 +20,8 @@
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 { Button } from '$lib/components/ui/button';
let errorOpen = $state(true); let errorOpen = $state(true);
let { form, data }: { form: ActionData; data: PageServerData } = $props(); let { form, data }: { form: ActionData; data: PageServerData } = $props();
@ -27,6 +29,11 @@
let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state(); let currentPage: OverviewUser | OverviewGroup | OverviewServer | undefined = $state();
let messages = $state([]); let messages = $state([]);
let inputValue = $state();
let sessionId: string | undefined = $state();
let previousSubscription: typeof currentPageID = $state(null);
const overview_data: OverviewData = $state({ const overview_data: OverviewData = $state({
friends: [], friends: [],
groups: [], groups: [],
@ -52,10 +59,26 @@
if (ServerID.is(currentPageID)) return; if (ServerID.is(currentPageID)) return;
async function getMessages() { async function getMessages() {
const req = await fetch('/api/messages/' + currentPageID); const req = await fetch('/api/messages/' + (currentPage!.dmId || currentPageID));
const data = await req.json(); const data = await req.json();
messages = data.messages; messages = data.messages;
if (previousSubscription && previousSubscription != (currentPage!.dmId || currentPageID)) {
await fetch('/api/updates/' + sessionId, {
body: JSON.stringify({ subscribeTo: previousSubscription }),
method: 'DELETE'
});
}
if (previousSubscription != (currentPage!.dmId || currentPageID)) {
await fetch('/api/updates/' + sessionId, {
body: JSON.stringify({ subscribeTo: currentPage!.dmId || currentPageID }),
method: 'POST'
});
}
previousSubscription = currentPageID;
} }
getMessages(); getMessages();
@ -68,6 +91,7 @@
id: ServerID.parse(z.id), id: ServerID.parse(z.id),
name: z.name, name: z.name,
ownerId: z.ownerId, ownerId: z.ownerId,
image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + z.name image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + z.name
}; };
}); });
@ -95,6 +119,7 @@
username: friend.username, username: friend.username,
status: status.status, status: status.status,
statusMessage: status.statusMessage, statusMessage: status.statusMessage,
dmId: friend.dmId,
image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + friend.username image: 'https://api.dicebear.com/7.x/pixel-art/svg?seed=' + friend.username
}; };
}) })
@ -109,11 +134,12 @@
sse.addEventListener('message', (e) => { sse.addEventListener('message', (e) => {
const json = JSON.parse(e.data) as const json = JSON.parse(e.data) as
| { type: 'connected' } | { type: 'connected'; sessionId: string }
| { type: 'status'; id: string; status: 1 | 2 | 3 }; | { type: 'status'; id: string; status: 1 | 2 | 3 };
if (json.type == 'connected') { if (json.type == 'connected') {
console.log('SSE connected.'); console.log('SSE connected. We are sessionID ' + json.sessionId);
sessionId = json.sessionId;
} }
if (json.type == 'status') { if (json.type == 'status') {
@ -122,6 +148,10 @@
friend.status = json.status; friend.status = json.status;
} }
} }
if (json.type == 'message') {
messages.push(json.message);
}
}); });
}); });
</script> </script>
@ -203,5 +233,29 @@
</div> </div>
{/if} {/if}
{/each} {/each}
{#if currentPageID && currentPage}
<Input
bind:value={inputValue}
placeholder="input box for messages (ignore how ugly it is right now please)"
></Input>
<Button
onclick={async () => {
const req = await fetch('/api/messages/' + (currentPage!.dmId || currentPageID), {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: inputValue
})
});
if (!req.ok) {
console.error('Failed to send message');
}
}}>send</Button
>
{/if}
</Sidebar.Inset> </Sidebar.Inset>
</Sidebar.Provider> </Sidebar.Provider>