Initial commit
This commit is contained in:
Executable
+96
@@ -0,0 +1,96 @@
|
||||
import db from '@adonisjs/lucid/services/db'
|
||||
import { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'
|
||||
|
||||
export async function getDatabaseTables(): Promise<string[]> {
|
||||
const result = await db
|
||||
.from('information_schema.tables')
|
||||
.select('table_name')
|
||||
.where('table_schema', 'public')
|
||||
.whereNotIn('table_name', [
|
||||
'adonis_schema',
|
||||
'adonis_schema_versions',
|
||||
'auth_access_tokens',
|
||||
'admins',
|
||||
'permissions',
|
||||
'roles',
|
||||
'admin_roles',
|
||||
'role_permissions',
|
||||
'migrations',
|
||||
'migrations_lock',
|
||||
'pg_stat_statements',
|
||||
'pg_stat_activity',
|
||||
'pg_type',
|
||||
'pg_enum',
|
||||
'pg_class',
|
||||
'pg_attribute',
|
||||
'pg_index',
|
||||
'pg_namespace',
|
||||
'pg_tables',
|
||||
])
|
||||
.whereRaw(`table_name NOT LIKE 'pg_%'`)
|
||||
.whereRaw(`table_name NOT LIKE 'sql_%'`)
|
||||
|
||||
return result.map((row) => row.table_name)
|
||||
}
|
||||
|
||||
export async function getColumnsForTable(table: string) {
|
||||
const rows = await db
|
||||
.from('information_schema.columns')
|
||||
.select('column_name', 'data_type', 'is_nullable')
|
||||
.where('table_schema', 'public')
|
||||
.andWhere('table_name', table)
|
||||
|
||||
return rows.map((row) => {
|
||||
const col = {
|
||||
...row,
|
||||
isSpecial: ['created_at', 'updated_at', 'deleted_at'].includes(row.column_name),
|
||||
}
|
||||
return col
|
||||
})
|
||||
}
|
||||
|
||||
export async function getForeignKeysReferencing(table: string) {
|
||||
return await db
|
||||
.from('information_schema.columns')
|
||||
.select('table_name', 'column_name')
|
||||
.where('column_name', `${table}_id`)
|
||||
.andWhere('table_schema', 'public')
|
||||
}
|
||||
|
||||
export async function searchableColumns(table: string) {
|
||||
const columns = await getColumnsForTable(table)
|
||||
return {
|
||||
string: columns
|
||||
.filter((col) =>
|
||||
['character varying', 'text', 'varchar', 'char', 'uuid'].includes(col.data_type)
|
||||
)
|
||||
.map((col) => col.column_name),
|
||||
number: columns
|
||||
.filter((col) => ['integer', 'int4', 'int8', 'numeric', 'bigint'].includes(col.data_type))
|
||||
.map((col) => col.column_name),
|
||||
}
|
||||
}
|
||||
|
||||
export function addSearchFilter<T extends ModelQueryBuilderContract<any>>(
|
||||
query: T,
|
||||
columns: { string: string[]; number: string[] },
|
||||
search: string
|
||||
) {
|
||||
const keywords = search.trim().split(/\s+/)
|
||||
|
||||
query.where((builder) => {
|
||||
for (const keyword of keywords) {
|
||||
builder.orWhere((subBuilder) => {
|
||||
for (const column of columns.string) {
|
||||
subBuilder.orWhereILike(column, `%${keyword}%`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (!Number.isNaN(Number(search))) {
|
||||
for (const column of columns.number) {
|
||||
builder.orWhere(column, Number(search))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
import Admins from '#base_modules/Admin/model/admin'
|
||||
import { readdirSync, existsSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import db from '@adonisjs/lucid/services/db'
|
||||
|
||||
interface PermissionItem {
|
||||
slug: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export async function getAdminPermissionSlugs(admin: Admins): Promise<Set<string>> {
|
||||
await admin.load('roles')
|
||||
const roles = admin.roles.map((role) => role.id)
|
||||
const permissions = await db
|
||||
.from('role_permissions')
|
||||
.whereIn('role_id', roles)
|
||||
.select('permission_slug')
|
||||
|
||||
const slugs = new Set<string>()
|
||||
permissions.map((perm) => slugs.add(perm.permission_slug))
|
||||
return slugs
|
||||
}
|
||||
|
||||
export async function getAllPermissions(): Promise<PermissionItem[]> {
|
||||
const appRoot = process.cwd()
|
||||
const baseModulesPath = join(appRoot, 'app', 'base_modules')
|
||||
const modulesPath = join(appRoot, 'modules')
|
||||
|
||||
const result: PermissionItem[] = []
|
||||
|
||||
const loadPermissionsFromDir = async (dir: string) => {
|
||||
if (!existsSync(dir)) return
|
||||
|
||||
const modules = readdirSync(dir, { withFileTypes: true })
|
||||
.filter((d) => d.isDirectory())
|
||||
.map((d) => d.name)
|
||||
|
||||
for (const moduleName of modules) {
|
||||
const configPath = join(dir, moduleName, 'config', 'permissions.ts')
|
||||
if (existsSync(configPath)) {
|
||||
try {
|
||||
const fileUrl = pathToFileURL(configPath)
|
||||
const mod = await import(fileUrl.href)
|
||||
const perms = mod.default || []
|
||||
if (Array.isArray(perms)) result.push(...perms)
|
||||
} catch (err) {
|
||||
console.error(`Failed to load permissions for module "${moduleName}":`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await loadPermissionsFromDir(baseModulesPath)
|
||||
await loadPermissionsFromDir(modulesPath)
|
||||
|
||||
return result
|
||||
}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
const typeMap: Record<string, any> = {
|
||||
number: { type: 'integer', format: 'int64' },
|
||||
string: { type: 'string' },
|
||||
Date: { type: 'string', format: 'date-time' },
|
||||
boolean: { type: 'boolean' },
|
||||
}
|
||||
export async function modelToSchema(model: any) {
|
||||
const properties: Record<string, any> = {}
|
||||
for (const [key, meta] of model.$columnsDefinitions.entries()) {
|
||||
const tsType = meta.meta?.designType?.name || 'string'
|
||||
const mapped = typeMap[tsType] ?? { type: 'string' }
|
||||
properties[meta?.serializeAs || key] = {
|
||||
...mapped,
|
||||
description: key.replace(/([A-Z])/g, ' $1').trim(),
|
||||
}
|
||||
}
|
||||
return JSON.stringify(properties)
|
||||
}
|
||||
Reference in New Issue
Block a user