feat: Sync chat presence with auth data

This commit is contained in:
April Hall 2025-02-09 23:39:57 -05:00
parent 9a28fdcf13
commit ee1eac23ab
Signed by: arithefirst
GPG Key ID: 4508A15C4DB91C5B
9 changed files with 49 additions and 16 deletions

2
.gitignore vendored
View File

@ -28,4 +28,4 @@ vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
# Sqlite # Sqlite
src/lib/server/db/users.db src/lib/server/db/users.db*

View File

@ -10,4 +10,4 @@ A more complex version of this list is available [here](https://trello.com/b/kJw
- [ ] Reactive channels list - [ ] Reactive channels list
- [ ] Show characters before message gets cut off in textarea - [ ] Show characters before message gets cut off in textarea
- [x] User Auth - [x] User Auth
- [ ] Usernames / Displaynames in chat - [x] Usernames / Displaynames in chat

View File

@ -3,6 +3,7 @@ import express from 'express';
import { createServer } from 'http'; import { createServer } from 'http';
import { Server } from 'socket.io'; import { Server } from 'socket.io';
import { db } from './src/lib/server/db'; import { db } from './src/lib/server/db';
import { authdb } from './src/lib/server/db/sqlite';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
const app = express(); const app = express();
@ -19,10 +20,11 @@ io.on('connection', async (socket) => {
console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`); console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`);
// Store the message in the database // Store the message in the database
await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4()); await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4());
const sender = authdb.getUser(msg.id);
io!.emit('message', { io!.emit('message', {
user: msg.id, user: sender.username,
message: msg.content, message: msg.content,
imageSrc: `https://api.dicebear.com/9.x/identicon/svg?seed=${msg.id}`, imageSrc: sender.image,
channel: msg.channel, channel: msg.channel,
}); });
} }

View File

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from '../../routes/$types'; import type { PageData } from '../../routes/(main)/$types';
const { data }: { data: PageData } = $props(); const { data }: { data: PageData } = $props();
const imageSrc = data.session?.user.image ?? `https://api.dicebear.com/9.x/identicon/svg?seed=${data.session?.user.name}`; const imageSrc = data.session?.user.image ?? `https://api.dicebear.com/9.x/identicon/svg?seed=${data.session?.user.id}`;
</script> </script>
{#if data.session} {#if data.session}

View File

@ -4,6 +4,7 @@ import type { HttpServer } from 'vite';
// file gets loaded as a vite plugin, it will crash // file gets loaded as a vite plugin, it will crash
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { db } from '../server/db'; import { db } from '../server/db';
import { authdb } from '../server/db/sqlite';
let io: SocketIOServer | undefined; let io: SocketIOServer | undefined;
@ -28,10 +29,11 @@ export function startupSocketIOServer(httpServer: HttpServer | null) {
console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`); console.log(`\x1b[35m[ws:kit]\x1b[0m message from ${socket.id}: ${msg.content}`);
// Store the message in the database // Store the message in the database
await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4()); await db.sendMessage(msg.channel, msg.content, msg.id, uuidv4());
const sender = authdb.getUser(msg.id);
io!.emit('message', { io!.emit('message', {
user: msg.id, user: sender.username,
message: msg.content, message: msg.content,
imageSrc: `https://api.dicebear.com/9.x/identicon/svg?seed=${msg.id}`, imageSrc: sender.image,
channel: msg.channel, channel: msg.channel,
}); });
} }

View File

@ -46,7 +46,7 @@ class Db {
message_content TEXT, message_content TEXT,
channel_name TEXT, channel_name TEXT,
timestamp TIMESTAMP, timestamp TIMESTAMP,
sender UUID, sender TEXT,
PRIMARY KEY (channel_name, timestamp) PRIMARY KEY (channel_name, timestamp)
) WITH CLUSTERING ORDER BY (timestamp DESC);`); ) WITH CLUSTERING ORDER BY (timestamp DESC);`);
} catch (e) { } catch (e) {
@ -67,7 +67,7 @@ class Db {
sender, sender,
}); });
} catch (e) { } catch (e) {
console.log(`Error storing messages: ${e as Error}`); console.log(`Error storing message: ${e as Error}`);
} }
} }

View File

@ -0,0 +1,28 @@
import Database from 'better-sqlite3';
interface Profile {
username: string;
image: string;
}
class AuthDb {
private client = new Database('./src/lib/server/db/users.db');
init() {
// Set WAL pragma for preformance reasons
this.client.pragma('journal_mode = WAL');
}
getUser(userId: string): Profile {
const row = this.client.prepare('SELECT username, image FROM user WHERE id = ?').get(userId);
return {
username: (row as Profile).username,
image: (row as Profile).image ?? `https://api.dicebear.com/9.x/identicon/svg?seed=${userId}`,
};
}
}
const authdb = new AuthDb();
authdb.init();
export { authdb };

View File

@ -1,9 +1,10 @@
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { authdb } from '$lib/server/db/sqlite.js';
import type { TypeMessage } from '$lib/types'; import type { TypeMessage } from '$lib/types';
import { error, redirect } from '@sveltejs/kit'; import { error, redirect } from '@sveltejs/kit';
import { auth } from '$lib/server/db/auth'; import { auth } from '$lib/server/db/auth';
export async function load({ params, request }): Promise<{ messages: TypeMessage[] }> { export async function load({ params, request }): Promise<{ messages: TypeMessage[]; currentUser: string }> {
const session = await auth.api.getSession({ const session = await auth.api.getSession({
headers: request.headers, headers: request.headers,
}); });
@ -18,10 +19,11 @@ export async function load({ params, request }): Promise<{ messages: TypeMessage
if (rows.messages !== null) { if (rows.messages !== null) {
messages = rows messages = rows
? rows.messages.map((value) => { ? rows.messages.map((value) => {
const sender = authdb.getUser(value.sender);
return { return {
message: value.message_content, message: value.message_content,
user: value.sender.toString(), user: sender.username,
imageSrc: `https://api.dicebear.com/9.x/identicon/svg?seed=${value.sender.toString()}`, imageSrc: sender.image,
channel: value.channel, channel: value.channel,
}; };
}) })
@ -32,5 +34,6 @@ export async function load({ params, request }): Promise<{ messages: TypeMessage
return { return {
messages: messages ?? [], messages: messages ?? [],
currentUser: session.user.id!,
}; };
} }

View File

@ -10,12 +10,10 @@
import Send from 'lucide-svelte/icons/send'; import Send from 'lucide-svelte/icons/send';
import { io } from 'socket.io-client'; import { io } from 'socket.io-client';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { v4 as uuidv4 } from 'uuid';
import type { PageData } from './$types'; import type { PageData } from './$types';
const { data }: { data: PageData } = $props(); const { data }: { data: PageData } = $props();
let user: string = uuidv4();
let socket: Websocket | undefined = $state(); let socket: Websocket | undefined = $state();
let msg: string = $state(''); let msg: string = $state('');
let showDialog: boolean = $state(false); let showDialog: boolean = $state(false);
@ -25,7 +23,7 @@
function submit() { function submit() {
if (msg.length <= 2000) { if (msg.length <= 2000) {
socket?.sendMessage(user!, msg); socket?.sendMessage(data.currentUser, msg);
if (textareaRef) textareaRef.style.height = '40px'; if (textareaRef) textareaRef.style.height = '40px';
msg = ''; msg = '';
} else { } else {