You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wiki/backend/api/schemas/user.ts

285 lines
6.9 KiB

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'
},
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'
},
location: {
type: 'string'
},
jobTitle: {
type: 'string'
},
pronouns: {
type: 'string'
},
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.'
}
}
})
/**
* 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
},
location: {
type: 'string',
maxLength: 255
},
jobTitle: {
type: 'string',
maxLength: 255
},
pronouns: {
type: 'string',
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'
}
}
}
}
}
}
]
})
}