@env-hopper/backend-core 2.0.1-alpha → 2.0.1-alpha-20260224145405
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 +2059 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2571 -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 +149 -0
- package/src/db/client.ts +42 -0
- package/src/db/index.ts +21 -0
- package/src/db/syncAppCatalog.ts +310 -0
- package/src/db/tableSyncMagazine.ts +32 -0
- package/src/db/tableSyncPrismaAdapter.ts +203 -0
- package/src/generated/prisma/client.d.ts +1 -0
- package/src/generated/prisma/client.js +4 -0
- package/src/generated/prisma/default.d.ts +1 -0
- package/src/generated/prisma/default.js +4 -0
- package/src/generated/prisma/edge.d.ts +1 -0
- package/src/generated/prisma/edge.js +227 -0
- package/src/generated/prisma/index-browser.js +214 -0
- package/src/generated/prisma/index.d.ts +4212 -0
- package/src/generated/prisma/index.js +248 -0
- package/src/generated/prisma/libquery_engine-darwin-arm64.dylib.node +0 -0
- package/src/generated/prisma/package.json +183 -0
- package/src/generated/prisma/query_engine_bg.js +2 -0
- package/src/generated/prisma/query_engine_bg.wasm +0 -0
- package/src/generated/prisma/runtime/edge-esm.js +34 -0
- package/src/generated/prisma/runtime/edge.js +34 -0
- package/src/generated/prisma/runtime/index-browser.d.ts +370 -0
- package/src/generated/prisma/runtime/index-browser.js +16 -0
- package/src/generated/prisma/runtime/library.d.ts +3982 -0
- package/src/generated/prisma/runtime/library.js +146 -0
- package/src/generated/prisma/runtime/react-native.js +83 -0
- package/src/generated/prisma/runtime/wasm-compiler-edge.js +84 -0
- package/src/generated/prisma/runtime/wasm-engine-edge.js +36 -0
- package/src/generated/prisma/schema.prisma +49 -0
- package/src/generated/prisma/wasm-edge-light-loader.mjs +4 -0
- package/src/generated/prisma/wasm-worker-loader.mjs +4 -0
- package/src/generated/prisma/wasm.d.ts +1 -0
- package/src/generated/prisma/wasm.js +234 -0
- package/src/index.ts +109 -1
- package/src/middleware/backendResolver.ts +49 -0
- package/src/middleware/createEhMiddleware.ts +171 -0
- package/src/middleware/database.ts +62 -0
- package/src/middleware/featureRegistry.ts +173 -0
- package/src/middleware/index.ts +43 -0
- package/src/middleware/types.ts +202 -0
- package/src/modules/admin/chat/createAdminChatHandler.ts +152 -0
- package/src/modules/admin/chat/createDatabaseTools.ts +261 -0
- package/src/modules/appCatalog/service.ts +130 -0
- package/src/modules/appCatalogAdmin/appCatalogAdminRouter.ts +187 -0
- package/src/modules/appCatalogAdmin/catalogBackupController.ts +213 -0
- package/src/modules/approvalMethod/approvalMethodRouter.ts +169 -0
- package/src/modules/approvalMethod/slugUtils.ts +17 -0
- package/src/modules/approvalMethod/syncApprovalMethods.ts +38 -0
- package/src/modules/assets/assetRestController.ts +271 -0
- package/src/modules/assets/assetUtils.ts +114 -0
- package/src/modules/assets/screenshotRestController.ts +195 -0
- package/src/modules/assets/screenshotRouter.ts +112 -0
- package/src/modules/assets/syncAssets.ts +277 -0
- package/src/modules/assets/upsertAsset.ts +46 -0
- package/src/modules/auth/auth.ts +51 -0
- package/src/modules/auth/authProviders.ts +40 -0
- package/src/modules/auth/authRouter.ts +75 -0
- package/src/modules/auth/authorizationUtils.ts +132 -0
- package/src/modules/auth/devMockUserUtils.ts +49 -0
- package/src/modules/auth/registerAuthRoutes.ts +33 -0
- package/src/modules/icons/iconRestController.ts +171 -0
- package/src/modules/icons/iconRouter.ts +180 -0
- package/src/modules/icons/iconService.ts +73 -0
- package/src/modules/icons/iconUtils.ts +46 -0
- package/src/prisma-json-types.d.ts +34 -0
- package/src/server/controller.ts +103 -44
- package/src/server/ehStaticControllerContract.ts +8 -1
- package/src/server/ehTrpcContext.ts +9 -6
- package/src/server/trpcSetup.ts +89 -0
- package/src/types/backend/api.ts +1 -14
- package/src/types/backend/companySpecificBackend.ts +17 -0
- package/src/types/common/appCatalogTypes.ts +56 -10
- package/src/types/common/approvalMethodTypes.ts +149 -0
- package/src/types/common/dataRootTypes.ts +72 -10
- package/src/types/index.ts +3 -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,234 @@
|
|
|
1
|
+
|
|
2
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
PrismaClientKnownRequestError,
|
|
9
|
+
PrismaClientUnknownRequestError,
|
|
10
|
+
PrismaClientRustPanicError,
|
|
11
|
+
PrismaClientInitializationError,
|
|
12
|
+
PrismaClientValidationError,
|
|
13
|
+
getPrismaClient,
|
|
14
|
+
sqltag,
|
|
15
|
+
empty,
|
|
16
|
+
join,
|
|
17
|
+
raw,
|
|
18
|
+
skip,
|
|
19
|
+
Decimal,
|
|
20
|
+
Debug,
|
|
21
|
+
objectEnumValues,
|
|
22
|
+
makeStrictEnum,
|
|
23
|
+
Extensions,
|
|
24
|
+
warnOnce,
|
|
25
|
+
defineDmmfProperty,
|
|
26
|
+
Public,
|
|
27
|
+
getRuntime,
|
|
28
|
+
createParam,
|
|
29
|
+
} = require('./runtime/wasm-engine-edge.js')
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
const Prisma = {}
|
|
33
|
+
|
|
34
|
+
exports.Prisma = Prisma
|
|
35
|
+
exports.$Enums = {}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Prisma Client JS version: 6.17.0
|
|
39
|
+
* Query Engine version: c0aafc03b8ef6cdced8654b9a817999e02457d6a
|
|
40
|
+
*/
|
|
41
|
+
Prisma.prismaVersion = {
|
|
42
|
+
client: "6.17.0",
|
|
43
|
+
engine: "c0aafc03b8ef6cdced8654b9a817999e02457d6a"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
|
|
47
|
+
Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
|
|
48
|
+
Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
|
|
49
|
+
Prisma.PrismaClientInitializationError = PrismaClientInitializationError
|
|
50
|
+
Prisma.PrismaClientValidationError = PrismaClientValidationError
|
|
51
|
+
Prisma.Decimal = Decimal
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Re-export of sql-template-tag
|
|
55
|
+
*/
|
|
56
|
+
Prisma.sql = sqltag
|
|
57
|
+
Prisma.empty = empty
|
|
58
|
+
Prisma.join = join
|
|
59
|
+
Prisma.raw = raw
|
|
60
|
+
Prisma.validator = Public.validator
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extensions
|
|
64
|
+
*/
|
|
65
|
+
Prisma.getExtensionContext = Extensions.getExtensionContext
|
|
66
|
+
Prisma.defineExtension = Extensions.defineExtension
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Shorthand utilities for JSON filtering
|
|
70
|
+
*/
|
|
71
|
+
Prisma.DbNull = objectEnumValues.instances.DbNull
|
|
72
|
+
Prisma.JsonNull = objectEnumValues.instances.JsonNull
|
|
73
|
+
Prisma.AnyNull = objectEnumValues.instances.AnyNull
|
|
74
|
+
|
|
75
|
+
Prisma.NullTypes = {
|
|
76
|
+
DbNull: objectEnumValues.classes.DbNull,
|
|
77
|
+
JsonNull: objectEnumValues.classes.JsonNull,
|
|
78
|
+
AnyNull: objectEnumValues.classes.AnyNull
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Enums
|
|
87
|
+
*/
|
|
88
|
+
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
|
89
|
+
ReadUncommitted: 'ReadUncommitted',
|
|
90
|
+
ReadCommitted: 'ReadCommitted',
|
|
91
|
+
RepeatableRead: 'RepeatableRead',
|
|
92
|
+
Serializable: 'Serializable'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
exports.Prisma.DbAppForCatalogScalarFieldEnum = {
|
|
96
|
+
id: 'id',
|
|
97
|
+
name: 'name',
|
|
98
|
+
description: 'description',
|
|
99
|
+
access: 'access',
|
|
100
|
+
teams: 'teams',
|
|
101
|
+
roles: 'roles',
|
|
102
|
+
approverName: 'approverName',
|
|
103
|
+
approverEmail: 'approverEmail',
|
|
104
|
+
notes: 'notes',
|
|
105
|
+
tags: 'tags',
|
|
106
|
+
appUrl: 'appUrl',
|
|
107
|
+
confluenceUrl: 'confluenceUrl',
|
|
108
|
+
createdAt: 'createdAt',
|
|
109
|
+
updatedAt: 'updatedAt'
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
exports.Prisma.IconScalarFieldEnum = {
|
|
113
|
+
id: 'id',
|
|
114
|
+
name: 'name',
|
|
115
|
+
svgContent: 'svgContent',
|
|
116
|
+
pngBase64: 'pngBase64',
|
|
117
|
+
createdAt: 'createdAt',
|
|
118
|
+
updatedAt: 'updatedAt'
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
exports.Prisma.SortOrder = {
|
|
122
|
+
asc: 'asc',
|
|
123
|
+
desc: 'desc'
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
exports.Prisma.JsonNullValueInput = {
|
|
127
|
+
JsonNull: Prisma.JsonNull
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
exports.Prisma.NullableJsonNullValueInput = {
|
|
131
|
+
DbNull: Prisma.DbNull,
|
|
132
|
+
JsonNull: Prisma.JsonNull
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
exports.Prisma.QueryMode = {
|
|
136
|
+
default: 'default',
|
|
137
|
+
insensitive: 'insensitive'
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
exports.Prisma.JsonNullValueFilter = {
|
|
141
|
+
DbNull: Prisma.DbNull,
|
|
142
|
+
JsonNull: Prisma.JsonNull,
|
|
143
|
+
AnyNull: Prisma.AnyNull
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
exports.Prisma.NullsOrder = {
|
|
147
|
+
first: 'first',
|
|
148
|
+
last: 'last'
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
exports.Prisma.ModelName = {
|
|
153
|
+
DbAppForCatalog: 'DbAppForCatalog',
|
|
154
|
+
Icon: 'Icon'
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Create the Client
|
|
158
|
+
*/
|
|
159
|
+
const config = {
|
|
160
|
+
"generator": {
|
|
161
|
+
"name": "client",
|
|
162
|
+
"provider": {
|
|
163
|
+
"fromEnvVar": null,
|
|
164
|
+
"value": "prisma-client-js"
|
|
165
|
+
},
|
|
166
|
+
"output": {
|
|
167
|
+
"value": "/Users/igolovin/src/umbrella/env-hopper/packages/backend-core/src/generated/prisma",
|
|
168
|
+
"fromEnvVar": null
|
|
169
|
+
},
|
|
170
|
+
"config": {
|
|
171
|
+
"engineType": "library"
|
|
172
|
+
},
|
|
173
|
+
"binaryTargets": [
|
|
174
|
+
{
|
|
175
|
+
"fromEnvVar": null,
|
|
176
|
+
"value": "darwin-arm64",
|
|
177
|
+
"native": true
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"previewFeatures": [],
|
|
181
|
+
"sourceFilePath": "/Users/igolovin/src/umbrella/env-hopper/packages/backend-core/prisma/schema.prisma",
|
|
182
|
+
"isCustomOutput": true
|
|
183
|
+
},
|
|
184
|
+
"relativeEnvPaths": {
|
|
185
|
+
"rootEnvPath": null
|
|
186
|
+
},
|
|
187
|
+
"relativePath": "../../../prisma",
|
|
188
|
+
"clientVersion": "6.17.0",
|
|
189
|
+
"engineVersion": "c0aafc03b8ef6cdced8654b9a817999e02457d6a",
|
|
190
|
+
"datasourceNames": [
|
|
191
|
+
"db"
|
|
192
|
+
],
|
|
193
|
+
"activeProvider": "postgresql",
|
|
194
|
+
"postinstall": false,
|
|
195
|
+
"inlineDatasources": {
|
|
196
|
+
"db": {
|
|
197
|
+
"url": {
|
|
198
|
+
"fromEnvVar": "EH_CORE_DATABASE_URL",
|
|
199
|
+
"value": null
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"inlineSchema": "// Prisma schema for backend-core library\n// Uses EH_CORE_DATABASE_URL environment variable\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"EH_CORE_DATABASE_URL\")\n}\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"../src/generated/prisma\"\n}\n\ngenerator json {\n provider = \"prisma-json-types-generator\"\n}\n\nmodel DbAppForCatalog {\n id String @id @default(cuid())\n name String\n description String\n /// [AccessMethod]\n access Json\n teams String[] @default([])\n /// [AppRole]\n roles Json?\n approverName String?\n approverEmail String?\n notes String?\n tags String[] @default([])\n appUrl String?\n confluenceUrl String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([name])\n @@index([tags])\n}\n\nmodel Icon {\n id String @id @default(cuid())\n name String @unique\n svgContent String? @db.Text\n pngBase64 String? @db.Text\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([name])\n}\n",
|
|
204
|
+
"inlineSchemaHash": "b83fbbc0d3c25dcdf35d0f9145415f8093b7124bc747546875740feee17ef2de",
|
|
205
|
+
"copyEngine": true
|
|
206
|
+
}
|
|
207
|
+
config.dirname = '/'
|
|
208
|
+
|
|
209
|
+
config.runtimeDataModel = JSON.parse("{\"models\":{\"DbAppForCatalog\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"access\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"teams\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"roles\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"approverName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"approverEmail\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"notes\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tags\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appUrl\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"confluenceUrl\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Icon\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"svgContent\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"pngBase64\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
|
210
|
+
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
|
|
211
|
+
config.engineWasm = {
|
|
212
|
+
getRuntime: async () => require('./query_engine_bg.js'),
|
|
213
|
+
getQueryEngineWasmModule: async () => {
|
|
214
|
+
const loader = (await import('#wasm-engine-loader')).default
|
|
215
|
+
const engine = (await loader).default
|
|
216
|
+
return engine
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
config.compilerWasm = undefined
|
|
220
|
+
|
|
221
|
+
config.injectableEdgeEnv = () => ({
|
|
222
|
+
parsed: {
|
|
223
|
+
EH_CORE_DATABASE_URL: typeof globalThis !== 'undefined' && globalThis['EH_CORE_DATABASE_URL'] || typeof process !== 'undefined' && process.env && process.env.EH_CORE_DATABASE_URL || undefined
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
if (typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined) {
|
|
228
|
+
Debug.enable(typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const PrismaClient = getPrismaClient(config)
|
|
232
|
+
exports.PrismaClient = PrismaClient
|
|
233
|
+
Object.assign(exports, Prisma)
|
|
234
|
+
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// common
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { createTrpcRouter } from './server/controller'
|
|
4
4
|
export type { TRPCRouter } from './server/controller'
|
|
5
5
|
export { createEhTrpcContext } from './server/ehTrpcContext'
|
|
6
6
|
export type {
|
|
@@ -15,4 +15,112 @@ export type { EhStaticControllerContract } from './server/ehStaticControllerCont
|
|
|
15
15
|
|
|
16
16
|
// backend-only
|
|
17
17
|
|
|
18
|
+
export type { AppForCatalog } from './types/common/appCatalogTypes'
|
|
18
19
|
export * from './types/index'
|
|
20
|
+
|
|
21
|
+
// Auth
|
|
22
|
+
export {
|
|
23
|
+
createAuth,
|
|
24
|
+
type AuthConfig,
|
|
25
|
+
type BetterAuth,
|
|
26
|
+
} from './modules/auth/auth'
|
|
27
|
+
|
|
28
|
+
export { registerAuthRoutes } from './modules/auth/registerAuthRoutes'
|
|
29
|
+
|
|
30
|
+
export { createAuthRouter, type AuthRouter } from './modules/auth/authRouter'
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
getUserGroups,
|
|
34
|
+
isMemberOfAnyGroup,
|
|
35
|
+
isMemberOfAllGroups,
|
|
36
|
+
isAdmin,
|
|
37
|
+
requireAdmin,
|
|
38
|
+
requireGroups,
|
|
39
|
+
type UserWithGroups,
|
|
40
|
+
} from './modules/auth/authorizationUtils'
|
|
41
|
+
|
|
42
|
+
// Admin
|
|
43
|
+
export {
|
|
44
|
+
createAdminChatHandler,
|
|
45
|
+
tool,
|
|
46
|
+
type AdminChatHandlerOptions,
|
|
47
|
+
} from './modules/admin/chat/createAdminChatHandler'
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
createDatabaseTools,
|
|
51
|
+
createPrismaDatabaseClient,
|
|
52
|
+
DEFAULT_ADMIN_SYSTEM_PROMPT,
|
|
53
|
+
type DatabaseClient,
|
|
54
|
+
} from './modules/admin/chat/createDatabaseTools'
|
|
55
|
+
|
|
56
|
+
// Icon management
|
|
57
|
+
export {
|
|
58
|
+
registerIconRestController,
|
|
59
|
+
type IconRestControllerConfig,
|
|
60
|
+
} from './modules/icons/iconRestController'
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
getAssetByName,
|
|
64
|
+
upsertIcon,
|
|
65
|
+
upsertIcons,
|
|
66
|
+
type UpsertIconInput,
|
|
67
|
+
} from './modules/icons/iconService'
|
|
68
|
+
|
|
69
|
+
// Asset management (universal for icons, screenshots, etc.)
|
|
70
|
+
export {
|
|
71
|
+
registerAssetRestController,
|
|
72
|
+
type AssetRestControllerConfig,
|
|
73
|
+
} from './modules/assets/assetRestController'
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
registerScreenshotRestController,
|
|
77
|
+
type ScreenshotRestControllerConfig,
|
|
78
|
+
} from './modules/assets/screenshotRestController'
|
|
79
|
+
|
|
80
|
+
export { createScreenshotRouter } from './modules/assets/screenshotRouter'
|
|
81
|
+
|
|
82
|
+
export { syncAssets, type SyncAssetsConfig } from './modules/assets/syncAssets'
|
|
83
|
+
|
|
84
|
+
// App Catalog Admin
|
|
85
|
+
export { createAppCatalogAdminRouter } from './modules/appCatalogAdmin/appCatalogAdminRouter'
|
|
86
|
+
|
|
87
|
+
// Approval Methods
|
|
88
|
+
export { createApprovalMethodRouter } from './modules/approvalMethod/approvalMethodRouter'
|
|
89
|
+
export {
|
|
90
|
+
syncApprovalMethods,
|
|
91
|
+
type ApprovalMethodSyncInput,
|
|
92
|
+
} from './modules/approvalMethod/syncApprovalMethods'
|
|
93
|
+
|
|
94
|
+
// Database utilities
|
|
95
|
+
export {
|
|
96
|
+
connectDb,
|
|
97
|
+
disconnectDb,
|
|
98
|
+
getDbClient,
|
|
99
|
+
setDbClient,
|
|
100
|
+
syncAppCatalog,
|
|
101
|
+
TABLE_SYNC_MAGAZINE,
|
|
102
|
+
tableSyncPrisma,
|
|
103
|
+
type MakeTFromPrismaModel,
|
|
104
|
+
type ObjectKeys,
|
|
105
|
+
type ScalarFilter,
|
|
106
|
+
type ScalarKeys,
|
|
107
|
+
type SyncAppCatalogResult,
|
|
108
|
+
type TableSyncMagazine,
|
|
109
|
+
type TableSyncMagazineModelNameKey,
|
|
110
|
+
type TableSyncParamsPrisma,
|
|
111
|
+
} from './db'
|
|
112
|
+
|
|
113
|
+
// Middleware (batteries-included backend setup)
|
|
114
|
+
export {
|
|
115
|
+
createEhMiddleware,
|
|
116
|
+
EhDatabaseManager,
|
|
117
|
+
type EhDatabaseConfig,
|
|
118
|
+
type EhAuthConfig,
|
|
119
|
+
type EhAdminChatConfig,
|
|
120
|
+
type EhFeatureToggles,
|
|
121
|
+
type EhBackendProvider,
|
|
122
|
+
type EhLifecycleHooks,
|
|
123
|
+
type EhMiddlewareOptions,
|
|
124
|
+
type EhMiddlewareResult,
|
|
125
|
+
type MiddlewareContext,
|
|
126
|
+
} from './middleware'
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { EhBackendCompanySpecificBackend } from '../types/backend/companySpecificBackend'
|
|
2
|
+
import type { EhBackendProvider } from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Type guard to check if an object implements EhBackendCompanySpecificBackend.
|
|
6
|
+
*/
|
|
7
|
+
function isBackendInstance(obj: unknown): obj is EhBackendCompanySpecificBackend {
|
|
8
|
+
return (
|
|
9
|
+
typeof obj === 'object' &&
|
|
10
|
+
obj !== null &&
|
|
11
|
+
typeof (obj as EhBackendCompanySpecificBackend).getBootstrapData === 'function' &&
|
|
12
|
+
typeof (obj as EhBackendCompanySpecificBackend).getAvailabilityMatrix ===
|
|
13
|
+
'function' &&
|
|
14
|
+
typeof (obj as EhBackendCompanySpecificBackend).getNameMigrations ===
|
|
15
|
+
'function' &&
|
|
16
|
+
typeof (obj as EhBackendCompanySpecificBackend).getResourceJumps ===
|
|
17
|
+
'function' &&
|
|
18
|
+
typeof (obj as EhBackendCompanySpecificBackend).getResourceJumpsExtended ===
|
|
19
|
+
'function'
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Normalizes different backend provider types into a consistent async factory function.
|
|
25
|
+
* Supports:
|
|
26
|
+
* - Direct object implementing EhBackendCompanySpecificBackend
|
|
27
|
+
* - Sync factory function that returns the backend
|
|
28
|
+
* - Async factory function that returns the backend
|
|
29
|
+
*/
|
|
30
|
+
export function createBackendResolver(
|
|
31
|
+
provider: EhBackendProvider,
|
|
32
|
+
): () => Promise<EhBackendCompanySpecificBackend> {
|
|
33
|
+
// If it's already an object with the required methods, wrap it
|
|
34
|
+
if (isBackendInstance(provider)) {
|
|
35
|
+
return async () => provider
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If it's a function, call it and handle both sync and async results
|
|
39
|
+
if (typeof provider === 'function') {
|
|
40
|
+
return async () => {
|
|
41
|
+
const result = provider()
|
|
42
|
+
return result instanceof Promise ? result : result
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
throw new Error(
|
|
47
|
+
'Invalid backend provider: must be an object implementing EhBackendCompanySpecificBackend or a factory function',
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import express, { Router } from 'express'
|
|
2
|
+
import * as trpcExpress from '@trpc/server/adapters/express'
|
|
3
|
+
import type {
|
|
4
|
+
EhMiddlewareOptions,
|
|
5
|
+
EhMiddlewareResult,
|
|
6
|
+
MiddlewareContext,
|
|
7
|
+
} from './types'
|
|
8
|
+
import { EhDatabaseManager } from './database'
|
|
9
|
+
import { createBackendResolver } from './backendResolver'
|
|
10
|
+
import { registerFeatures } from './featureRegistry'
|
|
11
|
+
import { createTrpcRouter } from '../server/controller'
|
|
12
|
+
import { createEhTrpcContext } from '../server/ehTrpcContext'
|
|
13
|
+
import { createAuth } from '../modules/auth/auth'
|
|
14
|
+
import { createMockUserFromDevConfig } from '../modules/auth/devMockUserUtils'
|
|
15
|
+
|
|
16
|
+
export async function createEhMiddleware(
|
|
17
|
+
options: EhMiddlewareOptions,
|
|
18
|
+
): Promise<EhMiddlewareResult> {
|
|
19
|
+
// Normalize options with defaults
|
|
20
|
+
const basePath = options.basePath ?? '/api'
|
|
21
|
+
const normalizedOptions = { ...options, basePath }
|
|
22
|
+
|
|
23
|
+
// Initialize database manager
|
|
24
|
+
const dbManager = new EhDatabaseManager(options.database)
|
|
25
|
+
// Initialize the client (which also sets the global singleton)
|
|
26
|
+
dbManager.getClient()
|
|
27
|
+
|
|
28
|
+
// Create auth instance
|
|
29
|
+
const auth = createAuth({
|
|
30
|
+
appName: options.auth.appName,
|
|
31
|
+
baseURL: options.auth.baseURL,
|
|
32
|
+
secret: options.auth.secret,
|
|
33
|
+
providers: options.auth.providers,
|
|
34
|
+
plugins: options.auth.plugins,
|
|
35
|
+
sessionExpiresIn: options.auth.sessionExpiresIn,
|
|
36
|
+
sessionUpdateAge: options.auth.sessionUpdateAge,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Create tRPC router
|
|
40
|
+
const trpcRouter = createTrpcRouter(auth)
|
|
41
|
+
|
|
42
|
+
// Normalize backend provider to async factory function
|
|
43
|
+
const resolveBackend = createBackendResolver(options.backend)
|
|
44
|
+
|
|
45
|
+
// Get admin groups from config with default
|
|
46
|
+
const adminGroups = options.auth.adminGroups ?? ['env_hopper_ui_super_admins']
|
|
47
|
+
|
|
48
|
+
// Create tRPC context factory
|
|
49
|
+
const createContext = async ({
|
|
50
|
+
req,
|
|
51
|
+
}: trpcExpress.CreateExpressContextOptions) => {
|
|
52
|
+
const companySpecificBackend = await resolveBackend()
|
|
53
|
+
|
|
54
|
+
let user = null
|
|
55
|
+
let userGroups: Array<string> = []
|
|
56
|
+
|
|
57
|
+
// Check if dev mock user is configured
|
|
58
|
+
if (options.auth.devMockUser) {
|
|
59
|
+
user = createMockUserFromDevConfig(options.auth.devMockUser)
|
|
60
|
+
userGroups = options.auth.devMockUser.groups
|
|
61
|
+
} else {
|
|
62
|
+
// Extract user from session
|
|
63
|
+
try {
|
|
64
|
+
const session = await auth.api.getSession({
|
|
65
|
+
headers: req.headers as HeadersInit,
|
|
66
|
+
})
|
|
67
|
+
user = session?.user ?? null
|
|
68
|
+
|
|
69
|
+
// If user is authenticated and Okta is configured, decode groups from access token
|
|
70
|
+
if (user && options.auth.oktaGroupsClaim) {
|
|
71
|
+
try {
|
|
72
|
+
// Get the current access token (auto-refreshes if expired)
|
|
73
|
+
// Note: better-auth requires providerId, but we use 'okta' as default
|
|
74
|
+
const tokenResult = await auth.api.getAccessToken({
|
|
75
|
+
body: {
|
|
76
|
+
providerId: 'okta',
|
|
77
|
+
},
|
|
78
|
+
headers: req.headers as HeadersInit,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
if (tokenResult.accessToken) {
|
|
82
|
+
// Decode JWT to extract groups claim
|
|
83
|
+
const parts = tokenResult.accessToken.split('.')
|
|
84
|
+
if (parts.length === 3 && parts[1]) {
|
|
85
|
+
const payload = JSON.parse(
|
|
86
|
+
Buffer.from(parts[1], 'base64').toString(),
|
|
87
|
+
)
|
|
88
|
+
const groups = payload[options.auth.oktaGroupsClaim]
|
|
89
|
+
userGroups = Array.isArray(groups) ? groups : []
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('[tRPC Context] Failed to get access token:', error)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('[tRPC Context] Failed to get session:', error)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Attach groups to user object for authorization checks
|
|
102
|
+
const userWithGroups = user ? { ...user, groups: userGroups } : null
|
|
103
|
+
|
|
104
|
+
return createEhTrpcContext({
|
|
105
|
+
companySpecificBackend,
|
|
106
|
+
user: userWithGroups,
|
|
107
|
+
adminGroups,
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create Express router
|
|
112
|
+
const router = Router()
|
|
113
|
+
router.use(express.json())
|
|
114
|
+
|
|
115
|
+
// Build middleware context for feature registration
|
|
116
|
+
const middlewareContext: MiddlewareContext = {
|
|
117
|
+
auth,
|
|
118
|
+
trpcRouter,
|
|
119
|
+
createContext: async () => {
|
|
120
|
+
const companySpecificBackend = await resolveBackend()
|
|
121
|
+
return createEhTrpcContext({
|
|
122
|
+
companySpecificBackend,
|
|
123
|
+
adminGroups,
|
|
124
|
+
})
|
|
125
|
+
},
|
|
126
|
+
authConfig: options.auth,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Register tRPC middleware (if enabled)
|
|
130
|
+
if (normalizedOptions.features?.trpc !== false) {
|
|
131
|
+
router.use(
|
|
132
|
+
`${basePath}/trpc`,
|
|
133
|
+
trpcExpress.createExpressMiddleware({
|
|
134
|
+
router: trpcRouter,
|
|
135
|
+
createContext,
|
|
136
|
+
}),
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Register all enabled features
|
|
141
|
+
registerFeatures(router, normalizedOptions, middlewareContext)
|
|
142
|
+
|
|
143
|
+
// Call onRoutesRegistered hook if provided
|
|
144
|
+
if (options.hooks?.onRoutesRegistered) {
|
|
145
|
+
await options.hooks.onRoutesRegistered(router)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
router,
|
|
150
|
+
auth,
|
|
151
|
+
trpcRouter,
|
|
152
|
+
|
|
153
|
+
async connect(): Promise<void> {
|
|
154
|
+
await dbManager.connect()
|
|
155
|
+
if (options.hooks?.onDatabaseConnected) {
|
|
156
|
+
await options.hooks.onDatabaseConnected()
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
async disconnect(): Promise<void> {
|
|
161
|
+
if (options.hooks?.onDatabaseDisconnecting) {
|
|
162
|
+
await options.hooks.onDatabaseDisconnecting()
|
|
163
|
+
}
|
|
164
|
+
await dbManager.disconnect()
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
addRoutes(callback: (router: Router) => void): void {
|
|
168
|
+
callback(router)
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client'
|
|
2
|
+
import type { EhDatabaseConfig } from './types'
|
|
3
|
+
import { setDbClient } from '../db/client'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Formats a database connection URL from structured config.
|
|
7
|
+
*/
|
|
8
|
+
function formatConnectionUrl(config: EhDatabaseConfig): string {
|
|
9
|
+
if ('url' in config) {
|
|
10
|
+
return config.url
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { host, port, database, username, password, schema = 'public' } = config
|
|
14
|
+
return `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}?schema=${schema}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Internal database manager used by the middleware.
|
|
19
|
+
* Handles connection URL formatting and lifecycle.
|
|
20
|
+
*/
|
|
21
|
+
export class EhDatabaseManager {
|
|
22
|
+
private client: PrismaClient | null = null
|
|
23
|
+
private config: EhDatabaseConfig
|
|
24
|
+
|
|
25
|
+
constructor(config: EhDatabaseConfig) {
|
|
26
|
+
this.config = config
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get or create the Prisma client instance.
|
|
31
|
+
* Uses lazy initialization for flexibility.
|
|
32
|
+
*/
|
|
33
|
+
getClient(): PrismaClient {
|
|
34
|
+
if (!this.client) {
|
|
35
|
+
const datasourceUrl = formatConnectionUrl(this.config)
|
|
36
|
+
|
|
37
|
+
this.client = new PrismaClient({
|
|
38
|
+
datasourceUrl,
|
|
39
|
+
log:
|
|
40
|
+
process.env.NODE_ENV === 'development'
|
|
41
|
+
? ['warn', 'error']
|
|
42
|
+
: ['warn', 'error'],
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Bridge with existing backend-core getDbClient() usage
|
|
46
|
+
setDbClient(this.client)
|
|
47
|
+
}
|
|
48
|
+
return this.client
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async connect(): Promise<void> {
|
|
52
|
+
const client = this.getClient()
|
|
53
|
+
await client.$connect()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async disconnect(): Promise<void> {
|
|
57
|
+
if (this.client) {
|
|
58
|
+
await this.client.$disconnect()
|
|
59
|
+
this.client = null
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|