refactor(scripts): update schema generation logic to adapt to new project structure

Former-commit-id: e0345494d98e2a4f07601ad3f9db74ce4d55465f [formerly dfcbb2b70f9c685849b0d9902334f56cfa0dd303] [formerly e7107aef101ebe9720b1c9312aba5ec3976ff057 [formerly d848268dd6bbe95d792536073d3f7a49daf87b20]]
Former-commit-id: eecb2c8c8f9b34fcb08dcc671477f991ff488f62 [formerly 9a670f559aa2ebfca6bdeed28077d59172f122ea]
Former-commit-id: 544a60e83d23b531ba4597e37d8fb334ddacfea7
This commit is contained in:
melvinchia3636
2025-10-05 12:09:16 +08:00
parent d42bf4c2f5
commit fdb2fdfe3f
19 changed files with 308 additions and 347 deletions

View File

@@ -1,7 +1,7 @@
import { LoggingService } from '@server/core/functions/logging/loggingService'
import chalk from 'chalk'
import dotenv from 'dotenv'
import fs from 'fs/promises'
import fs from 'fs'
import _ from 'lodash'
import path from 'path'
import PocketBase, { type CollectionModel } from 'pocketbase'
@@ -39,7 +39,10 @@ interface FieldTypeMapping {
// Constants
const PATHS = {
ENV_FILE: path.resolve(__dirname, '../env/.env.local'),
MODULES_DIR: path.resolve(__dirname, '../server/src/lib'),
MODULES_DIRS: [
path.resolve(__dirname, '../server/src/lib/**/schema.ts'),
path.resolve(__dirname, '../apps/**/server/schema.ts')
],
CORE_SCHEMA: path.resolve(__dirname, '../server/src/core/schema.ts')
} as const
@@ -154,22 +157,29 @@ async function buildModuleCollectionsMap(
): Promise<ModuleCollectionsMap> {
const moduleCollectionsMap: ModuleCollectionsMap = {}
let allModules: Array<{ name: string; isDirectory(): boolean }>
let allModules: string[] = []
try {
const moduleEntries = await fs.readdir(PATHS.MODULES_DIR, {
withFileTypes: true
})
allModules = moduleEntries.filter(entry => entry.isDirectory())
allModules = PATHS.MODULES_DIRS.map(dir => fs.globSync(dir))
.flat()
.map(entry => entry.split('/').slice(0, -1).join('/'))
} catch (error) {
LoggingService.error(`Failed to read modules directory: ${error}`)
process.exit(1)
}
console.log(allModules)
for (const collection of collections) {
const matchingModule = allModules.find(module =>
collection.name.startsWith(_.snakeCase(module.name))
collection.name.startsWith(
_.snakeCase(
module
.replace(/\/server$/, '')
.split('/')
.pop() || ''
)
)
)
if (!matchingModule) {
@@ -179,10 +189,18 @@ async function buildModuleCollectionsMap(
continue
}
if (!moduleCollectionsMap[matchingModule.name]) {
moduleCollectionsMap[matchingModule.name] = []
const moduleName = matchingModule
.replace(/\/server$/, '')
.split('/')
.pop()
const key = `${matchingModule}|${moduleName}`
if (!moduleCollectionsMap[key]) {
moduleCollectionsMap[key] = []
}
moduleCollectionsMap[matchingModule.name].push(collection)
moduleCollectionsMap[key].push(collection)
}
const totalCollections = Object.values(moduleCollectionsMap).flat().length
@@ -242,12 +260,20 @@ export default ${_.camelCase(moduleName)}Schemas
}
// Generate main schema content
function generateMainSchemaContent(moduleNames: string[]): string {
const imports = moduleNames
.map(
moduleName =>
` ${moduleName}: (await import('@lib/${moduleName === 'users' ? 'user' : _.camelCase(moduleName)}/schema')).default,`
)
function generateMainSchemaContent(moduleDirs: string[]): string {
const imports = moduleDirs
.map(moduleDir => {
const [moduleDirPath, moduleDirName] = moduleDir.split('|')
const targetPath = path.join(
'@lib/',
moduleDirName,
moduleDirPath.split(moduleDirName).pop() || '',
'schema'
)
return ` ${_.snakeCase(moduleDirName)}: (await import('${targetPath}')).default,`
})
.join('\n')
return `import flattenSchemas from '@functions/utils/flattenSchema'
@@ -269,10 +295,15 @@ async function writeFormattedFile(
): Promise<void> {
try {
const formattedContent = await prettier.format(content, {
parser: 'typescript'
parser: 'typescript',
semi: false,
singleQuote: true,
trailingComma: 'none',
arrowParens: 'avoid',
endOfLine: 'auto'
})
await fs.writeFile(filePath, formattedContent)
fs.writeFileSync(filePath, formattedContent)
} catch (error) {
LoggingService.error(`Failed to write file ${filePath}: ${error}`)
throw error
@@ -285,11 +316,11 @@ async function generateSchemas(
): Promise<SchemaGenerationResult> {
const moduleSchemas: Record<string, string> = {}
const moduleNames: string[] = []
const moduleDirs: string[] = []
for (const [moduleDir, collections] of Object.entries(moduleCollectionsMap)) {
const [moduleDirPath, moduleDirName] = moduleDir.split('|')
for (const [moduleDirName, collections] of Object.entries(
moduleCollectionsMap
)) {
if (!collections.length) {
LoggingService.warn(
`No collections found for module ${chalk.bold(moduleDirName)}`
@@ -299,7 +330,7 @@ async function generateSchemas(
const moduleName = collections[0].name.split('__')[0]
moduleNames.push(moduleName)
moduleDirs.push(moduleDir)
const moduleSchemaContent = generateModuleSchemaContent(
moduleName,
@@ -309,11 +340,7 @@ async function generateSchemas(
moduleSchemas[moduleDirName] = moduleSchemaContent
// Write individual module schema file
const moduleSchemaPath = path.join(
PATHS.MODULES_DIR,
moduleDirName,
'schema.ts'
)
const moduleSchemaPath = path.join(moduleDirPath, 'schema.ts')
await writeFormattedFile(moduleSchemaPath, moduleSchemaContent)
@@ -322,7 +349,7 @@ async function generateSchemas(
)
}
const mainSchemaContent = generateMainSchemaContent(moduleNames)
const mainSchemaContent = generateMainSchemaContent(moduleDirs)
return { moduleSchemas, mainSchemaContent }
}

View File

@@ -121,7 +121,7 @@ export class Create<
}
const result = await this._pb
.collection((this.collectionKey as string).replace(/^users__/, ''))
.collection((this.collectionKey as string).replace(/^user__/, ''))
.create(this._data, {
expand: this._expand,
fields: this._fields,

View File

@@ -55,7 +55,7 @@ export class Delete<TCollectionKey extends CollectionKey>
}
const result = await this._pb
.collection((this.collectionKey as string).replace(/^users__/, ''))
.collection((this.collectionKey as string).replace(/^user__/, ''))
.delete(this._recordId)
LoggingService.debug(

View File

@@ -102,7 +102,7 @@ export class GetFirstListItem<
: 'id != ""' // Default filter to ensure we get a valid response
const result = await this._pb
.collection((this.collectionKey as string).replace(/^users__/, ''))
.collection((this.collectionKey as string).replace(/^user__/, ''))
.getFirstListItem(filterString, {
sort: this._sort,
expand: this._expand,

View File

@@ -140,7 +140,7 @@ export class GetFullList<
: undefined
const result = (await this._pb
.collection((this.collectionKey as string).replace(/^users__/, ''))
.collection((this.collectionKey as string).replace(/^user__/, ''))
.getFullList({
filter: filterString,
sort: this._sort,

View File

@@ -135,7 +135,7 @@ export class GetList<
: undefined
const result = (await this._pb
.collection((this.collectionKey as string).replace(/^users__/, ''))
.collection((this.collectionKey as string).replace(/^user__/, ''))
.getList(this._page, this._perPage, {
filter: filterString,
sort: this._sort,

View File

@@ -114,7 +114,7 @@ export class GetOne<
}
const result = this._pb
.collection((this.collectionKey as string).replace(/^users__/, ''))
.collection((this.collectionKey as string).replace(/^user__/, ''))
.getOne(this._itemId, {
expand: this._expand,
fields: this._fields

View File

@@ -147,7 +147,7 @@ export class Update<
}
const result = await this._pb
.collection((this.collectionKey as string).replace(/^users__/, ''))
.collection((this.collectionKey as string).replace(/^user__/, ''))
.update(this._recordId, this._data, {
expand: this._expand,
fields: this._fields

View File

@@ -96,7 +96,7 @@ export async function connectToPocketBase(
* Handles special cases like users__users -> users
*/
function mapCollectionName(collectionName: string): string {
return collectionName === 'users__users' ? 'users' : collectionName
return collectionName === 'user__users' ? 'users' : collectionName
}
/**

View File

@@ -117,7 +117,6 @@ const coreRoutes = forgeRouter({
apiKeys: (await import('../../lib/apiKeys')).default,
pixabay: (await import('../../lib/pixabay')).default,
locations: (await import('../../lib/locations')).default,
modules: (await import('../../lib/modules')).default,
backups: (await import('../../lib/backups')).default,
database: (await import('../../lib/database')).default,
ping,

View File

@@ -1,7 +1,7 @@
import flattenSchemas from '@functions/utils/flattenSchema'
export const SCHEMAS = {
users: (await import('@lib/user/schema')).default,
user: (await import('@lib/user/schema')).default,
todo_list: (await import('@lib/todoList/server/schema')).default,
code_time: (await import('@lib/codeTime/server/schema')).default,
idea_box: (await import('@lib/ideaBox/server/schema')).default,

View File

@@ -28,7 +28,7 @@ const createOrUpdate = forgeController
const id = pb.instance.authStore.record!.id
await pb.update
.collection('users__users')
.collection('user__users')
.id(id)
.data({
APIKeysMasterPasswordHash
@@ -49,7 +49,7 @@ const verify = forgeController
const decryptedMaster = decrypt2(password, challenge)
const user = await pb.getOne.collection('users__users').id(id).execute()
const user = await pb.getOne.collection('user__users').id(id).execute()
const { APIKeysMasterPasswordHash } = user

View File

@@ -1,4 +1,4 @@
import { z } from "zod";
import z from 'zod'
const apiKeysSchemas = {
entries: {
@@ -9,129 +9,129 @@ const apiKeysSchemas = {
icon: z.string(),
key: z.string(),
created: z.string(),
updated: z.string(),
updated: z.string()
}),
raw: {
id: "zhm94t1hfoq97oa",
id: 'zhm94t1hfoq97oa',
listRule: '@request.auth.id != ""',
viewRule: '@request.auth.id != ""',
createRule: '@request.auth.id != ""',
updateRule: '@request.auth.id != ""',
deleteRule: '@request.auth.id != ""',
name: "api_keys__entries",
type: "base",
name: 'api_keys__entries',
type: 'base',
fields: [
{
autogeneratePattern: "[a-z0-9]{15}",
autogeneratePattern: '[a-z0-9]{15}',
hidden: false,
id: "text3208210256",
id: 'text3208210256',
max: 15,
min: 15,
name: "id",
pattern: "^[a-z0-9]+$",
name: 'id',
pattern: '^[a-z0-9]+$',
presentable: false,
primaryKey: true,
required: true,
system: true,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "4k7c8zdb",
id: '4k7c8zdb',
max: 0,
min: 0,
name: "keyId",
pattern: "",
name: 'keyId',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "tcnowiel",
id: 'tcnowiel',
max: 0,
min: 0,
name: "name",
pattern: "",
name: 'name',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "l1a35cql",
id: 'l1a35cql',
max: 0,
min: 0,
name: "description",
pattern: "",
name: 'description',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "vhho1bhf",
id: 'vhho1bhf',
max: 0,
min: 0,
name: "icon",
pattern: "",
name: 'icon',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "ddcugsw3",
id: 'ddcugsw3',
max: 0,
min: 0,
name: "key",
pattern: "",
name: 'key',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
hidden: false,
id: "autodate2990389176",
name: "created",
id: 'autodate2990389176',
name: 'created',
onCreate: true,
onUpdate: false,
presentable: false,
system: false,
type: "autodate",
type: 'autodate'
},
{
hidden: false,
id: "autodate3332085495",
name: "updated",
id: 'autodate3332085495',
name: 'updated',
onCreate: true,
onUpdate: true,
presentable: false,
system: false,
type: "autodate",
},
type: 'autodate'
}
],
indexes: [
"CREATE UNIQUE INDEX `idx_V6OVTl5FtN` ON `api_keys__entries` (`keyId`)",
'CREATE UNIQUE INDEX `idx_V6OVTl5FtN` ON `api_keys__entries` (`keyId`)'
],
system: false,
},
},
};
system: false
}
}
}
export default apiKeysSchemas;
export default apiKeysSchemas

View File

@@ -1,43 +0,0 @@
import { forgeController, forgeRouter } from '@functions/routes'
import { ClientError } from '@functions/routes/utils/response'
import z from 'zod'
const toggle = forgeController
.mutation()
.description('Toggle a module on/off')
.input({
query: z.object({
id: z.string()
})
})
.callback(async ({ pb, query: { id } }) => {
const user = pb.instance.authStore.record
if (!user) {
throw new ClientError('Unauthorized to toggle module')
}
const modules = user.enabledModules || []
if (!modules.includes(id)) {
modules.push(id)
} else {
const index = modules.indexOf(id)
if (index > -1) {
modules.splice(index, 1)
}
}
await pb.update
.collection('users__users')
.id(user.id)
.data({
enabledModules: modules
})
.execute()
})
export default forgeRouter({
toggle
})

View File

@@ -122,7 +122,7 @@ const updateBgImage = forgeController
})
.callback(async ({ pb, media: { file } }) => {
const newRecord = await pb.update
.collection('users__users')
.collection('user__users')
.id(pb.instance.authStore.record!.id)
.data({
...(await getMedia('bgImage', file)),
@@ -150,7 +150,7 @@ const deleteBgImage = forgeController
.statusCode(204)
.callback(({ pb }) =>
pb.update
.collection('users__users')
.collection('user__users')
.id(pb.instance.authStore.record!.id)
.data({
bgImage: null
@@ -199,7 +199,7 @@ const updatePersonalization = forgeController
}
await pb.update
.collection('users__users')
.collection('user__users')
.id(pb.instance.authStore.record!.id)
.data(toBeUpdated)
.execute()

View File

@@ -18,7 +18,7 @@ const updateAvatar = forgeController
const { id } = pb.instance.authStore.record!
const newRecord = await pb.update
.collection('users__users')
.collection('user__users')
.id(id)
.data(fileResult)
.execute()
@@ -35,7 +35,7 @@ const deleteAvatar = forgeController
const { id } = pb.instance.authStore.record!
await pb.update
.collection('users__users')
.collection('user__users')
.id(id)
.data({
avatar: ''
@@ -84,7 +84,7 @@ const updateProfile = forgeController
if (Object.keys(updateData).length > 0) {
await pb.update
.collection('users__users')
.collection('user__users')
.id(id)
.data(updateData)
.execute()

View File

@@ -144,7 +144,7 @@ const verifyAndEnable = forgeController
}
await pb.update
.collection('users__users')
.collection('user__users')
.id(pb.instance.authStore.record!.id)
.data({
twoFASecret: encrypt(
@@ -169,7 +169,7 @@ const disable = forgeController
}
await pb.update
.collection('users__users')
.collection('user__users')
.id(pb.instance.authStore.record!.id)
.data({
twoFASecret: ''

View File

@@ -1,4 +1,4 @@
import { z } from "zod";
import z from 'zod'
const usersSchemas = {
users: {
@@ -12,15 +12,13 @@ const usersSchemas = {
name: z.string(),
avatar: z.string(),
dateOfBirth: z.string(),
theme: z.enum(["system", "light", "dark"]),
theme: z.enum(['system', 'light', 'dark']),
color: z.string(),
bgTemp: z.string(),
bgImage: z.string(),
backdropFilters: z.any(),
fontFamily: z.string(),
language: z.enum(["zh-CN", "en", "ms", "zh-TW", ""]),
moduleConfigs: z.any(),
enabledModules: z.any(),
language: z.enum(['zh-CN', 'en', 'ms', 'zh-TW', '']),
dashboardLayout: z.any(),
spotifyAccessToken: z.string(),
spotifyRefreshToken: z.string(),
@@ -31,487 +29,467 @@ const usersSchemas = {
twoFASecret: z.string(),
fontScale: z.number(),
created: z.string(),
updated: z.string(),
updated: z.string()
}),
raw: {
id: "_pb_users_auth_",
listRule: "id = @request.auth.id",
viewRule: "id = @request.auth.id",
createRule: "",
updateRule: "id = @request.auth.id",
id: '_pb_users_auth_',
listRule: 'id = @request.auth.id',
viewRule: 'id = @request.auth.id',
createRule: '',
updateRule: 'id = @request.auth.id',
deleteRule: null,
name: "users",
type: "auth",
name: 'users',
type: 'auth',
fields: [
{
autogeneratePattern: "[a-z0-9]{15}",
autogeneratePattern: '[a-z0-9]{15}',
hidden: false,
id: "text3208210256",
id: 'text3208210256',
max: 15,
min: 15,
name: "id",
pattern: "^[a-z0-9]+$",
name: 'id',
pattern: '^[a-z0-9]+$',
presentable: false,
primaryKey: true,
required: true,
system: true,
type: "text",
type: 'text'
},
{
cost: 10,
hidden: true,
id: "password901924565",
id: 'password901924565',
max: 0,
min: 8,
name: "password",
pattern: "",
name: 'password',
pattern: '',
presentable: false,
required: true,
system: true,
type: "password",
type: 'password'
},
{
autogeneratePattern: "[a-zA-Z0-9_]{50}",
autogeneratePattern: '[a-zA-Z0-9_]{50}',
hidden: true,
id: "text2504183744",
id: 'text2504183744',
max: 60,
min: 30,
name: "tokenKey",
pattern: "",
name: 'tokenKey',
pattern: '',
presentable: false,
primaryKey: false,
required: true,
system: true,
type: "text",
type: 'text'
},
{
exceptDomains: null,
hidden: false,
id: "email3885137012",
name: "email",
id: 'email3885137012',
name: 'email',
onlyDomains: null,
presentable: false,
required: false,
system: true,
type: "email",
type: 'email'
},
{
hidden: false,
id: "bool1547992806",
name: "emailVisibility",
id: 'bool1547992806',
name: 'emailVisibility',
presentable: false,
required: false,
system: true,
type: "bool",
type: 'bool'
},
{
hidden: false,
id: "bool256245529",
name: "verified",
id: 'bool256245529',
name: 'verified',
presentable: false,
required: false,
system: true,
type: "bool",
type: 'bool'
},
{
autogeneratePattern: "users[0-9]{6}",
autogeneratePattern: 'users[0-9]{6}',
hidden: false,
id: "text4166911607",
id: 'text4166911607',
max: 150,
min: 3,
name: "username",
pattern: "^[\\w][\\w\\.\\-]*$",
name: 'username',
pattern: '^[\\w][\\w\\.\\-]*$',
presentable: false,
primaryKey: false,
required: true,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "users_name",
id: 'users_name',
max: 0,
min: 0,
name: "name",
pattern: "",
name: 'name',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
hidden: false,
id: "users_avatar",
id: 'users_avatar',
maxSelect: 1,
maxSize: 5242880,
mimeTypes: [
"image/jpeg",
"image/png",
"image/svg+xml",
"image/gif",
"image/webp",
'image/jpeg',
'image/png',
'image/svg+xml',
'image/gif',
'image/webp'
],
name: "avatar",
name: 'avatar',
presentable: false,
protected: false,
required: false,
system: false,
thumbs: ["256x0"],
type: "file",
thumbs: ['256x0'],
type: 'file'
},
{
hidden: false,
id: "fsmwvydl",
max: "",
min: "",
name: "dateOfBirth",
id: 'fsmwvydl',
max: '',
min: '',
name: 'dateOfBirth',
presentable: false,
required: false,
system: false,
type: "date",
type: 'date'
},
{
hidden: false,
id: "nppdhyft",
id: 'nppdhyft',
maxSelect: 1,
name: "theme",
name: 'theme',
presentable: false,
required: true,
system: false,
type: "select",
values: ["system", "light", "dark"],
type: 'select',
values: ['system', 'light', 'dark']
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "1ik4v2wa",
id: '1ik4v2wa',
max: 0,
min: 0,
name: "color",
pattern: "",
name: 'color',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "d0lgczhi",
id: 'd0lgczhi',
max: 0,
min: 0,
name: "bgTemp",
pattern: "",
name: 'bgTemp',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
hidden: false,
id: "xjb7bo9y",
id: 'xjb7bo9y',
maxSelect: 1,
maxSize: 5242880000,
mimeTypes: null,
name: "bgImage",
name: 'bgImage',
presentable: false,
protected: false,
required: false,
system: false,
thumbs: null,
type: "file",
type: 'file'
},
{
hidden: false,
id: "apt6wrsc",
id: 'apt6wrsc',
maxSize: 2000000,
name: "backdropFilters",
name: 'backdropFilters',
presentable: false,
required: false,
system: false,
type: "json",
type: 'json'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "c34od4fd",
id: 'c34od4fd',
max: 0,
min: 0,
name: "fontFamily",
pattern: "",
name: 'fontFamily',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
hidden: false,
id: "bv7hnazi",
id: 'bv7hnazi',
maxSelect: 1,
name: "language",
name: 'language',
presentable: false,
required: false,
system: false,
type: "select",
values: ["zh-CN", "en", "ms", "zh-TW"],
type: 'select',
values: ['zh-CN', 'en', 'ms', 'zh-TW']
},
{
hidden: false,
id: "wu4w5mks",
id: 'wjxwozyv',
maxSize: 2000000,
name: "moduleConfigs",
name: 'dashboardLayout',
presentable: false,
required: false,
system: false,
type: "json",
type: 'json'
},
{
autogeneratePattern: '',
hidden: false,
id: "a7qz6gsx",
maxSize: 2000000,
name: "enabledModules",
presentable: false,
required: false,
system: false,
type: "json",
},
{
hidden: false,
id: "wjxwozyv",
maxSize: 2000000,
name: "dashboardLayout",
presentable: false,
required: false,
system: false,
type: "json",
},
{
autogeneratePattern: "",
hidden: false,
id: "t4oem4rk",
id: 't4oem4rk',
max: 0,
min: 0,
name: "spotifyAccessToken",
pattern: "",
name: 'spotifyAccessToken',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "pmtavvft",
id: 'pmtavvft',
max: 0,
min: 0,
name: "spotifyRefreshToken",
pattern: "",
name: 'spotifyRefreshToken',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
hidden: false,
id: "n6afyo6x",
max: "",
min: "",
name: "spotifyTokenExpires",
id: 'n6afyo6x',
max: '',
min: '',
name: 'spotifyTokenExpires',
presentable: false,
required: false,
system: false,
type: "date",
type: 'date'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "ivwqhyvx",
id: 'ivwqhyvx',
max: 0,
min: 0,
name: "masterPasswordHash",
pattern: "",
name: 'masterPasswordHash',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "bva2tmvh",
id: 'bva2tmvh',
max: 0,
min: 0,
name: "journalMasterPasswordHash",
pattern: "",
name: 'journalMasterPasswordHash',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "afbxzben",
id: 'afbxzben',
max: 0,
min: 0,
name: "APIKeysMasterPasswordHash",
pattern: "",
name: 'APIKeysMasterPasswordHash',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
autogeneratePattern: "",
autogeneratePattern: '',
hidden: false,
id: "oi6j9b9d",
id: 'oi6j9b9d',
max: 0,
min: 0,
name: "twoFASecret",
pattern: "",
name: 'twoFASecret',
pattern: '',
presentable: false,
primaryKey: false,
required: false,
system: false,
type: "text",
type: 'text'
},
{
hidden: false,
id: "number3058924606",
id: 'number3058924606',
max: null,
min: 0.1,
name: "fontScale",
name: 'fontScale',
onlyInt: false,
presentable: false,
required: true,
system: false,
type: "number",
type: 'number'
},
{
hidden: false,
id: "autodate2990389176",
name: "created",
id: 'autodate2990389176',
name: 'created',
onCreate: true,
onUpdate: false,
presentable: false,
system: false,
type: "autodate",
type: 'autodate'
},
{
hidden: false,
id: "autodate3332085495",
name: "updated",
id: 'autodate3332085495',
name: 'updated',
onCreate: true,
onUpdate: true,
presentable: false,
system: false,
type: "autodate",
},
type: 'autodate'
}
],
indexes: [
"CREATE UNIQUE INDEX `__pb_users_auth__username_idx` ON `users` (username COLLATE NOCASE)",
'CREATE UNIQUE INDEX `__pb_users_auth__username_idx` ON `users` (username COLLATE NOCASE)',
"CREATE UNIQUE INDEX `__pb_users_auth__email_idx` ON `users` (`email`) WHERE `email` != ''",
"CREATE UNIQUE INDEX `__pb_users_auth__tokenKey_idx` ON `users` (`tokenKey`)",
'CREATE UNIQUE INDEX `__pb_users_auth__tokenKey_idx` ON `users` (`tokenKey`)'
],
system: false,
authRule: "verified=true",
authRule: 'verified=true',
manageRule: null,
authAlert: {
enabled: true,
emailTemplate: {
subject: "Login from a new location",
body: "<p>Hello,</p>\n<p>We noticed a login to your {APP_NAME} account from a new location.</p>\n<p>If this was you, you may disregard this email.</p>\n<p><strong>If this wasn't you, you should immediately change your {APP_NAME} account password to revoke access from all other locations.</strong></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
},
subject: 'Login from a new location',
body: "<p>Hello,</p>\n<p>We noticed a login to your {APP_NAME} account from a new location.</p>\n<p>If this was you, you may disregard this email.</p>\n<p><strong>If this wasn't you, you should immediately change your {APP_NAME} account password to revoke access from all other locations.</strong></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>"
}
},
oauth2: {
providers: [
{
pkce: null,
name: "github",
clientId: "3ffe0179e120fd734d1c",
authURL: "",
tokenURL: "",
userInfoURL: "",
displayName: "",
extra: null,
name: 'github',
clientId: '3ffe0179e120fd734d1c',
authURL: '',
tokenURL: '',
userInfoURL: '',
displayName: '',
extra: null
},
{
pkce: null,
name: "google",
name: 'google',
clientId:
"667707161573-3t64t2s6g5qr6rve19qibkmr6fbiq9f7.apps.googleusercontent.com",
authURL: "",
tokenURL: "",
userInfoURL: "",
displayName: "",
extra: null,
},
'667707161573-3t64t2s6g5qr6rve19qibkmr6fbiq9f7.apps.googleusercontent.com',
authURL: '',
tokenURL: '',
userInfoURL: '',
displayName: '',
extra: null
}
],
mappedFields: {
id: "username",
name: "name",
username: "name",
avatarURL: "avatar",
id: 'username',
name: 'name',
username: 'name',
avatarURL: 'avatar'
},
enabled: true,
enabled: true
},
passwordAuth: {
enabled: true,
identityFields: ["email", "username"],
identityFields: ['email', 'username']
},
mfa: {
enabled: false,
duration: 1800,
rule: "",
rule: ''
},
otp: {
enabled: true,
duration: 180,
length: 6,
emailTemplate: {
subject: "OTP for {APP_NAME}",
body: "<p>Hello,</p>\n<p>Your one-time password is: <strong>{OTP}</strong></p>\n<p><i>If you didn't ask for the one-time password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
},
subject: 'OTP for {APP_NAME}',
body: "<p>Hello,</p>\n<p>Your one-time password is: <strong>{OTP}</strong></p>\n<p><i>If you didn't ask for the one-time password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>"
}
},
authToken: {
duration: 1209600,
duration: 1209600
},
passwordResetToken: {
duration: 1800,
duration: 1800
},
emailChangeToken: {
duration: 1800,
duration: 1800
},
verificationToken: {
duration: 604800,
duration: 604800
},
fileToken: {
duration: 120,
duration: 120
},
verificationTemplate: {
subject: "Verify your {APP_NAME} email",
body: '<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class="btn" href="{APP_URL}/_/#/auth/confirm-verification/{TOKEN}" target="_blank" rel="noopener">Verify</a>\n</p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>',
subject: 'Verify your {APP_NAME} email',
body: '<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class="btn" href="{APP_URL}/_/#/auth/confirm-verification/{TOKEN}" target="_blank" rel="noopener">Verify</a>\n</p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>'
},
resetPasswordTemplate: {
subject: "Reset your {APP_NAME} password",
body: '<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class="btn" href="{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}" target="_blank" rel="noopener">Reset password</a>\n</p>\n<p><i>If you didn\'t ask to reset your password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>',
subject: 'Reset your {APP_NAME} password',
body: '<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class="btn" href="{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}" target="_blank" rel="noopener">Reset password</a>\n</p>\n<p><i>If you didn\'t ask to reset your password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>'
},
confirmEmailChangeTemplate: {
subject: "Confirm your {APP_NAME} new email address",
body: '<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class="btn" href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}" target="_blank" rel="noopener">Confirm new email</a>\n</p>\n<p><i>If you didn\'t ask to change your email address, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>',
},
},
},
};
subject: 'Confirm your {APP_NAME} new email address',
body: '<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class="btn" href="{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}" target="_blank" rel="noopener">Confirm new email</a>\n</p>\n<p><i>If you didn\'t ask to change your email address, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>'
}
}
}
}
export default usersSchemas;
export default usersSchemas

View File

@@ -22,7 +22,7 @@ export function removeSensitiveData(userData: Record<string, any>) {
return newUserData as SchemaWithPB<
Omit<
z.infer<(typeof COLLECTION_SCHEMAS)['users__users']>,
z.infer<(typeof COLLECTION_SCHEMAS)['user__users']>,
| 'masterPasswordHash'
| 'journalMasterPasswordHash'
| 'APIKeysMasterPasswordHash'