@adaas/a-server 0.0.20 → 0.0.22
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.mts +126 -10
- package/dist/index.d.ts +126 -10
- package/dist/index.js +504 -230
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +507 -225
- package/dist/index.mjs.map +1 -1
- package/examples/simple-server/concept.ts +8 -1
- package/package.json +2 -2
- package/src/components/A-ServerProxy/A-ServerProxy.component.ts +8 -7
- package/src/components/A-StaticLoader/A-StaticLoader.component.ts +220 -49
- package/src/components/A-StaticLoader/A-StaticLoader.component.types.ts +5 -107
- package/src/containers/A-Service/A-Service.container.ts +38 -18
- package/src/context/A-StaticConfig/A-StaticConfig.context.ts +192 -9
- package/src/entities/A-Request/A-Request.entity.ts +1 -1
- package/src/entities/A-Request/A-Request.entity.types.ts +1 -1
- package/src/entities/A-Response/A-Response.entity.ts +1 -1
- package/src/entities/A-Response/A-Response.entity.types.ts +1 -1
- package/tsconfig.build.json +0 -28
package/dist/index.js
CHANGED
|
@@ -2,22 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var aConcept = require('@adaas/a-concept');
|
|
4
4
|
var aUtils = require('@adaas/a-utils');
|
|
5
|
-
var http = require('http');
|
|
6
|
-
var crypto = require('crypto');
|
|
7
|
-
var https = require('https');
|
|
8
|
-
var fs = require('fs');
|
|
9
|
-
var path = require('path');
|
|
10
|
-
var url = require('url');
|
|
11
5
|
var AEntity_constants = require('@adaas/a-concept/dist/src/global/A-Entity/A-Entity.constants');
|
|
12
6
|
|
|
13
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
-
|
|
15
|
-
var http__default = /*#__PURE__*/_interopDefault(http);
|
|
16
|
-
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
17
|
-
var https__default = /*#__PURE__*/_interopDefault(https);
|
|
18
|
-
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
19
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
20
|
-
|
|
21
7
|
var __defProp = Object.defineProperty;
|
|
22
8
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
23
9
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
@@ -272,13 +258,13 @@ var A_HTTPChannel = class extends aUtils.A_Channel {
|
|
|
272
258
|
config
|
|
273
259
|
});
|
|
274
260
|
}
|
|
275
|
-
buildURL(
|
|
261
|
+
buildURL(path = "", params = {}) {
|
|
276
262
|
if (!this.baseUrl)
|
|
277
263
|
throw new A_HTTPChannelError(
|
|
278
264
|
A_HTTPChannelError.HttpRequestError,
|
|
279
265
|
"Base URL is not set for HTTP Channel"
|
|
280
266
|
);
|
|
281
|
-
const url = new URL(`${this.baseUrl}${
|
|
267
|
+
const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`);
|
|
282
268
|
Object.keys(params).forEach((key) => {
|
|
283
269
|
if (params[key] !== void 0 && params[key] !== null) {
|
|
284
270
|
url.searchParams.append(key, params[key]);
|
|
@@ -615,8 +601,148 @@ var A_Response = class extends aConcept.A_Entity {
|
|
|
615
601
|
}, {});
|
|
616
602
|
}
|
|
617
603
|
};
|
|
604
|
+
var A_ServerLogger = class extends aUtils.A_Logger {
|
|
605
|
+
async onRequestEnd(request, response) {
|
|
606
|
+
this.route({
|
|
607
|
+
method: request.method,
|
|
608
|
+
url: request.url,
|
|
609
|
+
status: response.statusCode,
|
|
610
|
+
responseTime: response.duration
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
async onRequestError(request) {
|
|
614
|
+
}
|
|
615
|
+
logStart(container) {
|
|
616
|
+
this.serverReady({
|
|
617
|
+
port: container.port,
|
|
618
|
+
app: {
|
|
619
|
+
name: container.name
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
logStop(server) {
|
|
624
|
+
this.log("red", `Server ${server.name} stopped`);
|
|
625
|
+
}
|
|
626
|
+
metrics() {
|
|
627
|
+
}
|
|
628
|
+
routes(routes) {
|
|
629
|
+
const time = this.getTime();
|
|
630
|
+
console.log(`\x1B[36m[${this.scope.name}] |${time}| Exposed Routes:
|
|
631
|
+
${" ".repeat(this.scopeLength + 3)}|-------------------------------
|
|
632
|
+
${routes.map((route) => `${" ".repeat(this.scopeLength + 3)}| [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.path}`).join("\n")}
|
|
633
|
+
${" ".repeat(this.scopeLength + 3)}|-------------------------------\x1B[0m`);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Logs the route information based on status code
|
|
637
|
+
*
|
|
638
|
+
* @param route
|
|
639
|
+
*/
|
|
640
|
+
route(route) {
|
|
641
|
+
switch (route.status) {
|
|
642
|
+
case 200:
|
|
643
|
+
this.log200(route);
|
|
644
|
+
break;
|
|
645
|
+
case 404:
|
|
646
|
+
this.log404(route);
|
|
647
|
+
break;
|
|
648
|
+
case 500:
|
|
649
|
+
this.log500(route);
|
|
650
|
+
break;
|
|
651
|
+
case 400:
|
|
652
|
+
this.log400(route);
|
|
653
|
+
break;
|
|
654
|
+
default:
|
|
655
|
+
this.logDefault(route);
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
log200(route) {
|
|
660
|
+
if (this.config.get("SERVER_IGNORE_LOG_200"))
|
|
661
|
+
return;
|
|
662
|
+
console.log(`\x1B[32m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
663
|
+
}
|
|
664
|
+
log404(route) {
|
|
665
|
+
if (this.config.get("SERVER_IGNORE_LOG_404"))
|
|
666
|
+
return;
|
|
667
|
+
console.log(`\x1B[33m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
668
|
+
}
|
|
669
|
+
log500(route) {
|
|
670
|
+
if (this.config.get("SERVER_IGNORE_LOG_500"))
|
|
671
|
+
return;
|
|
672
|
+
console.log(`\x1B[31m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
673
|
+
}
|
|
674
|
+
log400(route) {
|
|
675
|
+
if (this.config.get("SERVER_IGNORE_LOG_400"))
|
|
676
|
+
return;
|
|
677
|
+
console.log(`\x1B[33m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
678
|
+
}
|
|
679
|
+
logDefault(route) {
|
|
680
|
+
if (this.config.get("SERVER_IGNORE_LOG_DEFAULT"))
|
|
681
|
+
return;
|
|
682
|
+
console.log(`\x1B[36m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
683
|
+
}
|
|
684
|
+
serverReady(params) {
|
|
685
|
+
const processId = process.pid;
|
|
686
|
+
console.log(`\x1B[36m[${this.scope.name}] |${this.getTime()}| Server Ready:
|
|
687
|
+
${" ".repeat(this.scopeLength + 3)}|-------------------------------
|
|
688
|
+
${" ".repeat(this.scopeLength + 3)}| ${params.app.name} v${params.app.version || "0.0.1"} is running on port ${params.port}
|
|
689
|
+
${" ".repeat(this.scopeLength + 3)}| Process ID: ${processId}
|
|
690
|
+
${" ".repeat(this.scopeLength + 3)}|-------------------------------
|
|
691
|
+
${" ".repeat(this.scopeLength + 3)}| ==============================
|
|
692
|
+
${" ".repeat(this.scopeLength + 3)}| LISTENING...
|
|
693
|
+
${" ".repeat(this.scopeLength + 3)}| ==============================
|
|
694
|
+
\x1B[0m`);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Displays a proxy routes
|
|
698
|
+
*
|
|
699
|
+
* @param params
|
|
700
|
+
*/
|
|
701
|
+
proxy(params) {
|
|
702
|
+
console.log(`\x1B[35m[${this.scope.name}] |${this.getTime()}| Proxy:
|
|
703
|
+
${" ".repeat(this.scopeLength + 3)}| ${params.original} -> ${params.destination}
|
|
704
|
+
${" ".repeat(this.scopeLength + 3)}|-------------------------------\x1B[0m`);
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
__decorateClass([
|
|
708
|
+
aConcept.A_Feature.Extend({
|
|
709
|
+
name: "finish" /* Finish */,
|
|
710
|
+
scope: [A_Response]
|
|
711
|
+
}),
|
|
712
|
+
__decorateParam(0, aConcept.A_Inject(A_Request)),
|
|
713
|
+
__decorateParam(1, aConcept.A_Inject(A_Response))
|
|
714
|
+
], A_ServerLogger.prototype, "onRequestEnd");
|
|
715
|
+
__decorateClass([
|
|
716
|
+
aConcept.A_Feature.Extend({
|
|
717
|
+
name: "error" /* Error */
|
|
718
|
+
}),
|
|
719
|
+
__decorateParam(0, aConcept.A_Inject(A_Request))
|
|
720
|
+
], A_ServerLogger.prototype, "onRequestError");
|
|
721
|
+
__decorateClass([
|
|
722
|
+
aConcept.A_Feature.Extend({
|
|
723
|
+
name: "afterStart" /* afterStart */,
|
|
724
|
+
scope: [A_Service]
|
|
725
|
+
}),
|
|
726
|
+
__decorateParam(0, aConcept.A_Inject(A_Service))
|
|
727
|
+
], A_ServerLogger.prototype, "logStart");
|
|
728
|
+
__decorateClass([
|
|
729
|
+
aConcept.A_Feature.Extend({
|
|
730
|
+
name: "afterStop" /* afterStop */,
|
|
731
|
+
scope: [A_Service]
|
|
732
|
+
}),
|
|
733
|
+
__decorateParam(0, aConcept.A_Inject(A_Server))
|
|
734
|
+
], A_ServerLogger.prototype, "logStop");
|
|
735
|
+
|
|
736
|
+
// src/containers/A-Service/A-Service.container.ts
|
|
618
737
|
var A_Service = class extends aConcept.A_Container {
|
|
619
738
|
async load() {
|
|
739
|
+
if (!this.scope.has(A_ServerLogger))
|
|
740
|
+
this.scope.register(A_ServerLogger);
|
|
741
|
+
this.scope.resolve(A_ServerLogger);
|
|
742
|
+
let polyfill;
|
|
743
|
+
if (!this.scope.has(aUtils.A_Polyfill))
|
|
744
|
+
this.scope.register(aUtils.A_Polyfill);
|
|
745
|
+
polyfill = this.scope.resolve(aUtils.A_Polyfill);
|
|
620
746
|
let config;
|
|
621
747
|
if (!this.scope.has(aUtils.A_Config)) {
|
|
622
748
|
const config2 = new aUtils.A_Config({
|
|
@@ -636,6 +762,7 @@ var A_Service = class extends aConcept.A_Container {
|
|
|
636
762
|
});
|
|
637
763
|
}
|
|
638
764
|
this.port = config.get("A_SERVER_PORT");
|
|
765
|
+
const http = await polyfill.http();
|
|
639
766
|
this.server = http.createServer(this.onRequest.bind(this));
|
|
640
767
|
}
|
|
641
768
|
listen() {
|
|
@@ -697,20 +824,20 @@ var A_Service = class extends aConcept.A_Container {
|
|
|
697
824
|
}
|
|
698
825
|
async convertToAServer(request, response) {
|
|
699
826
|
if (!request.method || !request.url)
|
|
700
|
-
throw new
|
|
701
|
-
const id = this.generateRequestId(request.method, request.url);
|
|
827
|
+
throw new aConcept.A_Error("Request method or url is missing");
|
|
828
|
+
const id = await this.generateRequestId(request.method, request.url);
|
|
702
829
|
const req = new A_Request({ id, request, scope: this.scope.name });
|
|
703
830
|
const res = new A_Response({ id, response, scope: this.scope.name });
|
|
704
831
|
await req.init();
|
|
705
832
|
await res.init();
|
|
706
833
|
return { req, res };
|
|
707
834
|
}
|
|
708
|
-
generateRequestId(method, url) {
|
|
709
|
-
const
|
|
835
|
+
async generateRequestId(method, url) {
|
|
836
|
+
const crypto = await this.scope.resolve(aUtils.A_Polyfill).crypto();
|
|
710
837
|
const timeId = aConcept.A_IdentityHelper.generateTimeId();
|
|
711
838
|
const randomValue = Math.random().toString();
|
|
712
|
-
hash.
|
|
713
|
-
return `${timeId}-${hash
|
|
839
|
+
const hash = await crypto.createTextHash(`${timeId}-${method}-${url}-${randomValue}`, "sha256");
|
|
840
|
+
return `${timeId}-${hash}`;
|
|
714
841
|
}
|
|
715
842
|
async beforeStop() {
|
|
716
843
|
}
|
|
@@ -771,13 +898,13 @@ var PROXY_CONFIG_DEFAULTS = {
|
|
|
771
898
|
var A_ProxyConfig = class extends aConcept.A_Fragment {
|
|
772
899
|
constructor(configs = {}) {
|
|
773
900
|
super();
|
|
774
|
-
this._configs = Object.entries(configs).map(([
|
|
901
|
+
this._configs = Object.entries(configs).map(([path, config]) => {
|
|
775
902
|
const targetUrl = new URL(typeof config === "string" ? config : config.hostname || "");
|
|
776
903
|
const port = targetUrl.port || (targetUrl.protocol === "https:" ? "443" : "80");
|
|
777
904
|
const prepared = {
|
|
778
905
|
...PROXY_CONFIG_DEFAULTS,
|
|
779
906
|
...typeof config === "string" ? {
|
|
780
|
-
path
|
|
907
|
+
path,
|
|
781
908
|
port: parseInt(port),
|
|
782
909
|
protocol: targetUrl.protocol,
|
|
783
910
|
hostname: targetUrl.hostname
|
|
@@ -805,8 +932,8 @@ var A_ProxyConfig = class extends aConcept.A_Fragment {
|
|
|
805
932
|
* @param path
|
|
806
933
|
* @returns
|
|
807
934
|
*/
|
|
808
|
-
has(
|
|
809
|
-
return this._configs.some((route) => route.route.toRegExp().test(
|
|
935
|
+
has(path) {
|
|
936
|
+
return this._configs.some((route) => route.route.toRegExp().test(path));
|
|
810
937
|
}
|
|
811
938
|
/**
|
|
812
939
|
* Returns the proxy configuration for a given path, if exists
|
|
@@ -814,26 +941,170 @@ var A_ProxyConfig = class extends aConcept.A_Fragment {
|
|
|
814
941
|
* @param path
|
|
815
942
|
* @returns
|
|
816
943
|
*/
|
|
817
|
-
config(
|
|
818
|
-
return this._configs.find((route) => route.route.toRegExp().test(
|
|
944
|
+
config(path) {
|
|
945
|
+
return this._configs.find((route) => route.route.toRegExp().test(path));
|
|
819
946
|
}
|
|
820
947
|
};
|
|
821
948
|
var A_StaticConfig = class extends aConcept.A_Fragment {
|
|
822
|
-
constructor(directories = []) {
|
|
949
|
+
constructor(directories = [], directoryConfigs = []) {
|
|
823
950
|
super();
|
|
951
|
+
this._aliases = /* @__PURE__ */ new Map();
|
|
952
|
+
this._directoryConfigs = [];
|
|
824
953
|
this.directories = directories;
|
|
954
|
+
this._directoryConfigs = directoryConfigs;
|
|
955
|
+
this.initializeDefaultAliases();
|
|
956
|
+
this.initializeCustomAliases();
|
|
957
|
+
}
|
|
958
|
+
initializeDefaultAliases() {
|
|
959
|
+
this.directories.forEach((dir, index) => {
|
|
960
|
+
const alias = {
|
|
961
|
+
alias: `/static${index > 0 ? index : ""}`,
|
|
962
|
+
path: `/static${index > 0 ? index : ""}`,
|
|
963
|
+
directory: dir,
|
|
964
|
+
enabled: true
|
|
965
|
+
};
|
|
966
|
+
this._aliases.set(alias.path, alias);
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
initializeCustomAliases() {
|
|
970
|
+
this._directoryConfigs.forEach((config) => {
|
|
971
|
+
const alias = {
|
|
972
|
+
alias: config.alias || config.path,
|
|
973
|
+
path: config.path,
|
|
974
|
+
directory: config.directory,
|
|
975
|
+
enabled: true
|
|
976
|
+
};
|
|
977
|
+
this._aliases.set(alias.path, alias);
|
|
978
|
+
});
|
|
825
979
|
}
|
|
826
980
|
/**
|
|
827
|
-
*
|
|
828
|
-
*
|
|
981
|
+
* Add a custom static file alias
|
|
982
|
+
* @param alias - The URL path alias (e.g., '/assets')
|
|
983
|
+
* @param directory - The local directory path
|
|
984
|
+
* @param path - Optional custom path (defaults to alias)
|
|
985
|
+
*/
|
|
986
|
+
addAlias(alias, directory, path) {
|
|
987
|
+
const staticAlias = {
|
|
988
|
+
alias,
|
|
989
|
+
path: path || alias,
|
|
990
|
+
directory,
|
|
991
|
+
enabled: true
|
|
992
|
+
};
|
|
993
|
+
this._aliases.set(staticAlias.path, staticAlias);
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Remove a static file alias
|
|
997
|
+
* @param aliasPath - The path of the alias to remove
|
|
998
|
+
*/
|
|
999
|
+
removeAlias(aliasPath) {
|
|
1000
|
+
return this._aliases.delete(aliasPath);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Enable or disable an alias
|
|
1004
|
+
* @param aliasPath - The path of the alias
|
|
1005
|
+
* @param enabled - Whether to enable or disable
|
|
1006
|
+
*/
|
|
1007
|
+
setAliasEnabled(aliasPath, enabled) {
|
|
1008
|
+
const alias = this._aliases.get(aliasPath);
|
|
1009
|
+
if (alias) {
|
|
1010
|
+
alias.enabled = enabled;
|
|
1011
|
+
return true;
|
|
1012
|
+
}
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Get all configured aliases
|
|
1017
|
+
*/
|
|
1018
|
+
getAliases() {
|
|
1019
|
+
return Array.from(this._aliases.values());
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Get enabled aliases only
|
|
1023
|
+
*/
|
|
1024
|
+
getEnabledAliases() {
|
|
1025
|
+
return Array.from(this._aliases.values()).filter((alias) => alias.enabled !== false);
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Find the best matching alias for a given request path
|
|
1029
|
+
* @param requestPath - The request path to match
|
|
1030
|
+
*/
|
|
1031
|
+
findMatchingAlias(requestPath) {
|
|
1032
|
+
let bestMatch = null;
|
|
1033
|
+
let longestMatch = 0;
|
|
1034
|
+
for (const alias of this.getEnabledAliases()) {
|
|
1035
|
+
if (requestPath.startsWith(alias.path) && alias.path.length > longestMatch) {
|
|
1036
|
+
bestMatch = alias;
|
|
1037
|
+
longestMatch = alias.path.length;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return bestMatch;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Check if an alias exists
|
|
1044
|
+
* @param aliasPath - The path to check
|
|
1045
|
+
*/
|
|
1046
|
+
hasAlias(aliasPath) {
|
|
1047
|
+
return this._aliases.has(aliasPath);
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Get a specific alias by path
|
|
1051
|
+
* @param aliasPath - The path of the alias
|
|
1052
|
+
*/
|
|
1053
|
+
getAlias(aliasPath) {
|
|
1054
|
+
return this._aliases.get(aliasPath);
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Add multiple aliases at once
|
|
1058
|
+
* @param aliases - Array of alias configurations
|
|
1059
|
+
*/
|
|
1060
|
+
addAliases(aliases) {
|
|
1061
|
+
aliases.forEach((config) => {
|
|
1062
|
+
this.addAlias(config.alias || config.path, config.directory, config.path);
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Clear all aliases
|
|
1067
|
+
*/
|
|
1068
|
+
clearAliases() {
|
|
1069
|
+
this._aliases.clear();
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Update an existing alias
|
|
1073
|
+
* @param aliasPath - The path of the alias to update
|
|
1074
|
+
* @param updates - Partial updates to apply
|
|
1075
|
+
*/
|
|
1076
|
+
updateAlias(aliasPath, updates) {
|
|
1077
|
+
const alias = this._aliases.get(aliasPath);
|
|
1078
|
+
if (alias) {
|
|
1079
|
+
Object.assign(alias, updates);
|
|
1080
|
+
return true;
|
|
1081
|
+
}
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Get statistics about configured aliases
|
|
1086
|
+
*/
|
|
1087
|
+
getStats() {
|
|
1088
|
+
const aliases = this.getAliases();
|
|
1089
|
+
const enabled = aliases.filter((a) => a.enabled !== false);
|
|
1090
|
+
const disabled = aliases.filter((a) => a.enabled === false);
|
|
1091
|
+
const directories = [...new Set(aliases.map((a) => a.directory))];
|
|
1092
|
+
return {
|
|
1093
|
+
total: aliases.length,
|
|
1094
|
+
enabled: enabled.length,
|
|
1095
|
+
disabled: disabled.length,
|
|
1096
|
+
directories
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Checks if a given path is configured in the proxy (legacy method)
|
|
1101
|
+
* @deprecated Use findMatchingAlias instead
|
|
829
1102
|
* @param path
|
|
830
1103
|
* @returns
|
|
831
1104
|
*/
|
|
832
|
-
has(
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
});
|
|
836
|
-
return !!found && found;
|
|
1105
|
+
has(path) {
|
|
1106
|
+
const alias = this.findMatchingAlias(path);
|
|
1107
|
+
return alias ? alias.directory : false;
|
|
837
1108
|
}
|
|
838
1109
|
};
|
|
839
1110
|
var A_ListQueryFilter = class extends aConcept.A_Fragment {
|
|
@@ -1005,137 +1276,6 @@ var A_EntityList = class extends aConcept.A_Entity {
|
|
|
1005
1276
|
};
|
|
1006
1277
|
}
|
|
1007
1278
|
};
|
|
1008
|
-
var A_ServerLogger = class extends aUtils.A_Logger {
|
|
1009
|
-
async onRequestEnd(request, response) {
|
|
1010
|
-
this.route({
|
|
1011
|
-
method: request.method,
|
|
1012
|
-
url: request.url,
|
|
1013
|
-
status: response.statusCode,
|
|
1014
|
-
responseTime: response.duration
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
async onRequestError(request) {
|
|
1018
|
-
}
|
|
1019
|
-
logStart(container) {
|
|
1020
|
-
this.serverReady({
|
|
1021
|
-
port: container.port,
|
|
1022
|
-
app: {
|
|
1023
|
-
name: container.name
|
|
1024
|
-
}
|
|
1025
|
-
});
|
|
1026
|
-
}
|
|
1027
|
-
logStop(server) {
|
|
1028
|
-
this.log("red", `Server ${server.name} stopped`);
|
|
1029
|
-
}
|
|
1030
|
-
metrics() {
|
|
1031
|
-
}
|
|
1032
|
-
routes(routes) {
|
|
1033
|
-
const time = this.getTime();
|
|
1034
|
-
console.log(`\x1B[36m[${this.scope.name}] |${time}| Exposed Routes:
|
|
1035
|
-
${" ".repeat(this.scopeLength + 3)}|-------------------------------
|
|
1036
|
-
${routes.map((route) => `${" ".repeat(this.scopeLength + 3)}| [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.path}`).join("\n")}
|
|
1037
|
-
${" ".repeat(this.scopeLength + 3)}|-------------------------------\x1B[0m`);
|
|
1038
|
-
}
|
|
1039
|
-
/**
|
|
1040
|
-
* Logs the route information based on status code
|
|
1041
|
-
*
|
|
1042
|
-
* @param route
|
|
1043
|
-
*/
|
|
1044
|
-
route(route) {
|
|
1045
|
-
switch (route.status) {
|
|
1046
|
-
case 200:
|
|
1047
|
-
this.log200(route);
|
|
1048
|
-
break;
|
|
1049
|
-
case 404:
|
|
1050
|
-
this.log404(route);
|
|
1051
|
-
break;
|
|
1052
|
-
case 500:
|
|
1053
|
-
this.log500(route);
|
|
1054
|
-
break;
|
|
1055
|
-
case 400:
|
|
1056
|
-
this.log400(route);
|
|
1057
|
-
break;
|
|
1058
|
-
default:
|
|
1059
|
-
this.logDefault(route);
|
|
1060
|
-
break;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
log200(route) {
|
|
1064
|
-
if (this.config.get("SERVER_IGNORE_LOG_200"))
|
|
1065
|
-
return;
|
|
1066
|
-
console.log(`\x1B[32m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
1067
|
-
}
|
|
1068
|
-
log404(route) {
|
|
1069
|
-
if (this.config.get("SERVER_IGNORE_LOG_404"))
|
|
1070
|
-
return;
|
|
1071
|
-
console.log(`\x1B[33m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
1072
|
-
}
|
|
1073
|
-
log500(route) {
|
|
1074
|
-
if (this.config.get("SERVER_IGNORE_LOG_500"))
|
|
1075
|
-
return;
|
|
1076
|
-
console.log(`\x1B[31m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
1077
|
-
}
|
|
1078
|
-
log400(route) {
|
|
1079
|
-
if (this.config.get("SERVER_IGNORE_LOG_400"))
|
|
1080
|
-
return;
|
|
1081
|
-
console.log(`\x1B[33m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
1082
|
-
}
|
|
1083
|
-
logDefault(route) {
|
|
1084
|
-
if (this.config.get("SERVER_IGNORE_LOG_DEFAULT"))
|
|
1085
|
-
return;
|
|
1086
|
-
console.log(`\x1B[36m[${this.scope.name}] |${this.getTime()}| ${route.status} | [${route.method.toUpperCase()}]${" ".repeat(7 - route.method.length)} ${route.url} | ${route.responseTime}ms\x1B[0m`);
|
|
1087
|
-
}
|
|
1088
|
-
serverReady(params) {
|
|
1089
|
-
const processId = process.pid;
|
|
1090
|
-
console.log(`\x1B[36m[${this.scope.name}] |${this.getTime()}| Server Ready:
|
|
1091
|
-
${" ".repeat(this.scopeLength + 3)}|-------------------------------
|
|
1092
|
-
${" ".repeat(this.scopeLength + 3)}| ${params.app.name} v${params.app.version || "0.0.1"} is running on port ${params.port}
|
|
1093
|
-
${" ".repeat(this.scopeLength + 3)}| Process ID: ${processId}
|
|
1094
|
-
${" ".repeat(this.scopeLength + 3)}|-------------------------------
|
|
1095
|
-
${" ".repeat(this.scopeLength + 3)}| ==============================
|
|
1096
|
-
${" ".repeat(this.scopeLength + 3)}| LISTENING...
|
|
1097
|
-
${" ".repeat(this.scopeLength + 3)}| ==============================
|
|
1098
|
-
\x1B[0m`);
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Displays a proxy routes
|
|
1102
|
-
*
|
|
1103
|
-
* @param params
|
|
1104
|
-
*/
|
|
1105
|
-
proxy(params) {
|
|
1106
|
-
console.log(`\x1B[35m[${this.scope.name}] |${this.getTime()}| Proxy:
|
|
1107
|
-
${" ".repeat(this.scopeLength + 3)}| ${params.original} -> ${params.destination}
|
|
1108
|
-
${" ".repeat(this.scopeLength + 3)}|-------------------------------\x1B[0m`);
|
|
1109
|
-
}
|
|
1110
|
-
};
|
|
1111
|
-
__decorateClass([
|
|
1112
|
-
aConcept.A_Feature.Extend({
|
|
1113
|
-
name: "finish" /* Finish */,
|
|
1114
|
-
scope: [A_Response]
|
|
1115
|
-
}),
|
|
1116
|
-
__decorateParam(0, aConcept.A_Inject(A_Request)),
|
|
1117
|
-
__decorateParam(1, aConcept.A_Inject(A_Response))
|
|
1118
|
-
], A_ServerLogger.prototype, "onRequestEnd");
|
|
1119
|
-
__decorateClass([
|
|
1120
|
-
aConcept.A_Feature.Extend({
|
|
1121
|
-
name: "error" /* Error */
|
|
1122
|
-
}),
|
|
1123
|
-
__decorateParam(0, aConcept.A_Inject(A_Request))
|
|
1124
|
-
], A_ServerLogger.prototype, "onRequestError");
|
|
1125
|
-
__decorateClass([
|
|
1126
|
-
aConcept.A_Feature.Extend({
|
|
1127
|
-
name: "afterStart" /* afterStart */,
|
|
1128
|
-
scope: [A_Service]
|
|
1129
|
-
}),
|
|
1130
|
-
__decorateParam(0, aConcept.A_Inject(A_Service))
|
|
1131
|
-
], A_ServerLogger.prototype, "logStart");
|
|
1132
|
-
__decorateClass([
|
|
1133
|
-
aConcept.A_Feature.Extend({
|
|
1134
|
-
name: "afterStop" /* afterStop */,
|
|
1135
|
-
scope: [A_Service]
|
|
1136
|
-
}),
|
|
1137
|
-
__decorateParam(0, aConcept.A_Inject(A_Server))
|
|
1138
|
-
], A_ServerLogger.prototype, "logStop");
|
|
1139
1279
|
|
|
1140
1280
|
// src/components/A-Router/A-Router.component.types.ts
|
|
1141
1281
|
var A_SERVER_TYPES__RouterMethod = /* @__PURE__ */ ((A_SERVER_TYPES__RouterMethod2) => {
|
|
@@ -1161,12 +1301,12 @@ var _A_Router = class _A_Router extends aConcept.A_Component {
|
|
|
1161
1301
|
* @param path
|
|
1162
1302
|
* @returns
|
|
1163
1303
|
*/
|
|
1164
|
-
static Post(
|
|
1304
|
+
static Post(path) {
|
|
1165
1305
|
return this.defineRoute({
|
|
1166
1306
|
method: "POST" /* POST */,
|
|
1167
|
-
path: typeof
|
|
1168
|
-
version: typeof
|
|
1169
|
-
prefix: typeof
|
|
1307
|
+
path: typeof path === "object" && "path" in path ? path.path : path,
|
|
1308
|
+
version: typeof path === "object" && "version" in path && path.version ? path.version : "v1",
|
|
1309
|
+
prefix: typeof path === "object" && "prefix" in path && path.prefix ? path.prefix : "api"
|
|
1170
1310
|
});
|
|
1171
1311
|
}
|
|
1172
1312
|
/**
|
|
@@ -1175,12 +1315,12 @@ var _A_Router = class _A_Router extends aConcept.A_Component {
|
|
|
1175
1315
|
* @param path
|
|
1176
1316
|
* @returns
|
|
1177
1317
|
*/
|
|
1178
|
-
static Get(
|
|
1318
|
+
static Get(path) {
|
|
1179
1319
|
return this.defineRoute({
|
|
1180
1320
|
method: "GET" /* GET */,
|
|
1181
|
-
path: typeof
|
|
1182
|
-
version: typeof
|
|
1183
|
-
prefix: typeof
|
|
1321
|
+
path: typeof path === "object" && "path" in path ? path.path : path,
|
|
1322
|
+
version: typeof path === "object" && "version" in path && path.version ? path.version : "v1",
|
|
1323
|
+
prefix: typeof path === "object" && "prefix" in path && path.prefix ? path.prefix : "api"
|
|
1184
1324
|
});
|
|
1185
1325
|
}
|
|
1186
1326
|
/**
|
|
@@ -1189,12 +1329,12 @@ var _A_Router = class _A_Router extends aConcept.A_Component {
|
|
|
1189
1329
|
* @param path
|
|
1190
1330
|
* @returns
|
|
1191
1331
|
*/
|
|
1192
|
-
static Put(
|
|
1332
|
+
static Put(path) {
|
|
1193
1333
|
return this.defineRoute({
|
|
1194
1334
|
method: "PUT" /* PUT */,
|
|
1195
|
-
path: typeof
|
|
1196
|
-
version: typeof
|
|
1197
|
-
prefix: typeof
|
|
1335
|
+
path: typeof path === "object" && "path" in path ? path.path : path,
|
|
1336
|
+
version: typeof path === "object" && "version" in path && path.version ? path.version : "v1",
|
|
1337
|
+
prefix: typeof path === "object" && "prefix" in path && path.prefix ? path.prefix : "api"
|
|
1198
1338
|
});
|
|
1199
1339
|
}
|
|
1200
1340
|
/**
|
|
@@ -1203,12 +1343,12 @@ var _A_Router = class _A_Router extends aConcept.A_Component {
|
|
|
1203
1343
|
* @param path
|
|
1204
1344
|
* @returns
|
|
1205
1345
|
*/
|
|
1206
|
-
static Delete(
|
|
1346
|
+
static Delete(path) {
|
|
1207
1347
|
return this.defineRoute({
|
|
1208
1348
|
method: "DELETE" /* DELETE */,
|
|
1209
|
-
path: typeof
|
|
1210
|
-
version: typeof
|
|
1211
|
-
prefix: typeof
|
|
1349
|
+
path: typeof path === "object" && "path" in path ? path.path : path,
|
|
1350
|
+
version: typeof path === "object" && "version" in path && path.version ? path.version : "v1",
|
|
1351
|
+
prefix: typeof path === "object" && "prefix" in path && path.prefix ? path.prefix : "api"
|
|
1212
1352
|
});
|
|
1213
1353
|
}
|
|
1214
1354
|
/**
|
|
@@ -1217,12 +1357,12 @@ var _A_Router = class _A_Router extends aConcept.A_Component {
|
|
|
1217
1357
|
* @param path
|
|
1218
1358
|
* @returns
|
|
1219
1359
|
*/
|
|
1220
|
-
static Patch(
|
|
1360
|
+
static Patch(path) {
|
|
1221
1361
|
return this.defineRoute({
|
|
1222
1362
|
method: "PATCH" /* PATCH */,
|
|
1223
|
-
path: typeof
|
|
1224
|
-
version: typeof
|
|
1225
|
-
prefix: typeof
|
|
1363
|
+
path: typeof path === "object" && "path" in path ? path.path : path,
|
|
1364
|
+
version: typeof path === "object" && "version" in path && path.version ? path.version : "v1",
|
|
1365
|
+
prefix: typeof path === "object" && "prefix" in path && path.prefix ? path.prefix : "api"
|
|
1226
1366
|
});
|
|
1227
1367
|
}
|
|
1228
1368
|
/**
|
|
@@ -1231,12 +1371,12 @@ var _A_Router = class _A_Router extends aConcept.A_Component {
|
|
|
1231
1371
|
* @param path
|
|
1232
1372
|
* @returns
|
|
1233
1373
|
*/
|
|
1234
|
-
static Default(
|
|
1374
|
+
static Default(path) {
|
|
1235
1375
|
return this.defineRoute({
|
|
1236
1376
|
method: "DEFAULT" /* DEFAULT */,
|
|
1237
|
-
path: typeof
|
|
1238
|
-
version: typeof
|
|
1239
|
-
prefix: typeof
|
|
1377
|
+
path: typeof path === "object" && "path" in path ? path.path : path,
|
|
1378
|
+
version: typeof path === "object" && "version" in path && path.version ? path.version : "v1",
|
|
1379
|
+
prefix: typeof path === "object" && "prefix" in path && path.prefix ? path.prefix : "api"
|
|
1240
1380
|
});
|
|
1241
1381
|
}
|
|
1242
1382
|
/**
|
|
@@ -1523,8 +1663,8 @@ var A_ServerProxy = class extends aConcept.A_Component {
|
|
|
1523
1663
|
config.configs.map((c) => c.route).join("\n")
|
|
1524
1664
|
);
|
|
1525
1665
|
}
|
|
1526
|
-
async onRequest(req, res, proxyConfig, logger) {
|
|
1527
|
-
return new Promise((resolve, reject) => {
|
|
1666
|
+
async onRequest(req, res, proxyConfig, logger, polyfill) {
|
|
1667
|
+
return new Promise(async (resolve, reject) => {
|
|
1528
1668
|
const { method, url } = req;
|
|
1529
1669
|
const route = new A_Route(url, method);
|
|
1530
1670
|
const config = proxyConfig.config(route.toString());
|
|
@@ -1536,7 +1676,7 @@ var A_ServerProxy = class extends aConcept.A_Component {
|
|
|
1536
1676
|
`Proxying request ${method} ${url} to ${config.hostname}`,
|
|
1537
1677
|
config
|
|
1538
1678
|
);
|
|
1539
|
-
const client = config.protocol === "https:" ?
|
|
1679
|
+
const client = await (config.protocol === "https:" ? polyfill.https() : polyfill.http());
|
|
1540
1680
|
const proxyReq = client.request(
|
|
1541
1681
|
{
|
|
1542
1682
|
method: config.route.method,
|
|
@@ -1573,7 +1713,8 @@ __decorateClass([
|
|
|
1573
1713
|
__decorateParam(0, aConcept.A_Inject(A_Request)),
|
|
1574
1714
|
__decorateParam(1, aConcept.A_Inject(A_Response)),
|
|
1575
1715
|
__decorateParam(2, aConcept.A_Inject(A_ProxyConfig)),
|
|
1576
|
-
__decorateParam(3, aConcept.A_Inject(aUtils.A_Logger))
|
|
1716
|
+
__decorateParam(3, aConcept.A_Inject(aUtils.A_Logger)),
|
|
1717
|
+
__decorateParam(4, aConcept.A_Inject(aUtils.A_Polyfill))
|
|
1577
1718
|
], A_ServerProxy.prototype, "onRequest");
|
|
1578
1719
|
|
|
1579
1720
|
// src/components/A-ServerCORS/A_ServerCORS.component.defaults.ts
|
|
@@ -1624,80 +1765,212 @@ __decorateClass([
|
|
|
1624
1765
|
__decorateParam(1, aConcept.A_Inject(A_Response))
|
|
1625
1766
|
], A_ServerCORS.prototype, "apply");
|
|
1626
1767
|
var A_StaticLoader = class extends aConcept.A_Component {
|
|
1627
|
-
async load(logger, config) {
|
|
1768
|
+
async load(logger, config, polyfill) {
|
|
1769
|
+
this._fsPolyfill = await polyfill.fs();
|
|
1770
|
+
this._pathPolyfill = await polyfill.path();
|
|
1771
|
+
const aliases = config.getEnabledAliases();
|
|
1628
1772
|
logger.log(
|
|
1629
1773
|
"pink",
|
|
1630
|
-
`Static
|
|
1631
|
-
|
|
1774
|
+
`Static aliases configured:`,
|
|
1775
|
+
aliases.map((alias) => `${alias.alias} -> ${alias.directory}`).join("\n")
|
|
1632
1776
|
);
|
|
1633
1777
|
}
|
|
1634
|
-
async onRequest(req, res, logger, config) {
|
|
1778
|
+
async onRequest(req, res, logger, config, polyfill) {
|
|
1635
1779
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
1636
1780
|
return;
|
|
1637
1781
|
}
|
|
1638
1782
|
const { method, url } = req;
|
|
1639
1783
|
const route = new A_Route(url, method);
|
|
1640
|
-
const
|
|
1641
|
-
if (!
|
|
1784
|
+
const alias = config.findMatchingAlias(route.path);
|
|
1785
|
+
if (!alias) {
|
|
1642
1786
|
return;
|
|
1643
1787
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1788
|
+
try {
|
|
1789
|
+
const fs = this._fsPolyfill || await polyfill.fs();
|
|
1790
|
+
const path = this._pathPolyfill || await polyfill.path();
|
|
1791
|
+
const staticDir = path.resolve(process.cwd(), alias.directory);
|
|
1792
|
+
if (!fs.existsSync(staticDir)) {
|
|
1793
|
+
logger.log("red", `Static directory ${staticDir} does not exist.`);
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
const relativePath = route.path.replace(alias.path, "");
|
|
1797
|
+
const safePath = this.safeFilePath(staticDir, relativePath, req.headers?.host, path, fs);
|
|
1798
|
+
await this.serveFile(safePath, res, logger, fs, path);
|
|
1799
|
+
} catch (error) {
|
|
1800
|
+
logger.error(`Static file serving error: ${error.message}`);
|
|
1801
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
1802
|
+
res.send("File not found");
|
|
1648
1803
|
}
|
|
1649
|
-
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Add a custom static file alias through the config
|
|
1807
|
+
* @param alias - The URL path alias (e.g., '/assets')
|
|
1808
|
+
* @param directory - The local directory path
|
|
1809
|
+
* @param path - Optional custom path (defaults to alias)
|
|
1810
|
+
* @param config - Static config instance
|
|
1811
|
+
* @param logger - Logger instance for logging
|
|
1812
|
+
*/
|
|
1813
|
+
addAlias(alias, directory, config, logger, path) {
|
|
1814
|
+
config.addAlias(alias, directory, path);
|
|
1815
|
+
if (logger) {
|
|
1816
|
+
logger.log("cyan", `Static alias added: ${alias} -> ${directory}`);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Remove a static file alias through the config
|
|
1821
|
+
* @param aliasPath - The path of the alias to remove
|
|
1822
|
+
* @param config - Static config instance
|
|
1823
|
+
* @param logger - Logger instance for logging
|
|
1824
|
+
*/
|
|
1825
|
+
removeAlias(aliasPath, config, logger) {
|
|
1826
|
+
const removed = config.removeAlias(aliasPath);
|
|
1827
|
+
if (removed && logger) {
|
|
1828
|
+
logger.log("yellow", `Static alias removed: ${aliasPath}`);
|
|
1829
|
+
}
|
|
1830
|
+
return removed;
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Get all configured aliases from config
|
|
1834
|
+
* @param config - Static config instance
|
|
1835
|
+
*/
|
|
1836
|
+
getAliases(config) {
|
|
1837
|
+
return config.getAliases();
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* Enable or disable an alias
|
|
1841
|
+
* @param aliasPath - The path of the alias
|
|
1842
|
+
* @param enabled - Whether to enable or disable
|
|
1843
|
+
* @param config - Static config instance
|
|
1844
|
+
* @param logger - Logger instance for logging
|
|
1845
|
+
*/
|
|
1846
|
+
setAliasEnabled(aliasPath, enabled, config, logger) {
|
|
1847
|
+
const result = config.setAliasEnabled(aliasPath, enabled);
|
|
1848
|
+
if (result && logger) {
|
|
1849
|
+
logger.log("blue", `Static alias ${enabled ? "enabled" : "disabled"}: ${aliasPath}`);
|
|
1850
|
+
}
|
|
1851
|
+
return result;
|
|
1650
1852
|
}
|
|
1651
1853
|
getMimeType(ext) {
|
|
1652
1854
|
const mimeTypes = {
|
|
1855
|
+
// Text
|
|
1653
1856
|
".html": "text/html",
|
|
1654
|
-
".
|
|
1857
|
+
".htm": "text/html",
|
|
1655
1858
|
".css": "text/css",
|
|
1859
|
+
".txt": "text/plain",
|
|
1860
|
+
".md": "text/markdown",
|
|
1861
|
+
".xml": "application/xml",
|
|
1862
|
+
// JavaScript
|
|
1863
|
+
".js": "application/javascript",
|
|
1864
|
+
".mjs": "application/javascript",
|
|
1865
|
+
".jsx": "application/javascript",
|
|
1866
|
+
".ts": "application/typescript",
|
|
1867
|
+
".tsx": "application/typescript",
|
|
1868
|
+
// JSON
|
|
1656
1869
|
".json": "application/json",
|
|
1870
|
+
".jsonld": "application/ld+json",
|
|
1871
|
+
// Images
|
|
1657
1872
|
".png": "image/png",
|
|
1658
1873
|
".jpg": "image/jpeg",
|
|
1659
1874
|
".jpeg": "image/jpeg",
|
|
1660
1875
|
".gif": "image/gif",
|
|
1661
1876
|
".svg": "image/svg+xml",
|
|
1662
1877
|
".ico": "image/x-icon",
|
|
1663
|
-
".
|
|
1878
|
+
".webp": "image/webp",
|
|
1879
|
+
".bmp": "image/bmp",
|
|
1880
|
+
".tiff": "image/tiff",
|
|
1881
|
+
// Fonts
|
|
1882
|
+
".woff": "font/woff",
|
|
1883
|
+
".woff2": "font/woff2",
|
|
1884
|
+
".ttf": "font/ttf",
|
|
1885
|
+
".otf": "font/otf",
|
|
1886
|
+
".eot": "application/vnd.ms-fontobject",
|
|
1887
|
+
// Audio/Video
|
|
1888
|
+
".mp3": "audio/mpeg",
|
|
1889
|
+
".wav": "audio/wav",
|
|
1890
|
+
".mp4": "video/mp4",
|
|
1891
|
+
".webm": "video/webm",
|
|
1892
|
+
".ogg": "application/ogg",
|
|
1893
|
+
// Archives
|
|
1894
|
+
".zip": "application/zip",
|
|
1895
|
+
".tar": "application/x-tar",
|
|
1896
|
+
".gz": "application/gzip",
|
|
1897
|
+
// Documents
|
|
1898
|
+
".pdf": "application/pdf",
|
|
1899
|
+
".doc": "application/msword",
|
|
1900
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1901
|
+
".xls": "application/vnd.ms-excel",
|
|
1902
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
1664
1903
|
};
|
|
1665
1904
|
return mimeTypes[ext.toLowerCase()] || "application/octet-stream";
|
|
1666
1905
|
}
|
|
1667
|
-
safeFilePath(staticDir, reqUrl, host) {
|
|
1668
|
-
const parsedUrl = new
|
|
1906
|
+
safeFilePath(staticDir, reqUrl, host = "localhost", pathPolyfill, fsPolyfill) {
|
|
1907
|
+
const parsedUrl = new URL(reqUrl || "/", `http://${host}`);
|
|
1669
1908
|
let pathname = decodeURIComponent(parsedUrl.pathname);
|
|
1670
1909
|
pathname = pathname.replace(/\.\.[\/\\]/g, "");
|
|
1671
|
-
let filePath =
|
|
1672
|
-
if (!
|
|
1910
|
+
let filePath = pathPolyfill.join(staticDir, pathname);
|
|
1911
|
+
if (!fsPolyfill.existsSync(filePath)) {
|
|
1673
1912
|
throw new Error(`File not found: ${filePath}`);
|
|
1913
|
+
}
|
|
1674
1914
|
return filePath;
|
|
1675
1915
|
}
|
|
1676
|
-
serveFile(filePath, res) {
|
|
1916
|
+
serveFile(filePath, res, logger, fsPolyfill, pathPolyfill) {
|
|
1677
1917
|
return new Promise((resolve, reject) => {
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1918
|
+
try {
|
|
1919
|
+
if (fsPolyfill.existsSync(filePath)) {
|
|
1920
|
+
const ext = pathPolyfill.extname(filePath);
|
|
1921
|
+
const contentType = this.getMimeType(ext);
|
|
1922
|
+
const headers = {
|
|
1923
|
+
"Content-Type": contentType,
|
|
1924
|
+
"Cache-Control": this.getCacheControl(ext),
|
|
1925
|
+
"X-Content-Type-Options": "nosniff"
|
|
1926
|
+
};
|
|
1927
|
+
res.writeHead(200, headers);
|
|
1928
|
+
const stream = fsPolyfill.createReadStream(filePath);
|
|
1929
|
+
if (stream && res.original) {
|
|
1930
|
+
stream.pipe(res.original);
|
|
1931
|
+
stream.on("end", () => {
|
|
1932
|
+
logger.log("green", `Successfully served: ${filePath}`);
|
|
1933
|
+
resolve();
|
|
1934
|
+
});
|
|
1935
|
+
stream.on("error", (err) => {
|
|
1936
|
+
logger.error(`File stream error: ${err.message}`);
|
|
1937
|
+
reject(new Error(`File stream error: ${err.message}`));
|
|
1938
|
+
});
|
|
1939
|
+
} else {
|
|
1940
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
1941
|
+
res.send("Internal server error");
|
|
1942
|
+
reject(new Error("Failed to create file stream"));
|
|
1943
|
+
}
|
|
1944
|
+
} else {
|
|
1945
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
1946
|
+
res.send("File not found");
|
|
1947
|
+
reject(new Error(`File not found: ${filePath}`));
|
|
1948
|
+
}
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
logger.error(`Error serving file: ${error.message}`);
|
|
1951
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
1952
|
+
res.send("Internal server error");
|
|
1953
|
+
reject(error);
|
|
1692
1954
|
}
|
|
1693
|
-
resolve();
|
|
1694
1955
|
});
|
|
1695
1956
|
}
|
|
1957
|
+
getCacheControl(ext) {
|
|
1958
|
+
const staticAssets = [".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".woff", ".woff2", ".ttf", ".otf"];
|
|
1959
|
+
const dynamicContent = [".html", ".htm"];
|
|
1960
|
+
if (staticAssets.includes(ext.toLowerCase())) {
|
|
1961
|
+
return "public, max-age=31536000";
|
|
1962
|
+
} else if (dynamicContent.includes(ext.toLowerCase())) {
|
|
1963
|
+
return "public, max-age=3600";
|
|
1964
|
+
} else {
|
|
1965
|
+
return "public, max-age=86400";
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1696
1968
|
};
|
|
1697
1969
|
__decorateClass([
|
|
1698
1970
|
aConcept.A_Concept.Load(),
|
|
1699
1971
|
__decorateParam(0, aConcept.A_Inject(aUtils.A_Logger)),
|
|
1700
|
-
__decorateParam(1, aConcept.A_Inject(A_StaticConfig))
|
|
1972
|
+
__decorateParam(1, aConcept.A_Inject(A_StaticConfig)),
|
|
1973
|
+
__decorateParam(2, aConcept.A_Inject(aUtils.A_Polyfill))
|
|
1701
1974
|
], A_StaticLoader.prototype, "load");
|
|
1702
1975
|
__decorateClass([
|
|
1703
1976
|
aConcept.A_Feature.Extend({
|
|
@@ -1706,7 +1979,8 @@ __decorateClass([
|
|
|
1706
1979
|
__decorateParam(0, aConcept.A_Inject(A_Request)),
|
|
1707
1980
|
__decorateParam(1, aConcept.A_Inject(A_Response)),
|
|
1708
1981
|
__decorateParam(2, aConcept.A_Inject(aUtils.A_Logger)),
|
|
1709
|
-
__decorateParam(3, aConcept.A_Inject(A_StaticConfig))
|
|
1982
|
+
__decorateParam(3, aConcept.A_Inject(A_StaticConfig)),
|
|
1983
|
+
__decorateParam(4, aConcept.A_Inject(aUtils.A_Polyfill))
|
|
1710
1984
|
], A_StaticLoader.prototype, "onRequest");
|
|
1711
1985
|
var A_Controller = class extends aConcept.A_Component {
|
|
1712
1986
|
async callEntityMethod(request, response, scope) {
|