tickets
All repositories: gitoria
5.3 KB
// components/you.hl — `/you` (ticket #7): the logged-in user's own page.// * the display name, and the PER-APP IDENTITY ID ident gave tickets for this identity —// shown ONLY here and only to its owner (an admin adds a person to a project by it). Ticket #20: the// projects the user is a member of, with their role.// * API TOKENS for machine clients (CONCEPT point 5): create (the token is shown ONCE — only// its sha256 is stored), list, revoke. `Authorization: Bearer <token>` acts as this user.// Logged out: a hint. A login in the shell (no reload) fills the page via the face `youData`.parent './main.hl'import { siteName } from '../project.hl'import { youOf, userOfSession, createToken, revokeToken, tokenRows } from '../users.hl'import { projectsOfUser } from '../store.hl'session = nullyou = youOf(session)present = you != nullabsent = you == nullyouName = you != null ? you.name : ''identity = you != null ? you.identity : ''myProjects = projectsOfUser(userOfSession(session))hasProjects = myProjects.length > 0noProjects = myProjects.length == 0tokens = you != null ? you.tokens : []hasTokens = tokens.length > 0noTokens = tokens.length == 0newLabel = ''newToken = ''hasNewToken = falsetokenMessage = ''__title = siteName + ' | you'View {youView {h1 { "You" }if (absent) { p { id = "youhint" class = "empty" "Log in with ident (top right) to see your account and your API tokens." } }if (present) {section { id = "account"h2 { "Account" }dl {dt { "Display name" }dd { id = "youname" youName }dt { "Your ident id" }dd { code { id = "youidentity" identity } }}p { class = "muted" "The id ident shows for the identity you logged in with. An admin of a project can add you with it." }}section { id = "myprojects"h2 { "Your projects" }if (noProjects) { p { id = "noprojects" class = "empty" "You are not a member of any project yet." } }ul { id = "projectlist"for (p of myProjects) { li { a { href = p.href p.title } " " span { class = "role" p.role } } }}}section { id = "tokens"h2 { "API tokens" }p { class = "muted" "For machine clients (the architect, the scheduler): a token acts as you on every API write — send it as " code { "Authorization: Bearer <token>" } ". It is shown once." }form { id = "tokenform"on submit(e) {e.preventDefault()emit makeToken(e)}label { "Label (optional, e.g. what uses it)"input { id = "tokenlabel" name = "label" maxlength = "60" autocomplete = "off" value = newLabel on input(e) { emit setLabel(e.target.value) } }}button { id = "tokencreate" type = "submit" "Create a token" }}if (hasNewToken) {tokenShown { id = "newtokenbox"p { "Your new token — copy it now, it is not shown again:" }code { id = "newtoken" newToken }button { id = "tokendone" type = "button" class = "quiet" "I copied it" on click(e) { emit hideToken(e) } }}}p { id = "tokenmessage" class = "message" tokenMessage }if (noTokens) { p { id = "notokens" class = "empty" "No tokens." } }ul { id = "tokenlist"for (t of tokens) {li {span { class = "label" t.label }time { t.created }button { type = "button" class = "quiet danger revoke" value = t.id "Revoke" on click(e) { emit revoke(e.target.value) } }}}}}}}}fill = (y) => {you = ypresent = y != nullabsent = y == nullyouName = y != null ? y.name : ''identity = y != null ? y.identity : ''showTokens(y != null ? y.tokens : [])}showTokens = (list) => {tokens = listhasTokens = list.length > 0noTokens = list.length == 0}on setLabel(v) { newLabel = v }on makeToken(e) {let r = emit server tokenCreate(newLabel)if (r == null || r.error != null) {tokenMessage = r != null ? r.error : 'could not create the token'return null}tokenMessage = ''newLabel = ''newToken = r.tokenhasNewToken = trueshowTokens(r.tokens)}on hideToken(e) {newToken = ''hasNewToken = false}on revoke(id) {let r = emit server tokenRevoke(id)if (r == null || r.error != null) {tokenMessage = r != null ? r.error : 'could not revoke the token'return null}tokenMessage = ''showTokens(r.tokens)}// a login / name / logout in the shell, without a reloadon client signedIn(tag, info) {let y = emit server youData()fill(y)let mine = emit server myProjectsData()myProjects = mine != null && mine.projects != null ? mine.projects : []hasProjects = myProjects.length > 0noProjects = myProjects.length == 0}on client signedOut(tag) {fill(null)newToken = ''hasNewToken = false}// ---- the faces (#31: only a real session) -------------------------------------------------on server youData(session) { return youOf(session) }on server myProjectsData(session) { return { projects = projectsOfUser(userOfSession(session)) } }on server tokenCreate(label, session) {let u = userOfSession(session)if (u == null) { return { error = 'log in with ident first' } }if (u.name == '') { return { error = 'choose a display name first' } }return createToken(u.id, label)}on server tokenRevoke(id, session) {let u = userOfSession(session)if (u == null) { return { error = 'log in with ident first' } }return revokeToken(u.id, id)}
Branches
- mainmain branch
Latest commits
- 2ab91ee9tickets: gate checks rows appear once (session sync); re-vendor to ff51cf46 stopped on hybriel#122, stays 837fe120mre
- e01c2b1dtickets#24: installable app (manifest, service worker, offline list), own icon; gate waits for the hello's pongmre
- 752fbb7fdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- 38bdd5e4deploy.sh: never send .git or .gitignore to Byrodinmre
- f12fa1bcState of 2026-09-27, before the move to gitoriamre