mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
feat(seed): add seed data module and tests for database initialization
feat(types): introduce builder and level types for component and user roles feat(workflow): implement workflow engine with execution context and logging style(theme): create modular theme exports for consistent styling chore(tests): add comprehensive tests for workflow execution and Lua nodes
This commit is contained in:
@@ -11,12 +11,24 @@ export interface AdapterCapabilities {
|
||||
}
|
||||
|
||||
export interface DBALAdapter {
|
||||
// Core CRUD operations
|
||||
create(entity: string, data: Record<string, unknown>): Promise<unknown>
|
||||
read(entity: string, id: string): Promise<unknown | null>
|
||||
update(entity: string, id: string, data: Record<string, unknown>): Promise<unknown>
|
||||
delete(entity: string, id: string): Promise<boolean>
|
||||
list(entity: string, options?: ListOptions): Promise<ListResult<unknown>>
|
||||
|
||||
// Extended query operations
|
||||
findFirst(entity: string, filter?: Record<string, unknown>): Promise<unknown | null>
|
||||
findByField(entity: string, field: string, value: unknown): Promise<unknown | null>
|
||||
|
||||
// Extended mutation operations
|
||||
upsert(entity: string, uniqueField: string, uniqueValue: unknown, createData: Record<string, unknown>, updateData: Record<string, unknown>): Promise<unknown>
|
||||
updateByField(entity: string, field: string, value: unknown, data: Record<string, unknown>): Promise<unknown>
|
||||
deleteByField(entity: string, field: string, value: unknown): Promise<boolean>
|
||||
deleteMany(entity: string, filter?: Record<string, unknown>): Promise<number>
|
||||
createMany(entity: string, data: Record<string, unknown>[]): Promise<number>
|
||||
|
||||
getCapabilities(): Promise<AdapterCapabilities>
|
||||
close(): Promise<void>
|
||||
}
|
||||
|
||||
@@ -106,6 +106,108 @@ export class PrismaAdapter implements DBALAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async findFirst(entity: string, filter?: Record<string, unknown>): Promise<unknown | null> {
|
||||
try {
|
||||
const model = this.getModel(entity)
|
||||
const where = filter ? this.buildWhereClause(filter) : undefined
|
||||
const result = await this.withTimeout(
|
||||
model.findFirst({ where: where as never })
|
||||
)
|
||||
return result
|
||||
} catch (error) {
|
||||
throw this.handleError(error, 'findFirst', entity)
|
||||
}
|
||||
}
|
||||
|
||||
async findByField(entity: string, field: string, value: unknown): Promise<unknown | null> {
|
||||
try {
|
||||
const model = this.getModel(entity)
|
||||
const result = await this.withTimeout(
|
||||
model.findUnique({ where: { [field]: value } as never })
|
||||
)
|
||||
return result
|
||||
} catch (error) {
|
||||
throw this.handleError(error, 'findByField', entity)
|
||||
}
|
||||
}
|
||||
|
||||
async upsert(
|
||||
entity: string,
|
||||
uniqueField: string,
|
||||
uniqueValue: unknown,
|
||||
createData: Record<string, unknown>,
|
||||
updateData: Record<string, unknown>
|
||||
): Promise<unknown> {
|
||||
try {
|
||||
const model = this.getModel(entity)
|
||||
const result = await this.withTimeout(
|
||||
model.upsert({
|
||||
where: { [uniqueField]: uniqueValue } as never,
|
||||
create: createData as never,
|
||||
update: updateData as never,
|
||||
})
|
||||
)
|
||||
return result
|
||||
} catch (error) {
|
||||
throw this.handleError(error, 'upsert', entity)
|
||||
}
|
||||
}
|
||||
|
||||
async updateByField(entity: string, field: string, value: unknown, data: Record<string, unknown>): Promise<unknown> {
|
||||
try {
|
||||
const model = this.getModel(entity)
|
||||
const result = await this.withTimeout(
|
||||
model.update({
|
||||
where: { [field]: value } as never,
|
||||
data: data as never,
|
||||
})
|
||||
)
|
||||
return result
|
||||
} catch (error) {
|
||||
throw this.handleError(error, 'updateByField', entity)
|
||||
}
|
||||
}
|
||||
|
||||
async deleteByField(entity: string, field: string, value: unknown): Promise<boolean> {
|
||||
try {
|
||||
const model = this.getModel(entity)
|
||||
await this.withTimeout(
|
||||
model.delete({ where: { [field]: value } as never })
|
||||
)
|
||||
return true
|
||||
} catch (error) {
|
||||
if (this.isNotFoundError(error)) {
|
||||
return false
|
||||
}
|
||||
throw this.handleError(error, 'deleteByField', entity)
|
||||
}
|
||||
}
|
||||
|
||||
async deleteMany(entity: string, filter?: Record<string, unknown>): Promise<number> {
|
||||
try {
|
||||
const model = this.getModel(entity)
|
||||
const where = filter ? this.buildWhereClause(filter) : undefined
|
||||
const result = await this.withTimeout(
|
||||
model.deleteMany({ where: where as never })
|
||||
)
|
||||
return result.count
|
||||
} catch (error) {
|
||||
throw this.handleError(error, 'deleteMany', entity)
|
||||
}
|
||||
}
|
||||
|
||||
async createMany(entity: string, data: Record<string, unknown>[]): Promise<number> {
|
||||
try {
|
||||
const model = this.getModel(entity)
|
||||
const result = await this.withTimeout(
|
||||
model.createMany({ data: data as never })
|
||||
)
|
||||
return result.count
|
||||
} catch (error) {
|
||||
throw this.handleError(error, 'createMany', entity)
|
||||
}
|
||||
}
|
||||
|
||||
async getCapabilities(): Promise<AdapterCapabilities> {
|
||||
return {
|
||||
transactions: true,
|
||||
|
||||
@@ -1,19 +1,35 @@
|
||||
'use client'
|
||||
|
||||
import { forwardRef, TextareaHTMLAttributes } from 'react'
|
||||
import { InputBase } from '@mui/material'
|
||||
import { forwardRef } from 'react'
|
||||
import { InputBase, InputBaseProps } from '@mui/material'
|
||||
|
||||
export interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'style'> {
|
||||
export interface TextareaProps {
|
||||
error?: boolean
|
||||
disabled?: boolean
|
||||
placeholder?: string
|
||||
value?: string
|
||||
defaultValue?: string
|
||||
onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
|
||||
onBlur?: (event: React.FocusEvent<HTMLTextAreaElement>) => void
|
||||
onFocus?: (event: React.FocusEvent<HTMLTextAreaElement>) => void
|
||||
name?: string
|
||||
id?: string
|
||||
rows?: number
|
||||
minRows?: number
|
||||
maxRows?: number
|
||||
className?: string
|
||||
required?: boolean
|
||||
readOnly?: boolean
|
||||
autoFocus?: boolean
|
||||
}
|
||||
|
||||
const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
({ error, ...props }, ref) => {
|
||||
({ error, minRows = 3, ...props }, ref) => {
|
||||
return (
|
||||
<InputBase
|
||||
inputRef={ref}
|
||||
multiline
|
||||
minRows={3}
|
||||
minRows={minRows}
|
||||
error={error}
|
||||
sx={{
|
||||
width: '100%',
|
||||
|
||||
@@ -7,35 +7,50 @@
|
||||
export {
|
||||
// Button
|
||||
Button,
|
||||
type ButtonProps,
|
||||
type ButtonVariant,
|
||||
type ButtonSize,
|
||||
// Input
|
||||
Input,
|
||||
type InputProps,
|
||||
// Textarea
|
||||
Textarea,
|
||||
type TextareaProps,
|
||||
// Label
|
||||
Label,
|
||||
type LabelProps,
|
||||
// Checkbox
|
||||
Checkbox,
|
||||
type CheckboxProps,
|
||||
// Switch
|
||||
Switch,
|
||||
type SwitchProps,
|
||||
// Badge
|
||||
Badge,
|
||||
type BadgeProps,
|
||||
type BadgeVariant,
|
||||
// Avatar
|
||||
Avatar,
|
||||
AvatarGroup,
|
||||
AvatarImage,
|
||||
AvatarFallback,
|
||||
type AvatarProps,
|
||||
// Separator
|
||||
Separator,
|
||||
type SeparatorProps,
|
||||
// Skeleton
|
||||
Skeleton,
|
||||
SkeletonText,
|
||||
SkeletonCircular,
|
||||
SkeletonRectangular,
|
||||
type SkeletonProps,
|
||||
// Progress
|
||||
Progress,
|
||||
CircularProgress,
|
||||
type ProgressProps,
|
||||
// Slider
|
||||
Slider,
|
||||
type SliderProps,
|
||||
// Toggle
|
||||
Toggle,
|
||||
type ToggleProps,
|
||||
type ToggleVariant,
|
||||
type ToggleSize,
|
||||
} from './atoms'
|
||||
|
||||
// ============================================================================
|
||||
@@ -49,8 +64,6 @@ export {
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardActions,
|
||||
CardMedia,
|
||||
// Dialog
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
@@ -60,6 +73,8 @@ export {
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogClose,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
// Select
|
||||
Select,
|
||||
SelectTrigger,
|
||||
@@ -69,6 +84,8 @@ export {
|
||||
SelectLabel,
|
||||
SelectSeparator,
|
||||
SelectValue,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
// Tabs
|
||||
Tabs,
|
||||
TabsList,
|
||||
@@ -78,10 +95,14 @@ export {
|
||||
Tooltip,
|
||||
TooltipTrigger,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
SimpleTooltip,
|
||||
// Alert
|
||||
Alert,
|
||||
AlertTitle,
|
||||
AlertDescription,
|
||||
type AlertVariant,
|
||||
type AlertProps,
|
||||
// Accordion
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
@@ -101,7 +122,8 @@ export {
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuSubContent,
|
||||
useDropdownMenu,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuPortal,
|
||||
// RadioGroup
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
@@ -110,7 +132,6 @@ export {
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
PopoverAnchor,
|
||||
usePopover,
|
||||
// ToggleGroup
|
||||
ToggleGroup,
|
||||
ToggleGroupItem,
|
||||
|
||||
@@ -80,7 +80,7 @@ const AccordionTrigger = forwardRef<HTMLButtonElement, AccordionTriggerProps>(
|
||||
({ children, ...props }, ref) => {
|
||||
return (
|
||||
<AccordionSummary
|
||||
ref={ref as React.Ref<HTMLDivElement>}
|
||||
ref={ref as unknown as React.Ref<HTMLDivElement>}
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
sx={{
|
||||
'& .MuiAccordionSummary-content': {
|
||||
|
||||
@@ -67,7 +67,7 @@ const DialogClose = forwardRef<HTMLButtonElement, DialogCloseProps>(
|
||||
({ children, onClick, ...props }, ref) => {
|
||||
if (children) {
|
||||
return (
|
||||
<Box ref={ref as React.Ref<HTMLDivElement>} onClick={onClick} sx={{ display: 'inline-flex' }} {...props}>
|
||||
<Box ref={ref as unknown as React.Ref<HTMLDivElement>} onClick={onClick} sx={{ display: 'inline-flex' }} {...props}>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ interface DropdownMenuTriggerProps {
|
||||
const DropdownMenuTrigger = forwardRef<HTMLButtonElement, DropdownMenuTriggerProps>(
|
||||
({ children, asChild, ...props }, ref) => {
|
||||
return (
|
||||
<Box ref={ref as React.Ref<HTMLDivElement>} sx={{ display: 'inline-flex' }} {...props}>
|
||||
<Box ref={ref as unknown as React.Ref<HTMLDivElement>} sx={{ display: 'inline-flex' }} {...props}>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -44,10 +44,10 @@ interface TooltipContentProps {
|
||||
}
|
||||
|
||||
const TooltipContent = forwardRef<HTMLDivElement, TooltipContentProps>(
|
||||
({ children, side = 'top', sideOffset = 4, ...props }, ref) => {
|
||||
({ children, side = 'top', sideOffset = 4 }, ref) => {
|
||||
return (
|
||||
<MuiTooltip
|
||||
ref={ref}
|
||||
ref={ref as React.Ref<HTMLDivElement>}
|
||||
title={children}
|
||||
placement={side}
|
||||
arrow
|
||||
@@ -63,9 +63,8 @@ const TooltipContent = forwardRef<HTMLDivElement, TooltipContentProps>(
|
||||
},
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<span>{props.children}</span>
|
||||
<span>{children}</span>
|
||||
</MuiTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,10 +14,34 @@ import {
|
||||
Divider,
|
||||
Typography,
|
||||
Paper,
|
||||
Kbd,
|
||||
} from '@mui/material'
|
||||
import SearchIcon from '@mui/icons-material/Search'
|
||||
|
||||
// Custom Kbd component since MUI doesn't export one
|
||||
const Kbd = ({ children, ...props }: { children: ReactNode; [key: string]: unknown }) => (
|
||||
<Box
|
||||
component="kbd"
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.75rem',
|
||||
fontWeight: 500,
|
||||
px: 0.75,
|
||||
py: 0.25,
|
||||
borderRadius: 0.5,
|
||||
bgcolor: 'action.hover',
|
||||
border: 1,
|
||||
borderColor: 'divider',
|
||||
color: 'text.secondary',
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
|
||||
// Types
|
||||
interface CommandItem {
|
||||
id: string
|
||||
|
||||
@@ -246,7 +246,6 @@ const NavigationLink = forwardRef<HTMLElement, NavigationLinkProps>(
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
ref={ref}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
selected={active}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
canAccessLevel,
|
||||
getRoleDisplayName,
|
||||
} from './auth'
|
||||
import type { UserRole } from './level-types'
|
||||
import type { UserRole } from '../types/level-types'
|
||||
|
||||
describe('auth', () => {
|
||||
describe('DEFAULT_USERS', () => {
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UserRole } from '../level-types'
|
||||
import type { UserRole } from '../types/level-types'
|
||||
|
||||
/**
|
||||
* Role hierarchy mapping roles to their numeric permission levels
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { User } from '../level-types'
|
||||
import type { User } from '../types/level-types'
|
||||
|
||||
/**
|
||||
* Default users created during application initialization
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UserRole } from '../level-types'
|
||||
import type { UserRole } from '../types/level-types'
|
||||
|
||||
/**
|
||||
* Human-readable display names for user roles
|
||||
|
||||
3
frontends/nextjs/src/lib/components/index.ts
Normal file
3
frontends/nextjs/src/lib/components/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Component system exports
|
||||
export { componentCatalog, getComponentCatalog } from './component-catalog'
|
||||
export { ComponentRegistry, getComponentRegistry } from './component-registry'
|
||||
@@ -8,8 +8,8 @@ import type {
|
||||
Comment,
|
||||
Tenant,
|
||||
PowerTransferRequest,
|
||||
} from './level-types'
|
||||
import type { ModelSchema } from './schema-types'
|
||||
} from '../types/level-types'
|
||||
import type { ModelSchema } from './types/schema-types'
|
||||
import type { InstalledPackage } from './package-types'
|
||||
import type { SMTPConfig } from './password-utils'
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { AppConfiguration } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { AppConfiguration } from '../../types/level-types'
|
||||
|
||||
export async function getAppConfig(): Promise<AppConfiguration | null> {
|
||||
const config = await prisma.appConfiguration.findFirst()
|
||||
if (!config) return null
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('AppConfiguration', { limit: 1 })
|
||||
if (result.data.length === 0) return null
|
||||
const config = result.data[0] as any
|
||||
return {
|
||||
id: config.id,
|
||||
name: config.name,
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { AppConfiguration } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { AppConfiguration } from '../../types/level-types'
|
||||
|
||||
export async function 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),
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing configs
|
||||
const existing = await adapter.list('AppConfiguration')
|
||||
for (const c of existing.data as any[]) {
|
||||
await adapter.delete('AppConfiguration', c.id)
|
||||
}
|
||||
|
||||
// Create new config
|
||||
await adapter.create('AppConfiguration', {
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Comment } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Comment } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a single comment
|
||||
*/
|
||||
export async function 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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('Comment', {
|
||||
id: comment.id,
|
||||
userId: comment.userId,
|
||||
content: comment.content,
|
||||
createdAt: BigInt(comment.createdAt),
|
||||
updatedAt: comment.updatedAt ? BigInt(comment.updatedAt) : null,
|
||||
parentId: comment.parentId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Delete a comment by ID
|
||||
*/
|
||||
export async function deleteComment(commentId: string): Promise<void> {
|
||||
await prisma.comment.delete({ where: { id: commentId } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('Comment', commentId)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Comment } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Comment } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all comments from database
|
||||
*/
|
||||
export async function getComments(): Promise<Comment[]> {
|
||||
const comments = await prisma.comment.findMany()
|
||||
return comments.map((c) => ({
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('Comment')
|
||||
return (result.data as any[]).map((c) => ({
|
||||
id: c.id,
|
||||
userId: c.userId,
|
||||
content: c.content,
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Comment } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Comment } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all comments (replaces existing)
|
||||
*/
|
||||
export async function setComments(comments: Comment[]): Promise<void> {
|
||||
await prisma.comment.deleteMany()
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing comments
|
||||
const existing = await adapter.list('Comment')
|
||||
for (const c of existing.data as any[]) {
|
||||
await adapter.delete('Comment', c.id)
|
||||
}
|
||||
|
||||
// Create new comments
|
||||
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,
|
||||
},
|
||||
await adapter.create('Comment', {
|
||||
id: comment.id,
|
||||
userId: comment.userId,
|
||||
content: comment.content,
|
||||
createdAt: BigInt(comment.createdAt),
|
||||
updatedAt: comment.updatedAt ? BigInt(comment.updatedAt) : null,
|
||||
parentId: comment.parentId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Comment } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Comment } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a comment by ID
|
||||
*/
|
||||
export async function updateComment(commentId: string, updates: Partial<Comment>): Promise<void> {
|
||||
const data: any = {}
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, unknown> = {}
|
||||
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 })
|
||||
await adapter.update('Comment', commentId, data)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentConfig } from '../types'
|
||||
|
||||
export async function 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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('ComponentConfig', {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentNode } from '../types'
|
||||
|
||||
export async function 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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('ComponentNode', {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
parentId: node.parentId,
|
||||
childIds: JSON.stringify(node.childIds),
|
||||
order: node.order,
|
||||
pageId: node.pageId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
export async function deleteComponentConfig(configId: string): Promise<void> {
|
||||
await prisma.componentConfig.delete({ where: { id: configId } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('ComponentConfig', configId)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
export async function deleteComponentNode(nodeId: string): Promise<void> {
|
||||
await prisma.componentNode.delete({ where: { id: nodeId } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('ComponentNode', nodeId)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentConfig } from '../types'
|
||||
|
||||
export async function getComponentConfigs(): Promise<Record<string, ComponentConfig>> {
|
||||
const configs = await prisma.componentConfig.findMany()
|
||||
const result: Record<string, ComponentConfig> = {}
|
||||
for (const config of configs) {
|
||||
result[config.id] = {
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('ComponentConfig')
|
||||
const configs: Record<string, ComponentConfig> = {}
|
||||
for (const config of result.data as any[]) {
|
||||
configs[config.id] = {
|
||||
id: config.id,
|
||||
componentId: config.componentId,
|
||||
props: JSON.parse(config.props),
|
||||
@@ -14,5 +15,5 @@ export async function getComponentConfigs(): Promise<Record<string, ComponentCon
|
||||
conditionalRendering: config.conditionalRendering ? JSON.parse(config.conditionalRendering) : undefined,
|
||||
}
|
||||
}
|
||||
return result
|
||||
return configs
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentNode } from '../types'
|
||||
|
||||
export async function getComponentHierarchy(): Promise<Record<string, ComponentNode>> {
|
||||
const nodes = await prisma.componentNode.findMany()
|
||||
const result: Record<string, ComponentNode> = {}
|
||||
for (const node of nodes) {
|
||||
result[node.id] = {
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('ComponentNode')
|
||||
const hierarchy: Record<string, ComponentNode> = {}
|
||||
for (const node of result.data as any[]) {
|
||||
hierarchy[node.id] = {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
parentId: node.parentId || undefined,
|
||||
@@ -14,5 +15,5 @@ export async function getComponentHierarchy(): Promise<Record<string, ComponentN
|
||||
pageId: node.pageId,
|
||||
}
|
||||
}
|
||||
return result
|
||||
return hierarchy
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentConfig } from '../types'
|
||||
|
||||
export async function setComponentConfigs(configs: Record<string, ComponentConfig>): Promise<void> {
|
||||
await prisma.componentConfig.deleteMany()
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing configs
|
||||
const existing = await adapter.list('ComponentConfig')
|
||||
for (const c of existing.data as any[]) {
|
||||
await adapter.delete('ComponentConfig', c.id)
|
||||
}
|
||||
|
||||
// Create new configs
|
||||
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,
|
||||
},
|
||||
await adapter.create('ComponentConfig', {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentNode } from '../types'
|
||||
|
||||
export async function setComponentHierarchy(hierarchy: Record<string, ComponentNode>): Promise<void> {
|
||||
await prisma.componentNode.deleteMany()
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing hierarchy
|
||||
const existing = await adapter.list('ComponentNode')
|
||||
for (const n of existing.data as any[]) {
|
||||
await adapter.delete('ComponentNode', n.id)
|
||||
}
|
||||
|
||||
// Create new hierarchy
|
||||
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,
|
||||
},
|
||||
await adapter.create('ComponentNode', {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
parentId: node.parentId,
|
||||
childIds: JSON.stringify(node.childIds),
|
||||
order: node.order,
|
||||
pageId: node.pageId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentConfig } from '../types'
|
||||
|
||||
export async function updateComponentConfig(configId: string, updates: Partial<ComponentConfig>): Promise<void> {
|
||||
const data: any = {}
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, unknown> = {}
|
||||
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)
|
||||
@@ -10,5 +11,5 @@ export async function updateComponentConfig(configId: string, updates: Partial<C
|
||||
if (updates.conditionalRendering !== undefined) {
|
||||
data.conditionalRendering = updates.conditionalRendering ? JSON.stringify(updates.conditionalRendering) : null
|
||||
}
|
||||
await prisma.componentConfig.update({ where: { id: configId }, data })
|
||||
await adapter.update('ComponentConfig', configId, data)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ComponentNode } from '../types'
|
||||
|
||||
export async function updateComponentNode(nodeId: string, updates: Partial<ComponentNode>): Promise<void> {
|
||||
const data: any = {}
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, unknown> = {}
|
||||
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 })
|
||||
await adapter.update('ComponentNode', nodeId, data)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import { getPasswordResetTokens } from './get-password-reset-tokens'
|
||||
|
||||
/**
|
||||
* Delete a password reset token
|
||||
*/
|
||||
export async function deletePasswordResetToken(username: string): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
const tokens = await getPasswordResetTokens()
|
||||
delete tokens[username]
|
||||
await prisma.keyValue.upsert({
|
||||
where: { key: 'db_password_reset_tokens' },
|
||||
update: { value: JSON.stringify(tokens) },
|
||||
create: { key: 'db_password_reset_tokens', value: JSON.stringify(tokens) },
|
||||
})
|
||||
|
||||
const existing = await adapter.list('KeyValue', { filter: { key: 'db_password_reset_tokens' } })
|
||||
if (existing.data.length > 0) {
|
||||
const kv = existing.data[0] as any
|
||||
await adapter.update('KeyValue', kv.id || kv.key, { value: JSON.stringify(tokens) })
|
||||
} else {
|
||||
await adapter.create('KeyValue', { key: 'db_password_reset_tokens', value: JSON.stringify(tokens) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Get all credentials as a username->passwordHash map
|
||||
*/
|
||||
export async function getCredentials(): 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
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('Credential')
|
||||
const credentials: Record<string, string> = {}
|
||||
for (const cred of result.data as any[]) {
|
||||
credentials[cred.username] = cred.passwordHash
|
||||
}
|
||||
return result
|
||||
return credentials
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Get password change timestamps for all users
|
||||
*/
|
||||
export async function getPasswordChangeTimestamps(): 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) {
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('User')
|
||||
const timestamps: Record<string, number> = {}
|
||||
for (const user of result.data as any[]) {
|
||||
if (user.passwordChangeTimestamp) {
|
||||
result[user.username] = Number(user.passwordChangeTimestamp)
|
||||
timestamps[user.username] = Number(user.passwordChangeTimestamp)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return timestamps
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Get password reset tokens
|
||||
*/
|
||||
export async function getPasswordResetTokens(): Promise<Record<string, string>> {
|
||||
const kv = await prisma.keyValue.findUnique({ where: { key: 'db_password_reset_tokens' } })
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('KeyValue', { filter: { key: 'db_password_reset_tokens' } })
|
||||
if (result.data.length === 0) return {}
|
||||
const kv = result.data[0] as any
|
||||
return kv ? JSON.parse(kv.value) : {}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Set or update a user's credential
|
||||
*/
|
||||
export async function setCredential(username: string, passwordHash: string): Promise<void> {
|
||||
await prisma.credential.upsert({
|
||||
where: { username },
|
||||
update: { passwordHash },
|
||||
create: { username, passwordHash },
|
||||
})
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Check if credential exists
|
||||
const result = await adapter.list('Credential', { filter: { username } })
|
||||
|
||||
if (result.data.length > 0) {
|
||||
// Update existing
|
||||
const existing = result.data[0] as any
|
||||
await adapter.update('Credential', existing.id || existing.username, { passwordHash })
|
||||
} else {
|
||||
// Create new
|
||||
await adapter.create('Credential', { username, passwordHash })
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: { username },
|
||||
data: { passwordChangeTimestamp: BigInt(Date.now()) },
|
||||
})
|
||||
// Update password change timestamp on user
|
||||
const users = await adapter.list('User', { filter: { username } })
|
||||
if (users.data.length > 0) {
|
||||
const user = users.data[0] as any
|
||||
await adapter.update('User', user.id, { passwordChangeTimestamp: BigInt(Date.now()) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Set password change timestamps for users
|
||||
*/
|
||||
export async function setPasswordChangeTimestamps(timestamps: Record<string, number>): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
for (const [username, timestamp] of Object.entries(timestamps)) {
|
||||
await prisma.user.update({
|
||||
where: { username },
|
||||
data: { passwordChangeTimestamp: BigInt(timestamp) },
|
||||
})
|
||||
const users = await adapter.list('User', { filter: { username } })
|
||||
if (users.data.length > 0) {
|
||||
const user = users.data[0] as any
|
||||
await adapter.update('User', user.id, { passwordChangeTimestamp: BigInt(timestamp) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import { getPasswordResetTokens } from './get-password-reset-tokens'
|
||||
|
||||
/**
|
||||
* Set a password reset token for a user
|
||||
*/
|
||||
export async function setPasswordResetToken(username: string, token: string): Promise<void> {
|
||||
const adapter = getAdapter()
|
||||
const tokens = await getPasswordResetTokens()
|
||||
tokens[username] = token
|
||||
await prisma.keyValue.upsert({
|
||||
where: { key: 'db_password_reset_tokens' },
|
||||
update: { value: JSON.stringify(tokens) },
|
||||
create: { key: 'db_password_reset_tokens', value: JSON.stringify(tokens) },
|
||||
})
|
||||
|
||||
const existing = await adapter.list('KeyValue', { filter: { key: 'db_password_reset_tokens' } })
|
||||
if (existing.data.length > 0) {
|
||||
const kv = existing.data[0] as any
|
||||
await adapter.update('KeyValue', kv.id || kv.key, { value: JSON.stringify(tokens) })
|
||||
} else {
|
||||
await adapter.create('KeyValue', { key: 'db_password_reset_tokens', value: JSON.stringify(tokens) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { verifyPassword } from '../hash-password'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import { verifyPassword } from '../verify-password'
|
||||
|
||||
/**
|
||||
* Verify username/password combination
|
||||
*/
|
||||
export async function verifyCredentials(username: string, password: string): Promise<boolean> {
|
||||
const credential = await prisma.credential.findUnique({ where: { username } })
|
||||
if (!credential) return false
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('Credential', { filter: { username } })
|
||||
if (result.data.length === 0) return false
|
||||
const credential = result.data[0] as any
|
||||
return await verifyPassword(password, credential.passwordHash)
|
||||
}
|
||||
|
||||
142
frontends/nextjs/src/lib/db/dbal-client.ts
Normal file
142
frontends/nextjs/src/lib/db/dbal-client.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* DBAL Client Singleton
|
||||
*
|
||||
* Provides centralized access to the Database Abstraction Layer.
|
||||
* All db/ lambda functions should use this instead of importing Prisma directly.
|
||||
*
|
||||
* This uses the PrismaClient directly but wraps it in a DBAL-compatible interface,
|
||||
* providing a migration path to the full DBAL when ready.
|
||||
*/
|
||||
|
||||
import { prisma } from '../prisma'
|
||||
|
||||
export interface ListOptions {
|
||||
filter?: Record<string, unknown>
|
||||
sort?: Record<string, 'asc' | 'desc'>
|
||||
page?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface ListResult<T> {
|
||||
data: T[]
|
||||
total?: number
|
||||
page?: number
|
||||
limit?: number
|
||||
hasMore?: boolean
|
||||
}
|
||||
|
||||
export interface DBALAdapter {
|
||||
create(entity: string, data: Record<string, unknown>): Promise<unknown>
|
||||
read(entity: string, id: string): Promise<unknown | null>
|
||||
update(entity: string, id: string, data: Record<string, unknown>): Promise<unknown>
|
||||
delete(entity: string, id: string): Promise<boolean>
|
||||
list(entity: string, options?: ListOptions): Promise<ListResult<unknown>>
|
||||
close(): Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Prisma model by entity name
|
||||
*/
|
||||
const getModel = (entity: string): any => {
|
||||
const modelName = entity.charAt(0).toLowerCase() + entity.slice(1)
|
||||
const model = (prisma as any)[modelName]
|
||||
if (!model) {
|
||||
throw new Error(`Entity ${entity} not found in Prisma schema`)
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
/**
|
||||
* Build where clause from filter
|
||||
*/
|
||||
const buildWhereClause = (filter: Record<string, unknown>): Record<string, unknown> => {
|
||||
const where: Record<string, unknown> = {}
|
||||
for (const [key, value] of Object.entries(filter)) {
|
||||
if (value !== undefined) {
|
||||
where[key] = value
|
||||
}
|
||||
}
|
||||
return where
|
||||
}
|
||||
|
||||
/**
|
||||
* DBAL Adapter implementation using Prisma
|
||||
*/
|
||||
const prismaAdapter: DBALAdapter = {
|
||||
async create(entity: string, data: Record<string, unknown>): Promise<unknown> {
|
||||
const model = getModel(entity)
|
||||
return model.create({ data })
|
||||
},
|
||||
|
||||
async read(entity: string, id: string): Promise<unknown | null> {
|
||||
const model = getModel(entity)
|
||||
return model.findUnique({ where: { id } })
|
||||
},
|
||||
|
||||
async update(entity: string, id: string, data: Record<string, unknown>): Promise<unknown> {
|
||||
const model = getModel(entity)
|
||||
// Filter out undefined values
|
||||
const cleanData: Record<string, unknown> = {}
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (value !== undefined) {
|
||||
cleanData[key] = value
|
||||
}
|
||||
}
|
||||
return model.update({ where: { id }, data: cleanData })
|
||||
},
|
||||
|
||||
async delete(entity: string, id: string): Promise<boolean> {
|
||||
const model = getModel(entity)
|
||||
try {
|
||||
await model.delete({ where: { id } })
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
async list(entity: string, options?: ListOptions): Promise<ListResult<unknown>> {
|
||||
const model = getModel(entity)
|
||||
const page = options?.page || 1
|
||||
const limit = options?.limit || 1000
|
||||
const skip = (page - 1) * limit
|
||||
const where = options?.filter ? buildWhereClause(options.filter) : undefined
|
||||
const orderBy = options?.sort
|
||||
|
||||
const [data, total] = await Promise.all([
|
||||
model.findMany({
|
||||
where,
|
||||
orderBy,
|
||||
skip,
|
||||
take: limit,
|
||||
}),
|
||||
model.count({ where }),
|
||||
])
|
||||
|
||||
return {
|
||||
data,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
hasMore: skip + limit < total,
|
||||
}
|
||||
},
|
||||
|
||||
async close(): Promise<void> {
|
||||
await prisma.$disconnect()
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DBAL adapter singleton for database operations
|
||||
*/
|
||||
export const getAdapter = (): DBALAdapter => {
|
||||
return prismaAdapter
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the DBAL adapter connection
|
||||
*/
|
||||
export const closeAdapter = async (): Promise<void> => {
|
||||
await prismaAdapter.close()
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
// Types
|
||||
export type { CssCategory, DropdownConfig, DatabaseSchema, ComponentNode, ComponentConfig } from './types'
|
||||
|
||||
// DBAL Client
|
||||
export { getAdapter, closeAdapter } from './dbal-client'
|
||||
export type { DBALAdapter, ListOptions, ListResult } from './dbal-client'
|
||||
|
||||
// Core
|
||||
export { hashPassword } from './hash-password'
|
||||
export { verifyPassword } from './verify-password'
|
||||
@@ -13,6 +17,9 @@ export * from './workflows'
|
||||
export * from './lua-scripts'
|
||||
export * from './pages'
|
||||
export * from './schemas'
|
||||
export * from './comments'
|
||||
export * from './app-config'
|
||||
export * from './components'
|
||||
|
||||
// Import all for namespace class
|
||||
import { initializeDatabase } from './initialize-database'
|
||||
@@ -24,6 +31,9 @@ import * as workflows from './workflows'
|
||||
import * as luaScripts from './lua-scripts'
|
||||
import * as pages from './pages'
|
||||
import * as schemas from './schemas'
|
||||
import * as comments from './comments'
|
||||
import * as appConfig from './app-config'
|
||||
import * as components from './components'
|
||||
|
||||
/**
|
||||
* Database namespace class - groups all DB operations as static methods
|
||||
@@ -81,4 +91,27 @@ export class Database {
|
||||
static addSchema = schemas.addSchema
|
||||
static updateSchema = schemas.updateSchema
|
||||
static deleteSchema = schemas.deleteSchema
|
||||
|
||||
// Comments
|
||||
static getComments = comments.getComments
|
||||
static setComments = comments.setComments
|
||||
static addComment = comments.addComment
|
||||
static updateComment = comments.updateComment
|
||||
static deleteComment = comments.deleteComment
|
||||
|
||||
// App Config
|
||||
static getAppConfig = appConfig.getAppConfig
|
||||
static setAppConfig = appConfig.setAppConfig
|
||||
|
||||
// Components
|
||||
static getComponentHierarchy = components.getComponentHierarchy
|
||||
static setComponentHierarchy = components.setComponentHierarchy
|
||||
static addComponentNode = components.addComponentNode
|
||||
static updateComponentNode = components.updateComponentNode
|
||||
static deleteComponentNode = components.deleteComponentNode
|
||||
static getComponentConfigs = components.getComponentConfigs
|
||||
static setComponentConfigs = components.setComponentConfigs
|
||||
static addComponentConfig = components.addComponentConfig
|
||||
static updateComponentConfig = components.updateComponentConfig
|
||||
static deleteComponentConfig = components.deleteComponentConfig
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { LuaScript } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { LuaScript } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a Lua script
|
||||
*/
|
||||
export async function addLuaScript(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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('LuaScript', {
|
||||
id: script.id,
|
||||
name: script.name,
|
||||
description: script.description,
|
||||
code: script.code,
|
||||
parameters: JSON.stringify(script.parameters),
|
||||
returnType: script.returnType,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Delete a Lua script by ID
|
||||
*/
|
||||
export async function deleteLuaScript(scriptId: string): Promise<void> {
|
||||
await prisma.luaScript.delete({ where: { id: scriptId } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('LuaScript', scriptId)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { LuaScript } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { LuaScript } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all Lua scripts
|
||||
*/
|
||||
export async function getLuaScripts(): Promise<LuaScript[]> {
|
||||
const scripts = await prisma.luaScript.findMany()
|
||||
return scripts.map((s) => ({
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('LuaScript')
|
||||
return (result.data as any[]).map((s) => ({
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
description: s.description || undefined,
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { LuaScript } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { LuaScript } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all Lua scripts (replaces existing)
|
||||
*/
|
||||
export async function setLuaScripts(scripts: LuaScript[]): Promise<void> {
|
||||
await prisma.luaScript.deleteMany()
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing scripts
|
||||
const existing = await adapter.list('LuaScript')
|
||||
for (const s of existing.data as any[]) {
|
||||
await adapter.delete('LuaScript', s.id)
|
||||
}
|
||||
|
||||
// Create new scripts
|
||||
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,
|
||||
},
|
||||
await adapter.create('LuaScript', {
|
||||
id: script.id,
|
||||
name: script.name,
|
||||
description: script.description,
|
||||
code: script.code,
|
||||
parameters: JSON.stringify(script.parameters),
|
||||
returnType: script.returnType,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { LuaScript } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { LuaScript } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a Lua script by ID
|
||||
*/
|
||||
export async function updateLuaScript(scriptId: string, updates: Partial<LuaScript>): Promise<void> {
|
||||
const data: any = {}
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, unknown> = {}
|
||||
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,
|
||||
})
|
||||
await adapter.update('LuaScript', scriptId, data)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { PageConfig } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { PageConfig } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a page
|
||||
*/
|
||||
export async function addPage(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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('PageConfig', {
|
||||
id: page.id,
|
||||
path: page.path,
|
||||
title: page.title,
|
||||
level: page.level,
|
||||
componentTree: JSON.stringify(page.componentTree),
|
||||
requiresAuth: page.requiresAuth,
|
||||
requiredRole: page.requiredRole,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Delete a page by ID
|
||||
*/
|
||||
export async function deletePage(pageId: string): Promise<void> {
|
||||
await prisma.pageConfig.delete({ where: { id: pageId } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('PageConfig', pageId)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { PageConfig } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { PageConfig } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all pages
|
||||
*/
|
||||
export async function getPages(): Promise<PageConfig[]> {
|
||||
const pages = await prisma.pageConfig.findMany()
|
||||
return pages.map((p) => ({
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('PageConfig')
|
||||
return (result.data as any[]).map((p) => ({
|
||||
id: p.id,
|
||||
path: p.path,
|
||||
title: p.title,
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { PageConfig } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { PageConfig } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all pages (replaces existing)
|
||||
*/
|
||||
export async function setPages(pages: PageConfig[]): Promise<void> {
|
||||
await prisma.pageConfig.deleteMany()
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing pages
|
||||
const existing = await adapter.list('PageConfig')
|
||||
for (const p of existing.data as any[]) {
|
||||
await adapter.delete('PageConfig', p.id)
|
||||
}
|
||||
|
||||
// Create new pages
|
||||
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,
|
||||
},
|
||||
await adapter.create('PageConfig', {
|
||||
id: page.id,
|
||||
path: page.path,
|
||||
title: page.title,
|
||||
level: page.level,
|
||||
componentTree: JSON.stringify(page.componentTree),
|
||||
requiresAuth: page.requiresAuth,
|
||||
requiredRole: page.requiredRole,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { PageConfig } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { PageConfig } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a page by ID
|
||||
*/
|
||||
export async function updatePage(pageId: string, updates: Partial<PageConfig>): Promise<void> {
|
||||
const data: any = {}
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, unknown> = {}
|
||||
if (updates.path !== undefined) data.path = updates.path
|
||||
if (updates.title !== undefined) data.title = updates.title
|
||||
if (updates.level !== undefined) data.level = updates.level
|
||||
@@ -13,8 +14,5 @@ export async function updatePage(pageId: string, updates: Partial<PageConfig>):
|
||||
if (updates.requiresAuth !== undefined) data.requiresAuth = updates.requiresAuth
|
||||
if (updates.requiredRole !== undefined) data.requiredRole = updates.requiredRole
|
||||
|
||||
await prisma.pageConfig.update({
|
||||
where: { id: pageId },
|
||||
data,
|
||||
})
|
||||
await adapter.update('PageConfig', pageId, data)
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { ModelSchema } from '../../schema-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Add a schema
|
||||
*/
|
||||
export async function addSchema(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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('ModelSchema', {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Delete a schema by name
|
||||
*/
|
||||
export async function deleteSchema(schemaName: string): Promise<void> {
|
||||
await prisma.modelSchema.delete({ where: { name: schemaName } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('ModelSchema', schemaName)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { ModelSchema } from '../../schema-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Get all schemas
|
||||
*/
|
||||
export async function getSchemas(): Promise<ModelSchema[]> {
|
||||
const schemas = await prisma.modelSchema.findMany()
|
||||
return schemas.map((s) => ({
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('ModelSchema')
|
||||
return (result.data as any[]).map((s) => ({
|
||||
name: s.name,
|
||||
label: s.label || undefined,
|
||||
labelPlural: s.labelPlural || undefined,
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { ModelSchema } from '../../schema-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Set all schemas (replaces existing)
|
||||
*/
|
||||
export async function setSchemas(schemas: ModelSchema[]): Promise<void> {
|
||||
await prisma.modelSchema.deleteMany()
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing schemas
|
||||
const existing = await adapter.list('ModelSchema')
|
||||
for (const s of existing.data as any[]) {
|
||||
await adapter.delete('ModelSchema', s.name)
|
||||
}
|
||||
|
||||
// Create new schemas
|
||||
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,
|
||||
},
|
||||
await adapter.create('ModelSchema', {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { ModelSchema } from '../../schema-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { ModelSchema } from '../../types/schema-types'
|
||||
|
||||
/**
|
||||
* Update a schema by name
|
||||
*/
|
||||
export async function updateSchema(schemaName: string, updates: Partial<ModelSchema>): Promise<void> {
|
||||
const data: any = {}
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, unknown> = {}
|
||||
if (updates.label !== undefined) data.label = updates.label
|
||||
if (updates.labelPlural !== undefined) data.labelPlural = updates.labelPlural
|
||||
if (updates.icon !== undefined) data.icon = updates.icon
|
||||
@@ -15,8 +16,5 @@ export async function updateSchema(schemaName: string, updates: Partial<ModelSch
|
||||
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,
|
||||
})
|
||||
await adapter.update('ModelSchema', schemaName, data)
|
||||
}
|
||||
|
||||
@@ -47,14 +47,14 @@ export interface ComponentConfig {
|
||||
* Full database schema type
|
||||
*/
|
||||
export interface DatabaseSchema {
|
||||
users: import('../level-types').User[]
|
||||
users: import('../types/level-types').User[]
|
||||
credentials: Record<string, string>
|
||||
workflows: import('../level-types').Workflow[]
|
||||
luaScripts: import('../level-types').LuaScript[]
|
||||
pages: import('../level-types').PageConfig[]
|
||||
schemas: import('../schema-types').ModelSchema[]
|
||||
appConfig: import('../level-types').AppConfiguration
|
||||
comments: import('../level-types').Comment[]
|
||||
workflows: import('../types/level-types').Workflow[]
|
||||
luaScripts: import('../types/level-types').LuaScript[]
|
||||
pages: import('../types/level-types').PageConfig[]
|
||||
schemas: import('../types/schema-types').ModelSchema[]
|
||||
appConfig: import('../types/level-types').AppConfiguration
|
||||
comments: import('../types/level-types').Comment[]
|
||||
componentHierarchy: Record<string, ComponentNode>
|
||||
componentConfigs: Record<string, ComponentConfig>
|
||||
godCredentialsExpiry: number
|
||||
@@ -63,8 +63,8 @@ export interface DatabaseSchema {
|
||||
godCredentialsExpiryDuration: number
|
||||
cssClasses: CssCategory[]
|
||||
dropdownConfigs: DropdownConfig[]
|
||||
tenants: import('../level-types').Tenant[]
|
||||
powerTransferRequests: import('../level-types').PowerTransferRequest[]
|
||||
tenants: import('../types/level-types').Tenant[]
|
||||
powerTransferRequests: import('../types/level-types').PowerTransferRequest[]
|
||||
smtpConfig: import('../password').SMTPConfig
|
||||
passwordResetTokens: Record<string, string>
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { User } from '../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { User } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a single user
|
||||
*/
|
||||
export async function addUser(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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('User', {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
*/
|
||||
export async function deleteUser(userId: string): Promise<void> {
|
||||
await prisma.user.delete({ where: { id: userId } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('User', userId)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { User } from '../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { User } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get the SuperGod user (instance owner)
|
||||
*/
|
||||
export async function getSuperGod(): Promise<User | null> {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: { isInstanceOwner: true },
|
||||
})
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('User', { filter: { isInstanceOwner: true } })
|
||||
|
||||
if (!user) return null
|
||||
if (result.data.length === 0) return null
|
||||
const user = result.data[0] as any
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { User } from '../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { User } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all users from database
|
||||
*/
|
||||
export async function getUsers(): Promise<User[]> {
|
||||
const users = await prisma.user.findMany()
|
||||
return users.map((u) => ({
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('User')
|
||||
return (result.data as any[]).map((u) => ({
|
||||
id: u.id,
|
||||
username: u.username,
|
||||
email: u.email,
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { User } from '../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { User } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all users (replaces existing)
|
||||
* Note: Uses sequential operations - for atomic transactions use prisma directly
|
||||
*/
|
||||
export async function setUsers(users: User[]): Promise<void> {
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.user.deleteMany()
|
||||
for (const user of users) {
|
||||
await tx.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,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Get existing users and delete them
|
||||
const existing = await adapter.list('User')
|
||||
for (const user of existing.data as any[]) {
|
||||
await adapter.delete('User', user.id)
|
||||
}
|
||||
|
||||
// Create new users
|
||||
for (const user of users) {
|
||||
await adapter.create('User', {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Transfer SuperGod power from one user to another
|
||||
*/
|
||||
export async function transferSuperGodPower(fromUserId: string, toUserId: string): Promise<void> {
|
||||
await prisma.$transaction([
|
||||
prisma.user.update({
|
||||
where: { id: fromUserId },
|
||||
data: { isInstanceOwner: false, role: 'god' },
|
||||
}),
|
||||
prisma.user.update({
|
||||
where: { id: toUserId },
|
||||
data: { isInstanceOwner: true, role: 'supergod' },
|
||||
}),
|
||||
])
|
||||
const adapter = getAdapter()
|
||||
await adapter.update('User', fromUserId, { isInstanceOwner: false, role: 'god' })
|
||||
await adapter.update('User', toUserId, { isInstanceOwner: true, role: 'supergod' })
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { User } from '../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { User } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a user by ID
|
||||
*/
|
||||
export async function updateUser(userId: string, updates: Partial<User>): Promise<void> {
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
username: updates.username,
|
||||
email: updates.email,
|
||||
role: updates.role,
|
||||
profilePicture: updates.profilePicture,
|
||||
bio: updates.bio,
|
||||
tenantId: updates.tenantId,
|
||||
isInstanceOwner: updates.isInstanceOwner,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.update('User', userId, {
|
||||
username: updates.username,
|
||||
email: updates.email,
|
||||
role: updates.role,
|
||||
profilePicture: updates.profilePicture,
|
||||
bio: updates.bio,
|
||||
tenantId: updates.tenantId,
|
||||
isInstanceOwner: updates.isInstanceOwner,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Workflow } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Workflow } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Add a workflow
|
||||
*/
|
||||
export async function addWorkflow(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,
|
||||
},
|
||||
const adapter = getAdapter()
|
||||
await adapter.create('Workflow', {
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
description: workflow.description,
|
||||
nodes: JSON.stringify(workflow.nodes),
|
||||
edges: JSON.stringify(workflow.edges),
|
||||
enabled: workflow.enabled,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { prisma } from '../prisma'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
|
||||
/**
|
||||
* Delete a workflow by ID
|
||||
*/
|
||||
export async function deleteWorkflow(workflowId: string): Promise<void> {
|
||||
await prisma.workflow.delete({ where: { id: workflowId } })
|
||||
const adapter = getAdapter()
|
||||
await adapter.delete('Workflow', workflowId)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Workflow } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Workflow } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Get all workflows
|
||||
*/
|
||||
export async function getWorkflows(): Promise<Workflow[]> {
|
||||
const workflows = await prisma.workflow.findMany()
|
||||
return workflows.map((w) => ({
|
||||
const adapter = getAdapter()
|
||||
const result = await adapter.list('Workflow')
|
||||
return (result.data as any[]).map((w) => ({
|
||||
id: w.id,
|
||||
name: w.name,
|
||||
description: w.description || undefined,
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Workflow } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Workflow } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Set all workflows (replaces existing)
|
||||
*/
|
||||
export async function setWorkflows(workflows: Workflow[]): Promise<void> {
|
||||
await prisma.workflow.deleteMany()
|
||||
const adapter = getAdapter()
|
||||
|
||||
// Delete existing workflows
|
||||
const existing = await adapter.list('Workflow')
|
||||
for (const w of existing.data as any[]) {
|
||||
await adapter.delete('Workflow', w.id)
|
||||
}
|
||||
|
||||
// Create new workflows
|
||||
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,
|
||||
},
|
||||
await adapter.create('Workflow', {
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
description: workflow.description,
|
||||
nodes: JSON.stringify(workflow.nodes),
|
||||
edges: JSON.stringify(workflow.edges),
|
||||
enabled: workflow.enabled,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { prisma } from '../prisma'
|
||||
import type { Workflow } from '../../level-types'
|
||||
import { getAdapter } from '../dbal-client'
|
||||
import type { Workflow } from '../../types/level-types'
|
||||
|
||||
/**
|
||||
* Update a workflow by ID
|
||||
*/
|
||||
export async function updateWorkflow(workflowId: string, updates: Partial<Workflow>): Promise<void> {
|
||||
const data: any = {}
|
||||
const adapter = getAdapter()
|
||||
const data: Record<string, unknown> = {}
|
||||
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,
|
||||
})
|
||||
await adapter.update('Workflow', workflowId, data)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'server-only'
|
||||
|
||||
import { DBALClient } from '@/lib/dbal-stub'
|
||||
import type { DBALConfig } from '@/lib/dbal-stub'
|
||||
import type { User } from './level-types'
|
||||
import type { User } from '../types/level-types'
|
||||
|
||||
let dbalClient: DBALClient | null = null
|
||||
let initialized = false
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DBALClient } from '@/lib/dbal-stub'
|
||||
import type { DBALConfig } from '@/lib/dbal-stub'
|
||||
import type { User } from './level-types'
|
||||
import type { User } from '../types/level-types'
|
||||
|
||||
let dbalInstance: DBALClient | null = null
|
||||
|
||||
4
frontends/nextjs/src/lib/dbal/index.ts
Normal file
4
frontends/nextjs/src/lib/dbal/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// DBAL (Database Abstraction Layer) exports
|
||||
export { DBALClient, createDBALClient } from './dbal-client'
|
||||
export { getDBALIntegration, initializeDBAL } from './dbal-integration'
|
||||
export { createDBALStub, DBALStub } from './dbal-stub'
|
||||
51
frontends/nextjs/src/lib/index.ts
Normal file
51
frontends/nextjs/src/lib/index.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Library exports - Centralized re-exports for all lib modules
|
||||
*
|
||||
* This file provides a clean API for importing from @/lib
|
||||
* Instead of: import { User } from '@/lib/types/level-types'
|
||||
* Use: import { User } from '@/lib'
|
||||
*/
|
||||
|
||||
// Types
|
||||
export * from './types'
|
||||
|
||||
// Core utilities
|
||||
export * from './utils'
|
||||
export { prisma } from './prisma'
|
||||
|
||||
// Authentication
|
||||
export * from './auth'
|
||||
|
||||
// Database
|
||||
export { Database } from './database'
|
||||
export * from './db'
|
||||
|
||||
// DBAL
|
||||
export * from './dbal'
|
||||
|
||||
// Schema utilities
|
||||
export * from './schema'
|
||||
|
||||
// Security
|
||||
export * from './security'
|
||||
|
||||
// Lua engine
|
||||
export * from './lua'
|
||||
|
||||
// Components
|
||||
export * from './components'
|
||||
|
||||
// Packages
|
||||
export * from './packages'
|
||||
|
||||
// Rendering
|
||||
export * from './rendering'
|
||||
|
||||
// Seed data
|
||||
export * from './seed'
|
||||
|
||||
// Workflow
|
||||
export * from './workflow'
|
||||
|
||||
// Password utilities
|
||||
export * from './password'
|
||||
5
frontends/nextjs/src/lib/lua/index.ts
Normal file
5
frontends/nextjs/src/lib/lua/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// Lua engine exports
|
||||
export { LuaEngine } from './lua-engine'
|
||||
export { SandboxedLuaEngine } from './sandboxed-lua-engine'
|
||||
export { luaSnippets } from './lua-snippets'
|
||||
export { luaExamples } from './lua-examples'
|
||||
6
frontends/nextjs/src/lib/packages/index.ts
Normal file
6
frontends/nextjs/src/lib/packages/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// Package system exports
|
||||
export * from './package-types'
|
||||
export { initializePackageSystem, buildPackageRegistry } from './package-loader'
|
||||
export { exportAllPackagesForSeed } from './package-export'
|
||||
export { packageCatalog } from './package-catalog'
|
||||
export { packageGlue, getPackageGlue } from './package-glue'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user