mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-28 06:46:24 +00:00
Former-commit-id: 68c3a1e5867c1b34f4e439a6735e4d094c4aef08 [formerly c356ba449e4b90d8a585a4fe1d5509f3f5a9f77c] [formerly f722aab091f6d05405b65d46c9c4fbbefe856f5c [formerly 555c7a1db5910c3b0187357202a7663db72a5489]] Former-commit-id: 6ba8b762e82e176a08e0dd1924c81ce66d4f6546 [formerly 1738ea41e2ab3e4134aee976befbef5d0f261512] Former-commit-id: e72ccd9862d331cfc51f3d5f2564bdec134d4dc1
245 lines
6.6 KiB
TypeScript
245 lines
6.6 KiB
TypeScript
/* eslint-disable no-case-declarations */
|
|
import chalk from 'chalk'
|
|
import dotenv from 'dotenv'
|
|
import fs from 'fs'
|
|
import _ from 'lodash'
|
|
import path from 'path'
|
|
import Pocketbase, { type CollectionModel } from 'pocketbase'
|
|
import prettier from 'prettier'
|
|
|
|
dotenv.config({
|
|
path: path.resolve(__dirname, '../server/env/.env.local')
|
|
})
|
|
|
|
if (!process.env.PB_HOST || !process.env.PB_EMAIL || !process.env.PB_PASSWORD) {
|
|
console.error(
|
|
'Please provide PB_HOST, PB_EMAIL, and PB_PASSWORD in your environment variables.'
|
|
)
|
|
process.exit(1)
|
|
}
|
|
|
|
const pb = new Pocketbase(process.env.PB_HOST)
|
|
|
|
try {
|
|
await pb
|
|
.collection('_superusers')
|
|
.authWithPassword(process.env.PB_EMAIL, process.env.PB_PASSWORD)
|
|
|
|
if (!pb.authStore.isSuperuser || !pb.authStore.isValid) {
|
|
console.error('Invalid credentials.')
|
|
process.exit(1)
|
|
}
|
|
} catch {
|
|
console.error('Server is not reachable or credentials are invalid.')
|
|
process.exit(1)
|
|
}
|
|
|
|
let MAIN_SCHEMA_EXPORTS = `import flattenSchemas from '@functions/utils/flattenSchema'
|
|
|
|
export const SCHEMAS = {
|
|
`
|
|
|
|
const moduleSchemas: Record<string, string> = {}
|
|
|
|
const allModules = fs.readdirSync('./server/src/lib', { withFileTypes: true })
|
|
|
|
const modulesMap: Record<string, CollectionModel[]> = {}
|
|
|
|
const allCollections = await pb.collections.getFullList()
|
|
|
|
const collections = allCollections.filter(e => !e.system)
|
|
|
|
for (const collection of collections) {
|
|
const module = allModules.find(e =>
|
|
collection.name.startsWith(_.snakeCase(e.name))
|
|
)
|
|
|
|
if (!module) {
|
|
console.log(
|
|
chalk.yellow('[WARNING]') +
|
|
` Collection ${collection.name} does not have a corresponding module.`
|
|
)
|
|
|
|
continue
|
|
}
|
|
|
|
if (!modulesMap[module.name]) {
|
|
modulesMap[module.name] = []
|
|
}
|
|
modulesMap[module.name]?.push(collection)
|
|
}
|
|
|
|
console.log(
|
|
chalk.green('[INFO]') +
|
|
` Found ${Object.values(modulesMap).flat().length} collections across ${Object.keys(modulesMap).length} modules.`
|
|
)
|
|
|
|
for (const module of allModules) {
|
|
if (!modulesMap[module.name]) {
|
|
continue
|
|
}
|
|
|
|
const collections = modulesMap[module.name]
|
|
|
|
if (!collections) {
|
|
console.warn(
|
|
chalk.yellow('[WARNING]') +
|
|
` No collections found for module ${chalk.bold(module.name)}.`
|
|
)
|
|
continue
|
|
}
|
|
|
|
const moduleName = collections[0].name.split('__')[0]
|
|
|
|
// Initialize module schema content
|
|
let moduleSchemaContent = `import { z } from 'zod/v4'
|
|
|
|
const ${_.camelCase(moduleName)}Schemas = {
|
|
`
|
|
|
|
for (const collection of collections ?? []) {
|
|
console.log(
|
|
chalk.blue('[INFO]') +
|
|
` Found ${collection.fields.length} fields in collection ${chalk.bold(
|
|
collection.name
|
|
)} in module ${chalk.bold(moduleName)}.`
|
|
)
|
|
|
|
const zodSchemaObject: Record<string, string> = {}
|
|
|
|
for (const field of collection.fields) {
|
|
if (field.name === 'id') {
|
|
// Skip fields that are auto-generated by PocketBase
|
|
continue
|
|
}
|
|
|
|
switch (field.type) {
|
|
case 'text':
|
|
zodSchemaObject[field.name] = 'z.string()'
|
|
break
|
|
case 'richtext':
|
|
zodSchemaObject[field.name] = 'z.string()'
|
|
break
|
|
case 'number':
|
|
zodSchemaObject[field.name] = 'z.number()'
|
|
break
|
|
case 'bool':
|
|
zodSchemaObject[field.name] = 'z.boolean()'
|
|
break
|
|
case 'email':
|
|
zodSchemaObject[field.name] = 'z.email()'
|
|
break
|
|
case 'url':
|
|
zodSchemaObject[field.name] = 'z.url()'
|
|
break
|
|
case 'date':
|
|
zodSchemaObject[field.name] = 'z.string()'
|
|
break
|
|
case 'autodate':
|
|
zodSchemaObject[field.name] = 'z.string()'
|
|
break
|
|
case 'select':
|
|
const value = [...field.values, ...(field.required ? [] : [''])]
|
|
|
|
zodSchemaObject[field.name] =
|
|
field.maxSelect > 1
|
|
? `z.array(z.enum(${JSON.stringify(value)}))`
|
|
: `z.enum(${JSON.stringify(value)})`
|
|
break
|
|
case 'file':
|
|
zodSchemaObject[field.name] =
|
|
field.maxSelect > 1 ? 'z.array(z.string())' : 'z.string()'
|
|
break
|
|
case 'relation':
|
|
zodSchemaObject[field.name] =
|
|
field.maxSelect > 1 ? `z.array(z.string())` : `z.string()`
|
|
break
|
|
case 'json':
|
|
zodSchemaObject[field.name] = 'z.any()'
|
|
break
|
|
case 'geoPoint':
|
|
zodSchemaObject[field.name] =
|
|
'z.object({ lat: z.number(), lon: z.number() })'
|
|
break
|
|
case 'password':
|
|
zodSchemaObject[field.name] = 'z.string()'
|
|
break
|
|
default:
|
|
console.warn(
|
|
chalk.yellow('[WARNING]') +
|
|
` Unknown field type ${field.type} for field ${field.name} in collection ${collection.name}.`
|
|
)
|
|
continue
|
|
}
|
|
}
|
|
|
|
const zodSchemaString = `z.object({\n${Object.entries(zodSchemaObject)
|
|
.map(([key, value]) => ` ${key}: ${value},`)
|
|
.join('\n')}\n}),`
|
|
|
|
moduleSchemaContent += ` ${collection.name.split('__').pop()}: ${zodSchemaString}`
|
|
|
|
console.log(
|
|
chalk.green('[INFO]') +
|
|
` Generated Zod schema for collection ${chalk.bold(
|
|
collection.name
|
|
)} in module ${chalk.bold(moduleName)}.`
|
|
)
|
|
}
|
|
|
|
moduleSchemaContent += `}
|
|
|
|
export default ${_.camelCase(moduleName)}Schemas
|
|
`
|
|
|
|
// Store the module schema content
|
|
moduleSchemas[module.name] = moduleSchemaContent
|
|
|
|
// Add import and export to main schema
|
|
MAIN_SCHEMA_EXPORTS += ` ${moduleName}: (await import('@lib/${module.name}/schema')).default,\n`
|
|
|
|
// Write individual module schema file
|
|
const moduleSchemaPath = path.resolve(
|
|
__dirname,
|
|
`../server/src/lib/${module.name}/schema.ts`
|
|
)
|
|
|
|
const formattedModuleSchema = await prettier.format(moduleSchemaContent, {
|
|
parser: 'typescript'
|
|
})
|
|
|
|
fs.writeFileSync(moduleSchemaPath, formattedModuleSchema)
|
|
|
|
console.log(
|
|
chalk.green('[SUCCESS]') +
|
|
` Created schema file for module ${chalk.bold(module.name)} at ${chalk.bold(`lib/${module.name}/schema.ts`)}.`
|
|
)
|
|
}
|
|
|
|
// Complete the main schema file
|
|
MAIN_SCHEMA_EXPORTS += `}
|
|
|
|
const COLLECTION_SCHEMAS = flattenSchemas(SCHEMAS)
|
|
|
|
export default COLLECTION_SCHEMAS
|
|
`
|
|
|
|
const formattedMainSchemaString = await prettier.format(MAIN_SCHEMA_EXPORTS, {
|
|
parser: 'typescript'
|
|
})
|
|
|
|
fs.writeFileSync(
|
|
path.resolve(__dirname, '../server/src/core/schema.ts'),
|
|
formattedMainSchemaString
|
|
)
|
|
|
|
console.log(
|
|
chalk.green('[SUCCESS]') +
|
|
` Updated main schema file at ${chalk.bold('core/schema.ts')} with imports from all modules.`
|
|
)
|
|
|
|
console.log(
|
|
chalk.green('[COMPLETED]') +
|
|
` Schema generation completed successfully! Individual schema files created for ${Object.keys(moduleSchemas).length} modules.`
|
|
)
|