main.tsx (1916B)
1 // web/src/main.tsx - routes, and getting a token before any of them run 2 3 import m from 'mithril'; 4 import * as auth from './util/auth'; 5 import { ApiError, get } from './util/api'; 6 import { Chrome, Failed, session } from './component/layout'; 7 import { Jobs } from './screen/jobs'; 8 import { Job } from './screen/job'; 9 import { Task } from './screen/task'; 10 import { Projects } from './screen/projects'; 11 import { Project } from './screen/project'; 12 import { Workers, Worker } from './screen/workers'; 13 import { Users, Sessions } from './screen/users'; 14 15 const root = document.body; 16 17 function wrap(screen: m.Component, needsUser = false): m.RouteResolver { 18 return { 19 onmatch: () => (needsUser && !session.user ? auth.signIn() : undefined), 20 render: () => m(Chrome, m(screen)), 21 }; 22 } 23 24 const routes: m.RouteDefs = { 25 '/jobs': wrap(Jobs), 26 '/jobs/:job': wrap(Job), 27 '/tasks/:task': wrap(Task), 28 '/projects': wrap(Projects), 29 '/projects/:project': wrap(Project), 30 '/projects/:project/jobs': wrap(Jobs), 31 '/workers': wrap(Workers, true), 32 '/workers/:worker': wrap(Worker, true), 33 '/users': wrap(Users, true), 34 '/sessions': wrap(Sessions, true), 35 }; 36 37 function fail(error: unknown) { 38 m.mount(root, { view: () => m(Chrome, m(Failed, { error })) }); 39 } 40 41 async function start() { 42 let resume: string | null = null; 43 try { 44 resume = await auth.completeSignIn(); 45 } catch (e) { 46 fail(e); 47 return; 48 } 49 50 if (auth.signedIn()) { 51 try { 52 session.user = await get('/users/me'); 53 } catch (e) { 54 if ((e as ApiError).status === 401) auth.forget(); 55 // Still holding a credential means the conductor itself is unhappy. 56 if (auth.signedIn()) { 57 fail(e); 58 return; 59 } 60 } 61 } 62 63 m.route.prefix = '#!'; 64 m.route(root, '/jobs', routes); 65 if (resume) m.route.set(resume.replace(/^#!/, '')); 66 67 setInterval(() => { if (!document.hidden) m.redraw(); }, 1000); 68 } 69 70 start();