diff --git a/frontends/nextjs/src/lib/db/dbal-client.ts b/frontends/nextjs/src/lib/db/dbal-client.ts index 93cb4844f..1da402a6d 100644 --- a/frontends/nextjs/src/lib/db/dbal-client.ts +++ b/frontends/nextjs/src/lib/db/dbal-client.ts @@ -1,159 +1,14 @@ /** * 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 type { DBALAdapter, ListOptions, ListResult } from './dbal-client/types' -export interface ListOptions { - filter?: Record - sort?: Record - page?: number - limit?: number -} - -export interface ListResult { - data: T[] - total?: number - page?: number - limit?: number - hasMore?: boolean -} - -export interface DBALAdapter { - create(entity: string, data: Record): Promise - read(entity: string, id: string): Promise - update(entity: string, id: string, data: Record): Promise - delete(entity: string, id: string): Promise - list(entity: string, options?: ListOptions): Promise> - findFirst(entity: string, options?: { where?: Record }): Promise - upsert(entity: string, options: { where: Record; update: Record; create: Record }): Promise - close(): Promise -} - -/** - * 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): Record => { - const where: Record = {} - 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): Promise { - const model = getModel(entity) - return model.create({ data }) - }, - - async read(entity: string, id: string): Promise { - const model = getModel(entity) - return model.findUnique({ where: { id } }) - }, - - async update(entity: string, id: string, data: Record): Promise { - const model = getModel(entity) - // Filter out undefined values - const cleanData: Record = {} - 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 { - const model = getModel(entity) - try { - await model.delete({ where: { id } }) - return true - } catch { - return false - } - }, - - async list(entity: string, options?: ListOptions): Promise> { - 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 findFirst(entity: string, options?: { where?: Record }): Promise { - const model = getModel(entity) - const where = options?.where ? buildWhereClause(options.where) : undefined - return model.findFirst({ where }) - }, - - async upsert(entity: string, options: { where: Record; update: Record; create: Record }): Promise { - const model = getModel(entity) - return model.upsert({ - where: options.where, - update: options.update, - create: options.create, - }) - }, - - async close(): Promise { - 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 => { - await prismaAdapter.close() -} +export { getAdapter } from './dbal-client/get-adapter' +export { closeAdapter } from './dbal-client/close-adapter' diff --git a/frontends/nextjs/src/lib/lua/LuaSnippetUtils.ts b/frontends/nextjs/src/lib/lua/LuaSnippetUtils.ts index 4982c43f3..aef2d9c59 100644 --- a/frontends/nextjs/src/lib/lua/LuaSnippetUtils.ts +++ b/frontends/nextjs/src/lib/lua/LuaSnippetUtils.ts @@ -2,6 +2,8 @@ import { LUA_SNIPPETS, LUA_SNIPPET_CATEGORIES, type LuaSnippet } from './snippet import { getSnippetsByCategory } from './functions/get-snippets-by-category' import { searchSnippets } from './functions/search-snippets' import { getSnippetById } from './functions/get-snippet-by-id' +import { getSnippetCategoryCounts } from './functions/get-snippet-category-counts' +import { getAllSnippetTags } from './functions/get-all-snippet-tags' /** * LuaSnippetUtils - Class wrapper for Lua snippet utility functions @@ -39,30 +41,12 @@ export class LuaSnippetUtils { /** * Get count of snippets per category */ - static getCategoryCounts(): Record { - const counts: Record = { All: LUA_SNIPPETS.length } - - for (const snippet of LUA_SNIPPETS) { - counts[snippet.category] = (counts[snippet.category] || 0) + 1 - } - - return counts - } + static getCategoryCounts = getSnippetCategoryCounts /** * Get all unique tags across snippets */ - static getAllTags(): string[] { - const tagSet = new Set() - - for (const snippet of LUA_SNIPPETS) { - for (const tag of snippet.tags) { - tagSet.add(tag) - } - } - - return Array.from(tagSet).sort() - } + static getAllTags = getAllSnippetTags } // Re-export types for convenience diff --git a/frontends/nextjs/src/lib/lua/functions/index.ts b/frontends/nextjs/src/lib/lua/functions/index.ts index 416f297e0..bd55298ce 100644 --- a/frontends/nextjs/src/lib/lua/functions/index.ts +++ b/frontends/nextjs/src/lib/lua/functions/index.ts @@ -21,3 +21,5 @@ export { executeLuaCode } from './execution/execute-lua-code' export { getSnippetsByCategory } from './get-snippets-by-category' export { searchSnippets } from './search-snippets' export { getSnippetById } from './get-snippet-by-id' +export { getSnippetCategoryCounts } from './get-snippet-category-counts' +export { getAllSnippetTags } from './get-all-snippet-tags' diff --git a/frontends/nextjs/src/lib/security/functions/scanners/scan-for-vulnerabilities.ts b/frontends/nextjs/src/lib/security/functions/scanners/scan-for-vulnerabilities.ts new file mode 100644 index 000000000..4fc2acf2d --- /dev/null +++ b/frontends/nextjs/src/lib/security/functions/scanners/scan-for-vulnerabilities.ts @@ -0,0 +1,39 @@ +import type { SecurityScanResult } from '../types' +import { scanHTML } from './scan-html' +import { scanJavaScript } from './scan-javascript' +import { scanJSON } from './scan-json' +import { scanLua } from './scan-lua' + +/** + * Convenience function to scan code for vulnerabilities + * Automatically detects type based on content or explicit type parameter + */ +export const scanForVulnerabilities = ( + code: string, + type?: 'javascript' | 'lua' | 'json' | 'html' +): SecurityScanResult => { + let resolvedType = type + + if (!resolvedType) { + if (code.trim().startsWith('{') || code.trim().startsWith('[')) { + resolvedType = 'json' + } else if (code.includes('function') && code.includes('end')) { + resolvedType = 'lua' + } else if (code.includes('<') && code.includes('>')) { + resolvedType = 'html' + } else { + resolvedType = 'javascript' + } + } + + switch (resolvedType) { + case 'lua': + return scanLua(code) + case 'json': + return scanJSON(code) + case 'html': + return scanHTML(code) + default: + return scanJavaScript(code) + } +}