@env-hopper/backend-core 2.0.1-alpha → 2.0.1-alpha.1
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/dist/index.d.ts +1584 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1806 -0
- package/dist/index.js.map +1 -0
- package/package.json +26 -11
- package/prisma/migrations/20250526183023_init/migration.sql +71 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +121 -0
- package/src/db/client.ts +34 -0
- package/src/db/index.ts +17 -0
- package/src/db/syncAppCatalog.ts +67 -0
- package/src/db/tableSyncMagazine.ts +22 -0
- package/src/db/tableSyncPrismaAdapter.ts +202 -0
- package/src/index.ts +96 -3
- package/src/modules/admin/chat/createAdminChatHandler.ts +152 -0
- package/src/modules/admin/chat/createDatabaseTools.ts +261 -0
- package/src/modules/appCatalog/service.ts +79 -0
- package/src/modules/appCatalogAdmin/appCatalogAdminRouter.ts +113 -0
- package/src/modules/assets/assetRestController.ts +309 -0
- package/src/modules/assets/assetUtils.ts +81 -0
- package/src/modules/assets/screenshotRestController.ts +195 -0
- package/src/modules/assets/screenshotRouter.ts +116 -0
- package/src/modules/assets/syncAssets.ts +261 -0
- package/src/modules/auth/auth.ts +51 -0
- package/src/modules/auth/authProviders.ts +108 -0
- package/src/modules/auth/authRouter.ts +77 -0
- package/src/modules/auth/authorizationUtils.ts +114 -0
- package/src/modules/auth/registerAuthRoutes.ts +33 -0
- package/src/modules/icons/iconRestController.ts +190 -0
- package/src/modules/icons/iconRouter.ts +157 -0
- package/src/modules/icons/iconService.ts +73 -0
- package/src/server/controller.ts +102 -29
- package/src/server/ehStaticControllerContract.ts +8 -1
- package/src/server/ehTrpcContext.ts +0 -6
- package/src/types/backend/api.ts +1 -14
- package/src/types/backend/companySpecificBackend.ts +17 -0
- package/src/types/common/appCatalogTypes.ts +167 -0
- package/src/types/common/dataRootTypes.ts +72 -10
- package/src/types/index.ts +2 -0
- package/dist/esm/__tests__/dummy.test.d.ts +0 -1
- package/dist/esm/index.d.ts +0 -7
- package/dist/esm/index.js +0 -9
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/server/controller.d.ts +0 -32
- package/dist/esm/server/controller.js +0 -35
- package/dist/esm/server/controller.js.map +0 -1
- package/dist/esm/server/db.d.ts +0 -2
- package/dist/esm/server/ehStaticControllerContract.d.ts +0 -9
- package/dist/esm/server/ehStaticControllerContract.js +0 -12
- package/dist/esm/server/ehStaticControllerContract.js.map +0 -1
- package/dist/esm/server/ehTrpcContext.d.ts +0 -8
- package/dist/esm/server/ehTrpcContext.js +0 -11
- package/dist/esm/server/ehTrpcContext.js.map +0 -1
- package/dist/esm/types/backend/api.d.ts +0 -71
- package/dist/esm/types/backend/common.d.ts +0 -9
- package/dist/esm/types/backend/dataSources.d.ts +0 -20
- package/dist/esm/types/backend/deployments.d.ts +0 -34
- package/dist/esm/types/common/app/appTypes.d.ts +0 -12
- package/dist/esm/types/common/app/ui/appUiTypes.d.ts +0 -10
- package/dist/esm/types/common/appCatalogTypes.d.ts +0 -16
- package/dist/esm/types/common/dataRootTypes.d.ts +0 -32
- package/dist/esm/types/common/env/envTypes.d.ts +0 -6
- package/dist/esm/types/common/resourceTypes.d.ts +0 -8
- package/dist/esm/types/common/sharedTypes.d.ts +0 -4
- package/dist/esm/types/index.d.ts +0 -11
- package/src/server/db.ts +0 -4
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import type { Tool } from 'ai'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { getDbClient } from '../../../db'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generic interface for executing raw SQL queries.
|
|
7
|
+
* Can be implemented with Prisma's $queryRawUnsafe or any other SQL client.
|
|
8
|
+
*/
|
|
9
|
+
export interface DatabaseClient {
|
|
10
|
+
/** Execute a SELECT query and return results */
|
|
11
|
+
query: <T = unknown>(sql: string) => Promise<Array<T>>
|
|
12
|
+
/** Execute an INSERT/UPDATE/DELETE and return affected row count */
|
|
13
|
+
execute: (sql: string) => Promise<{ affectedRows: number }>
|
|
14
|
+
/** Get list of tables in the database */
|
|
15
|
+
getTables: () => Promise<Array<string>>
|
|
16
|
+
/** Get columns for a specific table */
|
|
17
|
+
getColumns: (
|
|
18
|
+
tableName: string,
|
|
19
|
+
) => Promise<Array<{ name: string; type: string; nullable: boolean }>>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a DatabaseClient from a Prisma client.
|
|
24
|
+
*/
|
|
25
|
+
export function createPrismaDatabaseClient(prisma: {
|
|
26
|
+
$queryRawUnsafe: <T>(sql: string) => Promise<T>
|
|
27
|
+
$executeRawUnsafe: (sql: string) => Promise<number>
|
|
28
|
+
}): DatabaseClient {
|
|
29
|
+
return {
|
|
30
|
+
query: async <T = unknown>(sql: string): Promise<Array<T>> => {
|
|
31
|
+
const result = await prisma.$queryRawUnsafe<Array<T>>(sql)
|
|
32
|
+
return result
|
|
33
|
+
},
|
|
34
|
+
execute: async (sql: string) => {
|
|
35
|
+
const affectedRows = await prisma.$executeRawUnsafe(sql)
|
|
36
|
+
return { affectedRows }
|
|
37
|
+
},
|
|
38
|
+
getTables: async () => {
|
|
39
|
+
const tables = await prisma.$queryRawUnsafe<Array<{ tablename: string }>>(
|
|
40
|
+
`SELECT tablename FROM pg_tables WHERE schemaname = 'public'`,
|
|
41
|
+
)
|
|
42
|
+
return tables.map((t) => t.tablename)
|
|
43
|
+
},
|
|
44
|
+
getColumns: async (tableName: string) => {
|
|
45
|
+
const columns = await prisma.$queryRawUnsafe<
|
|
46
|
+
Array<{
|
|
47
|
+
column_name: string
|
|
48
|
+
data_type: string
|
|
49
|
+
is_nullable: string
|
|
50
|
+
}>
|
|
51
|
+
>(
|
|
52
|
+
`SELECT column_name, data_type, is_nullable
|
|
53
|
+
FROM information_schema.columns
|
|
54
|
+
WHERE table_name = '${tableName}' AND table_schema = 'public'`,
|
|
55
|
+
)
|
|
56
|
+
return columns.map((c) => ({
|
|
57
|
+
name: c.column_name,
|
|
58
|
+
type: c.data_type,
|
|
59
|
+
nullable: c.is_nullable === 'YES',
|
|
60
|
+
}))
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Define zod schemas for tool parameters
|
|
66
|
+
const querySchema = z.object({
|
|
67
|
+
sql: z.string().describe('The SELECT SQL query to execute'),
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const modifySchema = z.object({
|
|
71
|
+
sql: z
|
|
72
|
+
.string()
|
|
73
|
+
.describe('The INSERT, UPDATE, or DELETE SQL query to execute'),
|
|
74
|
+
confirmed: z
|
|
75
|
+
.boolean()
|
|
76
|
+
.describe('Must be true to execute destructive operations'),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const schemaParamsSchema = z.object({
|
|
80
|
+
tableName: z
|
|
81
|
+
.string()
|
|
82
|
+
.optional()
|
|
83
|
+
.describe(
|
|
84
|
+
'Specific table name to get columns for. If not provided, returns list of all tables.',
|
|
85
|
+
),
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
type QueryInput = z.infer<typeof querySchema>
|
|
89
|
+
type ModifyInput = z.infer<typeof modifySchema>
|
|
90
|
+
type SchemaInput = z.infer<typeof schemaParamsSchema>
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a DatabaseClient using the internal backend-core Prisma client.
|
|
94
|
+
* This is a convenience function for apps that don't need to pass their own Prisma client.
|
|
95
|
+
*/
|
|
96
|
+
function createInternalDatabaseClient(): DatabaseClient {
|
|
97
|
+
return createPrismaDatabaseClient(getDbClient())
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Creates AI tools for generic database access.
|
|
102
|
+
*
|
|
103
|
+
* The AI uses these internally - users interact via natural language.
|
|
104
|
+
* Results are formatted as tables by the AI based on the system prompt.
|
|
105
|
+
* Uses the internal backend-core Prisma client automatically.
|
|
106
|
+
*/
|
|
107
|
+
export function createDatabaseTools(): Record<string, Tool> {
|
|
108
|
+
const db = createInternalDatabaseClient()
|
|
109
|
+
const queryDatabase: Tool<QueryInput, unknown> = {
|
|
110
|
+
description: `Execute a SELECT query to read data from the database.
|
|
111
|
+
Use this to list, search, or filter records from any table.
|
|
112
|
+
Always use double quotes around table and column names for PostgreSQL (e.g., SELECT * FROM "App").
|
|
113
|
+
Return results will be formatted as a table for the user.`,
|
|
114
|
+
inputSchema: querySchema,
|
|
115
|
+
execute: async ({ sql }) => {
|
|
116
|
+
console.log(`Executing ${sql}`)
|
|
117
|
+
// Safety check - only allow SELECT
|
|
118
|
+
const normalizedSql = sql.trim().toUpperCase()
|
|
119
|
+
if (!normalizedSql.startsWith('SELECT')) {
|
|
120
|
+
return {
|
|
121
|
+
error:
|
|
122
|
+
'Only SELECT queries are allowed with queryDatabase. Use modifyDatabase for changes.',
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const results = await db.query(sql)
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
rowCount: Array.isArray(results) ? results.length : 0,
|
|
130
|
+
data: results,
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
error: error instanceof Error ? error.message : 'Query failed',
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const modifyDatabase: Tool<ModifyInput, unknown> = {
|
|
142
|
+
description: `Execute an INSERT, UPDATE, or DELETE query to modify data.
|
|
143
|
+
Use double quotes around table and column names for PostgreSQL.
|
|
144
|
+
IMPORTANT: Always ask for user confirmation before executing. Set confirmed=true only after user confirms.
|
|
145
|
+
For UPDATE/DELETE, always include a WHERE clause to avoid affecting all rows.`,
|
|
146
|
+
inputSchema: modifySchema,
|
|
147
|
+
execute: async ({ sql, confirmed }) => {
|
|
148
|
+
if (!confirmed) {
|
|
149
|
+
return {
|
|
150
|
+
needsConfirmation: true,
|
|
151
|
+
message: 'Please confirm you want to execute this operation.',
|
|
152
|
+
sql,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Safety check - don't allow SELECT here
|
|
157
|
+
const normalizedSql = sql.trim().toUpperCase()
|
|
158
|
+
if (normalizedSql.startsWith('SELECT')) {
|
|
159
|
+
return { error: 'Use queryDatabase for SELECT queries.' }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Extra safety - warn about missing WHERE on UPDATE/DELETE
|
|
163
|
+
if (
|
|
164
|
+
(normalizedSql.startsWith('UPDATE') ||
|
|
165
|
+
normalizedSql.startsWith('DELETE')) &&
|
|
166
|
+
!normalizedSql.includes('WHERE')
|
|
167
|
+
) {
|
|
168
|
+
return {
|
|
169
|
+
error:
|
|
170
|
+
'UPDATE and DELETE queries must include a WHERE clause for safety.',
|
|
171
|
+
sql,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const result = await db.execute(sql)
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
affectedRows: result.affectedRows,
|
|
180
|
+
message: `Operation completed. ${result.affectedRows} row(s) affected.`,
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
return {
|
|
184
|
+
success: false,
|
|
185
|
+
error: error instanceof Error ? error.message : 'Operation failed',
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const getDatabaseSchema: Tool<SchemaInput, unknown> = {
|
|
192
|
+
description: `Get information about database tables and their columns.
|
|
193
|
+
Use this to understand the database structure before writing queries.
|
|
194
|
+
Call without tableName to list all tables, or with tableName to get columns for a specific table.`,
|
|
195
|
+
inputSchema: schemaParamsSchema,
|
|
196
|
+
execute: async ({ tableName }) => {
|
|
197
|
+
try {
|
|
198
|
+
if (tableName) {
|
|
199
|
+
const columns = await db.getColumns(tableName)
|
|
200
|
+
return {
|
|
201
|
+
success: true,
|
|
202
|
+
table: tableName,
|
|
203
|
+
columns,
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
const tables = await db.getTables()
|
|
207
|
+
return {
|
|
208
|
+
success: true,
|
|
209
|
+
tables,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
error:
|
|
216
|
+
error instanceof Error ? error.message : 'Failed to get schema',
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
queryDatabase,
|
|
224
|
+
modifyDatabase,
|
|
225
|
+
getDatabaseSchema,
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Default system prompt for the database admin assistant.
|
|
231
|
+
* Can be customized or extended.
|
|
232
|
+
*/
|
|
233
|
+
export const DEFAULT_ADMIN_SYSTEM_PROMPT = `You are a helpful database admin assistant. You help users view and manage data in the database.
|
|
234
|
+
|
|
235
|
+
IMPORTANT RULES:
|
|
236
|
+
1. When showing data, ALWAYS format it as a numbered ASCII table so users can reference rows by number
|
|
237
|
+
2. NEVER show raw SQL to users - just describe what you're doing in plain language
|
|
238
|
+
3. When users ask to modify data (update, delete, create), ALWAYS confirm before executing
|
|
239
|
+
4. For updates, show the current value and ask for confirmation before changing
|
|
240
|
+
5. Keep responses concise and focused on the data
|
|
241
|
+
|
|
242
|
+
FORMATTING EXAMPLE:
|
|
243
|
+
When user asks "show me all apps", respond like:
|
|
244
|
+
"Here are all the apps:
|
|
245
|
+
|
|
246
|
+
| # | ID | Slug | Display Name | Icon |
|
|
247
|
+
|---|----|---------|-----------------| -------|
|
|
248
|
+
| 1 | 1 | portal | Portal | portal |
|
|
249
|
+
| 2 | 2 | admin | Admin Dashboard | admin |
|
|
250
|
+
| 3 | 3 | api | API Service | null |
|
|
251
|
+
|
|
252
|
+
Found 3 apps total."
|
|
253
|
+
|
|
254
|
+
When user says "update row 2 display name to 'Admin Panel'":
|
|
255
|
+
1. First confirm: "I'll update the app 'Admin Dashboard' (ID: 2) to have display name 'Admin Panel'. Proceed?"
|
|
256
|
+
2. Only after user confirms, execute the update
|
|
257
|
+
3. Then show the updated row
|
|
258
|
+
|
|
259
|
+
AVAILABLE TABLES:
|
|
260
|
+
Use getDatabaseSchema tool to discover tables and their columns.
|
|
261
|
+
`
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { getDbClient } from '../../db/client'
|
|
2
|
+
import type {
|
|
3
|
+
AppCatalogData,
|
|
4
|
+
AppCategory,
|
|
5
|
+
AppForCatalog,
|
|
6
|
+
} from '../../types/common/appCatalogTypes'
|
|
7
|
+
|
|
8
|
+
function capitalize(word: string): string {
|
|
9
|
+
if (!word) return word
|
|
10
|
+
return word.charAt(0).toUpperCase() + word.slice(1)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function getAppsFromPrisma(): Promise<Array<AppForCatalog>> {
|
|
14
|
+
const prisma = getDbClient()
|
|
15
|
+
|
|
16
|
+
// Fetch all apps
|
|
17
|
+
const rows = await prisma.dbAppForCatalog.findMany()
|
|
18
|
+
|
|
19
|
+
return rows.map((row) => {
|
|
20
|
+
const access = row.access as unknown as AppForCatalog['access']
|
|
21
|
+
const roles =
|
|
22
|
+
row.roles == null
|
|
23
|
+
? undefined
|
|
24
|
+
: (row.roles as unknown as AppForCatalog['roles'])
|
|
25
|
+
const teams = (row.teams as unknown as Array<string> | null) ?? []
|
|
26
|
+
const tags = (row.tags as unknown as AppForCatalog['tags']) ?? []
|
|
27
|
+
const screenshotIds =
|
|
28
|
+
(row.screenshotIds as unknown as AppForCatalog['screenshotIds']) ?? []
|
|
29
|
+
const notes = row.notes == null ? undefined : row.notes
|
|
30
|
+
const appUrl = row.appUrl == null ? undefined : row.appUrl
|
|
31
|
+
const iconName = row.iconName == null ? undefined : row.iconName
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
id: row.id,
|
|
35
|
+
slug: row.slug,
|
|
36
|
+
displayName: row.displayName,
|
|
37
|
+
description: row.description,
|
|
38
|
+
access,
|
|
39
|
+
teams,
|
|
40
|
+
roles,
|
|
41
|
+
approver:
|
|
42
|
+
row.approverName && row.approverEmail
|
|
43
|
+
? { name: row.approverName, email: row.approverEmail }
|
|
44
|
+
: undefined,
|
|
45
|
+
notes,
|
|
46
|
+
tags,
|
|
47
|
+
appUrl,
|
|
48
|
+
iconName,
|
|
49
|
+
screenshotIds,
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function deriveCategories(
|
|
55
|
+
apps: Array<AppForCatalog>,
|
|
56
|
+
): Array<AppCategory> {
|
|
57
|
+
const tagSet = new Set<string>()
|
|
58
|
+
for (const app of apps) {
|
|
59
|
+
for (const tag of app.tags ?? []) {
|
|
60
|
+
const normalized = tag.trim().toLowerCase()
|
|
61
|
+
if (normalized) tagSet.add(normalized)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const categories: Array<AppCategory> = [{ id: 'all', name: 'All' }]
|
|
65
|
+
for (const tag of Array.from(tagSet).sort()) {
|
|
66
|
+
categories.push({ id: tag, name: capitalize(tag) })
|
|
67
|
+
}
|
|
68
|
+
return categories
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function getAppCatalogData(
|
|
72
|
+
getAppsOptional?: () => Promise<Array<AppForCatalog>>,
|
|
73
|
+
): Promise<AppCatalogData> {
|
|
74
|
+
const apps = getAppsOptional
|
|
75
|
+
? await getAppsOptional()
|
|
76
|
+
: await getAppsFromPrisma()
|
|
77
|
+
const categories = deriveCategories(apps)
|
|
78
|
+
return { apps, categories }
|
|
79
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { TRPCRootObject } from '@trpc/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { getDbClient } from '../../db'
|
|
4
|
+
import type { EhTrpcContext } from '../../server/ehTrpcContext'
|
|
5
|
+
|
|
6
|
+
// Zod schema for access method (simplified for now - you can expand this)
|
|
7
|
+
const AccessMethodSchema = z.object({
|
|
8
|
+
type: z.enum(['bot', 'ticketing', 'email', 'self-service', 'documentation', 'manual']),
|
|
9
|
+
}).passthrough()
|
|
10
|
+
|
|
11
|
+
const AppLinkSchema = z.object({
|
|
12
|
+
displayName: z.string().optional(),
|
|
13
|
+
url: z.string().url(),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const AppRoleSchema = z.object({
|
|
17
|
+
id: z.string(),
|
|
18
|
+
name: z.string(),
|
|
19
|
+
description: z.string().optional(),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const ApproverSchema = z.object({
|
|
23
|
+
name: z.string(),
|
|
24
|
+
email: z.string().email(),
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const CreateAppForCatalogSchema = z.object({
|
|
28
|
+
slug: z.string().min(1).regex(/^[a-z0-9-]+$/, 'Slug must be lowercase alphanumeric with hyphens'),
|
|
29
|
+
displayName: z.string().min(1),
|
|
30
|
+
description: z.string(),
|
|
31
|
+
access: AccessMethodSchema,
|
|
32
|
+
teams: z.array(z.string()).optional(),
|
|
33
|
+
roles: z.array(AppRoleSchema).optional(),
|
|
34
|
+
approver: ApproverSchema.optional(),
|
|
35
|
+
notes: z.string().optional(),
|
|
36
|
+
tags: z.array(z.string()).optional(),
|
|
37
|
+
appUrl: z.string().url().optional(),
|
|
38
|
+
links: z.array(AppLinkSchema).optional(),
|
|
39
|
+
iconName: z.string().optional(),
|
|
40
|
+
screenshotIds: z.array(z.string()).optional(),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const UpdateAppForCatalogSchema = CreateAppForCatalogSchema.partial().extend({
|
|
44
|
+
id: z.string(),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export function createAppCatalogAdminRouter(t: TRPCRootObject<EhTrpcContext, {}, {}>) {
|
|
48
|
+
const router = t.router
|
|
49
|
+
const publicProcedure = t.procedure
|
|
50
|
+
|
|
51
|
+
return router({
|
|
52
|
+
list: publicProcedure.query(async () => {
|
|
53
|
+
const prisma = getDbClient()
|
|
54
|
+
return prisma.dbAppForCatalog.findMany({
|
|
55
|
+
orderBy: { displayName: 'asc' },
|
|
56
|
+
})
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
getById: publicProcedure
|
|
60
|
+
.input(z.object({ id: z.string() }))
|
|
61
|
+
.query(async ({ input }) => {
|
|
62
|
+
const prisma = getDbClient()
|
|
63
|
+
return prisma.dbAppForCatalog.findUnique({
|
|
64
|
+
where: { id: input.id },
|
|
65
|
+
})
|
|
66
|
+
}),
|
|
67
|
+
|
|
68
|
+
create: publicProcedure
|
|
69
|
+
.input(CreateAppForCatalogSchema)
|
|
70
|
+
.mutation(async ({ input }) => {
|
|
71
|
+
const prisma = getDbClient()
|
|
72
|
+
const { approver, ...rest } = input
|
|
73
|
+
|
|
74
|
+
return prisma.dbAppForCatalog.create({
|
|
75
|
+
data: {
|
|
76
|
+
...rest,
|
|
77
|
+
approverName: approver?.name ?? null,
|
|
78
|
+
approverEmail: approver?.email ?? null,
|
|
79
|
+
teams: input.teams ?? [],
|
|
80
|
+
tags: input.tags ?? [],
|
|
81
|
+
screenshotIds: input.screenshotIds ?? [],
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
}),
|
|
85
|
+
|
|
86
|
+
update: publicProcedure
|
|
87
|
+
.input(UpdateAppForCatalogSchema)
|
|
88
|
+
.mutation(async ({ input }) => {
|
|
89
|
+
const prisma = getDbClient()
|
|
90
|
+
const { id, approver, ...rest } = input
|
|
91
|
+
|
|
92
|
+
return prisma.dbAppForCatalog.update({
|
|
93
|
+
where: { id },
|
|
94
|
+
data: {
|
|
95
|
+
...rest,
|
|
96
|
+
...(approver !== undefined && {
|
|
97
|
+
approverName: approver.name || null,
|
|
98
|
+
approverEmail: approver.email || null,
|
|
99
|
+
}),
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
delete: publicProcedure
|
|
105
|
+
.input(z.object({ id: z.string() }))
|
|
106
|
+
.mutation(async ({ input }) => {
|
|
107
|
+
const prisma = getDbClient()
|
|
108
|
+
return prisma.dbAppForCatalog.delete({
|
|
109
|
+
where: { id: input.id },
|
|
110
|
+
})
|
|
111
|
+
}),
|
|
112
|
+
})
|
|
113
|
+
}
|