@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.
- package/LICENSE +1 -0
- package/README.md +7 -0
- package/app/application.d.ts +43 -0
- package/app/application.js +112 -0
- package/app/create-app.d.ts +32 -0
- package/app/create-app.js +38 -0
- package/app/index.d.ts +2 -0
- package/app/index.js +18 -0
- package/app/lifecycle-manager.d.ts +20 -0
- package/app/lifecycle-manager.js +42 -0
- package/app/load-modules.d.ts +10 -0
- package/app/load-modules.js +50 -0
- package/app/load-providers.d.ts +8 -0
- package/app/load-providers.js +33 -0
- package/cache/cache-service.d.ts +72 -0
- package/cache/cache-service.js +202 -0
- package/cache/cache.d.ts +12 -0
- package/cache/cache.js +184 -0
- package/cache/caching.d.ts +8 -0
- package/cache/caching.js +72 -0
- package/cache/eviction/eviction-index.d.ts +35 -0
- package/cache/eviction/eviction-index.js +2 -0
- package/cache/eviction/fifo-eviction-index.d.ts +7 -0
- package/cache/eviction/fifo-eviction-index.js +28 -0
- package/cache/eviction/index.d.ts +4 -0
- package/cache/eviction/index.js +20 -0
- package/cache/eviction/lfu-eviction-index.d.ts +8 -0
- package/cache/eviction/lfu-eviction-index.js +82 -0
- package/cache/eviction/lru-eviction-index.d.ts +8 -0
- package/cache/eviction/lru-eviction-index.js +81 -0
- package/cache/index.d.ts +2 -0
- package/cache/index.js +18 -0
- package/cache/memory-cache.d.ts +4 -0
- package/cache/memory-cache.js +12 -0
- package/cache/redis-cache.d.ts +4 -0
- package/cache/redis-cache.js +12 -0
- package/context/context.d.ts +2 -0
- package/context/context.js +13 -0
- package/context/dependency-injection.d.ts +98 -0
- package/context/dependency-injection.js +276 -0
- package/context/index.d.ts +2 -0
- package/context/index.js +18 -0
- package/database/create-client.d.ts +11 -0
- package/database/create-client.js +142 -0
- package/database/index.d.ts +1 -0
- package/database/index.js +17 -0
- package/database/prisma-database.d.ts +15 -0
- package/database/prisma-database.js +53 -0
- package/errors/error-handler.d.ts +3 -0
- package/errors/error-handler.js +24 -0
- package/errors/error-schema.d.ts +32 -0
- package/errors/error-schema.js +31 -0
- package/errors/http-error.d.ts +21 -0
- package/errors/http-error.js +33 -0
- package/errors/index.d.ts +3 -0
- package/errors/index.js +19 -0
- package/events/node-events.d.ts +7 -0
- package/events/node-events.js +39 -0
- package/export/export-service.d.ts +19 -0
- package/export/export-service.js +154 -0
- package/export/index.d.ts +1 -0
- package/export/index.js +17 -0
- package/factory/create-model.d.ts +2 -0
- package/factory/create-model.js +366 -0
- package/factory/create-policy.d.ts +2 -0
- package/factory/create-policy.js +16 -0
- package/factory/create-routes.d.ts +2 -0
- package/factory/create-routes.js +21 -0
- package/factory/create-service.d.ts +3 -0
- package/factory/create-service.js +97 -0
- package/factory/index.d.ts +4 -0
- package/factory/index.js +20 -0
- package/health/health-routes.d.ts +2 -0
- package/health/health-routes.js +36 -0
- package/health/health-schema.d.ts +45 -0
- package/health/health-schema.js +52 -0
- package/health/health-service.d.ts +38 -0
- package/health/health-service.js +82 -0
- package/health/index.d.ts +2 -0
- package/health/index.js +18 -0
- package/index.d.ts +15 -0
- package/index.js +31 -0
- package/mailer/email-service.d.ts +36 -0
- package/mailer/email-service.js +89 -0
- package/mailer/index.d.ts +1 -0
- package/mailer/index.js +17 -0
- package/mailer/json-mailer.d.ts +7 -0
- package/mailer/json-mailer.js +32 -0
- package/mailer/smtp-mailer.d.ts +7 -0
- package/mailer/smtp-mailer.js +43 -0
- package/memory/in-memory.d.ts +24 -0
- package/memory/in-memory.js +164 -0
- package/memory/redis.d.ts +26 -0
- package/memory/redis.js +158 -0
- package/package.json +68 -0
- package/prisma/client/browser.d.ts +34 -0
- package/prisma/client/browser.js +56 -0
- package/prisma/client/client.d.ts +53 -0
- package/prisma/client/client.js +70 -0
- package/prisma/client/commonInputTypes.d.ts +395 -0
- package/prisma/client/commonInputTypes.js +11 -0
- package/prisma/client/enums.d.ts +1 -0
- package/prisma/client/enums.js +11 -0
- package/prisma/client/internal/class.d.ts +187 -0
- package/prisma/client/internal/class.js +85 -0
- package/prisma/client/internal/prismaNamespace.d.ts +987 -0
- package/prisma/client/internal/prismaNamespace.js +193 -0
- package/prisma/client/internal/prismaNamespaceBrowser.d.ts +125 -0
- package/prisma/client/internal/prismaNamespaceBrowser.js +164 -0
- package/prisma/client/models/ApiKey.d.ts +1167 -0
- package/prisma/client/models/ApiKey.js +2 -0
- package/prisma/client/models/File.d.ts +1248 -0
- package/prisma/client/models/File.js +2 -0
- package/prisma/client/models/OneTimeToken.d.ts +1083 -0
- package/prisma/client/models/OneTimeToken.js +2 -0
- package/prisma/client/models/Permission.d.ts +1209 -0
- package/prisma/client/models/Permission.js +2 -0
- package/prisma/client/models/Role.d.ts +1209 -0
- package/prisma/client/models/Role.js +2 -0
- package/prisma/client/models/Seeder.d.ts +1023 -0
- package/prisma/client/models/Seeder.js +2 -0
- package/prisma/client/models.d.ts +7 -0
- package/prisma/client/models.js +2 -0
- package/queue/bull-queue.d.ts +37 -0
- package/queue/bull-queue.js +207 -0
- package/queue/memory-queue.d.ts +54 -0
- package/queue/memory-queue.js +283 -0
- package/resource/index.d.ts +4 -0
- package/resource/index.js +20 -0
- package/resource/resource-loader.d.ts +26 -0
- package/resource/resource-loader.js +161 -0
- package/resource/resource-routes.d.ts +3 -0
- package/resource/resource-routes.js +174 -0
- package/resource/resource-schema.d.ts +33 -0
- package/resource/resource-schema.js +186 -0
- package/resource/resource-service.d.ts +89 -0
- package/resource/resource-service.js +772 -0
- package/resources.d.ts +9 -0
- package/resources.js +24 -0
- package/scheduler/cron-scheduler.d.ts +12 -0
- package/scheduler/cron-scheduler.js +73 -0
- package/security/account/account-routes.d.ts +2 -0
- package/security/account/account-routes.js +104 -0
- package/security/account/account-schema.d.ts +290 -0
- package/security/account/account-schema.js +166 -0
- package/security/account/account-service.d.ts +88 -0
- package/security/account/account-service.js +218 -0
- package/security/account/index.d.ts +3 -0
- package/security/account/index.js +19 -0
- package/security/api-key/api-key-auth.d.ts +4 -0
- package/security/api-key/api-key-auth.js +66 -0
- package/security/api-key/index.d.ts +1 -0
- package/security/api-key/index.js +17 -0
- package/security/auth-routes.d.ts +2 -0
- package/security/auth-routes.js +80 -0
- package/security/auth-schema.d.ts +215 -0
- package/security/auth-schema.js +129 -0
- package/security/auth-service.d.ts +117 -0
- package/security/auth-service.js +272 -0
- package/security/auth.d.ts +3 -0
- package/security/auth.js +127 -0
- package/security/basic/basic-auth.d.ts +4 -0
- package/security/basic/basic-auth.js +32 -0
- package/security/basic/index.d.ts +1 -0
- package/security/basic/index.js +17 -0
- package/security/create-auth-resources.d.ts +6 -0
- package/security/create-auth-resources.js +129 -0
- package/security/helper.d.ts +127 -0
- package/security/helper.js +329 -0
- package/security/index.d.ts +4 -0
- package/security/index.js +20 -0
- package/security/jwt/index.d.ts +2 -0
- package/security/jwt/index.js +18 -0
- package/security/jwt/jwt-auth.d.ts +4 -0
- package/security/jwt/jwt-auth.js +57 -0
- package/security/jwt/jwt-keys.d.ts +7 -0
- package/security/jwt/jwt-keys.js +43 -0
- package/security/oauth2/create-oauth2-plugin.d.ts +17 -0
- package/security/oauth2/create-oauth2-plugin.js +85 -0
- package/security/oauth2/index.d.ts +3 -0
- package/security/oauth2/index.js +19 -0
- package/security/oauth2/oauth2-custom.d.ts +7 -0
- package/security/oauth2/oauth2-custom.js +40 -0
- package/security/oauth2/oauth2-facebook.d.ts +1 -0
- package/security/oauth2/oauth2-facebook.js +30 -0
- package/security/oauth2/oauth2-google.d.ts +1 -0
- package/security/oauth2/oauth2-google.js +28 -0
- package/security/oauth2/oauth2-schema.d.ts +10 -0
- package/security/oauth2/oauth2-schema.js +53 -0
- package/security/recaptcha/index.d.ts +2 -0
- package/security/recaptcha/index.js +18 -0
- package/security/recaptcha/recaptcha-verify.d.ts +1 -0
- package/security/recaptcha/recaptcha-verify.js +33 -0
- package/security/recaptcha/recaptcha.d.ts +2 -0
- package/security/recaptcha/recaptcha.js +22 -0
- package/security/resources/api-key/model.d.ts +2 -0
- package/security/resources/api-key/model.js +63 -0
- package/security/resources/api-key/policy.d.ts +2 -0
- package/security/resources/api-key/policy.js +26 -0
- package/security/resources/api-key/routes.d.ts +2 -0
- package/security/resources/api-key/routes.js +15 -0
- package/security/resources/api-key/service.d.ts +3 -0
- package/security/resources/api-key/service.js +33 -0
- package/security/resources/one-time-token/model.d.ts +2 -0
- package/security/resources/one-time-token/model.js +32 -0
- package/security/resources/permission/model.d.ts +2 -0
- package/security/resources/permission/model.js +16 -0
- package/security/resources/role/model.d.ts +2 -0
- package/security/resources/role/model.js +22 -0
- package/security/store/database-security-store.d.ts +5 -0
- package/security/store/database-security-store.js +51 -0
- package/security/store/redis-security-store.d.ts +5 -0
- package/security/store/redis-security-store.js +32 -0
- package/seeder/create-seeder.d.ts +21 -0
- package/seeder/create-seeder.js +17 -0
- package/seeder/index.d.ts +2 -0
- package/seeder/index.js +18 -0
- package/seeder/resources/seeder/model.d.ts +2 -0
- package/seeder/resources/seeder/model.js +38 -0
- package/seeder/seeder.d.ts +20 -0
- package/seeder/seeder.js +175 -0
- package/server/create-server.d.ts +20 -0
- package/server/create-server.js +160 -0
- package/server/index.d.ts +4 -0
- package/server/index.js +20 -0
- package/server/info-route.d.ts +2 -0
- package/server/info-route.js +18 -0
- package/server/info-schema.d.ts +37 -0
- package/server/info-schema.js +19 -0
- package/server/register-model.d.ts +2 -0
- package/server/register-model.js +12 -0
- package/server/register-plugin.d.ts +2 -0
- package/server/register-plugin.js +13 -0
- package/server/register-route.d.ts +3 -0
- package/server/register-route.js +74 -0
- package/server/schemas.d.ts +3 -0
- package/server/schemas.js +30 -0
- package/server/swagger.d.ts +3 -0
- package/server/swagger.js +122 -0
- package/storage/file-route.d.ts +2 -0
- package/storage/file-route.js +39 -0
- package/storage/file-schema.d.ts +9 -0
- package/storage/file-schema.js +42 -0
- package/storage/file-service.d.ts +81 -0
- package/storage/file-service.js +384 -0
- package/storage/filesystem-storage.d.ts +10 -0
- package/storage/filesystem-storage.js +128 -0
- package/storage/index.d.ts +2 -0
- package/storage/index.js +18 -0
- package/storage/resources/file/model.d.ts +2 -0
- package/storage/resources/file/model.js +75 -0
- package/types/application-context.d.ts +20 -0
- package/types/application-context.js +2 -0
- package/types/auth.d.ts +31 -0
- package/types/auth.js +2 -0
- package/types/fastify-extensions.d.ts +24 -0
- package/types/fastify-extensions.js +4 -0
- package/types/generated.d.ts +193 -0
- package/types/generated.js +3 -0
- package/types/index.d.ts +4 -0
- package/types/index.js +20 -0
- package/types/server.d.ts +4 -0
- package/types/server.js +2 -0
- package/utils/export-util.d.ts +8 -0
- package/utils/export-util.js +25 -0
- package/utils/file-util.d.ts +64 -0
- package/utils/file-util.js +141 -0
- package/utils/index.d.ts +3 -0
- package/utils/index.js +19 -0
- package/utils/schema-util.d.ts +20 -0
- package/utils/schema-util.js +37 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.infoSchema = exports.InfoResponse = void 0;
|
|
4
|
+
const typebox_1 = require("@sinclair/typebox");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
exports.InfoResponse = typebox_1.Type.Object({
|
|
8
|
+
name: typebox_1.Type.String(),
|
|
9
|
+
version: typebox_1.Type.String()
|
|
10
|
+
}, { $id: 'InfoResponse' });
|
|
11
|
+
exports.infoSchema = {
|
|
12
|
+
tags: ['Info'],
|
|
13
|
+
summary: 'Application info',
|
|
14
|
+
description: 'Application info',
|
|
15
|
+
response: {
|
|
16
|
+
200: (0, utils_1.createSchemaModel)(exports.InfoResponse),
|
|
17
|
+
...errors_1.AllErrorResponses
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerModel = registerModel;
|
|
4
|
+
const common_1 = require("@appweaver/common");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function registerModel(schema, name) {
|
|
7
|
+
const refType = (0, utils_1.createSchemaModel)(schema, {
|
|
8
|
+
name,
|
|
9
|
+
addToServer: false
|
|
10
|
+
});
|
|
11
|
+
common_1.logger.debug({ modelName: refType.$ref }, 'Registered model');
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerPlugin = registerPlugin;
|
|
7
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
8
|
+
const common_1 = require("@appweaver/common");
|
|
9
|
+
const context_1 = require("../context");
|
|
10
|
+
function registerPlugin(name, plugin, dependencies = []) {
|
|
11
|
+
(0, context_1.define)((0, fastify_plugin_1.default)(plugin, { name, dependencies }), common_1.PLUGIN, 'append');
|
|
12
|
+
common_1.logger.debug({ plugin: name, dependencies }, 'Registered plugin');
|
|
13
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerRoute = registerRoute;
|
|
7
|
+
const fastify_1 = __importDefault(require("fastify"));
|
|
8
|
+
const typebox_1 = require("@sinclair/typebox");
|
|
9
|
+
const common_1 = require("@appweaver/common");
|
|
10
|
+
const context_1 = require("../context");
|
|
11
|
+
const security_1 = require("../security");
|
|
12
|
+
const errors_1 = require("../errors");
|
|
13
|
+
function registerRoute(handler, config) {
|
|
14
|
+
const routes = [];
|
|
15
|
+
const tempServer = (0, fastify_1.default)({
|
|
16
|
+
logger: false,
|
|
17
|
+
ajv: {
|
|
18
|
+
plugins: [(ajv) => ajv.addKeyword('example')]
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
tempServer.addHook('onRoute', (routeOptions) => {
|
|
22
|
+
routes.push(routeOptions);
|
|
23
|
+
});
|
|
24
|
+
tempServer.register(handler);
|
|
25
|
+
const routeBuilder = async (server) => {
|
|
26
|
+
const { authenticate, recaptcha } = server;
|
|
27
|
+
// Add all currently configured schemas to the temporary server
|
|
28
|
+
for (const [id, schema] of Object.entries(server.getSchemas())) {
|
|
29
|
+
if (!tempServer.getSchema(id)) {
|
|
30
|
+
tempServer.addSchema({ ...schema, $id: id });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Route array will be filled via the 'onRoute' hook after this step
|
|
34
|
+
await tempServer.ready();
|
|
35
|
+
for (const route of routes) {
|
|
36
|
+
const recaptchaHeader = (0, security_1.recaptchaHeaderSchema)({
|
|
37
|
+
recaptcha: config?.recaptcha,
|
|
38
|
+
recaptchaAction: config?.recaptchaAction
|
|
39
|
+
});
|
|
40
|
+
// Merge received route schema with default values based on configuration
|
|
41
|
+
const mergedRoute = {
|
|
42
|
+
...route,
|
|
43
|
+
schema: {
|
|
44
|
+
...route.schema,
|
|
45
|
+
security: config?.public
|
|
46
|
+
? []
|
|
47
|
+
: (0, security_1.authSchema)(config?.auth),
|
|
48
|
+
headers: route.schema?.headers
|
|
49
|
+
? typebox_1.Type.Composite([
|
|
50
|
+
recaptchaHeader,
|
|
51
|
+
typebox_1.Type.Composite([route.schema.headers])
|
|
52
|
+
])
|
|
53
|
+
: recaptchaHeader,
|
|
54
|
+
response: {
|
|
55
|
+
...(route.schema?.response ?? {}),
|
|
56
|
+
...errors_1.AllErrorResponses
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
onRequest: [
|
|
60
|
+
config?.public ? undefined : authenticate(config?.auth),
|
|
61
|
+
config?.recaptcha || config?.recaptchaAction ? recaptcha : undefined,
|
|
62
|
+
...((0, common_1.isArray)(route.onRequest) ? route.onRequest : [route.onRequest])
|
|
63
|
+
].filter((h) => h !== undefined),
|
|
64
|
+
config: {
|
|
65
|
+
...config,
|
|
66
|
+
...route.config
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
common_1.logger.debug({ method: mergedRoute.method, url: mergedRoute.url }, 'Registered route');
|
|
70
|
+
server.route(mergedRoute);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
(0, context_1.define)(routeBuilder, common_1.ROUTE, 'append');
|
|
74
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
7
|
+
const common_1 = require("@appweaver/common");
|
|
8
|
+
const context_1 = require("../context");
|
|
9
|
+
exports.default = (0, fastify_plugin_1.default)((server) => {
|
|
10
|
+
const usedSchemas = new Set();
|
|
11
|
+
// Add all resource models schemas
|
|
12
|
+
for (const model of context_1.context.resource.models.values()) {
|
|
13
|
+
for (const [suffix, property] of Object.entries(common_1.resourceModelProps)) {
|
|
14
|
+
const modelName = `${model.name}${suffix}`;
|
|
15
|
+
const modelSchema = model[property].$defs[modelName];
|
|
16
|
+
usedSchemas.add(modelSchema);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// Add additional schemas for registered models
|
|
20
|
+
const models = (0, context_1.injectAll)(common_1.MODEL);
|
|
21
|
+
for (const model of models) {
|
|
22
|
+
usedSchemas.add({ ...model.schema, $id: model.name });
|
|
23
|
+
}
|
|
24
|
+
// Add used schemas to the server instance
|
|
25
|
+
for (const schema of usedSchemas.values()) {
|
|
26
|
+
if (schema.$id && !server.getSchema(schema.$id)) {
|
|
27
|
+
server.addSchema({ ...schema });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
7
|
+
const swagger_1 = __importDefault(require("@fastify/swagger"));
|
|
8
|
+
const swagger_ui_1 = __importDefault(require("@fastify/swagger-ui"));
|
|
9
|
+
const common_1 = require("@appweaver/common");
|
|
10
|
+
const context_1 = require("../context");
|
|
11
|
+
exports.default = (0, fastify_plugin_1.default)((server) => {
|
|
12
|
+
server.register(swagger_1.default, {
|
|
13
|
+
hideUntagged: common_1.config.SWAGGER_HIDE_UNTAGGED,
|
|
14
|
+
transformObject: (document) => addConfig(pruneUnusedSchemas(document)),
|
|
15
|
+
openapi: {
|
|
16
|
+
info: {
|
|
17
|
+
title: common_1.config.APP_NAME,
|
|
18
|
+
description: common_1.config.APP_DESCRIPTION,
|
|
19
|
+
version: common_1.config.APP_VERSION
|
|
20
|
+
},
|
|
21
|
+
externalDocs: {
|
|
22
|
+
url: 'https://swagger.io',
|
|
23
|
+
description: 'Find more info here'
|
|
24
|
+
},
|
|
25
|
+
servers: [
|
|
26
|
+
{
|
|
27
|
+
url: common_1.config.APP_HOSTNAME
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
tags: [],
|
|
31
|
+
components: {
|
|
32
|
+
securitySchemes: {
|
|
33
|
+
bearer: {
|
|
34
|
+
scheme: 'bearer',
|
|
35
|
+
bearerFormat: 'token',
|
|
36
|
+
type: 'http'
|
|
37
|
+
},
|
|
38
|
+
...(common_1.config.SECURITY_API_KEY_ENABLED
|
|
39
|
+
? {
|
|
40
|
+
apiKeyAuth: {
|
|
41
|
+
type: 'apiKey',
|
|
42
|
+
in: 'header',
|
|
43
|
+
name: common_1.config.SECURITY_API_KEY_HEADER_NAME
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
: {}),
|
|
47
|
+
...(common_1.config.SECURITY_BASIC_ENABLED
|
|
48
|
+
? {
|
|
49
|
+
basicAuth: {
|
|
50
|
+
type: 'http',
|
|
51
|
+
scheme: 'basic'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
: {})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (common_1.config.SWAGGER_ENABLED) {
|
|
60
|
+
server.register(swagger_ui_1.default, {
|
|
61
|
+
routePrefix: common_1.config.SWAGGER_PATH,
|
|
62
|
+
indexPrefix: new URL(common_1.config.APP_HOSTNAME).pathname,
|
|
63
|
+
staticCSP: false
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
function pruneUnusedSchemas(document) {
|
|
68
|
+
const schemas = document.openapiObject.components?.schemas ?? {};
|
|
69
|
+
const used = new Set();
|
|
70
|
+
const visit = (node) => {
|
|
71
|
+
if (!node || typeof node !== 'object')
|
|
72
|
+
return;
|
|
73
|
+
if (typeof node.$ref === 'string') {
|
|
74
|
+
const match = node.$ref.match(/^#\/components\/schemas\/(.+)$/);
|
|
75
|
+
if (match) {
|
|
76
|
+
const schemaName = match[1];
|
|
77
|
+
if (!used.has(schemaName)) {
|
|
78
|
+
used.add(schemaName);
|
|
79
|
+
visit(schemas[schemaName]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(node)) {
|
|
84
|
+
for (const item of node)
|
|
85
|
+
visit(item);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
for (const value of Object.values(node)) {
|
|
89
|
+
visit(value);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
visit(document.openapiObject.paths);
|
|
93
|
+
if (document.openapiObject.components?.schemas) {
|
|
94
|
+
document.openapiObject.components.schemas = Object.fromEntries(Object.entries(document.openapiObject.components.schemas)
|
|
95
|
+
.sort(([nameA, objA], [nameB, objB]) => String(objA.title ?? nameA).localeCompare(String(objB.title ?? nameB)))
|
|
96
|
+
.filter(([name]) => used.has(name)));
|
|
97
|
+
}
|
|
98
|
+
return document.openapiObject;
|
|
99
|
+
}
|
|
100
|
+
function addConfig(document) {
|
|
101
|
+
const normalizePath = (path) => {
|
|
102
|
+
const withLeadingSlash = path.startsWith('/') ? path : `/${path}`;
|
|
103
|
+
return withLeadingSlash.endsWith('/') && withLeadingSlash.length > 1
|
|
104
|
+
? withLeadingSlash.slice(0, -1)
|
|
105
|
+
: withLeadingSlash;
|
|
106
|
+
};
|
|
107
|
+
document[`x-${common_1.CONFIG_NAME}-config`] = {
|
|
108
|
+
resourcePaths: [...context_1.context.resource.routes.values()].map((r) => ({
|
|
109
|
+
name: r.config.modelName,
|
|
110
|
+
basePath: normalizePath(r.basePath)
|
|
111
|
+
})),
|
|
112
|
+
routePrefixes: {
|
|
113
|
+
api: normalizePath(common_1.config.SERVER_API_PREFIX),
|
|
114
|
+
static: normalizePath(common_1.config.SERVER_STATIC_ROUTE_PREFIX),
|
|
115
|
+
health: normalizePath(common_1.config.HEALTH_CHECK_ROUTE_PREFIX),
|
|
116
|
+
auth: normalizePath(common_1.config.SECURITY_ROUTE_PREFIX),
|
|
117
|
+
account: normalizePath(common_1.config.SECURITY_ACCOUNT_ROUTE_PREFIX),
|
|
118
|
+
files: normalizePath(common_1.config.STORAGE_FILES_ROUTE_PREFIX)
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
return document;
|
|
122
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.files = files;
|
|
4
|
+
const common_1 = require("@appweaver/common");
|
|
5
|
+
const file_service_1 = require("./file-service");
|
|
6
|
+
const file_schema_1 = require("./file-schema");
|
|
7
|
+
const context_1 = require("../context");
|
|
8
|
+
function files(server) {
|
|
9
|
+
const { authenticate } = server;
|
|
10
|
+
const fileService = (0, context_1.inject)(file_service_1.FileService);
|
|
11
|
+
const addHeaders = (reply, data, hasRange) => {
|
|
12
|
+
reply.header('Accept-Ranges', 'bytes');
|
|
13
|
+
if (hasRange) {
|
|
14
|
+
reply.header('Content-Range', `bytes ${data.start}-${data.end}/${data.content.size}`);
|
|
15
|
+
}
|
|
16
|
+
reply.header('Content-Length', hasRange ? data.end - data.start + 1 : data.content.size);
|
|
17
|
+
reply.header('Content-Type', data.mimeType ?? 'application/octet-stream');
|
|
18
|
+
reply.header('Content-Disposition', 'inline; filename="' + data.fileName + '"');
|
|
19
|
+
reply.header('Pragma', 'public');
|
|
20
|
+
reply.header('Cache-Control', `public, max-age=${Math.floor(common_1.config.STORAGE_CACHE_TTL / 1000)}`);
|
|
21
|
+
reply.header('Expires', new Date(Date.now() + common_1.config.STORAGE_CACHE_TTL).toUTCString());
|
|
22
|
+
return hasRange ? 206 : 200;
|
|
23
|
+
};
|
|
24
|
+
server.get('/public/*', {
|
|
25
|
+
schema: (0, file_schema_1.createFileAccessSchema)(true)
|
|
26
|
+
}, async (request, reply) => {
|
|
27
|
+
const data = await fileService.stream(request.params['*'], request.headers.range);
|
|
28
|
+
const code = addHeaders(reply, data, !!request.headers.range);
|
|
29
|
+
return reply.status(code).send(data.content.stream);
|
|
30
|
+
});
|
|
31
|
+
server.get('/protected/*', {
|
|
32
|
+
schema: (0, file_schema_1.createFileAccessSchema)(false),
|
|
33
|
+
onRequest: authenticate()
|
|
34
|
+
}, async (request, reply) => {
|
|
35
|
+
const data = await fileService.stream(request.params['*'], request.headers.range);
|
|
36
|
+
const code = addHeaders(reply, data, !!request.headers.range);
|
|
37
|
+
return reply.status(code).send(data.content.stream);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { RouteSchema } from '@appweaver/common';
|
|
2
|
+
export declare const FileName: import("@sinclair/typebox").TObject<{
|
|
3
|
+
'*': import("@sinclair/typebox").TString;
|
|
4
|
+
}>;
|
|
5
|
+
export declare const FileRangeHeader: import("@sinclair/typebox").TObject<{
|
|
6
|
+
Range: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const FileResponse: import("@sinclair/typebox").TString;
|
|
9
|
+
export declare function createFileAccessSchema(isPublic: boolean): RouteSchema;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileResponse = exports.FileRangeHeader = exports.FileName = void 0;
|
|
4
|
+
exports.createFileAccessSchema = createFileAccessSchema;
|
|
5
|
+
const typebox_1 = require("@sinclair/typebox");
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
exports.FileName = typebox_1.Type.Object({
|
|
8
|
+
'*': typebox_1.Type.String()
|
|
9
|
+
});
|
|
10
|
+
exports.FileRangeHeader = typebox_1.Type.Object({
|
|
11
|
+
Range: typebox_1.Type.Optional(typebox_1.Type.String({ example: 'bytes=0-1023' }))
|
|
12
|
+
});
|
|
13
|
+
exports.FileResponse = typebox_1.Type.String({ format: 'binary' });
|
|
14
|
+
function createFileAccessSchema(isPublic) {
|
|
15
|
+
return {
|
|
16
|
+
tags: ['Files'],
|
|
17
|
+
security: isPublic ? [] : [{ bearer: [] }],
|
|
18
|
+
summary: `Fetch ${isPublic ? 'public' : 'protected'} files`,
|
|
19
|
+
description: `Fetch ${isPublic ? 'public' : 'protected'} files`,
|
|
20
|
+
response: {
|
|
21
|
+
200: {
|
|
22
|
+
description: 'Binary file content',
|
|
23
|
+
content: {
|
|
24
|
+
'application/octet-stream': {
|
|
25
|
+
schema: exports.FileResponse
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
206: {
|
|
30
|
+
description: 'Binary partial file content',
|
|
31
|
+
content: {
|
|
32
|
+
'application/octet-stream': {
|
|
33
|
+
schema: exports.FileResponse
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
...errors_1.AllErrorResponses
|
|
38
|
+
},
|
|
39
|
+
params: exports.FileName,
|
|
40
|
+
headers: exports.FileRangeHeader
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Multipart, MultipartFile } from '@fastify/multipart';
|
|
2
|
+
import { ContentStream, Resource, ResourceClient } from '@appweaver/common';
|
|
3
|
+
import { File } from '../types';
|
|
4
|
+
export type FileStream = {
|
|
5
|
+
content: ContentStream;
|
|
6
|
+
fileName: string;
|
|
7
|
+
mimeType: string;
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
};
|
|
11
|
+
export declare class FileService {
|
|
12
|
+
/**
|
|
13
|
+
* Searches for a file by its name from a database.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} fileName - The name of the file to search for.
|
|
16
|
+
* @return {Promise<File>} A promise that resolves to the file object if found.
|
|
17
|
+
* @throws {HttpError} Throws an error if a database error occurs or if the file is not found.
|
|
18
|
+
*/
|
|
19
|
+
findByName(fileName: string): Promise<File>;
|
|
20
|
+
/**
|
|
21
|
+
* Streams a file from storage, handling authorization and range-based requests.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} fileName - The name of the file to stream.
|
|
24
|
+
* @param {string} [range] - An optional range header specifying the byte range to stream.
|
|
25
|
+
* @return {Promise<FileStream>} A promise that resolves to a file stream object containing the content, metadata, and
|
|
26
|
+
* range details.
|
|
27
|
+
* @throws {HttpError} Throws a 404 error if the file does not exist.
|
|
28
|
+
* @throws {HttpError} Throws a 403 error if user access to the file is forbidden.
|
|
29
|
+
* @throws {HttpError} Throws a 500 error if an error occurs while reading the file from storage.
|
|
30
|
+
*/
|
|
31
|
+
stream(fileName: string, range?: string): Promise<FileStream>;
|
|
32
|
+
/**
|
|
33
|
+
* Saves a file to storage, validates its properties, and associates it with a specific resource and client.
|
|
34
|
+
*
|
|
35
|
+
* @param {MultipartFile} data The multipart file object containing the file data, field name, filename, and metadata.
|
|
36
|
+
* @param {Resource} resource The resource object with which the file is being associated.
|
|
37
|
+
* @param {ResourceClient} client The database client responsible for handling the resource.
|
|
38
|
+
* @return {Promise<File>} A promise that resolves to the saved file object or rejects with an error if the operation
|
|
39
|
+
* fails.
|
|
40
|
+
* @throws {HttpError} Throws an error if file validation, storage, or resource association fails.
|
|
41
|
+
*/
|
|
42
|
+
saveFile(data: MultipartFile, resource: Resource, client: ResourceClient): Promise<File>;
|
|
43
|
+
/**
|
|
44
|
+
* Saves files received as an asynchronous iterable of multipart data.
|
|
45
|
+
*
|
|
46
|
+
* @param {AsyncIterableIterator<Multipart>} parts - The asynchronous iterable providing multipart data where each
|
|
47
|
+
* part is expected to represent a file or other data.
|
|
48
|
+
* @param {Resource} resource - The resource associated with the files being saved, providing additional contextual
|
|
49
|
+
* information.
|
|
50
|
+
* @param {ResourceClient} client - The database client responsible for handling resource-related operations, such as
|
|
51
|
+
* database interaction.
|
|
52
|
+
* @return {Promise<File[]>} A promise that resolves to an array of successfully saved files. Rejects with an error
|
|
53
|
+
* if one or more files fail to save.
|
|
54
|
+
*/
|
|
55
|
+
saveFiles(parts: AsyncIterableIterator<Multipart>, resource: Resource, client: ResourceClient): Promise<File[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Deletes a file from the storage and database if it meets the specified conditions.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} fileName - The name of the file to be deleted.
|
|
60
|
+
* @param {string} fieldName - The field associated with the resource to which the file belongs.
|
|
61
|
+
* @param {Resource} resource - The resource that the file is associated with.
|
|
62
|
+
* @param {ResourceClient} client - The client instance representing the resource's type.
|
|
63
|
+
* @return {Promise<File>} A promise that resolves with the deleted file information upon successful deletion.
|
|
64
|
+
* @throws {HttpError} If the file is not associated with the provided resource or client,
|
|
65
|
+
* if the current user does not have permission to delete the file,
|
|
66
|
+
* if there is an error removing the file from storage,
|
|
67
|
+
* or if there is an error deleting the file from the database.
|
|
68
|
+
*/
|
|
69
|
+
deleteFile(fileName: string, fieldName: string, resource: Resource, client: ResourceClient): Promise<File>;
|
|
70
|
+
/**
|
|
71
|
+
* Deletes a list of files associated with a given resource using the provided resource client.
|
|
72
|
+
*
|
|
73
|
+
* @param {Record<string, string | string[]>} fileNames - An object where the keys correspond to file fields and the
|
|
74
|
+
* values are either a string (single file name) or an array of file names to be deleted.
|
|
75
|
+
* @param {Resource} resource - The resource instance associated with the files to be deleted.
|
|
76
|
+
* @param {ResourceClient} client - The resource a client used to handle deletion operations.
|
|
77
|
+
* @return {Promise<File[]>} A promise that resolves to an array of files that were successfully deleted. Rejects with
|
|
78
|
+
* an error if no files could be deleted.
|
|
79
|
+
*/
|
|
80
|
+
deleteFiles(fileNames: Record<string, string | string[]>, resource: Resource, client: ResourceClient): Promise<File[]>;
|
|
81
|
+
}
|