2026-07-25 14:09:48 +00:00
import type { FastifyInstance } from 'fastify'
export async function registerSchemas ( app : FastifyInstance ) : Promise < void > {
2026-06-14 01:45:40 -04:00
/**
* 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 : {
2026-07-25 14:09:48 +00:00
// -> 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.
2026-06-14 01:45:40 -04:00
type : 'string' ,
2026-07-25 14:09:48 +00:00
nullable : true ,
2026-06-14 01:45:40 -04:00
format : 'date-time' ,
2026-07-25 14:09:48 +00:00
description : 'RFC 3339 Date Time, or null if the user has never logged in'
2026-06-14 01:45:40 -04:00
}
}
})
2026-07-25 21:26:40 +00:00
/**
* 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' ]
}
}
})
2026-07-26 19:07:02 +00:00
/**
* 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' ]
}
}
})
2026-06-14 01:45:40 -04:00
/**
* USER - All fields
*/
app . addSchema ({
$id : 'User' ,
allOf : [
{
$ref : 'UserCore#'
},
{
type : 'object' ,
properties : {
meta : {
type : 'object' ,
additionalProperties : true
},
prefs : {
type : 'object' ,
additionalProperties : true
},
auth : {
2026-07-25 21:26:40 +00:00
type : 'array' ,
description :
'Authentication providers linked to this user. Secrets are never included — `config.isPasswordSet` and `config.tfaIsActive` 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
}
}
}
2026-06-14 01:45:40 -04:00
},
2026-07-25 21:26:40 +00:00
groups : {
type : 'array' ,
description : 'Groups this user belongs to.' ,
items : {
type : 'object' ,
properties : {
id : {
type : 'string' ,
format : 'uuid'
},
name : {
type : 'string'
}
}
}
2026-06-14 01:45:40 -04:00
}
}
}
]
})
}