tracker
All repositories: gitoria
4.7 KB
// project.hl — tracker.worldapi.org: STEP 1 (tracker.worldapi.org#1), an empty shell.// Login exactly like calendar/notes: ident only, no own passwords (README "How apps use// ident" of ident.worldapi.org). No shows, no data yet — later steps come from the creator// (CONCEPT.md).//// GET /login -> <ident>/login?key=<key>&return=<this app>/callback// /callback?ident_code=<code> the app's server exchanges the code for the identity's// short id (POST <ident>/api/exchange) and signs this// app's OWN session in (session.user = { identity })// POST /logout signs this app's session out (ident's own session,// and the browser's ident cookie, are untouched)//// Config (env; no committed secret — the first deploy registers this app in ident and sets// these, done by the architect, as with ident's own .env for SMTP):// TRACKER_PORT (45008), TRACKER_URL (this app's own origin, for the return URL),// IDENT_URL (https://ident.worldapi.org), TRACKER_KEY (pk_…), TRACKER_SECRET (sk_…)import WebFramework from 'hl:web'import { Response } from 'hl:http1'import { fetch } from 'hl:fetch'import { env } from 'hl:proc'import Styles from './styles.hl'import Home from './components/home.hl'static appTitle = "tracker"styles = Stylesport = env('TRACKER_PORT') != null ? toNumber(env('TRACKER_PORT')) : 45008selfUrl = env('TRACKER_URL') != null ? env('TRACKER_URL') : 'http://127.0.0.1:' + portidentUrl = env('IDENT_URL') != null ? env('IDENT_URL') : 'https://ident.worldapi.org'identKey = env('TRACKER_KEY') != null ? env('TRACKER_KEY') : ''identSecret = env('TRACKER_SECRET') != null ? env('TRACKER_SECRET') : ''escape = (s) => { return ('' + s).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"') }errorPage = (status, message) => {let html = '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>tracker</title></head><body style="margin:0;padding:2rem;font:16px/1.5 system-ui,sans-serif;color:rgb(195,200,205);background:rgb(25,30,35)"><p style="color:#f44747">' + escape(message) + '</p><p><a id="home" href="/" style="color:#4ec9b0">start page</a></p></body></html>'return new Response(html, { status = status headers = { 'Content-Type' = 'text/html; charset=utf-8' 'Cache-Control' = 'no-store' } })}redirect = (url) => { return new Response('', { status = 302 headers = { 'Location' = url 'Cache-Control' = 'no-store' } }) }// ---- GET /login: the login button --------------------------------------------------------login = (route, req) => {if (req.method != 'GET') { return errorPage(405, 'Use a GET request.') }if (identKey == '') { return errorPage(500, 'This app is not registered with ident yet (TRACKER_KEY/TRACKER_SECRET missing).') }let href = identUrl + '/login?key=' + identKey + '&return=' + encodeURIComponent(selfUrl + '/callback')return redirect(href)}// ---- /callback?ident_code=<code>: exchange it for the identity's short id ----------------callback = (route, req) => {let q = req.query != null ? req.query : {}let code = q.ident_codeif (code == null || code == '') { return errorPage(400, 'no ident_code on the callback.') }let r = fetch(identUrl + '/api/exchange', { method = 'POST' json = { key = identKey secret = identSecret code = code } timeoutMs = 5000 })let j = r.json()if (r.status != 200 || j == null || j.identity == null) {return errorPage(400, 'sign-in failed (' + r.status + '): ' + (j != null && j.error != null ? j.error : r.text()))}let s = req.sessionif (s == null) { return errorPage(500, 'no session — reload and try again.') }s.user = { identity = j.identity }return redirect('/')}// ---- POST /logout: ends THIS app's session; ident's own login is untouched --------------logout = (route, req) => {if (req.method != 'POST') { return redirect('/') }let s = req.sessionif (s != null) { s.user = null }return redirect('/')}routes = [{ pattern = "/favicon.ico" direct = "" }{ pattern = "/login" function = login }{ pattern = "/callback" function = callback }{ pattern = "/logout" function = logout }{ pattern = "/" component = Home }]// cookies are per host, not per port: an own name keeps this app's session apart from// ident's own (and from any other worldapi app sharing a dev host), see ident README "Design// tokens" / hybriel#10 hybriel#17.sessionCookie = 'trackersid'watching = env('TRACKER_WATCH') != '0'server = new WebFramework(routes = routes, styles = styles, port = port, sessionCookie = sessionCookie, watchMode = watching)
Branches
- mainmain branch
Latest commits
- 3691e176tracker#1: empty tracker with the ident login (state of 2026-09-27)mre