mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 22:34:56 +00:00
feat: Implement CRUD operations for pages, schemas, tenants, users, workflows, and SMTP configurations
- Added functions to set, update, delete, and retrieve page configurations. - Introduced model schema management with functions for adding, updating, deleting, and retrieving schemas. - Implemented tenant management with functions for adding, updating, deleting, and retrieving tenants. - Created user management functions for adding, updating, deleting, and retrieving users. - Developed workflow management functions for adding, updating, deleting, and retrieving workflows. - Added SMTP configuration management with functions to get and set SMTP configurations. - Implemented functions for managing god credentials, including expiry management and first login flags. - Introduced power transfer request management with functions for adding, updating, deleting, and retrieving requests. - Added Lua snippet management functions for retrieving snippets by ID and category, and searching snippets.
This commit is contained in:
934
frontends/nextjs/src/lib/db/Database.ts
Normal file
934
frontends/nextjs/src/lib/db/Database.ts
Normal file
@@ -0,0 +1,934 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type {
|
||||
User,
|
||||
Workflow,
|
||||
LuaScript,
|
||||
PageConfig,
|
||||
AppConfiguration,
|
||||
Comment,
|
||||
Tenant,
|
||||
PowerTransferRequest,
|
||||
} from '../../types/level-types'
|
||||
import type { ModelSchema } from '../types/schema-types'
|
||||
import type { InstalledPackage } from '../package-types'
|
||||
import type { SMTPConfig } from '../password-utils'
|
||||
|
||||
// Import individual functions (lambdas)
|
||||
import { hashPassword } from './functions/hash-password'
|
||||
import { verifyPassword } from './functions/verify-password'
|
||||
import { initializeDatabase } from './functions/initialize-database'
|
||||
|
||||
// Users
|
||||
import { getUsers } from './functions/users/get-users'
|
||||
import { setUsers } from './functions/users/set-users'
|
||||
import { addUser } from './functions/users/add-user'
|
||||
import { updateUser } from './functions/users/update-user'
|
||||
import { deleteUser } from './functions/users/delete-user'
|
||||
|
||||
// Credentials
|
||||
import { getCredentials } from './functions/credentials/get-credentials'
|
||||
import { setCredential } from './functions/credentials/set-credential'
|
||||
import { verifyCredentials } from './functions/credentials/verify-credentials'
|
||||
import { getPasswordChangeTimestamps } from './functions/credentials/get-password-change-timestamps'
|
||||
import { setPasswordChangeTimestamps } from './functions/credentials/set-password-change-timestamps'
|
||||
|
||||
// Workflows
|
||||
import { getWorkflows } from './functions/workflows/get-workflows'
|
||||
import { setWorkflows } from './functions/workflows/set-workflows'
|
||||
import { addWorkflow } from './functions/workflows/add-workflow'
|
||||
import { updateWorkflow } from './functions/workflows/update-workflow'
|
||||
import { deleteWorkflow } from './functions/workflows/delete-workflow'
|
||||
|
||||
// Lua Scripts
|
||||
import { getLuaScripts } from './functions/lua-scripts/get-lua-scripts'
|
||||
import { setLuaScripts } from './functions/lua-scripts/set-lua-scripts'
|
||||
import { addLuaScript } from './functions/lua-scripts/add-lua-script'
|
||||
import { updateLuaScript } from './functions/lua-scripts/update-lua-script'
|
||||
import { deleteLuaScript } from './functions/lua-scripts/delete-lua-script'
|
||||
|
||||
// Pages
|
||||
import { getPages } from './functions/pages/get-pages'
|
||||
import { setPages } from './functions/pages/set-pages'
|
||||
import { addPage } from './functions/pages/add-page'
|
||||
import { updatePage } from './functions/pages/update-page'
|
||||
import { deletePage } from './functions/pages/delete-page'
|
||||
|
||||
// Schemas
|
||||
import { getSchemas } from './functions/schemas/get-schemas'
|
||||
import { setSchemas } from './functions/schemas/set-schemas'
|
||||
import { addSchema } from './functions/schemas/add-schema'
|
||||
import { updateSchema } from './functions/schemas/update-schema'
|
||||
import { deleteSchema } from './functions/schemas/delete-schema'
|
||||
|
||||
// Tenants
|
||||
import { getTenants } from './functions/tenants/get-tenants'
|
||||
import { setTenants } from './functions/tenants/set-tenants'
|
||||
import { addTenant } from './functions/tenants/add-tenant'
|
||||
import { updateTenant } from './functions/tenants/update-tenant'
|
||||
import { deleteTenant } from './functions/tenants/delete-tenant'
|
||||
|
||||
// Re-export types from database.ts for compatibility
|
||||
export interface CssCategory {
|
||||
name: string
|
||||
classes: string[]
|
||||
}
|
||||
|
||||
export interface DropdownConfig {
|
||||
id: string
|
||||
name: string
|
||||
label: string
|
||||
options: Array<{ value: string; label: string }>
|
||||
}
|
||||
|
||||
export interface ComponentNode {
|
||||
id: string
|
||||
type: string
|
||||
parentId?: string
|
||||
childIds: string[]
|
||||
order: number
|
||||
pageId: string
|
||||
}
|
||||
|
||||
export interface ComponentConfig {
|
||||
id: string
|
||||
componentId: string
|
||||
props: Record<string, any>
|
||||
styles: Record<string, any>
|
||||
events: Record<string, string>
|
||||
conditionalRendering?: {
|
||||
condition: string
|
||||
luaScriptId?: string
|
||||
}
|
||||
}
|
||||
|
||||
export const DB_KEYS = {
|
||||
USERS: 'db_users',
|
||||
CREDENTIALS: 'db_credentials',
|
||||
WORKFLOWS: 'db_workflows',
|
||||
LUA_SCRIPTS: 'db_lua_scripts',
|
||||
PAGES: 'db_pages',
|
||||
SCHEMAS: 'db_schemas',
|
||||
APP_CONFIG: 'db_app_config',
|
||||
COMMENTS: 'db_comments',
|
||||
COMPONENT_HIERARCHY: 'db_component_hierarchy',
|
||||
COMPONENT_CONFIGS: 'db_component_configs',
|
||||
GOD_CREDENTIALS_EXPIRY: 'db_god_credentials_expiry',
|
||||
PASSWORD_CHANGE_TIMESTAMPS: 'db_password_change_timestamps',
|
||||
FIRST_LOGIN_FLAGS: 'db_first_login_flags',
|
||||
GOD_CREDENTIALS_EXPIRY_DURATION: 'db_god_credentials_expiry_duration',
|
||||
CSS_CLASSES: 'db_css_classes',
|
||||
DROPDOWN_CONFIGS: 'db_dropdown_configs',
|
||||
INSTALLED_PACKAGES: 'db_installed_packages',
|
||||
PACKAGE_DATA: 'db_package_data',
|
||||
TENANTS: 'db_tenants',
|
||||
POWER_TRANSFER_REQUESTS: 'db_power_transfer_requests',
|
||||
SMTP_CONFIG: 'db_smtp_config',
|
||||
PASSWORD_RESET_TOKENS: 'db_password_reset_tokens',
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Database - Class wrapper for database operations
|
||||
*
|
||||
* This class serves as a container for lambda functions related to database operations.
|
||||
* Each method delegates to an individual function file in the functions/ directory.
|
||||
*
|
||||
* Pattern: "class is container for lambdas"
|
||||
* - Each lambda is defined in its own file under functions/
|
||||
* - This class wraps them for convenient namespaced access
|
||||
* - Can be used as Database.methodName() or import individual functions
|
||||
*/
|
||||
export class Database {
|
||||
// Core operations
|
||||
static initializeDatabase = initializeDatabase
|
||||
|
||||
// User operations
|
||||
static getUsers = getUsers
|
||||
static setUsers = setUsers
|
||||
static addUser = addUser
|
||||
static updateUser = updateUser
|
||||
static deleteUser = deleteUser
|
||||
|
||||
// Credential operations
|
||||
static getCredentials = getCredentials
|
||||
static setCredential = setCredential
|
||||
static verifyCredentials = verifyCredentials
|
||||
static getPasswordChangeTimestamps = getPasswordChangeTimestamps
|
||||
static setPasswordChangeTimestamps = setPasswordChangeTimestamps
|
||||
|
||||
// Workflow operations
|
||||
static getWorkflows = getWorkflows
|
||||
static setWorkflows = setWorkflows
|
||||
static addWorkflow = addWorkflow
|
||||
static updateWorkflow = updateWorkflow
|
||||
static deleteWorkflow = deleteWorkflow
|
||||
|
||||
// Lua Script operations
|
||||
static getLuaScripts = getLuaScripts
|
||||
static setLuaScripts = setLuaScripts
|
||||
static addLuaScript = addLuaScript
|
||||
static updateLuaScript = updateLuaScript
|
||||
static deleteLuaScript = deleteLuaScript
|
||||
|
||||
// Page operations
|
||||
static getPages = getPages
|
||||
static setPages = setPages
|
||||
static addPage = addPage
|
||||
static updatePage = updatePage
|
||||
static deletePage = deletePage
|
||||
|
||||
// Schema operations
|
||||
static getSchemas = getSchemas
|
||||
static setSchemas = setSchemas
|
||||
static addSchema = addSchema
|
||||
static updateSchema = updateSchema
|
||||
static deleteSchema = deleteSchema
|
||||
|
||||
// Tenant operations
|
||||
static getTenants = getTenants
|
||||
static setTenants = setTenants
|
||||
static addTenant = addTenant
|
||||
static updateTenant = updateTenant
|
||||
static deleteTenant = deleteTenant
|
||||
|
||||
// ==============================================
|
||||
// Functions that require additional refactoring
|
||||
// These are kept inline for now but can be split later
|
||||
// ==============================================
|
||||
|
||||
static async getAppConfig(): Promise<AppConfiguration | null> {
|
||||
const config = await prisma.appConfiguration.findFirst()
|
||||
if (!config) return null
|
||||
|
||||
return {
|
||||
id: config.id,
|
||||
name: config.name,
|
||||
schemas: JSON.parse(config.schemas),
|
||||
workflows: JSON.parse(config.workflows),
|
||||
luaScripts: JSON.parse(config.luaScripts),
|
||||
pages: JSON.parse(config.pages),
|
||||
theme: JSON.parse(config.theme),
|
||||
}
|
||||
}
|
||||
|
||||
static async setAppConfig(config: AppConfiguration): Promise<void> {
|
||||
await prisma.appConfiguration.deleteMany()
|
||||
await prisma.appConfiguration.create({
|
||||
data: {
|
||||
id: config.id,
|
||||
name: config.name,
|
||||
schemas: JSON.stringify(config.schemas),
|
||||
workflows: JSON.stringify(config.workflows),
|
||||
luaScripts: JSON.stringify(config.luaScripts),
|
||||
pages: JSON.stringify(config.pages),
|
||||
theme: JSON.stringify(config.theme),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async getComments(): Promise<Comment[]> {
|
||||
const comments = await prisma.comment.findMany()
|
||||
return comments.map(c => ({
|
||||
id: c.id,
|
||||
userId: c.userId,
|
||||
content: c.content,
|
||||
createdAt: Number(c.createdAt),
|
||||
updatedAt: c.updatedAt ? Number(c.updatedAt) : undefined,
|
||||
parentId: c.parentId || undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
static async setComments(comments: Comment[]): Promise<void> {
|
||||
await prisma.comment.deleteMany()
|
||||
for (const comment of comments) {
|
||||
await prisma.comment.create({
|
||||
data: {
|
||||
id: comment.id,
|
||||
userId: comment.userId,
|
||||
content: comment.content,
|
||||
createdAt: BigInt(comment.createdAt),
|
||||
updatedAt: comment.updatedAt ? BigInt(comment.updatedAt) : null,
|
||||
parentId: comment.parentId,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async addComment(comment: Comment): Promise<void> {
|
||||
await prisma.comment.create({
|
||||
data: {
|
||||
id: comment.id,
|
||||
userId: comment.userId,
|
||||
content: comment.content,
|
||||
createdAt: BigInt(comment.createdAt),
|
||||
updatedAt: comment.updatedAt ? BigInt(comment.updatedAt) : null,
|
||||
parentId: comment.parentId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async updateComment(commentId: string, updates: Partial<Comment>): Promise<void> {
|
||||
const data: any = {}
|
||||
if (updates.content !== undefined) data.content = updates.content
|
||||
if (updates.updatedAt !== undefined) data.updatedAt = BigInt(updates.updatedAt)
|
||||
|
||||
await prisma.comment.update({
|
||||
where: { id: commentId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
static async deleteComment(commentId: string): Promise<void> {
|
||||
await prisma.comment.delete({ where: { id: commentId } })
|
||||
}
|
||||
|
||||
static async getComponentHierarchy(): Promise<Record<string, ComponentNode>> {
|
||||
const nodes = await prisma.componentNode.findMany()
|
||||
const result: Record<string, ComponentNode> = {}
|
||||
for (const node of nodes) {
|
||||
result[node.id] = {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
parentId: node.parentId || undefined,
|
||||
childIds: JSON.parse(node.childIds),
|
||||
order: node.order,
|
||||
pageId: node.pageId,
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
static async setComponentHierarchy(hierarchy: Record<string, ComponentNode>): Promise<void> {
|
||||
await prisma.componentNode.deleteMany()
|
||||
for (const node of Object.values(hierarchy)) {
|
||||
await prisma.componentNode.create({
|
||||
data: {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
parentId: node.parentId,
|
||||
childIds: JSON.stringify(node.childIds),
|
||||
order: node.order,
|
||||
pageId: node.pageId,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async addComponentNode(node: ComponentNode): Promise<void> {
|
||||
await prisma.componentNode.create({
|
||||
data: {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
parentId: node.parentId,
|
||||
childIds: JSON.stringify(node.childIds),
|
||||
order: node.order,
|
||||
pageId: node.pageId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async updateComponentNode(nodeId: string, updates: Partial<ComponentNode>): Promise<void> {
|
||||
const data: any = {}
|
||||
if (updates.type !== undefined) data.type = updates.type
|
||||
if (updates.parentId !== undefined) data.parentId = updates.parentId
|
||||
if (updates.childIds !== undefined) data.childIds = JSON.stringify(updates.childIds)
|
||||
if (updates.order !== undefined) data.order = updates.order
|
||||
if (updates.pageId !== undefined) data.pageId = updates.pageId
|
||||
|
||||
await prisma.componentNode.update({
|
||||
where: { id: nodeId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
static async deleteComponentNode(nodeId: string): Promise<void> {
|
||||
await prisma.componentNode.delete({ where: { id: nodeId } })
|
||||
}
|
||||
|
||||
static async getComponentConfigs(): Promise<Record<string, ComponentConfig>> {
|
||||
const configs = await prisma.componentConfig.findMany()
|
||||
const result: Record<string, ComponentConfig> = {}
|
||||
for (const config of configs) {
|
||||
result[config.id] = {
|
||||
id: config.id,
|
||||
componentId: config.componentId,
|
||||
props: JSON.parse(config.props),
|
||||
styles: JSON.parse(config.styles),
|
||||
events: JSON.parse(config.events),
|
||||
conditionalRendering: config.conditionalRendering ? JSON.parse(config.conditionalRendering) : undefined,
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
static async setComponentConfigs(configs: Record<string, ComponentConfig>): Promise<void> {
|
||||
await prisma.componentConfig.deleteMany()
|
||||
for (const config of Object.values(configs)) {
|
||||
await prisma.componentConfig.create({
|
||||
data: {
|
||||
id: config.id,
|
||||
componentId: config.componentId,
|
||||
props: JSON.stringify(config.props),
|
||||
styles: JSON.stringify(config.styles),
|
||||
events: JSON.stringify(config.events),
|
||||
conditionalRendering: config.conditionalRendering ? JSON.stringify(config.conditionalRendering) : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async addComponentConfig(config: ComponentConfig): Promise<void> {
|
||||
await prisma.componentConfig.create({
|
||||
data: {
|
||||
id: config.id,
|
||||
componentId: config.componentId,
|
||||
props: JSON.stringify(config.props),
|
||||
styles: JSON.stringify(config.styles),
|
||||
events: JSON.stringify(config.events),
|
||||
conditionalRendering: config.conditionalRendering ? JSON.stringify(config.conditionalRendering) : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async updateComponentConfig(configId: string, updates: Partial<ComponentConfig>): Promise<void> {
|
||||
const data: any = {}
|
||||
if (updates.componentId !== undefined) data.componentId = updates.componentId
|
||||
if (updates.props !== undefined) data.props = JSON.stringify(updates.props)
|
||||
if (updates.styles !== undefined) data.styles = JSON.stringify(updates.styles)
|
||||
if (updates.events !== undefined) data.events = JSON.stringify(updates.events)
|
||||
if (updates.conditionalRendering !== undefined) data.conditionalRendering = JSON.stringify(updates.conditionalRendering)
|
||||
|
||||
await prisma.componentConfig.update({
|
||||
where: { id: configId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
static async deleteComponentConfig(configId: string): Promise<void> {
|
||||
await prisma.componentConfig.delete({ where: { id: configId } })
|
||||
}
|
||||
|
||||
static async getGodCredentialsExpiry(): Promise<number> {
|
||||
const config = await prisma.systemConfig.findUnique({ where: { key: 'god_credentials_expiry' } })
|
||||
return config ? Number(config.value) : 0
|
||||
}
|
||||
|
||||
static async setGodCredentialsExpiry(timestamp: number): Promise<void> {
|
||||
await prisma.systemConfig.upsert({
|
||||
where: { key: 'god_credentials_expiry' },
|
||||
update: { value: timestamp.toString() },
|
||||
create: { key: 'god_credentials_expiry', value: timestamp.toString() },
|
||||
})
|
||||
}
|
||||
|
||||
static async getFirstLoginFlags(): Promise<Record<string, boolean>> {
|
||||
const users = await prisma.user.findMany({
|
||||
select: { username: true, firstLogin: true },
|
||||
})
|
||||
const result: Record<string, boolean> = {}
|
||||
for (const user of users) {
|
||||
result[user.username] = user.firstLogin
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
static async setFirstLoginFlag(username: string, isFirstLogin: boolean): Promise<void> {
|
||||
await prisma.user.update({
|
||||
where: { username },
|
||||
data: { firstLogin: isFirstLogin },
|
||||
})
|
||||
}
|
||||
|
||||
static async shouldShowGodCredentials(): Promise<boolean> {
|
||||
const expiry = await this.getGodCredentialsExpiry()
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { username: 'god' },
|
||||
select: { passwordChangeTimestamp: true },
|
||||
})
|
||||
const godPasswordChangeTime = user?.passwordChangeTimestamp ? Number(user.passwordChangeTimestamp) : 0
|
||||
|
||||
if (expiry === 0) {
|
||||
const duration = await this.getGodCredentialsExpiryDuration()
|
||||
const expiryTime = Date.now() + duration
|
||||
await this.setGodCredentialsExpiry(expiryTime)
|
||||
return true
|
||||
}
|
||||
|
||||
if (godPasswordChangeTime > expiry) {
|
||||
return false
|
||||
}
|
||||
|
||||
return Date.now() < expiry
|
||||
}
|
||||
|
||||
static async getGodCredentialsExpiryDuration(): Promise<number> {
|
||||
const config = await prisma.systemConfig.findUnique({ where: { key: 'god_credentials_expiry_duration' } })
|
||||
return config ? Number(config.value) : (60 * 60 * 1000)
|
||||
}
|
||||
|
||||
static async setGodCredentialsExpiryDuration(durationMs: number): Promise<void> {
|
||||
await prisma.systemConfig.upsert({
|
||||
where: { key: 'god_credentials_expiry_duration' },
|
||||
update: { value: durationMs.toString() },
|
||||
create: { key: 'god_credentials_expiry_duration', value: durationMs.toString() },
|
||||
})
|
||||
}
|
||||
|
||||
static async resetGodCredentialsExpiry(): Promise<void> {
|
||||
const duration = await this.getGodCredentialsExpiryDuration()
|
||||
const expiryTime = Date.now() + duration
|
||||
await this.setGodCredentialsExpiry(expiryTime)
|
||||
}
|
||||
|
||||
static async getCssClasses(): Promise<CssCategory[]> {
|
||||
const categories = await prisma.cssCategory.findMany()
|
||||
return categories.map(c => ({
|
||||
name: c.name,
|
||||
classes: JSON.parse(c.classes),
|
||||
}))
|
||||
}
|
||||
|
||||
static async setCssClasses(classes: CssCategory[]): Promise<void> {
|
||||
await prisma.cssCategory.deleteMany()
|
||||
for (const category of classes) {
|
||||
await prisma.cssCategory.create({
|
||||
data: {
|
||||
name: category.name,
|
||||
classes: JSON.stringify(category.classes),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async addCssCategory(category: CssCategory): Promise<void> {
|
||||
await prisma.cssCategory.create({
|
||||
data: {
|
||||
name: category.name,
|
||||
classes: JSON.stringify(category.classes),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async updateCssCategory(categoryName: string, classes: string[]): Promise<void> {
|
||||
await prisma.cssCategory.update({
|
||||
where: { name: categoryName },
|
||||
data: { classes: JSON.stringify(classes) },
|
||||
})
|
||||
}
|
||||
|
||||
static async deleteCssCategory(categoryName: string): Promise<void> {
|
||||
await prisma.cssCategory.delete({ where: { name: categoryName } })
|
||||
}
|
||||
|
||||
static async getDropdownConfigs(): Promise<DropdownConfig[]> {
|
||||
const configs = await prisma.dropdownConfig.findMany()
|
||||
return configs.map(c => ({
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
label: c.label,
|
||||
options: JSON.parse(c.options),
|
||||
}))
|
||||
}
|
||||
|
||||
static async setDropdownConfigs(configs: DropdownConfig[]): Promise<void> {
|
||||
await prisma.dropdownConfig.deleteMany()
|
||||
for (const config of configs) {
|
||||
await prisma.dropdownConfig.create({
|
||||
data: {
|
||||
id: config.id,
|
||||
name: config.name,
|
||||
label: config.label,
|
||||
options: JSON.stringify(config.options),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async addDropdownConfig(config: DropdownConfig): Promise<void> {
|
||||
await prisma.dropdownConfig.create({
|
||||
data: {
|
||||
id: config.id,
|
||||
name: config.name,
|
||||
label: config.label,
|
||||
options: JSON.stringify(config.options),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async updateDropdownConfig(id: string, updates: DropdownConfig): Promise<void> {
|
||||
await prisma.dropdownConfig.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: updates.name,
|
||||
label: updates.label,
|
||||
options: JSON.stringify(updates.options),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async deleteDropdownConfig(id: string): Promise<void> {
|
||||
await prisma.dropdownConfig.delete({ where: { id } })
|
||||
}
|
||||
|
||||
static async getInstalledPackages(): Promise<InstalledPackage[]> {
|
||||
const packages = await prisma.installedPackage.findMany()
|
||||
return packages.map(p => ({
|
||||
packageId: p.packageId,
|
||||
installedAt: Number(p.installedAt),
|
||||
version: p.version,
|
||||
enabled: p.enabled,
|
||||
}))
|
||||
}
|
||||
|
||||
static async setInstalledPackages(packages: InstalledPackage[]): Promise<void> {
|
||||
await prisma.installedPackage.deleteMany()
|
||||
for (const pkg of packages) {
|
||||
await prisma.installedPackage.create({
|
||||
data: {
|
||||
packageId: pkg.packageId,
|
||||
installedAt: BigInt(pkg.installedAt),
|
||||
version: pkg.version,
|
||||
enabled: pkg.enabled,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async installPackage(packageData: InstalledPackage): Promise<void> {
|
||||
const exists = await prisma.installedPackage.findUnique({ where: { packageId: packageData.packageId } })
|
||||
if (!exists) {
|
||||
await prisma.installedPackage.create({
|
||||
data: {
|
||||
packageId: packageData.packageId,
|
||||
installedAt: BigInt(packageData.installedAt),
|
||||
version: packageData.version,
|
||||
enabled: packageData.enabled,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async uninstallPackage(packageId: string): Promise<void> {
|
||||
await prisma.installedPackage.delete({ where: { packageId } })
|
||||
}
|
||||
|
||||
static async togglePackageEnabled(packageId: string, enabled: boolean): Promise<void> {
|
||||
await prisma.installedPackage.update({
|
||||
where: { packageId },
|
||||
data: { enabled },
|
||||
})
|
||||
}
|
||||
|
||||
static async getPackageData(packageId: string): Promise<Record<string, any[]>> {
|
||||
const pkg = await prisma.packageData.findUnique({ where: { packageId } })
|
||||
return pkg ? JSON.parse(pkg.data) : {}
|
||||
}
|
||||
|
||||
static async setPackageData(packageId: string, data: Record<string, any[]>): Promise<void> {
|
||||
await prisma.packageData.upsert({
|
||||
where: { packageId },
|
||||
update: { data: JSON.stringify(data) },
|
||||
create: { packageId, data: JSON.stringify(data) },
|
||||
})
|
||||
}
|
||||
|
||||
static async deletePackageData(packageId: string): Promise<void> {
|
||||
await prisma.packageData.delete({ where: { packageId } })
|
||||
}
|
||||
|
||||
static async getPowerTransferRequests(): Promise<PowerTransferRequest[]> {
|
||||
const requests = await prisma.powerTransferRequest.findMany()
|
||||
return requests.map(r => ({
|
||||
id: r.id,
|
||||
fromUserId: r.fromUserId,
|
||||
toUserId: r.toUserId,
|
||||
status: r.status as any,
|
||||
createdAt: Number(r.createdAt),
|
||||
expiresAt: Number(r.expiresAt),
|
||||
}))
|
||||
}
|
||||
|
||||
static async setPowerTransferRequests(requests: PowerTransferRequest[]): Promise<void> {
|
||||
await prisma.powerTransferRequest.deleteMany()
|
||||
for (const request of requests) {
|
||||
await prisma.powerTransferRequest.create({
|
||||
data: {
|
||||
id: request.id,
|
||||
fromUserId: request.fromUserId,
|
||||
toUserId: request.toUserId,
|
||||
status: request.status,
|
||||
createdAt: BigInt(request.createdAt),
|
||||
expiresAt: BigInt(request.expiresAt),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static async addPowerTransferRequest(request: PowerTransferRequest): Promise<void> {
|
||||
await prisma.powerTransferRequest.create({
|
||||
data: {
|
||||
id: request.id,
|
||||
fromUserId: request.fromUserId,
|
||||
toUserId: request.toUserId,
|
||||
status: request.status,
|
||||
createdAt: BigInt(request.createdAt),
|
||||
expiresAt: BigInt(request.expiresAt),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async updatePowerTransferRequest(requestId: string, updates: Partial<PowerTransferRequest>): Promise<void> {
|
||||
const data: any = {}
|
||||
if (updates.status !== undefined) data.status = updates.status
|
||||
|
||||
await prisma.powerTransferRequest.update({
|
||||
where: { id: requestId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
static async deletePowerTransferRequest(requestId: string): Promise<void> {
|
||||
await prisma.powerTransferRequest.delete({ where: { id: requestId } })
|
||||
}
|
||||
|
||||
static async getSuperGod(): Promise<User | null> {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: { role: 'supergod' },
|
||||
})
|
||||
if (!user) return null
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
role: user.role as any,
|
||||
profilePicture: user.profilePicture || undefined,
|
||||
bio: user.bio || undefined,
|
||||
createdAt: Number(user.createdAt),
|
||||
tenantId: user.tenantId || undefined,
|
||||
isInstanceOwner: user.isInstanceOwner,
|
||||
}
|
||||
}
|
||||
|
||||
static async transferSuperGodPower(fromUserId: string, toUserId: string): Promise<void> {
|
||||
await prisma.user.update({
|
||||
where: { id: fromUserId },
|
||||
data: { role: 'god', isInstanceOwner: false },
|
||||
})
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: toUserId },
|
||||
data: { role: 'supergod', isInstanceOwner: true },
|
||||
})
|
||||
}
|
||||
|
||||
static async getSMTPConfig(): Promise<SMTPConfig | null> {
|
||||
const config = await prisma.sMTPConfig.findFirst()
|
||||
if (!config) return null
|
||||
|
||||
return {
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: config.secure,
|
||||
username: config.username,
|
||||
password: config.password,
|
||||
fromEmail: config.fromEmail,
|
||||
fromName: config.fromName,
|
||||
}
|
||||
}
|
||||
|
||||
static async setSMTPConfig(config: SMTPConfig): Promise<void> {
|
||||
await prisma.sMTPConfig.deleteMany()
|
||||
await prisma.sMTPConfig.create({
|
||||
data: {
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: config.secure,
|
||||
username: config.username,
|
||||
password: config.password,
|
||||
fromEmail: config.fromEmail,
|
||||
fromName: config.fromName,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static async getPasswordResetTokens(): Promise<Record<string, string>> {
|
||||
const tokens = await prisma.passwordResetToken.findMany()
|
||||
const result: Record<string, string> = {}
|
||||
for (const token of tokens) {
|
||||
result[token.username] = token.token
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
static async setPasswordResetToken(username: string, token: string): Promise<void> {
|
||||
await prisma.passwordResetToken.upsert({
|
||||
where: { username },
|
||||
update: { token },
|
||||
create: { username, token },
|
||||
})
|
||||
}
|
||||
|
||||
static async deletePasswordResetToken(username: string): Promise<void> {
|
||||
await prisma.passwordResetToken.delete({ where: { username } })
|
||||
}
|
||||
|
||||
static async clearDatabase(): Promise<void> {
|
||||
await prisma.user.deleteMany()
|
||||
await prisma.credential.deleteMany()
|
||||
await prisma.workflow.deleteMany()
|
||||
await prisma.luaScript.deleteMany()
|
||||
await prisma.pageConfig.deleteMany()
|
||||
await prisma.modelSchema.deleteMany()
|
||||
await prisma.appConfiguration.deleteMany()
|
||||
await prisma.comment.deleteMany()
|
||||
await prisma.componentNode.deleteMany()
|
||||
await prisma.componentConfig.deleteMany()
|
||||
await prisma.systemConfig.deleteMany()
|
||||
await prisma.cssCategory.deleteMany()
|
||||
await prisma.dropdownConfig.deleteMany()
|
||||
await prisma.installedPackage.deleteMany()
|
||||
await prisma.packageData.deleteMany()
|
||||
await prisma.tenant.deleteMany()
|
||||
await prisma.powerTransferRequest.deleteMany()
|
||||
await prisma.sMTPConfig.deleteMany()
|
||||
await prisma.passwordResetToken.deleteMany()
|
||||
}
|
||||
|
||||
static async seedDefaultData(): Promise<void> {
|
||||
const users = await this.getUsers()
|
||||
const credentials = await this.getCredentials()
|
||||
|
||||
if (users.length === 0) {
|
||||
const defaultUsers: User[] = [
|
||||
{
|
||||
id: 'user_supergod',
|
||||
username: 'supergod',
|
||||
email: 'supergod@builder.com',
|
||||
role: 'supergod',
|
||||
bio: 'Supreme administrator with multi-tenant control',
|
||||
createdAt: Date.now(),
|
||||
isInstanceOwner: true,
|
||||
},
|
||||
{
|
||||
id: 'user_god',
|
||||
username: 'god',
|
||||
email: 'god@builder.com',
|
||||
role: 'god',
|
||||
bio: 'System architect with full access to all levels',
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: 'user_admin',
|
||||
username: 'admin',
|
||||
email: 'admin@builder.com',
|
||||
role: 'admin',
|
||||
bio: 'Administrator with data management access',
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: 'user_demo',
|
||||
username: 'demo',
|
||||
email: 'demo@builder.com',
|
||||
role: 'user',
|
||||
bio: 'Demo user account',
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
]
|
||||
|
||||
await this.setUsers(defaultUsers)
|
||||
}
|
||||
|
||||
if (Object.keys(credentials).length === 0) {
|
||||
const { getScrambledPassword } = await import('../auth')
|
||||
await this.setCredential('supergod', await hashPassword(getScrambledPassword('supergod')))
|
||||
await this.setCredential('god', await hashPassword(getScrambledPassword('god')))
|
||||
await this.setCredential('admin', await hashPassword(getScrambledPassword('admin')))
|
||||
await this.setCredential('demo', await hashPassword(getScrambledPassword('demo')))
|
||||
|
||||
await this.setFirstLoginFlag('supergod', true)
|
||||
await this.setFirstLoginFlag('god', true)
|
||||
await this.setFirstLoginFlag('admin', false)
|
||||
await this.setFirstLoginFlag('demo', false)
|
||||
}
|
||||
|
||||
const appConfig = await this.getAppConfig()
|
||||
if (!appConfig) {
|
||||
const defaultConfig: AppConfiguration = {
|
||||
id: 'app_001',
|
||||
name: 'MetaBuilder App',
|
||||
schemas: [],
|
||||
workflows: [],
|
||||
luaScripts: [],
|
||||
pages: [],
|
||||
theme: {
|
||||
colors: {},
|
||||
fonts: {},
|
||||
},
|
||||
}
|
||||
await this.setAppConfig(defaultConfig)
|
||||
}
|
||||
|
||||
const cssClasses = await this.getCssClasses()
|
||||
if (cssClasses.length === 0) {
|
||||
const defaultCssClasses: CssCategory[] = [
|
||||
{ name: 'Layout', classes: ['flex', 'flex-col', 'flex-row', 'grid', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'block', 'inline-block', 'inline', 'hidden'] },
|
||||
{ name: 'Spacing', classes: ['p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-6', 'p-8', 'm-0', 'm-1', 'm-2', 'm-3', 'm-4', 'm-6', 'm-8', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-6', 'gap-8'] },
|
||||
{ name: 'Sizing', classes: ['w-full', 'w-1/2', 'w-1/3', 'w-1/4', 'w-auto', 'h-full', 'h-screen', 'h-auto', 'min-h-screen', 'max-w-xs', 'max-w-sm', 'max-w-md', 'max-w-lg', 'max-w-xl', 'max-w-2xl', 'max-w-4xl', 'max-w-6xl', 'max-w-7xl'] },
|
||||
{ name: 'Typography', classes: ['text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl', 'text-4xl', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'text-left', 'text-center', 'text-right', 'uppercase', 'lowercase', 'capitalize'] },
|
||||
{ name: 'Colors', classes: ['text-primary', 'text-secondary', 'text-accent', 'text-muted-foreground', 'bg-primary', 'bg-secondary', 'bg-accent', 'bg-background', 'bg-card', 'bg-muted', 'border-primary', 'border-secondary', 'border-accent', 'border-border'] },
|
||||
{ name: 'Borders', classes: ['border', 'border-2', 'border-4', 'border-t', 'border-b', 'border-l', 'border-r', 'rounded', 'rounded-sm', 'rounded-md', 'rounded-lg', 'rounded-xl', 'rounded-2xl', 'rounded-full'] },
|
||||
{ name: 'Effects', classes: ['shadow', 'shadow-sm', 'shadow-md', 'shadow-lg', 'shadow-xl', 'hover:shadow-lg', 'opacity-0', 'opacity-50', 'opacity-75', 'opacity-100', 'transition', 'transition-all', 'duration-200', 'duration-300', 'duration-500'] },
|
||||
{ name: 'Positioning', classes: ['relative', 'absolute', 'fixed', 'sticky', 'top-0', 'bottom-0', 'left-0', 'right-0', 'z-10', 'z-20', 'z-30', 'z-40', 'z-50'] },
|
||||
{ name: 'Alignment', classes: ['items-start', 'items-center', 'items-end', 'justify-start', 'justify-center', 'justify-end', 'justify-between', 'justify-around', 'self-start', 'self-center', 'self-end'] },
|
||||
{ name: 'Interactivity', classes: ['cursor-pointer', 'cursor-default', 'pointer-events-none', 'select-none', 'hover:bg-accent', 'hover:text-accent-foreground', 'active:scale-95', 'disabled:opacity-50'] },
|
||||
]
|
||||
await this.setCssClasses(defaultCssClasses)
|
||||
}
|
||||
|
||||
const dropdowns = await this.getDropdownConfigs()
|
||||
if (dropdowns.length === 0) {
|
||||
const defaultDropdowns: DropdownConfig[] = [
|
||||
{ id: 'dropdown_status', name: 'status_options', label: 'Status', options: [{ value: 'draft', label: 'Draft' }, { value: 'published', label: 'Published' }, { value: 'archived', label: 'Archived' }] },
|
||||
{ id: 'dropdown_priority', name: 'priority_options', label: 'Priority', options: [{ value: 'low', label: 'Low' }, { value: 'medium', label: 'Medium' }, { value: 'high', label: 'High' }, { value: 'urgent', label: 'Urgent' }] },
|
||||
{ id: 'dropdown_category', name: 'category_options', label: 'Category', options: [{ value: 'general', label: 'General' }, { value: 'technical', label: 'Technical' }, { value: 'business', label: 'Business' }, { value: 'personal', label: 'Personal' }] },
|
||||
]
|
||||
await this.setDropdownConfigs(defaultDropdowns)
|
||||
}
|
||||
}
|
||||
|
||||
static async exportDatabase(): Promise<string> {
|
||||
const data = {
|
||||
users: await this.getUsers(),
|
||||
workflows: await this.getWorkflows(),
|
||||
luaScripts: await this.getLuaScripts(),
|
||||
pages: await this.getPages(),
|
||||
schemas: await this.getSchemas(),
|
||||
appConfig: (await this.getAppConfig()) || undefined,
|
||||
comments: await this.getComments(),
|
||||
componentHierarchy: await this.getComponentHierarchy(),
|
||||
componentConfigs: await this.getComponentConfigs(),
|
||||
}
|
||||
return JSON.stringify(data, null, 2)
|
||||
}
|
||||
|
||||
static async importDatabase(jsonData: string): Promise<void> {
|
||||
try {
|
||||
const data = JSON.parse(jsonData)
|
||||
|
||||
if (data.users) await this.setUsers(data.users)
|
||||
if (data.workflows) await this.setWorkflows(data.workflows)
|
||||
if (data.luaScripts) await this.setLuaScripts(data.luaScripts)
|
||||
if (data.pages) await this.setPages(data.pages)
|
||||
if (data.schemas) await this.setSchemas(data.schemas)
|
||||
if (data.appConfig) await this.setAppConfig(data.appConfig)
|
||||
if (data.comments) await this.setComments(data.comments)
|
||||
if (data.componentHierarchy) await this.setComponentHierarchy(data.componentHierarchy)
|
||||
if (data.componentConfigs) await this.setComponentConfigs(data.componentConfigs)
|
||||
} catch (error) {
|
||||
throw new Error('Failed to import database: Invalid JSON')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-export standalone functions for direct import
|
||||
export { hashPassword, verifyPassword }
|
||||
43
frontends/nextjs/src/lib/db/database-admin/clear-database.ts
Normal file
43
frontends/nextjs/src/lib/db/database-admin/clear-database.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
const ENTITY_TYPES = [
|
||||
'User',
|
||||
'Credential',
|
||||
'Workflow',
|
||||
'LuaScript',
|
||||
'PageConfig',
|
||||
'ModelSchema',
|
||||
'AppConfiguration',
|
||||
'Comment',
|
||||
'ComponentNode',
|
||||
'ComponentConfig',
|
||||
'SystemConfig',
|
||||
'CssCategory',
|
||||
'DropdownConfig',
|
||||
'InstalledPackage',
|
||||
'PackageData',
|
||||
'Tenant',
|
||||
'PowerTransferRequest',
|
||||
'SMTPConfig',
|
||||
'PasswordResetToken',
|
||||
] as const
|
||||
|
||||
/**
|
||||
* Clear all data from the database
|
||||
*/
|
||||
export async function clearDatabase(): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
for (const entityType of ENTITY_TYPES) {
|
||||
try {
|
||||
const result = await adapter.list(entityType)
|
||||
for (const item of result.data as any[]) {
|
||||
const id = item.id || item.packageId || item.name || item.key || item.username
|
||||
if (id) {
|
||||
await adapter.delete(entityType, id)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Skip if entity type doesn't exist
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { DatabaseSchema } from '../types'
|
||||
import { getUsers } from '../users'
|
||||
import { getWorkflows } from '../workflows'
|
||||
import { getLuaScripts } from '../lua-scripts'
|
||||
import { getPages } from '../pages'
|
||||
import { getSchemas } from '../schemas'
|
||||
import { getAppConfig } from '../app-config'
|
||||
import { getComments } from '../comments'
|
||||
import { getComponentHierarchy, getComponentConfigs } from '../components'
|
||||
|
||||
/**
|
||||
* Export database contents as JSON string
|
||||
*/
|
||||
export async function exportDatabase(): Promise<string> {
|
||||
const data: Partial<DatabaseSchema> = {
|
||||
users: await getUsers(),
|
||||
workflows: await getWorkflows(),
|
||||
luaScripts: await getLuaScripts(),
|
||||
pages: await getPages(),
|
||||
schemas: await getSchemas(),
|
||||
appConfig: (await getAppConfig()) || undefined,
|
||||
comments: await getComments(),
|
||||
componentHierarchy: await getComponentHierarchy(),
|
||||
componentConfigs: await getComponentConfigs(),
|
||||
}
|
||||
return JSON.stringify(data, null, 2)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { DatabaseSchema } from '../types'
|
||||
import { setUsers } from '../users'
|
||||
import { setWorkflows } from '../workflows'
|
||||
import { setLuaScripts } from '../lua-scripts'
|
||||
import { setPages } from '../pages'
|
||||
import { setSchemas } from '../schemas'
|
||||
import { setAppConfig } from '../app-config'
|
||||
import { setComments } from '../comments'
|
||||
import { setComponentHierarchy, setComponentConfigs } from '../components'
|
||||
|
||||
/**
|
||||
* Import database contents from JSON string
|
||||
*/
|
||||
export async function importDatabase(jsonData: string): Promise<void> {
|
||||
try {
|
||||
const data = JSON.parse(jsonData) as Partial<DatabaseSchema>
|
||||
|
||||
if (data.users) await setUsers(data.users)
|
||||
if (data.workflows) await setWorkflows(data.workflows)
|
||||
if (data.luaScripts) await setLuaScripts(data.luaScripts)
|
||||
if (data.pages) await setPages(data.pages)
|
||||
if (data.schemas) await setSchemas(data.schemas)
|
||||
if (data.appConfig) await setAppConfig(data.appConfig)
|
||||
if (data.comments) await setComments(data.comments)
|
||||
if (data.componentHierarchy) await setComponentHierarchy(data.componentHierarchy)
|
||||
if (data.componentConfigs) await setComponentConfigs(data.componentConfigs)
|
||||
} catch (error) {
|
||||
throw new Error('Failed to import database: Invalid JSON')
|
||||
}
|
||||
}
|
||||
4
frontends/nextjs/src/lib/db/database-admin/index.ts
Normal file
4
frontends/nextjs/src/lib/db/database-admin/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { clearDatabase } from './clear-database'
|
||||
export { exportDatabase } from './export-database'
|
||||
export { importDatabase } from './import-database'
|
||||
export { seedDefaultData } from './seed-default-data'
|
||||
151
frontends/nextjs/src/lib/db/database-admin/seed-default-data.ts
Normal file
151
frontends/nextjs/src/lib/db/database-admin/seed-default-data.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import { getUsers, setUsers } from '../users'
|
||||
import { setCredential } from '../credentials'
|
||||
import { getAppConfig, setAppConfig } from '../app-config'
|
||||
import { getCssClasses, setCssClasses } from '../css-classes'
|
||||
import { getDropdownConfigs, setDropdownConfigs } from '../dropdown-configs'
|
||||
import { hashPassword } from '../hash-password'
|
||||
import type { CssCategory, DropdownConfig } from '../types'
|
||||
import type { User, AppConfiguration } from '../../types/level-types'
|
||||
|
||||
const DEFAULT_CSS_CLASSES: CssCategory[] = [
|
||||
{
|
||||
name: 'Layout',
|
||||
classes: ['flex', 'flex-col', 'flex-row', 'grid', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'block', 'inline-block', 'inline', 'hidden'],
|
||||
},
|
||||
{
|
||||
name: 'Spacing',
|
||||
classes: ['p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-6', 'p-8', 'm-0', 'm-1', 'm-2', 'm-3', 'm-4', 'm-6', 'm-8', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-6', 'gap-8'],
|
||||
},
|
||||
{
|
||||
name: 'Sizing',
|
||||
classes: ['w-full', 'w-1/2', 'w-1/3', 'w-1/4', 'w-auto', 'h-full', 'h-screen', 'h-auto', 'min-h-screen', 'max-w-xs', 'max-w-sm', 'max-w-md', 'max-w-lg', 'max-w-xl', 'max-w-2xl', 'max-w-4xl', 'max-w-6xl', 'max-w-7xl'],
|
||||
},
|
||||
{
|
||||
name: 'Typography',
|
||||
classes: ['text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'text-3xl', 'text-4xl', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'text-left', 'text-center', 'text-right', 'uppercase', 'lowercase', 'capitalize'],
|
||||
},
|
||||
{
|
||||
name: 'Colors',
|
||||
classes: ['text-primary', 'text-secondary', 'text-accent', 'text-muted-foreground', 'bg-primary', 'bg-secondary', 'bg-accent', 'bg-background', 'bg-card', 'bg-muted', 'border-primary', 'border-secondary', 'border-accent', 'border-border'],
|
||||
},
|
||||
{
|
||||
name: 'Borders',
|
||||
classes: ['border', 'border-2', 'border-4', 'border-t', 'border-b', 'border-l', 'border-r', 'rounded', 'rounded-sm', 'rounded-md', 'rounded-lg', 'rounded-xl', 'rounded-2xl', 'rounded-full'],
|
||||
},
|
||||
{
|
||||
name: 'Effects',
|
||||
classes: ['shadow', 'shadow-sm', 'shadow-md', 'shadow-lg', 'shadow-xl', 'hover:shadow-lg', 'opacity-0', 'opacity-50', 'opacity-75', 'opacity-100', 'transition', 'transition-all', 'duration-200', 'duration-300', 'duration-500'],
|
||||
},
|
||||
{
|
||||
name: 'Positioning',
|
||||
classes: ['relative', 'absolute', 'fixed', 'sticky', 'top-0', 'bottom-0', 'left-0', 'right-0', 'z-10', 'z-20', 'z-30', 'z-40', 'z-50'],
|
||||
},
|
||||
{
|
||||
name: 'Alignment',
|
||||
classes: ['items-start', 'items-center', 'items-end', 'justify-start', 'justify-center', 'justify-end', 'justify-between', 'justify-around', 'self-start', 'self-center', 'self-end'],
|
||||
},
|
||||
{
|
||||
name: 'Interactivity',
|
||||
classes: ['cursor-pointer', 'cursor-default', 'pointer-events-none', 'select-none', 'hover:bg-accent', 'hover:text-accent-foreground', 'active:scale-95', 'disabled:opacity-50'],
|
||||
},
|
||||
]
|
||||
|
||||
const DEFAULT_DROPDOWN_CONFIGS: DropdownConfig[] = [
|
||||
{
|
||||
id: 'dropdown_status',
|
||||
name: 'status_options',
|
||||
label: 'Status',
|
||||
options: [
|
||||
{ value: 'draft', label: 'Draft' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
{ value: 'archived', label: 'Archived' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dropdown_priority',
|
||||
name: 'priority_options',
|
||||
label: 'Priority',
|
||||
options: [
|
||||
{ value: 'low', label: 'Low' },
|
||||
{ value: 'medium', label: 'Medium' },
|
||||
{ value: 'high', label: 'High' },
|
||||
{ value: 'urgent', label: 'Urgent' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'dropdown_category',
|
||||
name: 'category_options',
|
||||
label: 'Category',
|
||||
options: [
|
||||
{ value: 'general', label: 'General' },
|
||||
{ value: 'technical', label: 'Technical' },
|
||||
{ value: 'business', label: 'Business' },
|
||||
{ value: 'personal', label: 'Personal' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Seed database with default data
|
||||
*/
|
||||
export async function seedDefaultData(): Promise<void> {
|
||||
// Create default users if none exist
|
||||
const users = await getUsers()
|
||||
if (users.length === 0) {
|
||||
const defaultUsers: User[] = [
|
||||
{
|
||||
id: 'user_supergod',
|
||||
username: 'supergod',
|
||||
email: 'supergod@system.local',
|
||||
role: 'supergod',
|
||||
createdAt: Date.now(),
|
||||
isInstanceOwner: true,
|
||||
},
|
||||
{
|
||||
id: 'user_god',
|
||||
username: 'god',
|
||||
email: 'god@system.local',
|
||||
role: 'god',
|
||||
createdAt: Date.now(),
|
||||
isInstanceOwner: false,
|
||||
},
|
||||
]
|
||||
await setUsers(defaultUsers)
|
||||
|
||||
// Set default passwords
|
||||
for (const user of defaultUsers) {
|
||||
const hash = await hashPassword(user.username)
|
||||
await setCredential(user.username, hash)
|
||||
}
|
||||
}
|
||||
|
||||
// Create default app config if none exists
|
||||
const appConfig = await getAppConfig()
|
||||
if (!appConfig) {
|
||||
const defaultConfig: AppConfiguration = {
|
||||
id: 'app_001',
|
||||
name: 'MetaBuilder App',
|
||||
schemas: [],
|
||||
workflows: [],
|
||||
luaScripts: [],
|
||||
pages: [],
|
||||
theme: {
|
||||
colors: {},
|
||||
fonts: {},
|
||||
},
|
||||
}
|
||||
await setAppConfig(defaultConfig)
|
||||
}
|
||||
|
||||
// Create default CSS classes if none exist
|
||||
const cssClasses = await getCssClasses()
|
||||
if (cssClasses.length === 0) {
|
||||
await setCssClasses(DEFAULT_CSS_CLASSES)
|
||||
}
|
||||
|
||||
// Create default dropdown configs if none exist
|
||||
const dropdowns = await getDropdownConfigs()
|
||||
if (dropdowns.length === 0) {
|
||||
await setDropdownConfigs(DEFAULT_DROPDOWN_CONFIGS)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Get all credentials as username->hash map
|
||||
* @returns Record of username to password hash
|
||||
*/
|
||||
export const getCredentials = async (): Promise<Record<string, string>> => {
|
||||
const credentials = await prisma.credential.findMany()
|
||||
const result: Record<string, string> = {}
|
||||
for (const cred of credentials) {
|
||||
result[cred.username] = cred.passwordHash
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Get password change timestamps for all users
|
||||
* @returns Record of username to timestamp
|
||||
*/
|
||||
export const getPasswordChangeTimestamps = async (): Promise<Record<string, number>> => {
|
||||
const users = await prisma.user.findMany({
|
||||
where: { passwordChangeTimestamp: { not: null } },
|
||||
select: { username: true, passwordChangeTimestamp: true },
|
||||
})
|
||||
const result: Record<string, number> = {}
|
||||
for (const user of users) {
|
||||
if (user.passwordChangeTimestamp) {
|
||||
result[user.username] = Number(user.passwordChangeTimestamp)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export { getCredentials } from './get-credentials'
|
||||
export { setCredential } from './set-credential'
|
||||
export { verifyCredentials } from './verify-credentials'
|
||||
export { getPasswordChangeTimestamps } from './get-password-change-timestamps'
|
||||
export { setPasswordChangeTimestamps } from './set-password-change-timestamps'
|
||||
@@ -0,0 +1,19 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Set or update a credential for a user
|
||||
* @param username - The username
|
||||
* @param passwordHash - The hashed password
|
||||
*/
|
||||
export const setCredential = async (username: string, passwordHash: string): Promise<void> => {
|
||||
await prisma.credential.upsert({
|
||||
where: { username },
|
||||
update: { passwordHash },
|
||||
create: { username, passwordHash },
|
||||
})
|
||||
|
||||
await prisma.user.update({
|
||||
where: { username },
|
||||
data: { passwordChangeTimestamp: BigInt(Date.now()) },
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Set password change timestamps
|
||||
* @param timestamps - Record of username to timestamp
|
||||
*/
|
||||
export const setPasswordChangeTimestamps = async (timestamps: Record<string, number>): Promise<void> => {
|
||||
for (const [username, timestamp] of Object.entries(timestamps)) {
|
||||
await prisma.user.update({
|
||||
where: { username },
|
||||
data: { passwordChangeTimestamp: BigInt(timestamp) },
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import { verifyPassword } from '../verify-password'
|
||||
|
||||
/**
|
||||
* Verify user credentials
|
||||
* @param username - The username
|
||||
* @param password - The plain text password
|
||||
* @returns True if credentials are valid
|
||||
*/
|
||||
export const verifyCredentials = async (username: string, password: string): Promise<boolean> => {
|
||||
const credential = await prisma.credential.findUnique({ where: { username } })
|
||||
if (!credential) return false
|
||||
return await verifyPassword(password, credential.passwordHash)
|
||||
}
|
||||
13
frontends/nextjs/src/lib/db/functions/hash-password.ts
Normal file
13
frontends/nextjs/src/lib/db/functions/hash-password.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Hash a password using SHA-512
|
||||
* @param password - The plain text password
|
||||
* @returns The hashed password as a hex string
|
||||
*/
|
||||
export const hashPassword = async (password: string): Promise<string> => {
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(password)
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-512', data)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
|
||||
return hashHex
|
||||
}
|
||||
25
frontends/nextjs/src/lib/db/functions/index.ts
Normal file
25
frontends/nextjs/src/lib/db/functions/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// Core functions
|
||||
export { hashPassword } from './hash-password'
|
||||
export { verifyPassword } from './verify-password'
|
||||
export { initializeDatabase } from './initialize-database'
|
||||
|
||||
// Users
|
||||
export * from './users'
|
||||
|
||||
// Credentials
|
||||
export * from './credentials'
|
||||
|
||||
// Workflows
|
||||
export * from './workflows'
|
||||
|
||||
// Lua Scripts
|
||||
export * from './lua-scripts'
|
||||
|
||||
// Pages
|
||||
export * from './pages'
|
||||
|
||||
// Schemas
|
||||
export * from './schemas'
|
||||
|
||||
// Tenants
|
||||
export * from './tenants'
|
||||
14
frontends/nextjs/src/lib/db/functions/initialize-database.ts
Normal file
14
frontends/nextjs/src/lib/db/functions/initialize-database.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Initialize database connection
|
||||
*/
|
||||
export const initializeDatabase = async (): Promise<void> => {
|
||||
try {
|
||||
await prisma.$connect()
|
||||
console.log('Database initialized successfully')
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize database:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { LuaScript } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a single Lua script
|
||||
* @param script - The script to add
|
||||
*/
|
||||
export const addLuaScript = async (script: LuaScript): Promise<void> => {
|
||||
await prisma.luaScript.create({
|
||||
data: {
|
||||
id: script.id,
|
||||
name: script.name,
|
||||
description: script.description,
|
||||
code: script.code,
|
||||
parameters: JSON.stringify(script.parameters),
|
||||
returnType: script.returnType,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Delete a Lua script by ID
|
||||
* @param scriptId - The script ID
|
||||
*/
|
||||
export const deleteLuaScript = async (scriptId: string): Promise<void> => {
|
||||
await prisma.luaScript.delete({ where: { id: scriptId } })
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { LuaScript } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all Lua scripts
|
||||
* @returns Array of Lua scripts
|
||||
*/
|
||||
export const getLuaScripts = async (): Promise<LuaScript[]> => {
|
||||
const scripts = await prisma.luaScript.findMany()
|
||||
return scripts.map(s => ({
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
description: s.description || undefined,
|
||||
code: s.code,
|
||||
parameters: JSON.parse(s.parameters),
|
||||
returnType: s.returnType || undefined,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export { getLuaScripts } from './get-lua-scripts'
|
||||
export { setLuaScripts } from './set-lua-scripts'
|
||||
export { addLuaScript } from './add-lua-script'
|
||||
export { updateLuaScript } from './update-lua-script'
|
||||
export { deleteLuaScript } from './delete-lua-script'
|
||||
@@ -0,0 +1,22 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { LuaScript } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all Lua scripts (replaces existing)
|
||||
* @param scripts - Array of Lua scripts
|
||||
*/
|
||||
export const setLuaScripts = async (scripts: LuaScript[]): Promise<void> => {
|
||||
await prisma.luaScript.deleteMany()
|
||||
for (const script of scripts) {
|
||||
await prisma.luaScript.create({
|
||||
data: {
|
||||
id: script.id,
|
||||
name: script.name,
|
||||
description: script.description,
|
||||
code: script.code,
|
||||
parameters: JSON.stringify(script.parameters),
|
||||
returnType: script.returnType,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { LuaScript } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a Lua script by ID
|
||||
* @param scriptId - The script ID
|
||||
* @param updates - Partial script data
|
||||
*/
|
||||
export const updateLuaScript = async (scriptId: string, updates: Partial<LuaScript>): Promise<void> => {
|
||||
const data: any = {}
|
||||
if (updates.name !== undefined) data.name = updates.name
|
||||
if (updates.description !== undefined) data.description = updates.description
|
||||
if (updates.code !== undefined) data.code = updates.code
|
||||
if (updates.parameters !== undefined) data.parameters = JSON.stringify(updates.parameters)
|
||||
if (updates.returnType !== undefined) data.returnType = updates.returnType
|
||||
|
||||
await prisma.luaScript.update({
|
||||
where: { id: scriptId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
20
frontends/nextjs/src/lib/db/functions/pages/add-page.ts
Normal file
20
frontends/nextjs/src/lib/db/functions/pages/add-page.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { PageConfig } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a single page config
|
||||
* @param page - The page to add
|
||||
*/
|
||||
export const addPage = async (page: PageConfig): Promise<void> => {
|
||||
await prisma.pageConfig.create({
|
||||
data: {
|
||||
id: page.id,
|
||||
path: page.path,
|
||||
title: page.title,
|
||||
level: page.level,
|
||||
componentTree: JSON.stringify(page.componentTree),
|
||||
requiresAuth: page.requiresAuth,
|
||||
requiredRole: page.requiredRole,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Delete a page config by ID
|
||||
* @param pageId - The page ID
|
||||
*/
|
||||
export const deletePage = async (pageId: string): Promise<void> => {
|
||||
await prisma.pageConfig.delete({ where: { id: pageId } })
|
||||
}
|
||||
19
frontends/nextjs/src/lib/db/functions/pages/get-pages.ts
Normal file
19
frontends/nextjs/src/lib/db/functions/pages/get-pages.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { PageConfig } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all page configs
|
||||
* @returns Array of page configs
|
||||
*/
|
||||
export const getPages = async (): Promise<PageConfig[]> => {
|
||||
const pages = await prisma.pageConfig.findMany()
|
||||
return pages.map(p => ({
|
||||
id: p.id,
|
||||
path: p.path,
|
||||
title: p.title,
|
||||
level: p.level as any,
|
||||
componentTree: JSON.parse(p.componentTree),
|
||||
requiresAuth: p.requiresAuth,
|
||||
requiredRole: (p.requiredRole as any) || undefined,
|
||||
}))
|
||||
}
|
||||
5
frontends/nextjs/src/lib/db/functions/pages/index.ts
Normal file
5
frontends/nextjs/src/lib/db/functions/pages/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { getPages } from './get-pages'
|
||||
export { setPages } from './set-pages'
|
||||
export { addPage } from './add-page'
|
||||
export { updatePage } from './update-page'
|
||||
export { deletePage } from './delete-page'
|
||||
23
frontends/nextjs/src/lib/db/functions/pages/set-pages.ts
Normal file
23
frontends/nextjs/src/lib/db/functions/pages/set-pages.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { PageConfig } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all page configs (replaces existing)
|
||||
* @param pages - Array of page configs
|
||||
*/
|
||||
export const setPages = async (pages: PageConfig[]): Promise<void> => {
|
||||
await prisma.pageConfig.deleteMany()
|
||||
for (const page of pages) {
|
||||
await prisma.pageConfig.create({
|
||||
data: {
|
||||
id: page.id,
|
||||
path: page.path,
|
||||
title: page.title,
|
||||
level: page.level,
|
||||
componentTree: JSON.stringify(page.componentTree),
|
||||
requiresAuth: page.requiresAuth,
|
||||
requiredRole: page.requiredRole,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
22
frontends/nextjs/src/lib/db/functions/pages/update-page.ts
Normal file
22
frontends/nextjs/src/lib/db/functions/pages/update-page.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { PageConfig } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a page config by ID
|
||||
* @param pageId - The page ID
|
||||
* @param updates - Partial page data
|
||||
*/
|
||||
export const updatePage = async (pageId: string, updates: Partial<PageConfig>): Promise<void> => {
|
||||
const data: any = {}
|
||||
if (updates.path !== undefined) data.path = updates.path
|
||||
if (updates.title !== undefined) data.title = updates.title
|
||||
if (updates.level !== undefined) data.level = updates.level
|
||||
if (updates.componentTree !== undefined) data.componentTree = JSON.stringify(updates.componentTree)
|
||||
if (updates.requiresAuth !== undefined) data.requiresAuth = updates.requiresAuth
|
||||
if (updates.requiredRole !== undefined) data.requiredRole = updates.requiredRole
|
||||
|
||||
await prisma.pageConfig.update({
|
||||
where: { id: pageId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
22
frontends/nextjs/src/lib/db/functions/schemas/add-schema.ts
Normal file
22
frontends/nextjs/src/lib/db/functions/schemas/add-schema.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Add a single model schema
|
||||
* @param schema - The schema to add
|
||||
*/
|
||||
export const addSchema = async (schema: ModelSchema): Promise<void> => {
|
||||
await prisma.modelSchema.create({
|
||||
data: {
|
||||
name: schema.name,
|
||||
label: schema.label,
|
||||
labelPlural: schema.labelPlural,
|
||||
icon: schema.icon,
|
||||
fields: JSON.stringify(schema.fields),
|
||||
listDisplay: schema.listDisplay ? JSON.stringify(schema.listDisplay) : null,
|
||||
listFilter: schema.listFilter ? JSON.stringify(schema.listFilter) : null,
|
||||
searchFields: schema.searchFields ? JSON.stringify(schema.searchFields) : null,
|
||||
ordering: schema.ordering ? JSON.stringify(schema.ordering) : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Delete a model schema by name
|
||||
* @param schemaName - The schema name
|
||||
*/
|
||||
export const deleteSchema = async (schemaName: string): Promise<void> => {
|
||||
await prisma.modelSchema.delete({ where: { name: schemaName } })
|
||||
}
|
||||
21
frontends/nextjs/src/lib/db/functions/schemas/get-schemas.ts
Normal file
21
frontends/nextjs/src/lib/db/functions/schemas/get-schemas.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Get all model schemas
|
||||
* @returns Array of model schemas
|
||||
*/
|
||||
export const getSchemas = async (): Promise<ModelSchema[]> => {
|
||||
const schemas = await prisma.modelSchema.findMany()
|
||||
return schemas.map(s => ({
|
||||
name: s.name,
|
||||
label: s.label || undefined,
|
||||
labelPlural: s.labelPlural || undefined,
|
||||
icon: s.icon || undefined,
|
||||
fields: JSON.parse(s.fields),
|
||||
listDisplay: s.listDisplay ? JSON.parse(s.listDisplay) : undefined,
|
||||
listFilter: s.listFilter ? JSON.parse(s.listFilter) : undefined,
|
||||
searchFields: s.searchFields ? JSON.parse(s.searchFields) : undefined,
|
||||
ordering: s.ordering ? JSON.parse(s.ordering) : undefined,
|
||||
}))
|
||||
}
|
||||
5
frontends/nextjs/src/lib/db/functions/schemas/index.ts
Normal file
5
frontends/nextjs/src/lib/db/functions/schemas/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { getSchemas } from './get-schemas'
|
||||
export { setSchemas } from './set-schemas'
|
||||
export { addSchema } from './add-schema'
|
||||
export { updateSchema } from './update-schema'
|
||||
export { deleteSchema } from './delete-schema'
|
||||
25
frontends/nextjs/src/lib/db/functions/schemas/set-schemas.ts
Normal file
25
frontends/nextjs/src/lib/db/functions/schemas/set-schemas.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Set all model schemas (replaces existing)
|
||||
* @param schemas - Array of model schemas
|
||||
*/
|
||||
export const setSchemas = async (schemas: ModelSchema[]): Promise<void> => {
|
||||
await prisma.modelSchema.deleteMany()
|
||||
for (const schema of schemas) {
|
||||
await prisma.modelSchema.create({
|
||||
data: {
|
||||
name: schema.name,
|
||||
label: schema.label,
|
||||
labelPlural: schema.labelPlural,
|
||||
icon: schema.icon,
|
||||
fields: JSON.stringify(schema.fields),
|
||||
listDisplay: schema.listDisplay ? JSON.stringify(schema.listDisplay) : null,
|
||||
listFilter: schema.listFilter ? JSON.stringify(schema.listFilter) : null,
|
||||
searchFields: schema.searchFields ? JSON.stringify(schema.searchFields) : null,
|
||||
ordering: schema.ordering ? JSON.stringify(schema.ordering) : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Update a model schema by name
|
||||
* @param schemaName - The schema name
|
||||
* @param updates - Partial schema data
|
||||
*/
|
||||
export const updateSchema = async (schemaName: string, updates: Partial<ModelSchema>): Promise<void> => {
|
||||
const data: any = {}
|
||||
if (updates.label !== undefined) data.label = updates.label
|
||||
if (updates.labelPlural !== undefined) data.labelPlural = updates.labelPlural
|
||||
if (updates.icon !== undefined) data.icon = updates.icon
|
||||
if (updates.fields !== undefined) data.fields = JSON.stringify(updates.fields)
|
||||
if (updates.listDisplay !== undefined) data.listDisplay = JSON.stringify(updates.listDisplay)
|
||||
if (updates.listFilter !== undefined) data.listFilter = JSON.stringify(updates.listFilter)
|
||||
if (updates.searchFields !== undefined) data.searchFields = JSON.stringify(updates.searchFields)
|
||||
if (updates.ordering !== undefined) data.ordering = JSON.stringify(updates.ordering)
|
||||
|
||||
await prisma.modelSchema.update({
|
||||
where: { name: schemaName },
|
||||
data,
|
||||
})
|
||||
}
|
||||
18
frontends/nextjs/src/lib/db/functions/tenants/add-tenant.ts
Normal file
18
frontends/nextjs/src/lib/db/functions/tenants/add-tenant.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Tenant } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a single tenant
|
||||
* @param tenant - The tenant to add
|
||||
*/
|
||||
export const addTenant = async (tenant: Tenant): Promise<void> => {
|
||||
await prisma.tenant.create({
|
||||
data: {
|
||||
id: tenant.id,
|
||||
name: tenant.name,
|
||||
ownerId: tenant.ownerId,
|
||||
createdAt: BigInt(tenant.createdAt),
|
||||
homepageConfig: tenant.homepageConfig ? JSON.stringify(tenant.homepageConfig) : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Delete a tenant by ID
|
||||
* @param tenantId - The tenant ID
|
||||
*/
|
||||
export const deleteTenant = async (tenantId: string): Promise<void> => {
|
||||
await prisma.tenant.delete({ where: { id: tenantId } })
|
||||
}
|
||||
17
frontends/nextjs/src/lib/db/functions/tenants/get-tenants.ts
Normal file
17
frontends/nextjs/src/lib/db/functions/tenants/get-tenants.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Tenant } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all tenants
|
||||
* @returns Array of tenants
|
||||
*/
|
||||
export const getTenants = async (): Promise<Tenant[]> => {
|
||||
const tenants = await prisma.tenant.findMany()
|
||||
return tenants.map(t => ({
|
||||
id: t.id,
|
||||
name: t.name,
|
||||
ownerId: t.ownerId,
|
||||
createdAt: Number(t.createdAt),
|
||||
homepageConfig: t.homepageConfig ? JSON.parse(t.homepageConfig) : undefined,
|
||||
}))
|
||||
}
|
||||
5
frontends/nextjs/src/lib/db/functions/tenants/index.ts
Normal file
5
frontends/nextjs/src/lib/db/functions/tenants/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { getTenants } from './get-tenants'
|
||||
export { setTenants } from './set-tenants'
|
||||
export { addTenant } from './add-tenant'
|
||||
export { updateTenant } from './update-tenant'
|
||||
export { deleteTenant } from './delete-tenant'
|
||||
21
frontends/nextjs/src/lib/db/functions/tenants/set-tenants.ts
Normal file
21
frontends/nextjs/src/lib/db/functions/tenants/set-tenants.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Tenant } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all tenants (replaces existing)
|
||||
* @param tenants - Array of tenants
|
||||
*/
|
||||
export const setTenants = async (tenants: Tenant[]): Promise<void> => {
|
||||
await prisma.tenant.deleteMany()
|
||||
for (const tenant of tenants) {
|
||||
await prisma.tenant.create({
|
||||
data: {
|
||||
id: tenant.id,
|
||||
name: tenant.name,
|
||||
ownerId: tenant.ownerId,
|
||||
createdAt: BigInt(tenant.createdAt),
|
||||
homepageConfig: tenant.homepageConfig ? JSON.stringify(tenant.homepageConfig) : null,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Tenant } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a tenant by ID
|
||||
* @param tenantId - The tenant ID
|
||||
* @param updates - Partial tenant data
|
||||
*/
|
||||
export const updateTenant = async (tenantId: string, updates: Partial<Tenant>): Promise<void> => {
|
||||
const data: any = {}
|
||||
if (updates.name !== undefined) data.name = updates.name
|
||||
if (updates.ownerId !== undefined) data.ownerId = updates.ownerId
|
||||
if (updates.homepageConfig !== undefined) data.homepageConfig = JSON.stringify(updates.homepageConfig)
|
||||
|
||||
await prisma.tenant.update({
|
||||
where: { id: tenantId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
24
frontends/nextjs/src/lib/db/functions/users/add-user.ts
Normal file
24
frontends/nextjs/src/lib/db/functions/users/add-user.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { User } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a single user
|
||||
* @param user - The user to add
|
||||
*/
|
||||
export const addUser = async (user: User): Promise<void> => {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
profilePicture: user.profilePicture,
|
||||
bio: user.bio,
|
||||
createdAt: BigInt(user.createdAt),
|
||||
tenantId: user.tenantId,
|
||||
isInstanceOwner: user.isInstanceOwner || false,
|
||||
passwordChangeTimestamp: null,
|
||||
firstLogin: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
* @param userId - The user ID to delete
|
||||
*/
|
||||
export const deleteUser = async (userId: string): Promise<void> => {
|
||||
await prisma.user.delete({ where: { id: userId } })
|
||||
}
|
||||
21
frontends/nextjs/src/lib/db/functions/users/get-users.ts
Normal file
21
frontends/nextjs/src/lib/db/functions/users/get-users.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { User } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all users from database
|
||||
* @returns Array of users
|
||||
*/
|
||||
export const getUsers = async (): Promise<User[]> => {
|
||||
const users = await prisma.user.findMany()
|
||||
return users.map(u => ({
|
||||
id: u.id,
|
||||
username: u.username,
|
||||
email: u.email,
|
||||
role: u.role as any,
|
||||
profilePicture: u.profilePicture || undefined,
|
||||
bio: u.bio || undefined,
|
||||
createdAt: Number(u.createdAt),
|
||||
tenantId: u.tenantId || undefined,
|
||||
isInstanceOwner: u.isInstanceOwner,
|
||||
}))
|
||||
}
|
||||
5
frontends/nextjs/src/lib/db/functions/users/index.ts
Normal file
5
frontends/nextjs/src/lib/db/functions/users/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { getUsers } from './get-users'
|
||||
export { setUsers } from './set-users'
|
||||
export { addUser } from './add-user'
|
||||
export { updateUser } from './update-user'
|
||||
export { deleteUser } from './delete-user'
|
||||
27
frontends/nextjs/src/lib/db/functions/users/set-users.ts
Normal file
27
frontends/nextjs/src/lib/db/functions/users/set-users.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { User } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all users (replaces existing)
|
||||
* @param users - Array of users to set
|
||||
*/
|
||||
export const setUsers = async (users: User[]): Promise<void> => {
|
||||
await prisma.user.deleteMany()
|
||||
for (const user of users) {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
profilePicture: user.profilePicture,
|
||||
bio: user.bio,
|
||||
createdAt: BigInt(user.createdAt),
|
||||
tenantId: user.tenantId,
|
||||
isInstanceOwner: user.isInstanceOwner || false,
|
||||
passwordChangeTimestamp: null,
|
||||
firstLogin: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
23
frontends/nextjs/src/lib/db/functions/users/update-user.ts
Normal file
23
frontends/nextjs/src/lib/db/functions/users/update-user.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { User } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a user by ID
|
||||
* @param userId - The user ID to update
|
||||
* @param updates - Partial user data to update
|
||||
*/
|
||||
export const updateUser = async (userId: string, updates: Partial<User>): Promise<void> => {
|
||||
const data: any = {}
|
||||
if (updates.username !== undefined) data.username = updates.username
|
||||
if (updates.email !== undefined) data.email = updates.email
|
||||
if (updates.role !== undefined) data.role = updates.role
|
||||
if (updates.profilePicture !== undefined) data.profilePicture = updates.profilePicture
|
||||
if (updates.bio !== undefined) data.bio = updates.bio
|
||||
if (updates.tenantId !== undefined) data.tenantId = updates.tenantId
|
||||
if (updates.isInstanceOwner !== undefined) data.isInstanceOwner = updates.isInstanceOwner
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
12
frontends/nextjs/src/lib/db/functions/verify-password.ts
Normal file
12
frontends/nextjs/src/lib/db/functions/verify-password.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { hashPassword } from './hash-password'
|
||||
|
||||
/**
|
||||
* Verify a password against a hash
|
||||
* @param password - The plain text password
|
||||
* @param hash - The hash to compare against
|
||||
* @returns True if password matches hash
|
||||
*/
|
||||
export const verifyPassword = async (password: string, hash: string): Promise<boolean> => {
|
||||
const inputHash = await hashPassword(password)
|
||||
return inputHash === hash
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Workflow } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a single workflow
|
||||
* @param workflow - The workflow to add
|
||||
*/
|
||||
export const addWorkflow = async (workflow: Workflow): Promise<void> => {
|
||||
await prisma.workflow.create({
|
||||
data: {
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
description: workflow.description,
|
||||
nodes: JSON.stringify(workflow.nodes),
|
||||
edges: JSON.stringify(workflow.edges),
|
||||
enabled: workflow.enabled,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { prisma } from '../../prisma'
|
||||
|
||||
/**
|
||||
* Delete a workflow by ID
|
||||
* @param workflowId - The workflow ID
|
||||
*/
|
||||
export const deleteWorkflow = async (workflowId: string): Promise<void> => {
|
||||
await prisma.workflow.delete({ where: { id: workflowId } })
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Workflow } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all workflows
|
||||
* @returns Array of workflows
|
||||
*/
|
||||
export const getWorkflows = async (): Promise<Workflow[]> => {
|
||||
const workflows = await prisma.workflow.findMany()
|
||||
return workflows.map(w => ({
|
||||
id: w.id,
|
||||
name: w.name,
|
||||
description: w.description || undefined,
|
||||
nodes: JSON.parse(w.nodes),
|
||||
edges: JSON.parse(w.edges),
|
||||
enabled: w.enabled,
|
||||
}))
|
||||
}
|
||||
5
frontends/nextjs/src/lib/db/functions/workflows/index.ts
Normal file
5
frontends/nextjs/src/lib/db/functions/workflows/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { getWorkflows } from './get-workflows'
|
||||
export { setWorkflows } from './set-workflows'
|
||||
export { addWorkflow } from './add-workflow'
|
||||
export { updateWorkflow } from './update-workflow'
|
||||
export { deleteWorkflow } from './delete-workflow'
|
||||
@@ -0,0 +1,22 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Workflow } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all workflows (replaces existing)
|
||||
* @param workflows - Array of workflows
|
||||
*/
|
||||
export const setWorkflows = async (workflows: Workflow[]): Promise<void> => {
|
||||
await prisma.workflow.deleteMany()
|
||||
for (const workflow of workflows) {
|
||||
await prisma.workflow.create({
|
||||
data: {
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
description: workflow.description,
|
||||
nodes: JSON.stringify(workflow.nodes),
|
||||
edges: JSON.stringify(workflow.edges),
|
||||
enabled: workflow.enabled,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { prisma } from '../../prisma'
|
||||
import type { Workflow } from '../../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a workflow by ID
|
||||
* @param workflowId - The workflow ID
|
||||
* @param updates - Partial workflow data
|
||||
*/
|
||||
export const updateWorkflow = async (workflowId: string, updates: Partial<Workflow>): Promise<void> => {
|
||||
const data: any = {}
|
||||
if (updates.name !== undefined) data.name = updates.name
|
||||
if (updates.description !== undefined) data.description = updates.description
|
||||
if (updates.nodes !== undefined) data.nodes = JSON.stringify(updates.nodes)
|
||||
if (updates.edges !== undefined) data.edges = JSON.stringify(updates.edges)
|
||||
if (updates.enabled !== undefined) data.enabled = updates.enabled
|
||||
|
||||
await prisma.workflow.update({
|
||||
where: { id: workflowId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Get first login flags for all users
|
||||
*/
|
||||
export async function getFirstLoginFlags(): Promise<Record<string, boolean>> {
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('User')
|
||||
const users = result.data as any[]
|
||||
const flags: Record<string, boolean> = {}
|
||||
for (const user of users) {
|
||||
flags[user.username] = user.firstLogin ?? true
|
||||
}
|
||||
return flags
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Get god credentials expiry duration in ms
|
||||
*/
|
||||
export async function getGodCredentialsExpiryDuration(): Promise<number> {
|
||||
const adapter = getAdapter()
|
||||
const config = await adapter.findFirst('SystemConfig', {
|
||||
where: { key: 'god_credentials_expiry_duration' },
|
||||
})
|
||||
return config ? Number(config.value) : 60 * 60 * 1000 // Default 1 hour
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Get god credentials expiry timestamp
|
||||
*/
|
||||
export async function getGodCredentialsExpiry(): Promise<number> {
|
||||
const adapter = getAdapter()
|
||||
const config = await adapter.findFirst('SystemConfig', {
|
||||
where: { key: 'god_credentials_expiry' },
|
||||
})
|
||||
return config ? Number(config.value) : 0
|
||||
}
|
||||
8
frontends/nextjs/src/lib/db/god-credentials/index.ts
Normal file
8
frontends/nextjs/src/lib/db/god-credentials/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export { getGodCredentialsExpiry } from './get-god-credentials-expiry'
|
||||
export { setGodCredentialsExpiry } from './set-god-credentials-expiry'
|
||||
export { getFirstLoginFlags } from './get-first-login-flags'
|
||||
export { setFirstLoginFlag } from './set-first-login-flag'
|
||||
export { getGodCredentialsExpiryDuration } from './get-god-credentials-expiry-duration'
|
||||
export { setGodCredentialsExpiryDuration } from './set-god-credentials-expiry-duration'
|
||||
export { shouldShowGodCredentials } from './should-show-god-credentials'
|
||||
export { resetGodCredentialsExpiry } from './reset-god-credentials-expiry'
|
||||
@@ -0,0 +1,11 @@
|
||||
import { setGodCredentialsExpiry } from './set-god-credentials-expiry'
|
||||
import { getGodCredentialsExpiryDuration } from './get-god-credentials-expiry-duration'
|
||||
|
||||
/**
|
||||
* Reset god credentials expiry to extend it by the configured duration
|
||||
*/
|
||||
export async function resetGodCredentialsExpiry(): Promise<void> {
|
||||
const duration = await getGodCredentialsExpiryDuration()
|
||||
const expiryTime = Date.now() + duration
|
||||
await setGodCredentialsExpiry(expiryTime)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Set first login flag for a user
|
||||
*/
|
||||
export async function setFirstLoginFlag(username: string, isFirstLogin: boolean): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
// Find the user first
|
||||
const user = await adapter.findFirst('User', {
|
||||
where: { username },
|
||||
})
|
||||
if (user) {
|
||||
await adapter.update('User', user.id, { firstLogin: isFirstLogin })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Set god credentials expiry duration in ms
|
||||
*/
|
||||
export async function setGodCredentialsExpiryDuration(durationMs: number): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
await adapter.upsert('SystemConfig', {
|
||||
where: { key: 'god_credentials_expiry_duration' },
|
||||
update: { value: durationMs.toString() },
|
||||
create: { key: 'god_credentials_expiry_duration', value: durationMs.toString() },
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Set god credentials expiry timestamp
|
||||
*/
|
||||
export async function setGodCredentialsExpiry(timestamp: number): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
await adapter.upsert('SystemConfig', {
|
||||
where: { key: 'god_credentials_expiry' },
|
||||
update: { value: timestamp.toString() },
|
||||
create: { key: 'god_credentials_expiry', value: timestamp.toString() },
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { getGodCredentialsExpiry } from './get-god-credentials-expiry'
|
||||
import { setGodCredentialsExpiry } from './set-god-credentials-expiry'
|
||||
import { getGodCredentialsExpiryDuration } from './get-god-credentials-expiry-duration'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Check if god credentials should be shown
|
||||
*/
|
||||
export async function shouldShowGodCredentials(): Promise<boolean> {
|
||||
const adapter = getAdapter()
|
||||
const expiry = await getGodCredentialsExpiry()
|
||||
|
||||
// Get god user's password change timestamp
|
||||
const godUser = await adapter.findFirst('User', {
|
||||
where: { username: 'god' },
|
||||
})
|
||||
const godPasswordChangeTime = godUser?.passwordChangeTimestamp ? Number(godUser.passwordChangeTimestamp) : 0
|
||||
|
||||
if (expiry === 0) {
|
||||
const duration = await getGodCredentialsExpiryDuration()
|
||||
const expiryTime = Date.now() + duration
|
||||
await setGodCredentialsExpiry(expiryTime)
|
||||
return true
|
||||
}
|
||||
|
||||
if (godPasswordChangeTime > expiry) {
|
||||
return false
|
||||
}
|
||||
|
||||
return Date.now() < expiry
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Types
|
||||
export type { CssCategory, DropdownConfig, DatabaseSchema, ComponentNode, ComponentConfig } from './types'
|
||||
export { DB_KEYS } from './types'
|
||||
|
||||
// DBAL Client
|
||||
export { getAdapter, closeAdapter } from './dbal-client'
|
||||
@@ -20,6 +21,14 @@ export * from './schemas'
|
||||
export * from './comments'
|
||||
export * from './app-config'
|
||||
export * from './components'
|
||||
export * from './css-classes'
|
||||
export * from './dropdown-configs'
|
||||
export * from './tenants'
|
||||
export * from './packages'
|
||||
export * from './power-transfers'
|
||||
export * from './smtp-config'
|
||||
export * from './god-credentials'
|
||||
export * from './database-admin'
|
||||
|
||||
// Import all for namespace class
|
||||
import { initializeDatabase } from './initialize-database'
|
||||
@@ -34,6 +43,14 @@ import * as schemas from './schemas'
|
||||
import * as comments from './comments'
|
||||
import * as appConfig from './app-config'
|
||||
import * as components from './components'
|
||||
import * as cssClasses from './css-classes'
|
||||
import * as dropdownConfigs from './dropdown-configs'
|
||||
import * as tenants from './tenants'
|
||||
import * as packages from './packages'
|
||||
import * as powerTransfers from './power-transfers'
|
||||
import * as smtpConfig from './smtp-config'
|
||||
import * as godCredentials from './god-credentials'
|
||||
import * as databaseAdmin from './database-admin'
|
||||
|
||||
/**
|
||||
* Database namespace class - groups all DB operations as static methods
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { PowerTransferRequest } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a new power transfer request
|
||||
*/
|
||||
export async function addPowerTransferRequest(request: PowerTransferRequest): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('PowerTransferRequest', {
|
||||
id: request.id,
|
||||
fromUserId: request.fromUserId,
|
||||
toUserId: request.toUserId,
|
||||
status: request.status,
|
||||
createdAt: BigInt(request.createdAt),
|
||||
expiresAt: BigInt(request.expiresAt),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Delete a power transfer request
|
||||
*/
|
||||
export async function deletePowerTransferRequest(requestId: string): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('PowerTransferRequest', requestId)
|
||||
}
|
||||
5
frontends/nextjs/src/lib/db/power-transfers/index.ts
Normal file
5
frontends/nextjs/src/lib/db/power-transfers/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { getPowerTransferRequests } from './get-power-transfer-requests'
|
||||
export { setPowerTransferRequests } from './set-power-transfer-requests'
|
||||
export { addPowerTransferRequest } from './add-power-transfer-request'
|
||||
export { updatePowerTransferRequest } from './update-power-transfer-request'
|
||||
export { deletePowerTransferRequest } from './delete-power-transfer-request'
|
||||
@@ -0,0 +1,12 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { PowerTransferRequest } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update an existing power transfer request
|
||||
*/
|
||||
export async function updatePowerTransferRequest(requestId: string, updates: Partial<PowerTransferRequest>): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, any> = {}
|
||||
if (updates.status !== undefined) data.status = updates.status
|
||||
await adapter.update('PowerTransferRequest', requestId, data)
|
||||
}
|
||||
22
frontends/nextjs/src/lib/db/smtp-config/get-smtp-config.ts
Normal file
22
frontends/nextjs/src/lib/db/smtp-config/get-smtp-config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { SMTPConfig } from '../../password-utils'
|
||||
|
||||
/**
|
||||
* Get SMTP configuration
|
||||
*/
|
||||
export async function getSMTPConfig(): Promise<SMTPConfig | null> {
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('SMTPConfig')
|
||||
const config = (result.data as any[])[0]
|
||||
if (!config) return null
|
||||
|
||||
return {
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: config.secure,
|
||||
username: config.username,
|
||||
password: config.password,
|
||||
fromEmail: config.fromEmail,
|
||||
fromName: config.fromName,
|
||||
}
|
||||
}
|
||||
2
frontends/nextjs/src/lib/db/smtp-config/index.ts
Normal file
2
frontends/nextjs/src/lib/db/smtp-config/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { getSMTPConfig } from './get-smtp-config'
|
||||
export { setSMTPConfig } from './set-smtp-config'
|
||||
24
frontends/nextjs/src/lib/db/smtp-config/set-smtp-config.ts
Normal file
24
frontends/nextjs/src/lib/db/smtp-config/set-smtp-config.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { SMTPConfig } from '../../password-utils'
|
||||
|
||||
/**
|
||||
* Set SMTP configuration (replaces existing)
|
||||
*/
|
||||
export async function setSMTPConfig(config: SMTPConfig): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
// Delete all existing
|
||||
const existing = await adapter.list('SMTPConfig')
|
||||
for (const item of existing.data as any[]) {
|
||||
await adapter.delete('SMTPConfig', item.id)
|
||||
}
|
||||
// Create new
|
||||
await adapter.create('SMTPConfig', {
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: config.secure,
|
||||
username: config.username,
|
||||
password: config.password,
|
||||
fromEmail: config.fromEmail,
|
||||
fromName: config.fromName,
|
||||
})
|
||||
}
|
||||
10
frontends/nextjs/src/lib/lua/functions/get-snippet-by-id.ts
Normal file
10
frontends/nextjs/src/lib/lua/functions/get-snippet-by-id.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { LUA_SNIPPETS, type LuaSnippet } from '../snippets/lua-snippets-data'
|
||||
|
||||
/**
|
||||
* Get a snippet by its ID
|
||||
* @param id - Snippet ID
|
||||
* @returns The snippet or undefined if not found
|
||||
*/
|
||||
export const getSnippetById = (id: string): LuaSnippet | undefined => {
|
||||
return LUA_SNIPPETS.find(snippet => snippet.id === id)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { LUA_SNIPPETS, type LuaSnippet } from '../snippets/lua-snippets-data'
|
||||
|
||||
/**
|
||||
* Get snippets filtered by category
|
||||
* @param category - Category name or 'All' for all snippets
|
||||
* @returns Array of matching snippets
|
||||
*/
|
||||
export const getSnippetsByCategory = (category: string): LuaSnippet[] => {
|
||||
if (category === 'All') {
|
||||
return LUA_SNIPPETS
|
||||
}
|
||||
return LUA_SNIPPETS.filter(snippet => snippet.category === category)
|
||||
}
|
||||
3
frontends/nextjs/src/lib/lua/functions/index.ts
Normal file
3
frontends/nextjs/src/lib/lua/functions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { getSnippetsByCategory } from './get-snippets-by-category'
|
||||
export { searchSnippets } from './search-snippets'
|
||||
export { getSnippetById } from './get-snippet-by-id'
|
||||
15
frontends/nextjs/src/lib/lua/functions/search-snippets.ts
Normal file
15
frontends/nextjs/src/lib/lua/functions/search-snippets.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { LUA_SNIPPETS, type LuaSnippet } from '../snippets/lua-snippets-data'
|
||||
|
||||
/**
|
||||
* Search snippets by query string
|
||||
* @param query - Search query
|
||||
* @returns Array of matching snippets
|
||||
*/
|
||||
export const searchSnippets = (query: string): LuaSnippet[] => {
|
||||
const lowerQuery = query.toLowerCase()
|
||||
return LUA_SNIPPETS.filter(snippet =>
|
||||
snippet.name.toLowerCase().includes(lowerQuery) ||
|
||||
snippet.description.toLowerCase().includes(lowerQuery) ||
|
||||
snippet.tags.some(tag => tag.toLowerCase().includes(lowerQuery))
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user