@akanjs/server 0.0.54 → 0.0.56
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/index.mjs +1 -0
- package/package.json +7 -1
- package/src/boot.mjs +191 -0
- package/src/controller.mjs +109 -0
- package/src/gql.mjs +123 -0
- package/src/index.mjs +10 -0
- package/src/module.mjs +229 -0
- package/src/processor.mjs +73 -0
- package/src/resolver.mjs +118 -0
- package/src/schema.mjs +219 -0
- package/src/searchDaemon.mjs +212 -0
- package/src/types.mjs +0 -0
- package/src/websocket.mjs +124 -0
package/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./src";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.56",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"postinstall": "pnpm install @nestjs/mongoose@^10.1.0"
|
|
@@ -41,5 +41,11 @@
|
|
|
41
41
|
"mongoose": "^8.9.3",
|
|
42
42
|
"redis": "^4.7.0",
|
|
43
43
|
"rxjs": "^7.8.1"
|
|
44
|
+
},
|
|
45
|
+
"exports": {
|
|
46
|
+
".": {
|
|
47
|
+
"require": "./index.js",
|
|
48
|
+
"import": "./index.mjs"
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
}
|
package/src/boot.mjs
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result)
|
|
9
|
+
__defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
import { baseEnv, logo } from "@akanjs/base";
|
|
13
|
+
import { Logger } from "@akanjs/common";
|
|
14
|
+
import {
|
|
15
|
+
AllExceptionsFilter,
|
|
16
|
+
CacheClient,
|
|
17
|
+
CacheInterceptor,
|
|
18
|
+
DatabaseClient,
|
|
19
|
+
generateJwtSecret,
|
|
20
|
+
generateMeiliKey,
|
|
21
|
+
generateMeiliUri,
|
|
22
|
+
generateMongoUri,
|
|
23
|
+
generateRedisUri,
|
|
24
|
+
initMongoDB,
|
|
25
|
+
LoggingInterceptor,
|
|
26
|
+
RedisIoAdapter,
|
|
27
|
+
SearchClient,
|
|
28
|
+
TimeoutInterceptor,
|
|
29
|
+
verifyToken
|
|
30
|
+
} from "@akanjs/nest";
|
|
31
|
+
import { client } from "@akanjs/signal";
|
|
32
|
+
import { ApolloDriver } from "@nestjs/apollo";
|
|
33
|
+
import { BullModule } from "@nestjs/bull";
|
|
34
|
+
import {
|
|
35
|
+
Global,
|
|
36
|
+
Injectable,
|
|
37
|
+
Module,
|
|
38
|
+
RequestMethod
|
|
39
|
+
} from "@nestjs/common";
|
|
40
|
+
import { NestFactory } from "@nestjs/core";
|
|
41
|
+
import { GraphQLModule } from "@nestjs/graphql";
|
|
42
|
+
import { MongooseModule } from "@nestjs/mongoose";
|
|
43
|
+
import { ScheduleModule } from "@nestjs/schedule";
|
|
44
|
+
import { json, urlencoded } from "body-parser";
|
|
45
|
+
import cookieParser from "cookie-parser";
|
|
46
|
+
import dgram from "dgram";
|
|
47
|
+
import events from "events";
|
|
48
|
+
import { graphqlUploadExpress } from "graphql-upload";
|
|
49
|
+
import { MeiliSearch } from "meilisearch";
|
|
50
|
+
import { join } from "path";
|
|
51
|
+
import { createClient } from "redis";
|
|
52
|
+
import { DateScalar } from "./gql";
|
|
53
|
+
import { useGlobals } from "./module";
|
|
54
|
+
import { SearchDaemonModule } from "./searchDaemon";
|
|
55
|
+
const createNestApp = async ({ registerModules, serverMode = "federation", env, log = true }) => {
|
|
56
|
+
const backendLogger = new Logger("Backend");
|
|
57
|
+
if (log)
|
|
58
|
+
backendLogger.rawLog(logo);
|
|
59
|
+
const jwtSecret = generateJwtSecret(env.appName, env.environment);
|
|
60
|
+
const [redisUri, mongoUri, meiliUri] = await Promise.all([
|
|
61
|
+
env.redisUri ?? generateRedisUri({ ...env, ...env.redis?.sshOptions ? { sshOptions: env.redis.sshOptions } : {} }),
|
|
62
|
+
env.mongoUri ?? generateMongoUri({
|
|
63
|
+
...env,
|
|
64
|
+
...env.mongo.username ? { username: env.mongo.username } : {},
|
|
65
|
+
password: env.mongo.password,
|
|
66
|
+
...env.mongo.sshOptions ? { sshOptions: env.mongo.sshOptions } : {}
|
|
67
|
+
}),
|
|
68
|
+
env.meiliUri ?? generateMeiliUri(env)
|
|
69
|
+
]);
|
|
70
|
+
if (env.operationMode === "local") {
|
|
71
|
+
backendLogger.verbose(`connect to redis: ${redisUri}`);
|
|
72
|
+
backendLogger.verbose(`connect to mongo: ${mongoUri}`);
|
|
73
|
+
backendLogger.verbose(`connect to meili: ${meiliUri}`);
|
|
74
|
+
}
|
|
75
|
+
initMongoDB({ logging: baseEnv.environment !== "main", sendReport: false });
|
|
76
|
+
events.EventEmitter.defaultMaxListeners = 1e3;
|
|
77
|
+
let AuthMiddleWare = class {
|
|
78
|
+
use(req, res, next) {
|
|
79
|
+
const requestHeader = req;
|
|
80
|
+
requestHeader.account = verifyToken(
|
|
81
|
+
jwtSecret,
|
|
82
|
+
requestHeader.headers.authorization ?? (requestHeader.cookies?.jwt ? `Bearer ${requestHeader.cookies.jwt}` : void 0)
|
|
83
|
+
);
|
|
84
|
+
requestHeader.userAgent = requestHeader["user-agent"];
|
|
85
|
+
next();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
AuthMiddleWare = __decorateClass([
|
|
89
|
+
Injectable()
|
|
90
|
+
], AuthMiddleWare);
|
|
91
|
+
const redisClient = await createClient({ url: redisUri }).connect();
|
|
92
|
+
let SubDatabaseModule = class {
|
|
93
|
+
};
|
|
94
|
+
SubDatabaseModule = __decorateClass([
|
|
95
|
+
Global(),
|
|
96
|
+
Module({
|
|
97
|
+
providers: [
|
|
98
|
+
{ provide: "REDIS_CLIENT", useValue: redisClient },
|
|
99
|
+
{
|
|
100
|
+
provide: "MEILI_CLIENT",
|
|
101
|
+
useFactory: () => new MeiliSearch({ host: meiliUri, apiKey: generateMeiliKey(env) })
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
exports: ["REDIS_CLIENT", "MEILI_CLIENT"]
|
|
105
|
+
})
|
|
106
|
+
], SubDatabaseModule);
|
|
107
|
+
let AppModule = class {
|
|
108
|
+
configure(consumer) {
|
|
109
|
+
consumer.apply(AuthMiddleWare).forRoutes({ path: "*", method: RequestMethod.ALL });
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
AppModule = __decorateClass([
|
|
113
|
+
Module({
|
|
114
|
+
imports: [
|
|
115
|
+
BullModule.forRoot({ redis: redisUri }),
|
|
116
|
+
ScheduleModule.forRoot(),
|
|
117
|
+
GraphQLModule.forRootAsync({
|
|
118
|
+
imports: [],
|
|
119
|
+
useFactory: () => ({
|
|
120
|
+
useGlobalPrefix: true,
|
|
121
|
+
autoSchemaFile: join(process.cwd(), "src/schema.gql"),
|
|
122
|
+
sortSchema: true,
|
|
123
|
+
playground: baseEnv.environment !== "main",
|
|
124
|
+
introspection: baseEnv.environment !== "main",
|
|
125
|
+
uploads: false,
|
|
126
|
+
debug: false
|
|
127
|
+
}),
|
|
128
|
+
driver: ApolloDriver
|
|
129
|
+
}),
|
|
130
|
+
MongooseModule.forRootAsync({
|
|
131
|
+
useFactory: () => ({ uri: mongoUri, autoIndex: baseEnv.environment !== "main" })
|
|
132
|
+
}),
|
|
133
|
+
SubDatabaseModule,
|
|
134
|
+
useGlobals({
|
|
135
|
+
injects: { SearchClient, DatabaseClient, CacheClient }
|
|
136
|
+
}),
|
|
137
|
+
...["batch", "all"].includes(serverMode) && baseEnv.operationMode !== "edge" ? [SearchDaemonModule] : [],
|
|
138
|
+
...registerModules(env).filter((m) => !!m)
|
|
139
|
+
],
|
|
140
|
+
providers: [DateScalar]
|
|
141
|
+
})
|
|
142
|
+
], AppModule);
|
|
143
|
+
const app = await NestFactory.create(AppModule, { logger: backendLogger });
|
|
144
|
+
const redisIoAdapter = new RedisIoAdapter(app, { jwtSecret });
|
|
145
|
+
await redisIoAdapter.connectToRedis(redisUri);
|
|
146
|
+
app.enableShutdownHooks();
|
|
147
|
+
const udp = dgram.createSocket("udp4");
|
|
148
|
+
client.setUdp(udp);
|
|
149
|
+
if (["federation", "all"].includes(serverMode)) {
|
|
150
|
+
app.setGlobalPrefix(process.env.GLOBAL_PREFIX ?? "/backend");
|
|
151
|
+
app.enableCors({
|
|
152
|
+
origin: "*",
|
|
153
|
+
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
|
|
154
|
+
preflightContinue: false,
|
|
155
|
+
optionsSuccessStatus: 204,
|
|
156
|
+
allowedHeaders: "DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,apollo-require-preflight"
|
|
157
|
+
});
|
|
158
|
+
if (env.operationMode !== "local" && process.env.USE_REDIS_IO_ADAPTER !== "false")
|
|
159
|
+
app.useWebSocketAdapter(redisIoAdapter);
|
|
160
|
+
app.use(json({ limit: "100mb" }));
|
|
161
|
+
app.use(urlencoded({ limit: "100mb", extended: true }));
|
|
162
|
+
app.use("/backend/graphql", graphqlUploadExpress());
|
|
163
|
+
app.use(cookieParser());
|
|
164
|
+
app.useGlobalInterceptors(new LoggingInterceptor());
|
|
165
|
+
app.useGlobalInterceptors(new TimeoutInterceptor());
|
|
166
|
+
app.useGlobalInterceptors(new CacheInterceptor(redisClient));
|
|
167
|
+
app.useGlobalFilters(new AllExceptionsFilter());
|
|
168
|
+
await app.listen(process.env.PORT ?? env.port ?? 8080);
|
|
169
|
+
backendLogger.log(`\u{1F680} Server is running on: ${await app.getUrl()}`);
|
|
170
|
+
} else {
|
|
171
|
+
await app.init();
|
|
172
|
+
backendLogger.log(`\u{1F680} Batch Server is running`);
|
|
173
|
+
}
|
|
174
|
+
if (module.hot) {
|
|
175
|
+
module.hot.accept();
|
|
176
|
+
module.hot.dispose(() => {
|
|
177
|
+
void app.close();
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
nestApp: app,
|
|
182
|
+
close: async () => {
|
|
183
|
+
await app.close();
|
|
184
|
+
await redisIoAdapter.destroy();
|
|
185
|
+
await redisClient.quit();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
export {
|
|
190
|
+
createNestApp
|
|
191
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { getNonArrayModel, Upload } from "@akanjs/base";
|
|
2
|
+
import { lowerlize } from "@akanjs/common";
|
|
3
|
+
import {
|
|
4
|
+
Access,
|
|
5
|
+
Account,
|
|
6
|
+
getBodyPipes,
|
|
7
|
+
getQueryPipes,
|
|
8
|
+
guards,
|
|
9
|
+
Me,
|
|
10
|
+
MulterToUploadPipe,
|
|
11
|
+
Req,
|
|
12
|
+
Res,
|
|
13
|
+
Self,
|
|
14
|
+
UserIp
|
|
15
|
+
} from "@akanjs/nest";
|
|
16
|
+
import { isServiceEnabled } from "@akanjs/service";
|
|
17
|
+
import {
|
|
18
|
+
copySignal,
|
|
19
|
+
getArgMetas,
|
|
20
|
+
getControllerPath,
|
|
21
|
+
getControllerPrefix,
|
|
22
|
+
getGqlMetas,
|
|
23
|
+
getSigMeta
|
|
24
|
+
} from "@akanjs/signal";
|
|
25
|
+
import {
|
|
26
|
+
Body,
|
|
27
|
+
Controller,
|
|
28
|
+
Get,
|
|
29
|
+
Inject,
|
|
30
|
+
Param,
|
|
31
|
+
Post,
|
|
32
|
+
Query,
|
|
33
|
+
UploadedFiles,
|
|
34
|
+
UseGuards,
|
|
35
|
+
UseInterceptors
|
|
36
|
+
} from "@nestjs/common";
|
|
37
|
+
import { AuthGuard } from "@nestjs/passport";
|
|
38
|
+
import { FilesInterceptor } from "@nestjs/platform-express";
|
|
39
|
+
const internalArgMap = {
|
|
40
|
+
// Parent: Nest.Parent,
|
|
41
|
+
Account,
|
|
42
|
+
UserIp,
|
|
43
|
+
Access,
|
|
44
|
+
Self,
|
|
45
|
+
Me,
|
|
46
|
+
Req,
|
|
47
|
+
Res
|
|
48
|
+
};
|
|
49
|
+
const controllerOf = (sigRef, allSrvs) => {
|
|
50
|
+
const sigMeta = getSigMeta(sigRef);
|
|
51
|
+
const gqlMetas = getGqlMetas(sigRef);
|
|
52
|
+
const prefix = getControllerPrefix(sigMeta);
|
|
53
|
+
const Ctrl = copySignal(sigRef);
|
|
54
|
+
Object.keys(allSrvs).forEach((srv) => {
|
|
55
|
+
if (!isServiceEnabled(allSrvs[srv]))
|
|
56
|
+
return;
|
|
57
|
+
Inject(allSrvs[srv])(Ctrl.prototype, lowerlize(srv));
|
|
58
|
+
});
|
|
59
|
+
for (const gqlMeta of gqlMetas) {
|
|
60
|
+
if (gqlMeta.guards.some((guard) => guard === "None") || gqlMeta.signalOption.onlyFor === "graphql" || !["Query", "Mutation"].includes(gqlMeta.type))
|
|
61
|
+
continue;
|
|
62
|
+
const [argMetas, internalArgMetas] = getArgMetas(Ctrl, gqlMeta.key);
|
|
63
|
+
internalArgMetas.forEach((internalArgMeta) => {
|
|
64
|
+
const internalDecorator = internalArgMap[internalArgMeta.type];
|
|
65
|
+
internalDecorator(internalArgMeta.option ?? {})(Ctrl.prototype, gqlMeta.key, internalArgMeta.idx);
|
|
66
|
+
});
|
|
67
|
+
const uploadArgMeta = argMetas.find((argMeta) => argMeta.type === "Upload");
|
|
68
|
+
if (uploadArgMeta && gqlMeta.signalOption.onlyFor === "restapi") {
|
|
69
|
+
const [modelRef, arrDepth] = getNonArrayModel(uploadArgMeta.returns());
|
|
70
|
+
if (modelRef.prototype !== Upload.prototype)
|
|
71
|
+
throw new Error("Upload must be Upload");
|
|
72
|
+
else if (!arrDepth)
|
|
73
|
+
throw new Error(`Only Array of Upload is allowed - ${sigMeta.refName}/${gqlMeta.key}`);
|
|
74
|
+
UseInterceptors(FilesInterceptor(uploadArgMeta.name))(Ctrl.prototype, gqlMeta.key, gqlMeta.descriptor);
|
|
75
|
+
UploadedFiles(MulterToUploadPipe)(Ctrl.prototype, gqlMeta.key, uploadArgMeta.idx);
|
|
76
|
+
}
|
|
77
|
+
const queryArgMetas = argMetas.filter((argMeta) => argMeta.type === "Query");
|
|
78
|
+
queryArgMetas.forEach((argMeta) => {
|
|
79
|
+
const [modelRef, arrDepth] = getNonArrayModel(argMeta.returns());
|
|
80
|
+
Query(argMeta.name, ...getQueryPipes(modelRef, arrDepth))(Ctrl.prototype, gqlMeta.key, argMeta.idx);
|
|
81
|
+
});
|
|
82
|
+
const paramArgMetas = argMetas.filter((argMeta) => argMeta.type === "Param");
|
|
83
|
+
paramArgMetas.forEach((argMeta) => {
|
|
84
|
+
Param(argMeta.name)(Ctrl.prototype, gqlMeta.key, argMeta.idx);
|
|
85
|
+
});
|
|
86
|
+
const path = getControllerPath(gqlMeta, paramArgMetas);
|
|
87
|
+
const bodyArgMetas = argMetas.filter((argMeta) => argMeta.type === "Body");
|
|
88
|
+
if (bodyArgMetas.length)
|
|
89
|
+
bodyArgMetas.forEach((argMeta) => {
|
|
90
|
+
Body(argMeta.name, ...getBodyPipes(argMeta))(Ctrl.prototype, gqlMeta.key, argMeta.idx);
|
|
91
|
+
});
|
|
92
|
+
UseGuards(
|
|
93
|
+
...gqlMeta.guards.map((guard) => guards[guard]),
|
|
94
|
+
...gqlMeta.signalOption.sso ? [AuthGuard(gqlMeta.signalOption.sso)] : []
|
|
95
|
+
)(Ctrl.prototype, gqlMeta.key, gqlMeta.descriptor);
|
|
96
|
+
if (gqlMeta.type === "Query")
|
|
97
|
+
Get(path)(Ctrl.prototype, gqlMeta.key, gqlMeta.descriptor);
|
|
98
|
+
else if (gqlMeta.type === "Mutation")
|
|
99
|
+
Post(path)(Ctrl.prototype, gqlMeta.key, gqlMeta.descriptor);
|
|
100
|
+
}
|
|
101
|
+
if (prefix)
|
|
102
|
+
Controller(prefix)(Ctrl);
|
|
103
|
+
else
|
|
104
|
+
Controller()(Ctrl);
|
|
105
|
+
return Ctrl;
|
|
106
|
+
};
|
|
107
|
+
export {
|
|
108
|
+
controllerOf
|
|
109
|
+
};
|
package/src/gql.mjs
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result)
|
|
9
|
+
__defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
import { arraiedModel, dayjs, Float, ID, Int, JSON } from "@akanjs/base";
|
|
13
|
+
import {
|
|
14
|
+
getClassMeta,
|
|
15
|
+
getFieldMetas,
|
|
16
|
+
getFullModelRef,
|
|
17
|
+
getInputModelRef
|
|
18
|
+
} from "@akanjs/constant";
|
|
19
|
+
import * as Nest from "@nestjs/graphql";
|
|
20
|
+
import { Field, InputType, ObjectType } from "@nestjs/graphql";
|
|
21
|
+
import { isDayjs } from "dayjs";
|
|
22
|
+
import { Kind } from "graphql";
|
|
23
|
+
import { default as GraphQLJSON } from "graphql-type-json";
|
|
24
|
+
let DateScalar = class {
|
|
25
|
+
description = "Date custom scalar type";
|
|
26
|
+
parseValue(value) {
|
|
27
|
+
return dayjs(value);
|
|
28
|
+
}
|
|
29
|
+
serialize(value) {
|
|
30
|
+
if (isDayjs(value))
|
|
31
|
+
return value.toDate();
|
|
32
|
+
else
|
|
33
|
+
return new Date(value);
|
|
34
|
+
}
|
|
35
|
+
parseLiteral(ast) {
|
|
36
|
+
if (ast.kind === Kind.INT)
|
|
37
|
+
return dayjs(ast.value);
|
|
38
|
+
else if (ast.kind === Kind.STRING)
|
|
39
|
+
return dayjs(ast.value);
|
|
40
|
+
else
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
DateScalar = __decorateClass([
|
|
45
|
+
Nest.Scalar("Date", () => Date)
|
|
46
|
+
], DateScalar);
|
|
47
|
+
class ObjectGqlStorage {
|
|
48
|
+
}
|
|
49
|
+
class InputGqlStorage {
|
|
50
|
+
}
|
|
51
|
+
const getPredefinedInqutGql = (refName) => {
|
|
52
|
+
const inputGql = Reflect.getMetadata(refName, InputGqlStorage.prototype);
|
|
53
|
+
return inputGql;
|
|
54
|
+
};
|
|
55
|
+
const setPredefinedInqutGql = (refName, inputGql) => {
|
|
56
|
+
Reflect.defineMetadata(refName, inputGql, InputGqlStorage.prototype);
|
|
57
|
+
};
|
|
58
|
+
const getPredefinedObjectGql = (refName) => {
|
|
59
|
+
const objectGql = Reflect.getMetadata(refName, ObjectGqlStorage.prototype);
|
|
60
|
+
return objectGql;
|
|
61
|
+
};
|
|
62
|
+
const setPredefinedObjectGql = (refName, objectGql) => {
|
|
63
|
+
Reflect.defineMetadata(refName, objectGql, ObjectGqlStorage.prototype);
|
|
64
|
+
};
|
|
65
|
+
const gqlNestFieldMap = /* @__PURE__ */ new Map([
|
|
66
|
+
[ID, Nest.ID],
|
|
67
|
+
[Int, Nest.Int],
|
|
68
|
+
[Float, Nest.Float],
|
|
69
|
+
[JSON, GraphQLJSON],
|
|
70
|
+
[Map, GraphQLJSON]
|
|
71
|
+
]);
|
|
72
|
+
const applyNestField = (model, fieldMeta, type = "object") => {
|
|
73
|
+
if (fieldMeta.fieldType === "hidden" && type === "object")
|
|
74
|
+
return;
|
|
75
|
+
const modelRef = fieldMeta.isClass ? type === "object" ? generateGql(fieldMeta.modelRef) : fieldMeta.isScalar ? generateGqlInput(fieldMeta.modelRef) : Nest.ID : gqlNestFieldMap.get(fieldMeta.modelRef) ?? fieldMeta.modelRef;
|
|
76
|
+
Field(() => arraiedModel(modelRef, fieldMeta.arrDepth), { nullable: fieldMeta.nullable })(
|
|
77
|
+
model.prototype,
|
|
78
|
+
fieldMeta.key
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
const generateGqlInput = (inputRef) => {
|
|
82
|
+
const classMeta = getClassMeta(inputRef);
|
|
83
|
+
const predefinedInputGql = getPredefinedInqutGql(classMeta.refName);
|
|
84
|
+
if (predefinedInputGql)
|
|
85
|
+
return predefinedInputGql;
|
|
86
|
+
const fieldMetas = getFieldMetas(inputRef);
|
|
87
|
+
class InputGql {
|
|
88
|
+
}
|
|
89
|
+
const inputGql = classMeta.type === "scalar" ? InputGql : getInputModelRef(classMeta.refName);
|
|
90
|
+
fieldMetas.forEach((fieldMeta) => {
|
|
91
|
+
applyNestField(inputGql, fieldMeta, "input");
|
|
92
|
+
});
|
|
93
|
+
InputType(classMeta.refName + (classMeta.type === "scalar" ? "Input" : ""))(inputGql);
|
|
94
|
+
setPredefinedInqutGql(classMeta.refName, inputGql);
|
|
95
|
+
return inputGql;
|
|
96
|
+
};
|
|
97
|
+
const generateGql = (objectRef) => {
|
|
98
|
+
const classMeta = getClassMeta(objectRef);
|
|
99
|
+
if (classMeta.type === "light") {
|
|
100
|
+
const fullModelRefName = classMeta.refName.slice(5);
|
|
101
|
+
const fullModelRef = getFullModelRef(fullModelRefName);
|
|
102
|
+
return generateGql(fullModelRef);
|
|
103
|
+
}
|
|
104
|
+
const predefinedObjectGql = getPredefinedObjectGql(classMeta.refName);
|
|
105
|
+
if (predefinedObjectGql)
|
|
106
|
+
return predefinedObjectGql;
|
|
107
|
+
const fieldMetas = getFieldMetas(objectRef);
|
|
108
|
+
class ObjectGql {
|
|
109
|
+
}
|
|
110
|
+
const objectGql = classMeta.type === "scalar" ? ObjectGql : getFullModelRef(classMeta.refName);
|
|
111
|
+
fieldMetas.forEach((fieldMeta) => {
|
|
112
|
+
applyNestField(objectGql, fieldMeta);
|
|
113
|
+
});
|
|
114
|
+
ObjectType(classMeta.refName)(objectGql);
|
|
115
|
+
setPredefinedObjectGql(classMeta.refName, objectGql);
|
|
116
|
+
return objectGql;
|
|
117
|
+
};
|
|
118
|
+
export {
|
|
119
|
+
DateScalar,
|
|
120
|
+
applyNestField,
|
|
121
|
+
generateGql,
|
|
122
|
+
generateGqlInput
|
|
123
|
+
};
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./gql";
|
|
2
|
+
export * from "./resolver";
|
|
3
|
+
export * from "./controller";
|
|
4
|
+
export * from "./processor";
|
|
5
|
+
export * from "./module";
|
|
6
|
+
export * from "./types";
|
|
7
|
+
export * from "./websocket";
|
|
8
|
+
export * from "./boot";
|
|
9
|
+
export * from "./searchDaemon";
|
|
10
|
+
export * from "./schema";
|
package/src/module.mjs
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result)
|
|
9
|
+
__defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
import { capitalize, lowerlize } from "@akanjs/common";
|
|
13
|
+
import { getChildClassRefs, getClassMeta } from "@akanjs/constant";
|
|
14
|
+
import { databaseModelOf } from "@akanjs/document";
|
|
15
|
+
import { isServiceEnabled, serviceOf } from "@akanjs/service";
|
|
16
|
+
import { getGqlMetas, getSigMeta, LogSignal, Signal } from "@akanjs/signal";
|
|
17
|
+
import { BullModule, getQueueToken } from "@nestjs/bull";
|
|
18
|
+
import { Global, Module } from "@nestjs/common";
|
|
19
|
+
import { getModelToken, MongooseModule } from "@nestjs/mongoose";
|
|
20
|
+
import { controllerOf } from "./controller";
|
|
21
|
+
import { processorOf } from "./processor";
|
|
22
|
+
import { resolverOf } from "./resolver";
|
|
23
|
+
import { addSchema, schemaOf } from "./schema";
|
|
24
|
+
import { websocketOf, websocketServerOf } from "./websocket";
|
|
25
|
+
const hasWebsocket = (signal) => getGqlMetas(signal).some((gqlMeta) => ["Message", "Pubsub"].includes(gqlMeta.type));
|
|
26
|
+
const hasProcessor = (signal) => getGqlMetas(signal).some((gqlMeta) => gqlMeta.type === "Process");
|
|
27
|
+
const filterSrvs = (srvs) => Object.fromEntries(Object.entries(srvs).filter(([_, srv]) => !!srv));
|
|
28
|
+
const databaseModuleOf = ({
|
|
29
|
+
constant,
|
|
30
|
+
database,
|
|
31
|
+
signal,
|
|
32
|
+
service,
|
|
33
|
+
uses = {},
|
|
34
|
+
useAsyncs = {},
|
|
35
|
+
providers = [],
|
|
36
|
+
extended
|
|
37
|
+
}, allSrvs) => {
|
|
38
|
+
if (!isServiceEnabled(service))
|
|
39
|
+
return null;
|
|
40
|
+
const [modelName, className] = [lowerlize(constant.refName), capitalize(constant.refName)];
|
|
41
|
+
const mongoToken = getModelToken(className);
|
|
42
|
+
let DatabaseModule = class {
|
|
43
|
+
};
|
|
44
|
+
DatabaseModule = __decorateClass([
|
|
45
|
+
Global(),
|
|
46
|
+
Module({
|
|
47
|
+
imports: [
|
|
48
|
+
MongooseModule.forFeature([
|
|
49
|
+
{
|
|
50
|
+
name: className,
|
|
51
|
+
schema: extended ? addSchema(database.Model, database.Doc, database.Input, database.Middleware) : schemaOf(database.Model, database.Doc, database.Middleware)
|
|
52
|
+
}
|
|
53
|
+
]),
|
|
54
|
+
...hasProcessor(signal) ? [
|
|
55
|
+
BullModule.registerQueue({
|
|
56
|
+
name: modelName,
|
|
57
|
+
defaultJobOptions: { removeOnComplete: true, removeOnFail: true }
|
|
58
|
+
})
|
|
59
|
+
] : []
|
|
60
|
+
],
|
|
61
|
+
providers: [
|
|
62
|
+
serviceOf(service),
|
|
63
|
+
resolverOf(signal, filterSrvs(allSrvs)),
|
|
64
|
+
...hasProcessor(signal) ? [
|
|
65
|
+
processorOf(signal, filterSrvs(allSrvs)),
|
|
66
|
+
{ provide: `${className}Queue`, useFactory: (queue) => queue, inject: [getQueueToken(modelName)] }
|
|
67
|
+
] : [],
|
|
68
|
+
...hasWebsocket(signal) ? [websocketOf(signal, filterSrvs(allSrvs)), { provide: "Websocket", useClass: websocketServerOf(signal) }] : [],
|
|
69
|
+
...Object.entries(uses).map(([key, useValue]) => ({ provide: capitalize(key), useValue })),
|
|
70
|
+
...Object.entries(useAsyncs).map(([key, useFactory]) => ({ provide: capitalize(key), useFactory })),
|
|
71
|
+
{
|
|
72
|
+
provide: `${modelName}Model`,
|
|
73
|
+
useFactory: (model, redis, meili) => {
|
|
74
|
+
return databaseModelOf(database, model, redis, meili);
|
|
75
|
+
},
|
|
76
|
+
inject: [mongoToken, "REDIS_CLIENT", "MEILI_CLIENT"]
|
|
77
|
+
},
|
|
78
|
+
...providers
|
|
79
|
+
],
|
|
80
|
+
controllers: [controllerOf(signal, filterSrvs(allSrvs))],
|
|
81
|
+
exports: [service]
|
|
82
|
+
})
|
|
83
|
+
], DatabaseModule);
|
|
84
|
+
return DatabaseModule;
|
|
85
|
+
};
|
|
86
|
+
const serviceModuleOf = ({ signal, service, uses = {}, useAsyncs = {}, providers = [] }, allSrvs) => {
|
|
87
|
+
if (!isServiceEnabled(service))
|
|
88
|
+
return null;
|
|
89
|
+
const sigMeta = getSigMeta(signal);
|
|
90
|
+
const [modelName, className] = [lowerlize(sigMeta.refName), capitalize(sigMeta.refName)];
|
|
91
|
+
let ServiceModule = class {
|
|
92
|
+
};
|
|
93
|
+
ServiceModule = __decorateClass([
|
|
94
|
+
Global(),
|
|
95
|
+
Module({
|
|
96
|
+
imports: [
|
|
97
|
+
...hasProcessor(signal) ? [
|
|
98
|
+
BullModule.registerQueue({
|
|
99
|
+
name: modelName,
|
|
100
|
+
defaultJobOptions: { removeOnComplete: true, removeOnFail: true }
|
|
101
|
+
})
|
|
102
|
+
] : []
|
|
103
|
+
],
|
|
104
|
+
providers: [
|
|
105
|
+
serviceOf(service),
|
|
106
|
+
resolverOf(signal, filterSrvs(allSrvs)),
|
|
107
|
+
...hasWebsocket(signal) ? [websocketOf(signal, filterSrvs(allSrvs)), { provide: "Websocket", useClass: websocketServerOf(signal) }] : [],
|
|
108
|
+
...hasProcessor(signal) ? [
|
|
109
|
+
processorOf(signal, filterSrvs(allSrvs)),
|
|
110
|
+
{ provide: `${className}Queue`, useFactory: (queue) => queue, inject: [getQueueToken(modelName)] }
|
|
111
|
+
] : [],
|
|
112
|
+
...Object.entries(uses).map(([key, useValue]) => ({ provide: capitalize(key), useValue })),
|
|
113
|
+
...Object.entries(useAsyncs).map(([key, useFactory]) => ({ provide: capitalize(key), useFactory })),
|
|
114
|
+
...providers
|
|
115
|
+
],
|
|
116
|
+
controllers: [controllerOf(signal, filterSrvs(allSrvs))],
|
|
117
|
+
exports: [service]
|
|
118
|
+
})
|
|
119
|
+
], ServiceModule);
|
|
120
|
+
return ServiceModule;
|
|
121
|
+
};
|
|
122
|
+
const scalarModuleOf = ({ signals, uses = {}, useAsyncs = {}, providers = [], enabled = true }, allSrvs) => {
|
|
123
|
+
if (!enabled)
|
|
124
|
+
return null;
|
|
125
|
+
let ScalarModule = class {
|
|
126
|
+
};
|
|
127
|
+
ScalarModule = __decorateClass([
|
|
128
|
+
Global(),
|
|
129
|
+
Module({
|
|
130
|
+
imports: [],
|
|
131
|
+
providers: [
|
|
132
|
+
...signals.map((signal) => resolverOf(signal, filterSrvs(allSrvs))),
|
|
133
|
+
...signals.filter(hasWebsocket).map((signal) => [
|
|
134
|
+
websocketOf(signal, filterSrvs(allSrvs)),
|
|
135
|
+
{ provide: "Websocket", useClass: websocketServerOf(signal) }
|
|
136
|
+
]).flat(),
|
|
137
|
+
...Object.entries(uses).map(([key, useValue]) => ({ provide: capitalize(key), useValue })),
|
|
138
|
+
...Object.entries(useAsyncs).map(([key, useFactory]) => ({ provide: capitalize(key), useFactory })),
|
|
139
|
+
...providers
|
|
140
|
+
],
|
|
141
|
+
controllers: signals.map((signal) => controllerOf(signal, filterSrvs(allSrvs)))
|
|
142
|
+
})
|
|
143
|
+
], ScalarModule);
|
|
144
|
+
return ScalarModule;
|
|
145
|
+
};
|
|
146
|
+
const scalarModulesOf = ({ constants }, allSrvs) => {
|
|
147
|
+
const signals = constants.filter((modelRef) => {
|
|
148
|
+
const childRefs = getChildClassRefs(modelRef);
|
|
149
|
+
return childRefs.some((childRef) => {
|
|
150
|
+
const classMeta = getClassMeta(childRef);
|
|
151
|
+
return ["full", "light"].includes(classMeta.type);
|
|
152
|
+
});
|
|
153
|
+
}).map((modelRef) => {
|
|
154
|
+
let ScalarSignal = class extends LogSignal({}) {
|
|
155
|
+
};
|
|
156
|
+
ScalarSignal = __decorateClass([
|
|
157
|
+
Signal(() => modelRef)
|
|
158
|
+
], ScalarSignal);
|
|
159
|
+
return ScalarSignal;
|
|
160
|
+
});
|
|
161
|
+
let ScalarModule = class {
|
|
162
|
+
};
|
|
163
|
+
ScalarModule = __decorateClass([
|
|
164
|
+
Global(),
|
|
165
|
+
Module({
|
|
166
|
+
imports: [],
|
|
167
|
+
providers: [...signals.map((signal) => resolverOf(signal, filterSrvs(allSrvs)))]
|
|
168
|
+
// controllers: signals.map((signal) => controllerOf(signal, filterSrvs(allSrvs))),
|
|
169
|
+
})
|
|
170
|
+
], ScalarModule);
|
|
171
|
+
return ScalarModule;
|
|
172
|
+
};
|
|
173
|
+
const batchModuleOf = ({
|
|
174
|
+
service,
|
|
175
|
+
uses = {},
|
|
176
|
+
useAsyncs = {},
|
|
177
|
+
providers = []
|
|
178
|
+
}) => {
|
|
179
|
+
if (!isServiceEnabled(service))
|
|
180
|
+
return null;
|
|
181
|
+
let BatchModule = class {
|
|
182
|
+
};
|
|
183
|
+
BatchModule = __decorateClass([
|
|
184
|
+
Global(),
|
|
185
|
+
Module({
|
|
186
|
+
imports: [],
|
|
187
|
+
providers: [
|
|
188
|
+
serviceOf(service),
|
|
189
|
+
...Object.entries(uses).map(([key, useValue]) => ({ provide: capitalize(key), useValue })),
|
|
190
|
+
...Object.entries(useAsyncs).map(([key, useFactory]) => ({ provide: capitalize(key), useFactory })),
|
|
191
|
+
...providers
|
|
192
|
+
],
|
|
193
|
+
exports: [service]
|
|
194
|
+
})
|
|
195
|
+
], BatchModule);
|
|
196
|
+
return BatchModule;
|
|
197
|
+
};
|
|
198
|
+
const useGlobals = ({ uses, useAsyncs, injects }) => {
|
|
199
|
+
let GlobalsModule = class {
|
|
200
|
+
};
|
|
201
|
+
GlobalsModule = __decorateClass([
|
|
202
|
+
Global(),
|
|
203
|
+
Module({
|
|
204
|
+
imports: [],
|
|
205
|
+
providers: [
|
|
206
|
+
...Object.entries(uses ?? {}).map(([key, useValue]) => ({
|
|
207
|
+
provide: capitalize(key),
|
|
208
|
+
useValue
|
|
209
|
+
})),
|
|
210
|
+
...Object.entries(useAsyncs ?? {}).map(([key, useFactory]) => ({ provide: capitalize(key), useFactory })),
|
|
211
|
+
...Object.entries(injects ?? {}).map(([key, inject]) => ({ provide: capitalize(key), useClass: inject }))
|
|
212
|
+
],
|
|
213
|
+
exports: [
|
|
214
|
+
...Object.keys(uses ?? {}).map((key) => capitalize(key)),
|
|
215
|
+
...Object.keys(useAsyncs ?? {}).map((key) => capitalize(key)),
|
|
216
|
+
...Object.keys(injects ?? {}).map((key) => capitalize(key))
|
|
217
|
+
]
|
|
218
|
+
})
|
|
219
|
+
], GlobalsModule);
|
|
220
|
+
return GlobalsModule;
|
|
221
|
+
};
|
|
222
|
+
export {
|
|
223
|
+
batchModuleOf,
|
|
224
|
+
databaseModuleOf,
|
|
225
|
+
scalarModuleOf,
|
|
226
|
+
scalarModulesOf,
|
|
227
|
+
serviceModuleOf,
|
|
228
|
+
useGlobals
|
|
229
|
+
};
|