diff --git a/.vscode/launch.json b/.vscode/launch.json index f19048b..54e899c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,8 @@ "name": "Python Debugger: Main", "type": "debugpy", "request": "launch", - "program": "${cwd}/backend_py/main.py", + "cwd": "${workspaceFolder}", + "module": "simple_chat_api", "console": "integratedTerminal", "justMyCode": true }, diff --git a/backend_py/endpoints/__pycache__/auth.cpython-312.pyc b/backend_py/endpoints/__pycache__/auth.cpython-312.pyc deleted file mode 100644 index 25ac622..0000000 Binary files a/backend_py/endpoints/__pycache__/auth.cpython-312.pyc and /dev/null differ diff --git a/backend_py/endpoints/__pycache__/messages.cpython-312.pyc b/backend_py/endpoints/__pycache__/messages.cpython-312.pyc deleted file mode 100644 index 0a68292..0000000 Binary files a/backend_py/endpoints/__pycache__/messages.cpython-312.pyc and /dev/null differ diff --git a/data/data.sqlite b/data/data.sqlite new file mode 100644 index 0000000..299dd8b Binary files /dev/null and b/data/data.sqlite differ diff --git a/data/db.sqlite b/data/db.sqlite index 7e073db..6e85316 100644 Binary files a/data/db.sqlite and b/data/db.sqlite differ diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..efdc014 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,58 @@ +FROM debian:trixie-slim AS base +ENV DEBIAN_FRONTEND=noninteractive + +# Install sys utils, dependencies and nginx +RUN apt-get update && \ + apt-get install -yq nginx npm python3 python3-pip + +# Copy files +COPY docker/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +RUN mkdir /simple-chat +COPY . /simple-chat + +# Install app dependencies +RUN pip3 install -r /simple-chat/simple_chat_api/requirements.txt --break-system-packages --ignore-installed +RUN npm install --prefix /simple-chat/frontend || true + +# Building +# || true to allow failure +RUN npm run build --prefix /simple-chat/frontend || true + +# Setup nginx +RUN rm /etc/nginx/nginx.conf +RUN ln -s /simple-chat/docker/nginx.conf /etc/nginx/nginx.conf +RUN mkdir -p /var/nginx + + + +# Dev stage +FROM base AS dev +ARG ROOT_PASSWD +ENV PIP_BREAK_SYSTEM_PACKAGES=1 +ENV DEV=true + +# additional dependencies for dev +RUN apt-get update && \ + apt-get install -y build-essential git openssh-server + +# SSH configuration +RUN if [ -n "$ROOT_PASSWD" ]; then echo "root:$ROOT_PASSWD" | chpasswd; else passwd -d root; fi && \ + echo "PermitRootLogin yes" > /etc/ssh/sshd_config && \ + echo "ListenAddress 0.0.0.0" >> /etc/ssh/sshd_config && \ + echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config && \ + echo "PermitEmptyPasswords no" >> /etc/ssh/sshd_config && \ + echo "Subsystem sftp /usr/lib/ssh/sftp-server" >> /etc/ssh/sshd_config + +RUN ssh-keygen -A && \ + # echo "sshd: ALL" > /etc/hosts.deny && \ + echo "sshd: ALL" > /etc/hosts.allow + +# Run entrypoint for dev +CMD ["bash", "/entrypoint.sh"] + +# Production stage +FROM base AS production +# Run entrypoint +CMD ["bash", "/entrypoint.sh"] diff --git a/docker/default.sqlite b/docker/default.sqlite new file mode 100644 index 0000000..d2f45ab Binary files /dev/null and b/docker/default.sqlite differ diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..71508c6 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if [ "$DEV" ]; then + echo "Running in development mode" + echo "Entering endless loop" + while true; do + sleep 20 + done +fi + +echo "Running in production mode" +echo "Reachable via port 8080" +nginx -g "daemon off;" & +cd /simple-chat || exit 1 +python3 -m simple_chat_api \ No newline at end of file diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..3dc81fa --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,46 @@ +# Linked to /etc/nginx/nginx.conf +worker_processes 1; +error_log /var/nginx/error.log; +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + + sendfile on; + tcp_nopush on; + + keepalive_timeout 65; + client_max_body_size 2000M; + gzip on; + + + server { + listen 8080; + listen [::]:8080; + + server_name simple-chat.default.local; + + root /simple-chat/frontend/dist; + index index.html; + + # Proxy specific API endpoints + location /api { + proxy_pass http://localhost:7000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # Handle all other routes - serve SPA for all other paths + location / { + try_files $uri $uri/ /index.html; + } + } +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b4c2253..2386c91 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1220,13 +1220,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", - "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.0", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -1234,9 +1234,9 @@ } }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index c5e690a..039d12a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,10 +5,10 @@ "type": "module", "scripts": { "dev": "vite", - "build": "run-p type-check \"build-only {@}\" --", + "build-typecheck": "run-p type-check \"build-only {@}\" --", "preview": "vite preview", "test:unit": "vitest", - "build-only": "vite build", + "build": "vite build", "type-check": "vue-tsc --build", "lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore", "lint:eslint": "eslint . --fix", diff --git a/frontend/src/components/HelloWorld.vue b/frontend/src/components/HelloWorld.vue deleted file mode 100644 index 0c62211..0000000 --- a/frontend/src/components/HelloWorld.vue +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/frontend/src/components/__tests__/HelloWorld.spec.ts b/frontend/src/components/__tests__/HelloWorld.spec.ts deleted file mode 100644 index ed3bb12..0000000 --- a/frontend/src/components/__tests__/HelloWorld.spec.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describe, it, expect } from 'vitest' - -import { mount } from '@vue/test-utils' -import HelloWorld from '../HelloWorld.vue' - -describe('HelloWorld', () => {}) diff --git a/frontend/src/composable/message.ts b/frontend/src/composable/message.ts index b859dac..c76e514 100644 --- a/frontend/src/composable/message.ts +++ b/frontend/src/composable/message.ts @@ -25,6 +25,15 @@ export const messageHandler = (room: string = 'general') => { }, }).then(async (response) => { let data = await getJsonOrError(response) + let latest_new_msg = Math.max(...Object.keys(data).map(Number), -Infinity) + if (latest_new_msg === -Infinity) { + throw new Error('Invalid server response') + } + if (messages.value[latest_new_msg]) { + // If the latest message is already in the cache, return the cached messages + return messages.value + } + messages.value = { ...messages.value, ...(data as Record) } return messages.value }) @@ -49,7 +58,7 @@ export const messageHandler = (room: string = 'general') => { sendMessage, messages, lastMsg: () => { - const highestKey = Math.max(...Object.keys(messages).map(Number)) + const highestKey: number = Math.max(...Object.keys(messages.value).map(Number), -Infinity) if (highestKey === -Infinity) { return undefined } @@ -59,7 +68,7 @@ export const messageHandler = (room: string = 'general') => { const keys = Object.keys(messages.value).map(Number) const index = keys.indexOf(Number(id)) if (index > 0) { - console.log('Previous message found:', messages.value[keys[index - 1]]) + //console.log('Previous message found:', messages.value[keys[index - 1]]) return messages.value[keys[index - 1]] } return undefined diff --git a/frontend/src/views/Chat.vue b/frontend/src/views/Chat.vue index e9abeb4..7e9e4ce 100644 --- a/frontend/src/views/Chat.vue +++ b/frontend/src/views/Chat.vue @@ -42,7 +42,6 @@ onMounted(() => { document.addEventListener('keypress', handleKeyPress) msg.requestMessages() msgTimer.value = setInterval(() => { - console.log('Polling for messages...') msg.requestMessages(msg.lastMsg()?.timestamp) }, 3000) // 3s }) @@ -57,7 +56,7 @@ onBeforeUnmount(() => {