added an authenticated room websocket for messages

This commit is contained in:
2025-12-15 19:51:31 +01:00
parent 22c187e4ee
commit 4d939f611d
4 changed files with 85 additions and 15 deletions

View File

@@ -1,6 +1,5 @@
import { initAuth } from '../stores/auth.ts' import { initAuth } from '../stores/auth.ts'
import { API } from '../main.ts'
const BASE_URL = 'http://localhost:8080'
export async function apiFetch<T>( export async function apiFetch<T>(
path: string, path: string,
@@ -8,7 +7,7 @@ export async function apiFetch<T>(
): Promise<T> { ): Promise<T> {
const auth = await initAuth() const auth = await initAuth()
const res = await fetch(`${BASE_URL}${path}`, { const res = await fetch(`${API}${path}`, {
...options, ...options,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@@ -15,3 +15,7 @@ export function sendMessage(roomUuid: string, content: string) {
}) })
} }
export async function getWsToken(roomUuid: string): Promise<string> {
const data = await apiFetch<{ token: string }>(`/ws/issue-token/rooms/${roomUuid}`);
return data.token;
}

View File

@@ -7,3 +7,6 @@ import './base.css'
createApp(App) createApp(App)
.use(router) .use(router)
.mount('#app') .mount('#app')
export const API = 'http://127.0.0.1:8080'
export const API_WS = 'ws://127.0.0.1:8080/ws'

View File

@@ -11,39 +11,72 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, nextTick } from "vue"; import { onMounted, onUnmounted, ref, nextTick } from "vue";
import { fetchMessages, sendMessage } from "../api/messages"; import { fetchMessages, sendMessage, getWsToken } from "../api/messages";
import type { Message } from "../types/api"; import type { Message } from "../types/api";
import MessageList from "../components/MessageList.vue"; import MessageList from "../components/MessageList.vue";
import MessageInput from "../components/MessageInput.vue"; import MessageInput from "../components/MessageInput.vue";
import { API_WS } from '../main.ts'
let socket: WebSocket | null = null;
const props = defineProps<{ uuid: string }>(); const props = defineProps<{ uuid: string }>();
const messages = ref<Message[]>([]); const messages = ref<Message[]>([]);
const messageListRef = ref<HTMLElement | null>(null);
async function load() { async function load() {
messages.value = await fetchMessages(props.uuid); messages.value = await fetchMessages(props.uuid);
} }
async function onSend(content: string) { async function onSend(content: string) {
const msg = await sendMessage(props.uuid, content); await sendMessage(props.uuid, content);
messages.value.push(msg); // messages.value.push(msg);
await nextTick(); await nextTick();
scrollToBottom(); scrollToBottom();
} }
onMounted(async () => {
await load();
await nextTick();
scrollToBottom();
});
const messageListRef = ref<HTMLElement | null>(null);
function scrollToBottom() { function scrollToBottom() {
if (messageListRef.value) { if (messageListRef.value) {
messageListRef.value.scrollTop = messageListRef.value.scrollHeight; messageListRef.value.scrollTop = messageListRef.value.scrollHeight;
} }
} }
onMounted(async () => {
await load();
await nextTick();
scrollToBottom();
// Fetch WebSocket token from the backend
const wsToken = await getWsToken(props.uuid);
// Connect to the WebSocket with the token
socket = new WebSocket(`${API_WS}/rooms/${props.uuid}?token=${wsToken}`);
socket.onmessage = (event) => {
const msg: Message = JSON.parse(event.data);
const exists = messages.value.some(
(m) =>
m.sent_at === msg.sent_at &&
m.sender === msg.sender &&
m.content === msg.content
);
if (!exists) {
messages.value.push(msg);
nextTick().then(scrollToBottom);
}
};
socket.onclose = () => {
console.warn("WebSocket closed");
};
});
onUnmounted(() => {
socket?.close();
});
</script> </script>
<style scoped> <style scoped>
@@ -75,3 +108,34 @@ function scrollToBottom() {
background: var(--panel); background: var(--panel);
} }
</style> </style>
<style scoped>
.chat-page {
display: flex;
flex-direction: column;
height: 95%;
width: 90%;
padding: 15px;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--panel);
overflow: hidden;
}
.messages-container {
flex: 1;
padding: 1rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.5rem;
word-wrap: break-word;
}
.input-container {
padding: 0.5rem 1rem;
border-top: 1px solid var(--border);
background: var(--panel);
}
</style>