nuxt-auto-crud 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -84
- package/dist/module.d.mts +26 -22
- package/dist/module.json +1 -1
- package/dist/module.mjs +52 -9
- package/dist/runtime/composables/useRelationDisplay.d.ts +12 -0
- package/dist/runtime/composables/useRelationDisplay.js +48 -0
- package/dist/runtime/composables/useResourceSchemas.d.ts +19 -0
- package/dist/runtime/composables/useResourceSchemas.js +17 -0
- package/dist/runtime/server/api/[model]/[id].patch.js +3 -0
- package/dist/runtime/server/api/[model]/index.get.js +2 -1
- package/dist/runtime/server/api/_relations.get.d.ts +2 -0
- package/dist/runtime/server/api/_relations.get.js +24 -0
- package/dist/runtime/server/api/_schema/[table].get.d.ts +10 -0
- package/dist/runtime/server/api/_schema/[table].get.js +32 -0
- package/dist/runtime/server/api/_schema/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_schema/index.get.js +24 -0
- package/dist/runtime/server/plugins/seed.d.ts +2 -0
- package/dist/runtime/server/plugins/seed.js +36 -0
- package/dist/runtime/server/utils/auth.js +1 -1
- package/dist/runtime/server/utils/config.d.ts +2 -2
- package/dist/runtime/server/utils/modelMapper.js +16 -8
- package/dist/runtime/server/utils/schema.d.ts +20 -0
- package/dist/runtime/server/utils/schema.js +70 -0
- package/package.json +8 -5
- package/src/runtime/composables/useRelationDisplay.ts +67 -0
- package/src/runtime/composables/useResourceSchemas.ts +42 -0
- package/src/runtime/server/api/[model]/[id].patch.ts +5 -0
- package/src/runtime/server/api/[model]/index.get.ts +5 -2
- package/src/runtime/server/api/_relations.get.ts +31 -0
- package/src/runtime/server/api/_schema/[table].get.ts +41 -0
- package/src/runtime/server/api/_schema/index.get.ts +31 -0
- package/src/runtime/server/plugins/seed.ts +55 -0
- package/src/runtime/server/utils/auth.ts +1 -1
- package/src/runtime/server/utils/config.ts +3 -3
- package/src/runtime/server/utils/modelMapper.ts +25 -11
- package/src/runtime/server/utils/schema.ts +96 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as schema from '#site/schema'
|
|
4
4
|
import pluralize from 'pluralize'
|
|
5
5
|
import { pascalCase } from 'scule'
|
|
6
|
-
import { getTableColumns as getDrizzleTableColumns } from 'drizzle-orm'
|
|
6
|
+
import { getTableColumns as getDrizzleTableColumns, getTableName } from 'drizzle-orm'
|
|
7
7
|
import type { SQLiteTable } from 'drizzle-orm/sqlite-core'
|
|
8
8
|
import { createError } from 'h3'
|
|
9
9
|
import { useRuntimeConfig } from '#imports'
|
|
@@ -11,7 +11,7 @@ import { useRuntimeConfig } from '#imports'
|
|
|
11
11
|
/**
|
|
12
12
|
* Fields that should never be updatable via PATCH requests
|
|
13
13
|
*/
|
|
14
|
-
const PROTECTED_FIELDS = ['id', 'createdAt', '
|
|
14
|
+
const PROTECTED_FIELDS = ['id', 'created_at', 'updated_at', 'createdAt', 'updatedAt']
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Fields that should never be returned in API responses
|
|
@@ -50,15 +50,18 @@ function buildModelTableMap(): Record<string, unknown> {
|
|
|
50
50
|
// Iterate through all exports from schema
|
|
51
51
|
for (const [key, value] of Object.entries(schema)) {
|
|
52
52
|
// Check if it's a Drizzle table
|
|
53
|
-
// Drizzle tables have specific properties we can check
|
|
54
53
|
if (value && typeof value === 'object') {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
try {
|
|
55
|
+
// getTableName returns the table name for valid tables, and undefined/null for others (like relations)
|
|
56
|
+
// This is a more robust check than checking for properties
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
const tableName = getTableName(value as any)
|
|
59
|
+
if (tableName) {
|
|
60
|
+
tableMap[key] = value
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Ignore if it throws (not a table)
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
67
|
}
|
|
@@ -139,10 +142,21 @@ export function getUpdatableFields(modelName: string): string[] {
|
|
|
139
142
|
export function filterUpdatableFields(modelName: string, data: Record<string, unknown>): Record<string, unknown> {
|
|
140
143
|
const allowedFields = getUpdatableFields(modelName)
|
|
141
144
|
const filtered: Record<string, unknown> = {}
|
|
145
|
+
const table = modelTableMap[modelName]
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
147
|
+
const columns = table ? getDrizzleTableColumns(table as any) : {}
|
|
142
148
|
|
|
143
149
|
for (const field of allowedFields) {
|
|
144
150
|
if (data[field] !== undefined) {
|
|
145
|
-
|
|
151
|
+
let value = data[field]
|
|
152
|
+
const column = columns[field]
|
|
153
|
+
|
|
154
|
+
// Coerce timestamp fields to Date objects if they are strings
|
|
155
|
+
if (column && column.mode === 'timestamp' && typeof value === 'string') {
|
|
156
|
+
value = new Date(value)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
filtered[field] = value
|
|
146
160
|
}
|
|
147
161
|
}
|
|
148
162
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getTableColumns } from 'drizzle-orm'
|
|
2
|
+
import { getTableConfig } from 'drizzle-orm/sqlite-core'
|
|
3
|
+
import { modelTableMap } from './modelMapper'
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
export function drizzleTableToFields(table: any, resourceName: string) {
|
|
7
|
+
const columns = getTableColumns(table)
|
|
8
|
+
const fields = []
|
|
9
|
+
|
|
10
|
+
for (const [key, col] of Object.entries(columns)) {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
const column = col as any
|
|
13
|
+
const isRequired = column.notNull
|
|
14
|
+
let type = 'string'
|
|
15
|
+
const selectOptions: string[] | undefined = undefined
|
|
16
|
+
|
|
17
|
+
// Map Drizzle types to frontend types
|
|
18
|
+
if (column.dataType === 'number' || column.columnType === 'SQLiteInteger' || column.columnType === 'SQLiteReal') {
|
|
19
|
+
type = 'number'
|
|
20
|
+
// Check if it is a timestamp
|
|
21
|
+
if (column.name.endsWith('_at') || column.name.endsWith('At')) {
|
|
22
|
+
type = 'date'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else if (column.dataType === 'boolean') {
|
|
26
|
+
type = 'boolean'
|
|
27
|
+
}
|
|
28
|
+
else if (column.dataType === 'date' || (column.dataType === 'string' && (column.name.endsWith('_at') || column.name.endsWith('At')))) {
|
|
29
|
+
type = 'date'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fields.push({
|
|
33
|
+
name: key,
|
|
34
|
+
type,
|
|
35
|
+
required: isRequired,
|
|
36
|
+
selectOptions,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
resource: resourceName,
|
|
42
|
+
fields,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function getRelations() {
|
|
47
|
+
const relations: Record<string, Record<string, string>> = {}
|
|
48
|
+
|
|
49
|
+
for (const [tableName, table] of Object.entries(modelTableMap)) {
|
|
50
|
+
try {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
|
+
const config = getTableConfig(table as any)
|
|
53
|
+
if (config.foreignKeys.length > 0) {
|
|
54
|
+
const tableRelations: Record<string, string> = {}
|
|
55
|
+
relations[tableName] = tableRelations
|
|
56
|
+
|
|
57
|
+
// Map column names to property names
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
const columns = getTableColumns(table as any)
|
|
60
|
+
const columnToProperty: Record<string, string> = {}
|
|
61
|
+
for (const [key, col] of Object.entries(columns)) {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
columnToProperty[(col as any).name] = key
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
config.foreignKeys.forEach((fk: any) => {
|
|
68
|
+
const sourceColumnName = fk.reference().columns[0].name
|
|
69
|
+
const sourceProperty = columnToProperty[sourceColumnName] || sourceColumnName
|
|
70
|
+
const targetTable = fk.reference().foreignTable[Symbol.for('drizzle:Name')]
|
|
71
|
+
tableRelations[sourceProperty] = targetTable
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Ignore tables that don't have config (e.g. not Drizzle tables)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return relations
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function getAllSchemas() {
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
const schemas: Record<string, any> = {}
|
|
85
|
+
|
|
86
|
+
for (const [tableName, table] of Object.entries(modelTableMap)) {
|
|
87
|
+
schemas[tableName] = drizzleTableToFields(table, tableName)
|
|
88
|
+
}
|
|
89
|
+
return schemas
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function getSchema(tableName: string) {
|
|
93
|
+
const table = modelTableMap[tableName]
|
|
94
|
+
if (!table) return undefined
|
|
95
|
+
return drizzleTableToFields(table, tableName)
|
|
96
|
+
}
|