@appweaver/core 1.0.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.
Files changed (271) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +7 -0
  3. package/app/application.d.ts +43 -0
  4. package/app/application.js +112 -0
  5. package/app/create-app.d.ts +32 -0
  6. package/app/create-app.js +38 -0
  7. package/app/index.d.ts +2 -0
  8. package/app/index.js +18 -0
  9. package/app/lifecycle-manager.d.ts +20 -0
  10. package/app/lifecycle-manager.js +42 -0
  11. package/app/load-modules.d.ts +10 -0
  12. package/app/load-modules.js +50 -0
  13. package/app/load-providers.d.ts +8 -0
  14. package/app/load-providers.js +33 -0
  15. package/cache/cache-service.d.ts +72 -0
  16. package/cache/cache-service.js +202 -0
  17. package/cache/cache.d.ts +12 -0
  18. package/cache/cache.js +184 -0
  19. package/cache/caching.d.ts +8 -0
  20. package/cache/caching.js +72 -0
  21. package/cache/eviction/eviction-index.d.ts +35 -0
  22. package/cache/eviction/eviction-index.js +2 -0
  23. package/cache/eviction/fifo-eviction-index.d.ts +7 -0
  24. package/cache/eviction/fifo-eviction-index.js +28 -0
  25. package/cache/eviction/index.d.ts +4 -0
  26. package/cache/eviction/index.js +20 -0
  27. package/cache/eviction/lfu-eviction-index.d.ts +8 -0
  28. package/cache/eviction/lfu-eviction-index.js +82 -0
  29. package/cache/eviction/lru-eviction-index.d.ts +8 -0
  30. package/cache/eviction/lru-eviction-index.js +81 -0
  31. package/cache/index.d.ts +2 -0
  32. package/cache/index.js +18 -0
  33. package/cache/memory-cache.d.ts +4 -0
  34. package/cache/memory-cache.js +12 -0
  35. package/cache/redis-cache.d.ts +4 -0
  36. package/cache/redis-cache.js +12 -0
  37. package/context/context.d.ts +2 -0
  38. package/context/context.js +13 -0
  39. package/context/dependency-injection.d.ts +98 -0
  40. package/context/dependency-injection.js +276 -0
  41. package/context/index.d.ts +2 -0
  42. package/context/index.js +18 -0
  43. package/database/create-client.d.ts +11 -0
  44. package/database/create-client.js +142 -0
  45. package/database/index.d.ts +1 -0
  46. package/database/index.js +17 -0
  47. package/database/prisma-database.d.ts +15 -0
  48. package/database/prisma-database.js +53 -0
  49. package/errors/error-handler.d.ts +3 -0
  50. package/errors/error-handler.js +24 -0
  51. package/errors/error-schema.d.ts +32 -0
  52. package/errors/error-schema.js +31 -0
  53. package/errors/http-error.d.ts +21 -0
  54. package/errors/http-error.js +33 -0
  55. package/errors/index.d.ts +3 -0
  56. package/errors/index.js +19 -0
  57. package/events/node-events.d.ts +7 -0
  58. package/events/node-events.js +39 -0
  59. package/export/export-service.d.ts +19 -0
  60. package/export/export-service.js +154 -0
  61. package/export/index.d.ts +1 -0
  62. package/export/index.js +17 -0
  63. package/factory/create-model.d.ts +2 -0
  64. package/factory/create-model.js +366 -0
  65. package/factory/create-policy.d.ts +2 -0
  66. package/factory/create-policy.js +16 -0
  67. package/factory/create-routes.d.ts +2 -0
  68. package/factory/create-routes.js +21 -0
  69. package/factory/create-service.d.ts +3 -0
  70. package/factory/create-service.js +97 -0
  71. package/factory/index.d.ts +4 -0
  72. package/factory/index.js +20 -0
  73. package/health/health-routes.d.ts +2 -0
  74. package/health/health-routes.js +36 -0
  75. package/health/health-schema.d.ts +45 -0
  76. package/health/health-schema.js +52 -0
  77. package/health/health-service.d.ts +38 -0
  78. package/health/health-service.js +82 -0
  79. package/health/index.d.ts +2 -0
  80. package/health/index.js +18 -0
  81. package/index.d.ts +15 -0
  82. package/index.js +31 -0
  83. package/mailer/email-service.d.ts +36 -0
  84. package/mailer/email-service.js +89 -0
  85. package/mailer/index.d.ts +1 -0
  86. package/mailer/index.js +17 -0
  87. package/mailer/json-mailer.d.ts +7 -0
  88. package/mailer/json-mailer.js +32 -0
  89. package/mailer/smtp-mailer.d.ts +7 -0
  90. package/mailer/smtp-mailer.js +43 -0
  91. package/memory/in-memory.d.ts +24 -0
  92. package/memory/in-memory.js +164 -0
  93. package/memory/redis.d.ts +26 -0
  94. package/memory/redis.js +158 -0
  95. package/package.json +68 -0
  96. package/prisma/client/browser.d.ts +34 -0
  97. package/prisma/client/browser.js +56 -0
  98. package/prisma/client/client.d.ts +53 -0
  99. package/prisma/client/client.js +70 -0
  100. package/prisma/client/commonInputTypes.d.ts +395 -0
  101. package/prisma/client/commonInputTypes.js +11 -0
  102. package/prisma/client/enums.d.ts +1 -0
  103. package/prisma/client/enums.js +11 -0
  104. package/prisma/client/internal/class.d.ts +187 -0
  105. package/prisma/client/internal/class.js +85 -0
  106. package/prisma/client/internal/prismaNamespace.d.ts +987 -0
  107. package/prisma/client/internal/prismaNamespace.js +193 -0
  108. package/prisma/client/internal/prismaNamespaceBrowser.d.ts +125 -0
  109. package/prisma/client/internal/prismaNamespaceBrowser.js +164 -0
  110. package/prisma/client/models/ApiKey.d.ts +1167 -0
  111. package/prisma/client/models/ApiKey.js +2 -0
  112. package/prisma/client/models/File.d.ts +1248 -0
  113. package/prisma/client/models/File.js +2 -0
  114. package/prisma/client/models/OneTimeToken.d.ts +1083 -0
  115. package/prisma/client/models/OneTimeToken.js +2 -0
  116. package/prisma/client/models/Permission.d.ts +1209 -0
  117. package/prisma/client/models/Permission.js +2 -0
  118. package/prisma/client/models/Role.d.ts +1209 -0
  119. package/prisma/client/models/Role.js +2 -0
  120. package/prisma/client/models/Seeder.d.ts +1023 -0
  121. package/prisma/client/models/Seeder.js +2 -0
  122. package/prisma/client/models.d.ts +7 -0
  123. package/prisma/client/models.js +2 -0
  124. package/queue/bull-queue.d.ts +37 -0
  125. package/queue/bull-queue.js +207 -0
  126. package/queue/memory-queue.d.ts +54 -0
  127. package/queue/memory-queue.js +283 -0
  128. package/resource/index.d.ts +4 -0
  129. package/resource/index.js +20 -0
  130. package/resource/resource-loader.d.ts +26 -0
  131. package/resource/resource-loader.js +161 -0
  132. package/resource/resource-routes.d.ts +3 -0
  133. package/resource/resource-routes.js +174 -0
  134. package/resource/resource-schema.d.ts +33 -0
  135. package/resource/resource-schema.js +186 -0
  136. package/resource/resource-service.d.ts +89 -0
  137. package/resource/resource-service.js +772 -0
  138. package/resources.d.ts +9 -0
  139. package/resources.js +24 -0
  140. package/scheduler/cron-scheduler.d.ts +12 -0
  141. package/scheduler/cron-scheduler.js +73 -0
  142. package/security/account/account-routes.d.ts +2 -0
  143. package/security/account/account-routes.js +104 -0
  144. package/security/account/account-schema.d.ts +290 -0
  145. package/security/account/account-schema.js +166 -0
  146. package/security/account/account-service.d.ts +88 -0
  147. package/security/account/account-service.js +218 -0
  148. package/security/account/index.d.ts +3 -0
  149. package/security/account/index.js +19 -0
  150. package/security/api-key/api-key-auth.d.ts +4 -0
  151. package/security/api-key/api-key-auth.js +66 -0
  152. package/security/api-key/index.d.ts +1 -0
  153. package/security/api-key/index.js +17 -0
  154. package/security/auth-routes.d.ts +2 -0
  155. package/security/auth-routes.js +80 -0
  156. package/security/auth-schema.d.ts +215 -0
  157. package/security/auth-schema.js +129 -0
  158. package/security/auth-service.d.ts +117 -0
  159. package/security/auth-service.js +272 -0
  160. package/security/auth.d.ts +3 -0
  161. package/security/auth.js +127 -0
  162. package/security/basic/basic-auth.d.ts +4 -0
  163. package/security/basic/basic-auth.js +32 -0
  164. package/security/basic/index.d.ts +1 -0
  165. package/security/basic/index.js +17 -0
  166. package/security/create-auth-resources.d.ts +6 -0
  167. package/security/create-auth-resources.js +129 -0
  168. package/security/helper.d.ts +127 -0
  169. package/security/helper.js +329 -0
  170. package/security/index.d.ts +4 -0
  171. package/security/index.js +20 -0
  172. package/security/jwt/index.d.ts +2 -0
  173. package/security/jwt/index.js +18 -0
  174. package/security/jwt/jwt-auth.d.ts +4 -0
  175. package/security/jwt/jwt-auth.js +57 -0
  176. package/security/jwt/jwt-keys.d.ts +7 -0
  177. package/security/jwt/jwt-keys.js +43 -0
  178. package/security/oauth2/create-oauth2-plugin.d.ts +17 -0
  179. package/security/oauth2/create-oauth2-plugin.js +85 -0
  180. package/security/oauth2/index.d.ts +3 -0
  181. package/security/oauth2/index.js +19 -0
  182. package/security/oauth2/oauth2-custom.d.ts +7 -0
  183. package/security/oauth2/oauth2-custom.js +40 -0
  184. package/security/oauth2/oauth2-facebook.d.ts +1 -0
  185. package/security/oauth2/oauth2-facebook.js +30 -0
  186. package/security/oauth2/oauth2-google.d.ts +1 -0
  187. package/security/oauth2/oauth2-google.js +28 -0
  188. package/security/oauth2/oauth2-schema.d.ts +10 -0
  189. package/security/oauth2/oauth2-schema.js +53 -0
  190. package/security/recaptcha/index.d.ts +2 -0
  191. package/security/recaptcha/index.js +18 -0
  192. package/security/recaptcha/recaptcha-verify.d.ts +1 -0
  193. package/security/recaptcha/recaptcha-verify.js +33 -0
  194. package/security/recaptcha/recaptcha.d.ts +2 -0
  195. package/security/recaptcha/recaptcha.js +22 -0
  196. package/security/resources/api-key/model.d.ts +2 -0
  197. package/security/resources/api-key/model.js +63 -0
  198. package/security/resources/api-key/policy.d.ts +2 -0
  199. package/security/resources/api-key/policy.js +26 -0
  200. package/security/resources/api-key/routes.d.ts +2 -0
  201. package/security/resources/api-key/routes.js +15 -0
  202. package/security/resources/api-key/service.d.ts +3 -0
  203. package/security/resources/api-key/service.js +33 -0
  204. package/security/resources/one-time-token/model.d.ts +2 -0
  205. package/security/resources/one-time-token/model.js +32 -0
  206. package/security/resources/permission/model.d.ts +2 -0
  207. package/security/resources/permission/model.js +16 -0
  208. package/security/resources/role/model.d.ts +2 -0
  209. package/security/resources/role/model.js +22 -0
  210. package/security/store/database-security-store.d.ts +5 -0
  211. package/security/store/database-security-store.js +51 -0
  212. package/security/store/redis-security-store.d.ts +5 -0
  213. package/security/store/redis-security-store.js +32 -0
  214. package/seeder/create-seeder.d.ts +21 -0
  215. package/seeder/create-seeder.js +17 -0
  216. package/seeder/index.d.ts +2 -0
  217. package/seeder/index.js +18 -0
  218. package/seeder/resources/seeder/model.d.ts +2 -0
  219. package/seeder/resources/seeder/model.js +38 -0
  220. package/seeder/seeder.d.ts +20 -0
  221. package/seeder/seeder.js +175 -0
  222. package/server/create-server.d.ts +20 -0
  223. package/server/create-server.js +160 -0
  224. package/server/index.d.ts +4 -0
  225. package/server/index.js +20 -0
  226. package/server/info-route.d.ts +2 -0
  227. package/server/info-route.js +18 -0
  228. package/server/info-schema.d.ts +37 -0
  229. package/server/info-schema.js +19 -0
  230. package/server/register-model.d.ts +2 -0
  231. package/server/register-model.js +12 -0
  232. package/server/register-plugin.d.ts +2 -0
  233. package/server/register-plugin.js +13 -0
  234. package/server/register-route.d.ts +3 -0
  235. package/server/register-route.js +74 -0
  236. package/server/schemas.d.ts +3 -0
  237. package/server/schemas.js +30 -0
  238. package/server/swagger.d.ts +3 -0
  239. package/server/swagger.js +122 -0
  240. package/storage/file-route.d.ts +2 -0
  241. package/storage/file-route.js +39 -0
  242. package/storage/file-schema.d.ts +9 -0
  243. package/storage/file-schema.js +42 -0
  244. package/storage/file-service.d.ts +81 -0
  245. package/storage/file-service.js +384 -0
  246. package/storage/filesystem-storage.d.ts +10 -0
  247. package/storage/filesystem-storage.js +128 -0
  248. package/storage/index.d.ts +2 -0
  249. package/storage/index.js +18 -0
  250. package/storage/resources/file/model.d.ts +2 -0
  251. package/storage/resources/file/model.js +75 -0
  252. package/types/application-context.d.ts +20 -0
  253. package/types/application-context.js +2 -0
  254. package/types/auth.d.ts +31 -0
  255. package/types/auth.js +2 -0
  256. package/types/fastify-extensions.d.ts +24 -0
  257. package/types/fastify-extensions.js +4 -0
  258. package/types/generated.d.ts +193 -0
  259. package/types/generated.js +3 -0
  260. package/types/index.d.ts +4 -0
  261. package/types/index.js +20 -0
  262. package/types/server.d.ts +4 -0
  263. package/types/server.js +2 -0
  264. package/utils/export-util.d.ts +8 -0
  265. package/utils/export-util.js +25 -0
  266. package/utils/file-util.d.ts +64 -0
  267. package/utils/file-util.js +141 -0
  268. package/utils/index.d.ts +3 -0
  269. package/utils/index.js +19 -0
  270. package/utils/schema-util.d.ts +20 -0
  271. package/utils/schema-util.js +37 -0
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadResources = loadResources;
4
+ const typebox_1 = require("@sinclair/typebox");
5
+ const common_1 = require("@appweaver/common");
6
+ /**
7
+ * Loads application resources including models, services, policies, and routes.
8
+ *
9
+ * @param {string} [baseDir] - Optional base directory path from which to load the resources.
10
+ * If not provided, defaults to the application's root directory.
11
+ * @param {LoadResourcePaths} [paths={}] Path patterns for loading resources: models, services, policies, and routes.
12
+ * @return {Promise<ResourceContext>} A promise that resolves to an object containing
13
+ * the loaded resources:
14
+ *
15
+ * - `models`: The application models.
16
+ * - `services`: The application services.
17
+ * - `policies`: The access policies.
18
+ * - `routes`: The application routes.
19
+ */
20
+ async function loadResources(baseDir, paths = {}) {
21
+ const models = await loadModels(baseDir, paths.modelPattern);
22
+ const services = await loadServices(baseDir, paths.servicePattern);
23
+ const policies = await loadPolicies(baseDir, paths.policyPattern);
24
+ const routes = await loadRoutes(baseDir, paths.routePattern);
25
+ return {
26
+ models: new Map(Object.entries(models)),
27
+ services: new Map(Object.entries(services)),
28
+ policies: new Map(Object.entries(policies)),
29
+ routes: new Map(Object.entries(routes))
30
+ };
31
+ }
32
+ async function loadModels(baseDir, modelPattern) {
33
+ const cwd = baseDir ?? process.cwd();
34
+ const pathPattern = modelPattern || common_1.config.RESOURCE_MODEL_PATTERN;
35
+ const models = {};
36
+ const modelPaths = await findAllFiles(pathPattern, cwd);
37
+ for (const path of modelPaths) {
38
+ const modelSchema = await importPath(path);
39
+ if (!modelSchema) {
40
+ continue;
41
+ }
42
+ if ((0, common_1.isResourceModel)(modelSchema)) {
43
+ models[modelSchema.name] = modelSchema;
44
+ }
45
+ else {
46
+ for (const maybeSchema of Object.values(modelSchema)) {
47
+ if ((0, common_1.isResourceModel)(maybeSchema)) {
48
+ models[maybeSchema.name] = maybeSchema;
49
+ }
50
+ }
51
+ }
52
+ }
53
+ // Map model variants to schemas using their corresponding suffixes
54
+ const resourceModels = {};
55
+ for (const model of Object.values(models)) {
56
+ for (const [suffix, property] of Object.entries(common_1.resourceModelProps)) {
57
+ resourceModels[`${model.name}${suffix}`] = model[property];
58
+ }
59
+ }
60
+ // Resolve all model references through the TypeBox module system
61
+ const module = typebox_1.Type.Module(resourceModels);
62
+ // Apply resolved models back to the resource schema definitions
63
+ for (const model of Object.values(models)) {
64
+ for (const [suffix, property] of Object.entries(common_1.resourceModelProps)) {
65
+ const schemaKey = `${model.name}${suffix}`;
66
+ const importedModel = module.Import(schemaKey);
67
+ model[property] = importedModel;
68
+ }
69
+ }
70
+ return models;
71
+ }
72
+ async function loadServices(baseDir, servicePattern) {
73
+ const cwd = baseDir ?? process.cwd();
74
+ const pathPattern = servicePattern || common_1.config.RESOURCE_SERVICE_PATTERN;
75
+ const services = {};
76
+ const servicePaths = await findAllFiles(pathPattern, cwd);
77
+ for (const path of servicePaths) {
78
+ const resourceService = await importPath(path);
79
+ if (!resourceService) {
80
+ continue;
81
+ }
82
+ if ((0, common_1.isResourceService)(resourceService)) {
83
+ services[resourceService.modelName] = resourceService;
84
+ }
85
+ else {
86
+ for (const maybeService of Object.values(resourceService)) {
87
+ if ((0, common_1.isResourceService)(maybeService)) {
88
+ services[maybeService.modelName] = maybeService;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ return services;
94
+ }
95
+ async function loadPolicies(baseDir, policyPattern) {
96
+ const cwd = baseDir ?? process.cwd();
97
+ const pathPattern = policyPattern || common_1.config.RESOURCE_POLICY_PATTERN;
98
+ const policies = {};
99
+ const policyPaths = await findAllFiles(pathPattern, cwd);
100
+ for (const path of policyPaths) {
101
+ const resourcePolicy = await importPath(path);
102
+ if (!resourcePolicy) {
103
+ continue;
104
+ }
105
+ if ((0, common_1.isResourcePolicy)(resourcePolicy)) {
106
+ policies[resourcePolicy.modelName] = resourcePolicy;
107
+ }
108
+ else {
109
+ for (const maybePolicy of Object.values(resourcePolicy)) {
110
+ if ((0, common_1.isResourcePolicy)(maybePolicy)) {
111
+ policies[maybePolicy.modelName] = maybePolicy;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ return policies;
117
+ }
118
+ async function loadRoutes(baseDir, routePattern) {
119
+ const cwd = baseDir ?? process.cwd();
120
+ const pathPattern = routePattern || common_1.config.RESOURCE_ROUTES_PATTERN;
121
+ const routes = {};
122
+ const routePaths = await findAllFiles(pathPattern, cwd);
123
+ for (const path of routePaths) {
124
+ const resourceRoute = await importPath(path);
125
+ if (!resourceRoute) {
126
+ continue;
127
+ }
128
+ if ((0, common_1.isResourceRoutes)(resourceRoute)) {
129
+ routes[resourceRoute.config.modelName] = resourceRoute;
130
+ }
131
+ else {
132
+ for (const maybeRoute of Object.values(resourceRoute)) {
133
+ if ((0, common_1.isResourceRoutes)(maybeRoute)) {
134
+ routes[maybeRoute.config.modelName] = maybeRoute;
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return routes;
140
+ }
141
+ async function findAllFiles(pattern, cwd) {
142
+ const files = [];
143
+ // Find all project files matching provided glob pattern
144
+ const projectFiles = await (0, common_1.findFilesByPattern)(pattern, cwd);
145
+ files.push(...projectFiles);
146
+ // Add exported core module resources
147
+ files.push('@appweaver/core/resources');
148
+ // Add additional modules from config
149
+ for (const module of common_1.config.APP_AUTOLOAD_MODULES) {
150
+ files.push(module);
151
+ }
152
+ return files;
153
+ }
154
+ async function importPath(filePath) {
155
+ const { value, error } = await (0, common_1.importModule)(filePath);
156
+ if (error) {
157
+ common_1.logger.error(error, 'Resource path import error');
158
+ return null;
159
+ }
160
+ return value;
161
+ }
@@ -0,0 +1,3 @@
1
+ import { ResourceRoutesConfig } from '@appweaver/common';
2
+ import { Server } from '../types';
3
+ export declare function resourceRoutes(name: string, routesConfig?: Omit<ResourceRoutesConfig, 'modelName' | 'path'>): (server: Server) => void;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resourceRoutes = resourceRoutes;
4
+ const resource_schema_1 = require("./resource-schema");
5
+ const context_1 = require("../context");
6
+ const storage_1 = require("../storage");
7
+ const export_1 = require("../export");
8
+ const utils_1 = require("../utils");
9
+ function resourceRoutes(name, routesConfig = {}) {
10
+ const routeConfig = (configName) => {
11
+ return {
12
+ ...(routesConfig[configName] ?? {}),
13
+ resourceRouteName: configName,
14
+ cacheModelName: ['find', 'query', 'aggregate'].includes(configName)
15
+ ? name
16
+ : undefined
17
+ };
18
+ };
19
+ const routeNames = [
20
+ 'find',
21
+ 'query',
22
+ 'aggregate',
23
+ 'create',
24
+ 'update',
25
+ 'delete',
26
+ 'export',
27
+ 'fileUpload',
28
+ 'fileDelete'
29
+ ];
30
+ const routeAuthTypes = routeNames.reduce((acc, routeName) => {
31
+ const config = routeConfig(routeName);
32
+ if (config?.public || config?.auth?.length === 0) {
33
+ acc[routeName] = [];
34
+ }
35
+ else if (config?.auth && config.auth.length > 0) {
36
+ acc[routeName] = config.auth;
37
+ }
38
+ else {
39
+ acc[routeName] = undefined;
40
+ }
41
+ return acc;
42
+ }, {});
43
+ const routeRecaptcha = routeNames.reduce((acc, routeName) => {
44
+ const config = routeConfig(routeName);
45
+ acc[routeName] = {
46
+ recaptcha: config?.recaptcha,
47
+ recaptchaAction: config?.recaptchaAction
48
+ };
49
+ return acc;
50
+ }, {});
51
+ return (server) => {
52
+ const { authenticate, recaptcha } = server;
53
+ const schema = (0, resource_schema_1.createSchema)(name, routeAuthTypes, routeRecaptcha);
54
+ const service = (0, context_1.injectService)(name);
55
+ const resourceModel = (0, context_1.injectModel)(name);
56
+ const routeAuth = (config) => [
57
+ config?.public ? undefined : authenticate(config?.auth),
58
+ config?.recaptcha || config?.recaptchaAction ? recaptcha : undefined
59
+ ].filter((v) => v !== undefined);
60
+ const hasFiles = Object.keys(resourceModel.config.files ?? {}).length > 0;
61
+ const findConfig = routeConfig('find');
62
+ if (!findConfig?.exclude) {
63
+ server.get('/:id', {
64
+ schema: schema.findSchema,
65
+ onRequest: routeAuth(findConfig),
66
+ config: findConfig
67
+ }, async (request, reply) => {
68
+ const response = await service.find(request.params.id);
69
+ return reply.send(response);
70
+ });
71
+ }
72
+ const queryConfig = routeConfig('query');
73
+ if (!queryConfig?.exclude) {
74
+ server.post('/query', {
75
+ schema: schema.querySchema,
76
+ onRequest: routeAuth(queryConfig),
77
+ config: queryConfig
78
+ }, async (request, reply) => {
79
+ const { page, size, sort, ...body } = request.body;
80
+ const response = await service.query(body.filter, page, size, sort);
81
+ return reply.send(response);
82
+ });
83
+ }
84
+ const aggregateConfig = routeConfig('aggregate');
85
+ if (!aggregateConfig?.exclude) {
86
+ server.post('/aggregate', {
87
+ schema: schema.aggregateSchema,
88
+ onRequest: routeAuth(aggregateConfig),
89
+ config: aggregateConfig
90
+ }, async (request, reply) => {
91
+ const { dateField, from, to, step, safeIncrement, ...body } = request.body;
92
+ const response = await service.aggregate(body.filter, body.select, dateField, from, to, step, safeIncrement);
93
+ return reply.send(response);
94
+ });
95
+ }
96
+ const createConfig = routeConfig('create');
97
+ if (!createConfig?.exclude) {
98
+ server.post('', {
99
+ schema: schema.createSchema,
100
+ onRequest: routeAuth(createConfig),
101
+ config: createConfig
102
+ }, async (request, reply) => {
103
+ const response = await service.create(request.body);
104
+ return reply.status(201).send(response);
105
+ });
106
+ }
107
+ const updateConfig = routeConfig('update');
108
+ if (!updateConfig?.exclude) {
109
+ server.put('/:id', {
110
+ schema: schema.updateSchema,
111
+ onRequest: routeAuth(updateConfig),
112
+ config: updateConfig
113
+ }, async (request, reply) => {
114
+ const response = await service.update(request.params.id, request.body);
115
+ return reply.send(response);
116
+ });
117
+ }
118
+ const deleteConfig = routeConfig('delete');
119
+ if (!deleteConfig?.exclude) {
120
+ server.delete('/:id', {
121
+ schema: schema.deleteSchema,
122
+ onRequest: routeAuth(deleteConfig),
123
+ config: deleteConfig
124
+ }, async (request, reply) => {
125
+ const response = await service.delete(request.params.id);
126
+ return reply.send(response);
127
+ });
128
+ }
129
+ const exportConfig = routeConfig('export');
130
+ if (!exportConfig?.exclude) {
131
+ server.post('/export', {
132
+ schema: schema.exportSchema,
133
+ onRequest: routeAuth(exportConfig),
134
+ config: exportConfig
135
+ }, async (request, reply) => {
136
+ const { sort, ...body } = request.body;
137
+ const response = await (0, context_1.inject)(export_1.ExportService).exportCsv(name, body.filter, sort);
138
+ return reply
139
+ .type(response.mimeType)
140
+ .header('Content-Disposition', `attachment; filename="${response.fileName}"`)
141
+ .send(response.stream);
142
+ });
143
+ }
144
+ const fileUploadConfig = routeConfig('fileUpload');
145
+ if (hasFiles && !fileUploadConfig?.exclude && resourceModel.config.files) {
146
+ const config = resourceModel.config.files;
147
+ const maxSize = (0, utils_1.maxFileSize)(config);
148
+ server.post('/:id/files', {
149
+ schema: schema.fileUploadSchema,
150
+ onRequest: routeAuth(fileUploadConfig),
151
+ config: fileUploadConfig,
152
+ attachValidation: true // Disable validation because of a multipart body.
153
+ }, async (request, reply) => {
154
+ const resource = await service.find(request.params.id);
155
+ const parts = request.parts({ limits: { fileSize: maxSize } });
156
+ const files = await (0, context_1.inject)(storage_1.FileService).saveFiles(parts, resource, service.client);
157
+ return reply.send((0, utils_1.aggregateFiles)(files, config));
158
+ });
159
+ }
160
+ const fileDeleteConfig = routeConfig('fileDelete');
161
+ if (hasFiles && !fileDeleteConfig?.exclude && resourceModel.config.files) {
162
+ const config = resourceModel.config.files;
163
+ server.post('/:id/delete-files', {
164
+ schema: schema.fileDeleteSchema,
165
+ onRequest: routeAuth(fileDeleteConfig),
166
+ config: fileDeleteConfig
167
+ }, async (request, reply) => {
168
+ const resource = await service.find(request.params.id);
169
+ const files = await (0, context_1.inject)(storage_1.FileService).deleteFiles(request.body, resource, service.client);
170
+ return reply.send((0, utils_1.aggregateFiles)(files, config));
171
+ });
172
+ }
173
+ };
174
+ }
@@ -0,0 +1,33 @@
1
+ import { AuthType, RecaptchaConfig, ResourceRoutesConfig, ResourceSchemaConfig } from '@appweaver/common';
2
+ export declare const Id: import("@sinclair/typebox").TObject<{
3
+ id: import("@sinclair/typebox").TInteger;
4
+ }>;
5
+ export declare const IdString: import("@sinclair/typebox").TObject<{
6
+ id: import("@sinclair/typebox").TString;
7
+ }>;
8
+ export declare const AuditData: import("@sinclair/typebox").TObject<{
9
+ updatedAt: import("@sinclair/typebox").TUnsafe<Date>;
10
+ createdAt: import("@sinclair/typebox").TUnsafe<Date>;
11
+ createdById: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<number | null>>;
12
+ }>;
13
+ export declare const QueryRequestData: import("@sinclair/typebox").TObject<{
14
+ filter: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TAny>;
15
+ page: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
16
+ size: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
17
+ sort: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
18
+ }>;
19
+ export declare const QueryResponseData: import("@sinclair/typebox").TObject<{
20
+ resultCount: import("@sinclair/typebox").TNumber;
21
+ totalCount: import("@sinclair/typebox").TNumber;
22
+ }>;
23
+ export declare const AggregateRequestData: import("@sinclair/typebox").TObject<{
24
+ filter: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TAny>;
25
+ select: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TAny>;
26
+ dateField: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
27
+ from: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<Date>>;
28
+ to: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnsafe<Date>>;
29
+ step: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
30
+ safeIncrement: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
31
+ }>;
32
+ export declare const AggregateResponseData: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TAny>;
33
+ export declare function createSchema(name: string, routeAuthTypes: Record<keyof ResourceRoutesConfig, AuthType[] | undefined>, routeRecaptcha: Record<keyof ResourceRoutesConfig, RecaptchaConfig>): ResourceSchemaConfig;
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AggregateResponseData = exports.AggregateRequestData = exports.QueryResponseData = exports.QueryRequestData = exports.AuditData = exports.IdString = exports.Id = void 0;
4
+ exports.createSchema = createSchema;
5
+ const typebox_1 = require("@sinclair/typebox");
6
+ const common_1 = require("@appweaver/common");
7
+ const context_1 = require("../context");
8
+ const security_1 = require("../security");
9
+ const errors_1 = require("../errors");
10
+ const utils_1 = require("../utils");
11
+ exports.Id = typebox_1.Type.Object({
12
+ id: typebox_1.Type.Integer({ minimum: 1 })
13
+ });
14
+ exports.IdString = typebox_1.Type.Object({
15
+ id: typebox_1.Type.String({ maxLength: 36 })
16
+ });
17
+ exports.AuditData = typebox_1.Type.Object({
18
+ updatedAt: (0, common_1.StringDate)(),
19
+ createdAt: (0, common_1.StringDate)(),
20
+ createdById: (0, common_1.Nullable)(typebox_1.Type.Integer({ minimum: 1, example: 1 }))
21
+ });
22
+ exports.QueryRequestData = typebox_1.Type.Object({
23
+ filter: typebox_1.Type.Optional((0, common_1.AnyJson)({ example: { field: 'value' } })),
24
+ page: typebox_1.Type.Optional(typebox_1.Type.Number({ minimum: 1, example: 1 })),
25
+ size: typebox_1.Type.Optional(typebox_1.Type.Number({ minimum: 0, maximum: 1000, example: 50 })),
26
+ sort: typebox_1.Type.Optional(typebox_1.Type.String({ example: '-createdAt,id' }))
27
+ });
28
+ exports.QueryResponseData = typebox_1.Type.Object({
29
+ resultCount: typebox_1.Type.Number({ example: 10 }),
30
+ totalCount: typebox_1.Type.Number({ example: 100 })
31
+ });
32
+ exports.AggregateRequestData = typebox_1.Type.Object({
33
+ filter: typebox_1.Type.Optional((0, common_1.AnyJson)({ example: { field: 'value' } })),
34
+ select: typebox_1.Type.Optional((0, common_1.AnyJson)({ example: { field: 'value' } })),
35
+ dateField: typebox_1.Type.Optional(typebox_1.Type.String({ example: 'createdAt' })),
36
+ from: typebox_1.Type.Optional((0, common_1.StringDate)()),
37
+ to: typebox_1.Type.Optional((0, common_1.StringDate)()),
38
+ step: typebox_1.Type.Optional(typebox_1.Type.Integer({ minimum: 1, example: 3600 })),
39
+ safeIncrement: typebox_1.Type.Optional(typebox_1.Type.Boolean({ example: true }))
40
+ });
41
+ exports.AggregateResponseData = typebox_1.Type.Optional((0, common_1.AnyJson)({ example: { field: 'value' } }));
42
+ function createSchema(name, routeAuthTypes, routeRecaptcha) {
43
+ const resourceModel = (0, context_1.injectModel)(name);
44
+ const resourceName = (0, common_1.camelToSnakeCase)(name, ' ');
45
+ const tag = (0, common_1.plural)(name);
46
+ const queryRequest = typebox_1.Type.Composite([exports.QueryRequestData], {
47
+ $id: `${name}QueryRequest`
48
+ });
49
+ const queryResponse = typebox_1.Type.Composite([
50
+ exports.QueryResponseData,
51
+ typebox_1.Type.Object({
52
+ items: typebox_1.Type.Array(resourceModel.readManyModel)
53
+ })
54
+ ], { $id: `${name}QueryResponse` });
55
+ const aggregateRequest = typebox_1.Type.Composite([exports.AggregateRequestData], {
56
+ $id: `${name}AggregateRequest`
57
+ });
58
+ const aggregateResponse = typebox_1.Type.Composite([exports.AggregateResponseData], {
59
+ $id: `${name}AggregateResponse`
60
+ });
61
+ const exportRequest = typebox_1.Type.Composite([typebox_1.Type.Pick(exports.QueryRequestData, ['filter', 'sort'])], { $id: `${name}ExportRequest` });
62
+ const resourceSchemaConfig = {
63
+ findSchema: {
64
+ tags: [tag],
65
+ security: (0, security_1.authSchema)(routeAuthTypes['find']),
66
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['find']),
67
+ summary: `Find ${resourceName} data`,
68
+ description: `Find ${resourceName} data`,
69
+ response: {
70
+ 200: resourceModel.readOneModel,
71
+ ...errors_1.AllErrorResponses
72
+ },
73
+ params: exports.Id
74
+ },
75
+ querySchema: {
76
+ tags: [tag],
77
+ security: (0, security_1.authSchema)(routeAuthTypes['query']),
78
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['query']),
79
+ summary: `Query ${resourceName} data`,
80
+ description: `Query ${resourceName} data`,
81
+ response: {
82
+ 200: (0, utils_1.createSchemaModel)(queryResponse),
83
+ ...errors_1.AllErrorResponses
84
+ },
85
+ body: (0, utils_1.createSchemaModel)(queryRequest)
86
+ },
87
+ aggregateSchema: {
88
+ tags: [tag],
89
+ security: (0, security_1.authSchema)(routeAuthTypes['aggregate']),
90
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['aggregate']),
91
+ summary: `Aggregate ${resourceName} data`,
92
+ description: `Aggregate ${resourceName} data`,
93
+ response: {
94
+ 200: (0, utils_1.createSchemaModel)(aggregateResponse),
95
+ ...errors_1.AllErrorResponses
96
+ },
97
+ body: (0, utils_1.createSchemaModel)(aggregateRequest)
98
+ },
99
+ createSchema: {
100
+ tags: [tag],
101
+ security: (0, security_1.authSchema)(routeAuthTypes['create']),
102
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['create']),
103
+ summary: `Create ${resourceName} data`,
104
+ description: `Create ${resourceName} data`,
105
+ response: {
106
+ 201: resourceModel.readOneModel,
107
+ ...errors_1.AllErrorResponses
108
+ },
109
+ body: resourceModel.createOneModel
110
+ },
111
+ updateSchema: {
112
+ tags: [tag],
113
+ security: (0, security_1.authSchema)(routeAuthTypes['update']),
114
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['update']),
115
+ summary: `Update ${resourceName} data`,
116
+ description: `Update ${resourceName} data`,
117
+ response: {
118
+ 200: resourceModel.readOneModel,
119
+ ...errors_1.AllErrorResponses
120
+ },
121
+ body: resourceModel.updateOneModel,
122
+ params: exports.Id
123
+ },
124
+ deleteSchema: {
125
+ tags: [tag],
126
+ security: (0, security_1.authSchema)(routeAuthTypes['delete']),
127
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['delete']),
128
+ summary: `Delete ${resourceName} data`,
129
+ description: `Delete ${resourceName} data`,
130
+ response: {
131
+ 200: resourceModel.readOneModel,
132
+ ...errors_1.AllErrorResponses
133
+ },
134
+ params: exports.Id
135
+ },
136
+ exportSchema: {
137
+ tags: [tag],
138
+ security: (0, security_1.authSchema)(routeAuthTypes['export']),
139
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['export']),
140
+ summary: `Export ${resourceName} data`,
141
+ description: `Export ${resourceName} data`,
142
+ response: {
143
+ 200: {
144
+ content: {
145
+ 'text/csv': {
146
+ schema: typebox_1.Type.String({ format: 'binary' })
147
+ }
148
+ }
149
+ },
150
+ ...errors_1.AllErrorResponses
151
+ },
152
+ body: (0, utils_1.createSchemaModel)(exportRequest)
153
+ },
154
+ fileUploadSchema: {
155
+ tags: [tag],
156
+ security: (0, security_1.authSchema)(routeAuthTypes['fileUpload']),
157
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['fileUpload']),
158
+ summary: `Upload ${resourceName} files`,
159
+ description: `Upload ${resourceName} files`,
160
+ consumes: ['multipart/form-data'],
161
+ response: {
162
+ 200: resourceModel.filesModel,
163
+ ...errors_1.AllErrorResponses
164
+ },
165
+ body: resourceModel.fileUploadModel,
166
+ params: exports.Id
167
+ },
168
+ fileDeleteSchema: {
169
+ tags: [tag],
170
+ security: (0, security_1.authSchema)(routeAuthTypes['fileDelete']),
171
+ headers: (0, security_1.recaptchaHeaderSchema)(routeRecaptcha['fileDelete']),
172
+ summary: `Delete ${resourceName} files`,
173
+ description: `Delete ${resourceName} files`,
174
+ response: {
175
+ 200: resourceModel.filesModel,
176
+ ...errors_1.AllErrorResponses
177
+ },
178
+ body: resourceModel.fileDeleteModel,
179
+ params: exports.Id
180
+ }
181
+ };
182
+ for (const resourceConfig of Object.values(resourceSchemaConfig)) {
183
+ resourceConfig[`x-${common_1.CONFIG_NAME}-resource`] = name;
184
+ }
185
+ return resourceSchemaConfig;
186
+ }
@@ -0,0 +1,89 @@
1
+ import { ActionType, AggregateResponse, AggregateSelect, IResourceService, QueryResponse, Resource, ResourceClient, ResourceData } from '@appweaver/common';
2
+ export declare abstract class ResourceService<ReadOne = Resource, ReadMany = Resource, Create = ResourceData<Resource>, Update = Partial<ResourceData<Resource>>, Query = any> implements IResourceService<ReadOne, ReadMany, Create, Update, Query> {
3
+ readonly modelName: string;
4
+ constructor(modelName: string);
5
+ get client(): ResourceClient;
6
+ find(id: number): Promise<ReadOne>;
7
+ query(filter?: Query, page?: number, size?: number, sort?: string): Promise<QueryResponse<ReadMany>>;
8
+ aggregate(filter: Query | undefined, select: AggregateSelect<ReadOne>, dateField?: string, from?: string, to?: string, step?: number, safeIncrement?: boolean): Promise<AggregateResponse<ReadOne>>;
9
+ create(data: Create): Promise<ReadOne>;
10
+ update(id: number, data: Update): Promise<ReadOne>;
11
+ delete(id: number): Promise<ReadOne>;
12
+ /**
13
+ * This method should be overridden with custom logic for restricting read
14
+ * operations on specific data for currently logged-in user and other
15
+ * authorization rules. The returned object will be applied as a filter on
16
+ * all actions (except create action) which will prevent unwanted data access
17
+ * and modifications. This method can also cancel current action by throwing
18
+ * an error, recommended is {@link HttpError } with appropriate HTTP error
19
+ * code.
20
+ *
21
+ * @param action The called action method on this service (find, query,
22
+ * aggregate, update, or delete)
23
+ * @param data The passed data to the called function, can be number or
24
+ * object. If the data is a type of number, then it represents the resource
25
+ * id, otherwise it depends on the action and can be one of the following:
26
+ *
27
+ * - query and aggregate -> filter object
28
+ * - update -> combined id and the data object (i.e. { id, ...data })
29
+ *
30
+ * For other actions (find and delete) it represents the resource id.
31
+ * @return The filter containing additional query restrictions.
32
+ */
33
+ protected readRestrictions(action: Exclude<ActionType, 'create'>, data: any): Promise<Query>;
34
+ /**
35
+ * This method should be overridden with custom logic for restricting write
36
+ * operations on specific data for currently logged-in users and other
37
+ * authorization rules. The returned object will be applied as a filter on
38
+ * all actions (create and update) to ensure that users can only modify
39
+ * data they are authorized to access. This method can also cancel the
40
+ * current action by throwing an error, with the recommended type being
41
+ * {@link HttpError} with the appropriate HTTP error code.
42
+ *
43
+ * @param action The called action method on this service (create or update)
44
+ * @param data The passed data to the called function, which should be an
45
+ * object representing the resource data to be created or updated. For
46
+ * update operations, this object will also include the resource ID.
47
+ *
48
+ * @return A partial object containing additional data restrictions to be
49
+ * applied, or an empty object if no restrictions are necessary.
50
+ */
51
+ protected writeRestrictions(action: 'create' | 'update', data: any): Promise<Partial<Create & Update>>;
52
+ /**
53
+ * This method should provide logic for checking the access permissions for
54
+ * the provided resource object. If the resource should not be accessible by
55
+ * a currently authenticated user or other logic, this method should return
56
+ * false. Otherwise, it returns true and continues with the request execution.
57
+ *
58
+ * @param action The called action method on this service (find, query,
59
+ * aggregate, create, update, or delete)
60
+ * @param resource The resource object that is being check for access.
61
+ * @returns True if the access for resource is granted, false otherwise.
62
+ */
63
+ protected checkAccess(action: ActionType, resource: ReadOne): Promise<boolean>;
64
+ /**
65
+ * Constructs a query object for performing a text search on resources in the
66
+ * database based on the provided search text. This method will transform the
67
+ * search text into a format suitable for text search functionality, returning
68
+ * a query object that can be used to filter results.
69
+ *
70
+ * @param searchText The text string used for searching resources.
71
+ * @returns A query object that represents the conditions for the text
72
+ * search operation. This will be used by the database query methods to
73
+ * retrieve matching resources.
74
+ */
75
+ protected textSearchQuery(searchText: string): Query;
76
+ private extractTextSearchQuery;
77
+ private mapSortValues;
78
+ private mapAggregationValues;
79
+ private makeAggregationIterator;
80
+ private projectResource;
81
+ private projectVirtualFields;
82
+ private sanitizeData;
83
+ private mapQueryFilter;
84
+ private mapRelationInclusions;
85
+ private buildNestedInclusion;
86
+ private shouldIncludeRelation;
87
+ private mapRelationActions;
88
+ private createdByConnect;
89
+ }