import { env } from 'bun'; import homepage from './index.html'; import robotsTxt from './static/robots.txt'; import sitemapTxt from './static/sitemap.txt'; // @ts-ignore ts2307 import icon from './static/favicon.png' with { type: 'file' }; const favicon = await Bun.file(icon).bytes(); const development = env.NODE_ENV !== 'production'; import { randomUUIDv7 } from 'bun'; import ptApi from './server/pt-api'; declare global { var loginTokens: Set; } if (!globalThis.loginTokens) { globalThis.loginTokens = new Set(); } const authCookie = 'pt-auth'; let entriesCache = ''; let entriesCacheTime = 0; const entriesCacheTimeLimit = 1000 * 15; async function getEntriesCached() { if (entriesCacheTime + entriesCacheTimeLimit < Date.now()) { const data = await ptApi.query(); entriesCache = JSON.stringify(data); entriesCacheTime = Date.now(); } return entriesCache; } Bun.serve({ routes: { '/': homepage, '/robots.txt': new Response(robotsTxt, { headers: { 'Content-Type': 'text/plain' }, }), '/sitemap.txt': new Response(sitemapTxt, { headers: { 'Content-Type': 'text/plain' }, }), '/favicon.ico': new Response(favicon, { headers: { 'Content-Type': 'image/png' }, }), '/health': new Response('OK'), '/api/entries': { async GET(req) { if (!isLoggedIn(req)) return unauthorizedResp(); return new Response(await getEntriesCached(), { headers: { 'Content-Type': 'application/json' } }); }, }, '/auth/logout': { async POST() { return new Response('Logout successful', { headers: logoutHeaders() }); }, }, '/auth/login': { async POST(req) { const json = await req.json(); const data = json as Record; const email = data.email; const password = data.password; if ( typeof email !== 'string' || typeof password !== 'string' || email.length < 3 || password.length === 0 ) { return new Response('Missing email or password', { status: 400 }); } await new Promise((resolve) => setTimeout(resolve, 100)); if (email === env.EMAIL && password === env.PASSWORD) { let token = randomUUIDv7('base64url'); while (globalThis.loginTokens.has(token)) { // generate a new token if it already exists // this is unlikely to happen, but just in case token = randomUUIDv7(); } const cookie = new Bun.Cookie({ name: authCookie, value: token, path: '/', maxAge: 60 * 60 * 24 * 31, secure: true, sameSite: 'strict', }); const headers = new Headers({ 'Set-Cookie': cookie.toString() }); globalThis.loginTokens.add(token); setTimeout(getEntriesCached, 1); return new Response('Login successful', { headers }); } await new Promise((resolve) => setTimeout(resolve, 900)); return new Response('Incorrect email or password', { status: 400, }); }, }, }, development, reusePort: true, // async fetch(req, server) { // return new Response("Not found", { status: 404 }); // }, }); function isLoggedIn(req: Request) { const cookie = new Bun.CookieMap(req.headers.get('cookie') || ''); const token = cookie.get(authCookie); if (!token) { return false; } return globalThis.loginTokens.has(token); } function logoutHeaders() { return new Headers({ 'Set-Cookie': new Bun.Cookie({ name: authCookie, path: '/', maxAge: -1, secure: true, sameSite: 'strict', }).toString(), }); } function unauthorizedResp() { return new Response('Unauthorized', { status: 401, headers: logoutHeaders() }); }