aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorGuillermo Serrahima <dev@vylion.com>2026-03-12 11:09:46 +0100
committerGuillermo Serrahima <dev@vylion.com>2026-03-12 11:09:46 +0100
commit20abd199c381eeab138563af897b54ecadc57bae (patch)
treef1340c1d9e13585fcfd29f9fab2280fcd889387b /frontend
parent60a0ae44cdf9bece1c15f20386e2ffdf80d80e96 (diff)
downloadllostre-20abd199c381eeab138563af897b54ecadc57bae.tar.xz
llostre-20abd199c381eeab138563af897b54ecadc57bae.zip
Implemented OAuth login
Diffstat (limited to 'frontend')
-rw-r--r--frontend/login.html25
-rw-r--r--frontend/src/login.ts16
-rw-r--r--frontend/src/main.ts12
-rw-r--r--frontend/src/style.css17
-rw-r--r--frontend/vite.config.ts13
5 files changed, 82 insertions, 1 deletions
diff --git a/frontend/login.html b/frontend/login.html
new file mode 100644
index 0000000..0f5ba0b
--- /dev/null
+++ b/frontend/login.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>llostre - login</title>
+ <link rel="stylesheet" href="/src/style.css">
+</head>
+<body>
+ <div id="login-container">
+ <h2>log in to llostre</h2>
+ <form id="login-form">
+ <input
+ type="text"
+ id="handle-input"
+ placeholder="your_handle.bsky.social"
+ required
+ />
+ <button type="submit">log in</button>
+ </form>
+ <div id="login-error"></div>
+ </div>
+ <script type="module" src="/src/login.ts"></script>
+</body>
+</html>
diff --git a/frontend/src/login.ts b/frontend/src/login.ts
new file mode 100644
index 0000000..d8054c0
--- /dev/null
+++ b/frontend/src/login.ts
@@ -0,0 +1,16 @@
+const loginForm = document.getElementById('login-form') as HTMLFormElement;
+const handleInput = document.getElementById('handle-input') as HTMLInputElement;
+const loginError = document.getElementById('login-error') as HTMLDivElement;
+
+loginForm.addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const handle = handleInput.value.trim();
+
+ if (!handle) {
+ loginError.textContent = 'please enter a handle';
+ return;
+ }
+
+ loginError.textContent = '';
+ window.location.href = `/api/login?handle=${encodeURIComponent(handle)}`;
+});
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index 20c3bea..4868365 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -68,6 +68,14 @@ function renderAuthor(note: any): string {
}
async function main() {
+ const sessionCheck = await fetch('/api/session', {
+ credentials: 'include'
+ });
+ if (!sessionCheck.ok) {
+ window.location.href = '/login.html';
+ throw new Error('not authenticated');
+ }
+
const app = document.querySelector('#app');
if (!app) {
console.error('App container not found');
@@ -77,7 +85,9 @@ async function main() {
app.innerHTML = '<div class="spinner"></div>';
try {
- const response = await fetch('http://localhost:8080/api/posts');
+ const response = await fetch('/api/posts', {
+ credentials: 'include'
+ });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const notes = await response.json();
diff --git a/frontend/src/style.css b/frontend/src/style.css
index 549c596..700135c 100644
--- a/frontend/src/style.css
+++ b/frontend/src/style.css
@@ -13,6 +13,23 @@
-moz-osx-font-smoothing: grayscale;
}
+#login-container {
+ max-width: 400px;
+ margin: 2rem auto;
+ padding: 2rem;
+}
+
+#handle-input {
+ width: 100%;
+ padding: 0.5rem;
+ margin-bottom: 1rem;
+}
+
+#login-error {
+ color: red;
+ margin-top: 1rem;
+}
+
a {
font-weight: 500;
color: #646cff;
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
new file mode 100644
index 0000000..4420756
--- /dev/null
+++ b/frontend/vite.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vite'
+import { resolve } from 'path'
+
+export default defineConfig({
+ build: {
+ rollupOptions: {
+ input: {
+ main: resolve(__dirname, 'index.html'),
+ login: resolve(__dirname, 'login.html'),
+ },
+ },
+ },
+})