Files
wiki/backend/api/schemas/user.ts
T

362 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { FastifyInstance } from 'fastify'
export async function registerSchemas(app: FastifyInstance): Promise<void> {
/**
* PASSKEY - One registered authenticator, without any of its key material
*/
app.addSchema({
$id: 'Passkey',
type: 'object',
properties: {
id: {
type: 'string',
description: 'The WebAuthn credential ID, base64url-encoded.'
},
name: {
type: 'string',
description: 'What the user called it, e.g. the device it lives on.'
},
siteHostname: {
type: 'string',
description:
'The hostname it was registered against. A passkey only works on that host, so this is stored rather than resolved from the site, which may since have been renamed.'
},
createdAt: {
type: 'string',
format: 'date-time',
description: 'RFC 3339 Date Time'
}
}
})
/**
* USER CORE - Essential fields only
*/
app.addSchema({
$id: 'UserCore',
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string',
minLength: 1,
maxLength: 255
},
email: {
type: 'string',
format: 'email'
},
hasAvatar: {
type: 'boolean'
},
isSystem: {
type: 'boolean'
},
isActive: {
type: 'boolean'
},
isVerified: {
type: 'boolean'
},
isProvisioned: {
type: 'boolean',
description:
'Whether a SCIM client owns this account. Set by the first provisioning write, so an account created here is adopted by a directory that later claims it — and it is what gates deprovisioning: `DELETE /_scim/v2/Users/:id` answers 404 for an account no directory owns.'
},
createdAt: {
type: 'string',
format: 'date-time',
description: 'RFC 3339 Date Time'
},
updatedAt: {
type: 'string',
format: 'date-time',
description: 'RFC 3339 Date Time'
},
lastLoginAt: {
// -> Users who have never logged in have no value here, and a plain `string` would make the
// serializer coerce null to an empty string. `nullable` is used rather than
// `type: ['string', 'null']` because the emitted spec declares OpenAPI 3.0, where a type
// array is not valid.
type: 'string',
nullable: true,
format: 'date-time',
description: 'RFC 3339 Date Time, or null if the user has never logged in'
}
}
})
/**
* USER DEFAULTS - Instance-wide defaults applied to new users
*/
app.addSchema({
$id: 'UserDefaults',
type: 'object',
properties: {
timezone: {
type: 'string',
description: 'IANA time zone name, e.g. `America/New_York`.',
maxLength: 255
},
dateFormat: {
type: 'string',
description: 'Empty string means the locale default.',
enum: ['', 'DD/MM/YYYY', 'DD.MM.YYYY', 'MM/DD/YYYY', 'YYYY-MM-DD', 'YYYY/MM/DD']
},
timeFormat: {
type: 'string',
enum: ['12h', '24h']
}
}
})
/**
* USER PROFILE - The logged in user's own view of itself
*
* The `meta` / `prefs` blobs are flattened into plain fields here. Values are deliberately typed as
* strings rather than enums: this is the serialized response, and a preference stored before an
* option existed must still be readable.
*/
app.addSchema({
$id: 'UserProfile',
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string'
},
email: {
type: 'string',
format: 'email'
},
hasAvatar: {
type: 'boolean'
},
handle: {
type: 'string',
description:
'The name this user is mentioned by in a comment, without the `@`. An empty string for somebody who has not set one, who is therefore not mentionable — nothing is derived from a display name on anybody’s behalf.'
},
location: {
type: 'string'
},
jobTitle: {
type: 'string'
},
pronouns: {
type: 'string'
},
locale: {
type: 'string',
description:
'The language the wiki writes to this user in — emails today. An empty string means the language of the site the mail is about.'
},
timezone: {
type: 'string',
description: 'IANA time zone name, or an empty string to use the client time zone.'
},
dateFormat: {
type: 'string',
description: 'Empty string means the locale default.'
},
timeFormat: {
type: 'string'
},
appearance: {
type: 'string'
},
cvd: {
type: 'string',
description: 'Color vision deficiency to adjust the palette for.'
}
}
})
/**
* PUBLIC USER PROFILE - What a user's profile page shows anyone who opens it
*
* Everything here is either something the user typed into their own profile to be seen, or the fact
* that they were here. The email is absent on purpose — see `PublicUserProfile` in the model.
*/
app.addSchema({
$id: 'PublicUserProfile',
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string'
},
hasAvatar: {
type: 'boolean'
},
handle: {
type: 'string',
description:
'The handle this user is mentioned by, without the `@`, or an empty string if they have not set one. Public because it is written into every comment that mentions them.'
},
location: {
type: 'string'
},
jobTitle: {
type: 'string'
},
pronouns: {
type: 'string'
},
timezone: {
type: 'string',
description:
'IANA time zone name, or an empty string for a user who never picked one — in which case there is no local time to show for them.'
},
lastLoginAt: {
// -> `nullable` rather than a type array, as on `UserCore`: the emitted spec is OpenAPI 3.0
type: 'string',
nullable: true,
format: 'date-time',
description: 'RFC 3339 Date Time, or null if the user has never logged in'
}
}
})
/**
* USER PROFILE UPDATE - The fields a user may change on its own profile
*
* The email is absent on purpose: it identifies the account and is the local strategy's username.
*/
app.addSchema({
$id: 'UserProfileUpdate',
type: 'object',
properties: {
name: {
type: 'string',
minLength: 1,
maxLength: 255
},
handle: {
type: 'string',
maxLength: 32,
pattern: '^$|^[A-Za-z0-9_-]{3,32}$',
description:
'The name to be mentioned by in comments, without the `@`. Unique across the wiki, case-insensitively; an empty string takes it off. 409 if somebody else already has it.'
},
location: {
type: 'string',
maxLength: 255
},
jobTitle: {
type: 'string',
maxLength: 255
},
pronouns: {
type: 'string',
maxLength: 255
},
locale: {
type: 'string',
description:
'The language to be written to in, as a locale code the wiki has installed. An empty string follows the site instead. A code naming no installed locale is ignored when the mail is rendered.',
maxLength: 255
},
timezone: {
type: 'string',
description: 'IANA time zone name, e.g. `America/New_York`.',
maxLength: 255
},
dateFormat: {
type: 'string',
description: 'Empty string means the locale default.',
enum: ['', 'DD/MM/YYYY', 'DD.MM.YYYY', 'MM/DD/YYYY', 'YYYY-MM-DD', 'YYYY/MM/DD']
},
timeFormat: {
type: 'string',
enum: ['12h', '24h']
},
appearance: {
type: 'string',
enum: ['site', 'light', 'dark']
},
cvd: {
type: 'string',
enum: ['none', 'protanopia', 'deuteranopia', 'tritanopia']
}
}
})
/**
* USER - All fields
*/
app.addSchema({
$id: 'User',
allOf: [
{
$ref: 'UserCore#'
},
{
type: 'object',
properties: {
meta: {
type: 'object',
additionalProperties: true
},
prefs: {
type: 'object',
additionalProperties: true
},
auth: {
type: 'array',
description:
'Authentication providers linked to this user. Secrets are never included — `config.isPasswordSet` and `config.isTfaSetup` report their state instead.',
items: {
type: 'object',
properties: {
authId: {
type: 'string',
format: 'uuid'
},
authName: {
type: 'string'
},
strategyKey: {
type: 'string'
},
strategyIcon: {
type: 'string'
},
config: {
type: 'object',
additionalProperties: true
}
}
}
},
groups: {
type: 'array',
description: 'Groups this user belongs to.',
items: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string'
}
}
}
}
}
}
]
})
}