taleem-kernel 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/.env +5 -0
- package/package.json +30 -0
- package/prisma/content.db +0 -0
- package/prisma/dev.db +0 -0
- package/prisma/schema.prisma +121 -0
- package/readme.md +116 -0
- package/src/Auth.js +105 -0
- package/src/CommunicationPolicy.js +34 -0
- package/src/Config.js +26 -0
- package/src/JWT.js +31 -0
- package/src/Logger.js +33 -0
- package/src/ServerKernel.js +139 -0
- package/src/api.md +584 -0
- package/src/enums/Resources.js +8 -0
- package/src/modules/Admin.js +57 -0
- package/src/modules/Audio.js +49 -0
- package/src/modules/Communication.js +116 -0
- package/src/modules/Course.js +26 -0
- package/src/modules/Image.js +48 -0
- package/src/modules/Library.js +59 -0
- package/src/modules/Subscription.js +97 -0
- package/src/modules/Svg.js +59 -0
- package/src/modules/User.js +117 -0
- package/tests/admin.test.js +52 -0
- package/tests/course.test.js +68 -0
- package/tests/kernel.test.js +12 -0
- package/tests/library.test.js +23 -0
package/.env
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "taleem-kernel",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Taleem data and business-logic kernel. HTTP is only an adapter.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/ServerKernel.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/ServerKernel.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "vitest run"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@prisma/client": "^6.19.3",
|
|
15
|
+
"bcrypt": "^6.0.0",
|
|
16
|
+
"dotenv": "^17.4.2",
|
|
17
|
+
"jsonwebtoken": "^9.0.3",
|
|
18
|
+
"zod": "^4.4.3"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"prisma": "^6.19.3",
|
|
22
|
+
"vitest": "^4.1.11"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"taleem",
|
|
26
|
+
"kernel",
|
|
27
|
+
"education"
|
|
28
|
+
],
|
|
29
|
+
"license": "ISC"
|
|
30
|
+
}
|
|
Binary file
|
package/prisma/dev.db
ADDED
|
Binary file
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "sqlite"
|
|
7
|
+
url = env("DATABASE_URL")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum ContentType {
|
|
11
|
+
ARTICLE
|
|
12
|
+
PLAYER
|
|
13
|
+
MCQ
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
enum CourseAccess {
|
|
17
|
+
OPEN
|
|
18
|
+
MEMBERS
|
|
19
|
+
SUBSCRIPTION
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
enum AdminRole {
|
|
23
|
+
ADMIN
|
|
24
|
+
SUPER_ADMIN
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
model Course {
|
|
28
|
+
slug String @unique
|
|
29
|
+
title String
|
|
30
|
+
description String?
|
|
31
|
+
thumbnail String?
|
|
32
|
+
access CourseAccess @default(OPEN)
|
|
33
|
+
price Int @default(0)
|
|
34
|
+
groupings String @default("[]")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
model User {
|
|
38
|
+
id Int @id @default(autoincrement())
|
|
39
|
+
email String @unique
|
|
40
|
+
password String
|
|
41
|
+
name String?
|
|
42
|
+
role String @default("student")
|
|
43
|
+
resource String?
|
|
44
|
+
createdAt DateTime @default(now())
|
|
45
|
+
subscriptions Subscription[]
|
|
46
|
+
communications Communication[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
model Admin {
|
|
50
|
+
id Int @id @default(autoincrement())
|
|
51
|
+
email String @unique
|
|
52
|
+
password String
|
|
53
|
+
courseSlugs String @default("[]")
|
|
54
|
+
role AdminRole @default(ADMIN)
|
|
55
|
+
isActive Boolean @default(true)
|
|
56
|
+
createdAt DateTime @default(now())
|
|
57
|
+
updatedAt DateTime @updatedAt
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
model Library {
|
|
61
|
+
slug String @id
|
|
62
|
+
title String
|
|
63
|
+
description String?
|
|
64
|
+
thumbnail String?
|
|
65
|
+
type ContentType
|
|
66
|
+
body String?
|
|
67
|
+
courseSlug String
|
|
68
|
+
groupSlug String
|
|
69
|
+
sortOrder Int @default(0)
|
|
70
|
+
allowCommunication Boolean @default(true)
|
|
71
|
+
meta String?
|
|
72
|
+
createdAt DateTime @default(now())
|
|
73
|
+
updatedAt DateTime @updatedAt
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
model Subscription {
|
|
77
|
+
id Int @id @default(autoincrement())
|
|
78
|
+
userId Int
|
|
79
|
+
courseSlug String
|
|
80
|
+
startsAt DateTime @default(now())
|
|
81
|
+
endsAt DateTime?
|
|
82
|
+
amount Int?
|
|
83
|
+
user User @relation(fields: [userId], references: [id])
|
|
84
|
+
cancelledAt DateTime?
|
|
85
|
+
|
|
86
|
+
@@index([userId])
|
|
87
|
+
@@index([courseSlug])
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
model Communication {
|
|
91
|
+
id Int @id @default(autoincrement())
|
|
92
|
+
userId Int
|
|
93
|
+
librarySlug String
|
|
94
|
+
type String
|
|
95
|
+
meta String?
|
|
96
|
+
message String
|
|
97
|
+
authorResponse String?
|
|
98
|
+
isPublic Boolean @default(false)
|
|
99
|
+
readAt DateTime?
|
|
100
|
+
createdAt DateTime @default(now())
|
|
101
|
+
updatedAt DateTime @updatedAt
|
|
102
|
+
user User @relation(fields: [userId], references: [id])
|
|
103
|
+
|
|
104
|
+
@@index([userId])
|
|
105
|
+
@@index([librarySlug])
|
|
106
|
+
}
|
|
107
|
+
model Svg {
|
|
108
|
+
|
|
109
|
+
slug String @id
|
|
110
|
+
|
|
111
|
+
title String
|
|
112
|
+
|
|
113
|
+
body String
|
|
114
|
+
|
|
115
|
+
tags String @default("[]")
|
|
116
|
+
|
|
117
|
+
createdAt DateTime @default(now())
|
|
118
|
+
|
|
119
|
+
updatedAt DateTime @updatedAt
|
|
120
|
+
|
|
121
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
|
|
2
|
+
# Taleem Server Kernel Constitution
|
|
3
|
+
|
|
4
|
+
### Purpose
|
|
5
|
+
|
|
6
|
+
The **serverKernel** is the product. It contains all business logic and data access. HTTP is only an adapter.
|
|
7
|
+
|
|
8
|
+
## Owns
|
|
9
|
+
|
|
10
|
+
* Database (Prisma)
|
|
11
|
+
* Validation
|
|
12
|
+
* Business rules
|
|
13
|
+
* Authentication
|
|
14
|
+
* Authorization
|
|
15
|
+
* Domain errors
|
|
16
|
+
|
|
17
|
+
## Never owns
|
|
18
|
+
|
|
19
|
+
* Express
|
|
20
|
+
* Routes
|
|
21
|
+
* `req` / `res`
|
|
22
|
+
* HTML pages
|
|
23
|
+
* Cookies
|
|
24
|
+
* Headers
|
|
25
|
+
* Static files
|
|
26
|
+
* URL parsing
|
|
27
|
+
|
|
28
|
+
## Authentication Boundary
|
|
29
|
+
|
|
30
|
+
### HTTP Layer
|
|
31
|
+
|
|
32
|
+
Responsible for:
|
|
33
|
+
|
|
34
|
+
* Reading `Authorization` header
|
|
35
|
+
* Reading cookies
|
|
36
|
+
* Extracting bearer token
|
|
37
|
+
* Passing token to kernel
|
|
38
|
+
|
|
39
|
+
Never verifies anything.
|
|
40
|
+
|
|
41
|
+
### Kernel
|
|
42
|
+
|
|
43
|
+
Responsible for:
|
|
44
|
+
|
|
45
|
+
* Verifying JWT
|
|
46
|
+
* Loading user
|
|
47
|
+
* Checking permissions
|
|
48
|
+
* Checking subscriptions
|
|
49
|
+
* Returning authenticated identity
|
|
50
|
+
|
|
51
|
+
Never reads headers.
|
|
52
|
+
|
|
53
|
+
## Public API Rules
|
|
54
|
+
|
|
55
|
+
Every public method:
|
|
56
|
+
|
|
57
|
+
* validates input
|
|
58
|
+
* validates authentication (if required)
|
|
59
|
+
* enforces authorization
|
|
60
|
+
* throws explicit errors
|
|
61
|
+
* never silently fails
|
|
62
|
+
* never returns ambiguous `null`
|
|
63
|
+
|
|
64
|
+
## Error Rules
|
|
65
|
+
|
|
66
|
+
Errors are for developers.
|
|
67
|
+
|
|
68
|
+
Every error answers:
|
|
69
|
+
|
|
70
|
+
1. What failed?
|
|
71
|
+
2. Why?
|
|
72
|
+
3. Which method?
|
|
73
|
+
4. Which object/user/resource?
|
|
74
|
+
5. What should the caller check?
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
AuthenticationError
|
|
80
|
+
Method : library.get()
|
|
81
|
+
Reason : JWT expired
|
|
82
|
+
User : 31
|
|
83
|
+
Token : valid format, expired at 2026-07-27T09:10Z
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Story Rule
|
|
87
|
+
|
|
88
|
+
Every feature must be explainable as a story.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
HTTP
|
|
92
|
+
↓
|
|
93
|
+
Extract token
|
|
94
|
+
↓
|
|
95
|
+
Kernel
|
|
96
|
+
↓
|
|
97
|
+
Authenticate
|
|
98
|
+
↓
|
|
99
|
+
Authorize
|
|
100
|
+
↓
|
|
101
|
+
Business logic
|
|
102
|
+
↓
|
|
103
|
+
Return result
|
|
104
|
+
↓
|
|
105
|
+
HTTP formats response
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
No business logic is allowed before entering the kernel.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
I would add one final principle because it will save you from years of debugging:
|
|
113
|
+
|
|
114
|
+
> **The kernel trusts nothing and explains everything.**
|
|
115
|
+
|
|
116
|
+
That one sentence should be visible at the top of the `serverKernel` folder. It captures the philosophy you've been moving toward: every input is validated, every decision is explicit, and every failure tells you exactly why it happened.
|
package/src/Auth.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
///home/bilal-tariq/00--TALEEM/taleem-server/src/serverKernel/Auth.js
|
|
2
|
+
|
|
3
|
+
import JWT from "./JWT.js";
|
|
4
|
+
|
|
5
|
+
export default class Auth {
|
|
6
|
+
|
|
7
|
+
constructor(kernel) {
|
|
8
|
+
this.kernel = kernel;
|
|
9
|
+
this.jwt = new JWT(kernel);
|
|
10
|
+
}
|
|
11
|
+
// --------------------------------------------------
|
|
12
|
+
// Token Creation
|
|
13
|
+
// --------------------------------------------------
|
|
14
|
+
createUserToken(user) {
|
|
15
|
+
|
|
16
|
+
return this.jwt.sign({ id: user.id, type: "user" });
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
createAdminToken(admin) {
|
|
21
|
+
|
|
22
|
+
return this.jwt.sign({ id: admin.id, type: "admin" });
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// --------------------------------------------------
|
|
27
|
+
// Authentication
|
|
28
|
+
// --------------------------------------------------
|
|
29
|
+
|
|
30
|
+
async authenticate(token) {
|
|
31
|
+
|
|
32
|
+
const { id, type } = this.verifyToken(token);
|
|
33
|
+
|
|
34
|
+
if (type === "user") return this.authenticateUser(id);
|
|
35
|
+
|
|
36
|
+
if (type === "admin") return this.authenticateAdmin(id);
|
|
37
|
+
|
|
38
|
+
this.fail("authenticate()", `Unknown identity type '${type}'.`);
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async authenticateUser(id) {
|
|
43
|
+
|
|
44
|
+
const user = await this.kernel.user.get(id);
|
|
45
|
+
|
|
46
|
+
if (!user)
|
|
47
|
+
this.fail(
|
|
48
|
+
"authenticateUser()",
|
|
49
|
+
`User '${id}' does not exist.`
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return user;
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async authenticateAdmin(id) {
|
|
57
|
+
|
|
58
|
+
const admin = await this.kernel.admin.get(id);
|
|
59
|
+
|
|
60
|
+
if (!admin)
|
|
61
|
+
this.fail(
|
|
62
|
+
"authenticateAdmin()",
|
|
63
|
+
`Admin '${id}' does not exist.`
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return admin;
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
verifyToken(token) {
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
|
|
74
|
+
return this.jwt.verify(token);
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
|
|
79
|
+
this.fail("verifyToken()", error.message);
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// --------------------------------------------------
|
|
86
|
+
// Helpers
|
|
87
|
+
// --------------------------------------------------
|
|
88
|
+
|
|
89
|
+
fail(method, reason) {
|
|
90
|
+
|
|
91
|
+
throw new Error(
|
|
92
|
+
[
|
|
93
|
+
"",
|
|
94
|
+
"========================================",
|
|
95
|
+
"AUTHENTICATION FAILED",
|
|
96
|
+
"----------------------------------------",
|
|
97
|
+
`Method : Auth.${method}`,
|
|
98
|
+
`Reason : ${reason}`,
|
|
99
|
+
"========================================"
|
|
100
|
+
].join("\n")
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/serverKernel/CommunicationPolicy.js
|
|
2
|
+
|
|
3
|
+
export default class CommunicationPolicy {
|
|
4
|
+
|
|
5
|
+
constructor(kernel) {
|
|
6
|
+
this.kernel = kernel;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// --------------------------------------------------
|
|
10
|
+
// Require permission to create a new communication.
|
|
11
|
+
// --------------------------------------------------
|
|
12
|
+
|
|
13
|
+
async require(user) {
|
|
14
|
+
|
|
15
|
+
const MAX_OPEN_QUESTIONS = 5;
|
|
16
|
+
|
|
17
|
+
const count =
|
|
18
|
+
await this.kernel.communication.countUserOpenQuestions(
|
|
19
|
+
user.id
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
if (count >= MAX_OPEN_QUESTIONS) {
|
|
23
|
+
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Maximum of ${MAX_OPEN_QUESTIONS} open questions reached.`
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return true;
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
package/src/Config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/serverKernel/Config.js
|
|
2
|
+
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
export default class Config {
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
|
|
11
|
+
this.port = Number(
|
|
12
|
+
process.env.PORT ?? 9000
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
this.jwtSecret =
|
|
16
|
+
process.env.JWT_SECRET;
|
|
17
|
+
|
|
18
|
+
this.databaseUrl =
|
|
19
|
+
process.env.DATABASE_URL;
|
|
20
|
+
|
|
21
|
+
this.nodeEnv =
|
|
22
|
+
process.env.NODE_ENV ?? "development";
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
}
|
package/src/JWT.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/serverKernel/JWT.js
|
|
2
|
+
|
|
3
|
+
import jwt from "jsonwebtoken";
|
|
4
|
+
|
|
5
|
+
export default class JWT {
|
|
6
|
+
|
|
7
|
+
constructor(kernel) {
|
|
8
|
+
|
|
9
|
+
this.kernel = kernel;
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
sign(payload) {
|
|
14
|
+
|
|
15
|
+
return jwt.sign(
|
|
16
|
+
payload,
|
|
17
|
+
this.kernel.config.jwtSecret
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
verify(token) {
|
|
23
|
+
|
|
24
|
+
return jwt.verify(
|
|
25
|
+
token,
|
|
26
|
+
this.kernel.config.jwtSecret
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
package/src/Logger.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/serverKernel/Logger.js
|
|
2
|
+
|
|
3
|
+
export default class Logger {
|
|
4
|
+
|
|
5
|
+
info(...args) {
|
|
6
|
+
|
|
7
|
+
console.log("[INFO]", ...args);
|
|
8
|
+
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
warn(...args) {
|
|
12
|
+
|
|
13
|
+
console.warn("[WARN]", ...args);
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
error(...args) {
|
|
18
|
+
|
|
19
|
+
console.error("[ERROR]", ...args);
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
debug(...args) {
|
|
24
|
+
|
|
25
|
+
if (process.env.NODE_ENV !== "production") {
|
|
26
|
+
|
|
27
|
+
console.log("[DEBUG]", ...args);
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
///home/bilal-tariq/00--TALEEM/taleem-server-prod/src/serverKernel/ServerKernel.js
|
|
2
|
+
import { PrismaClient } from "@prisma/client";
|
|
3
|
+
|
|
4
|
+
import Config from "./Config.js";
|
|
5
|
+
import Auth from "./Auth.js";
|
|
6
|
+
import Logger from "./Logger.js";
|
|
7
|
+
import CommunicationPolicy from "./CommunicationPolicy.js";
|
|
8
|
+
import User from "./modules/User.js";
|
|
9
|
+
import Admin from "./modules/Admin.js";
|
|
10
|
+
import Library from "./modules/Library.js";
|
|
11
|
+
import Course from "./modules/Course.js";
|
|
12
|
+
import Communication from "./modules/Communication.js";
|
|
13
|
+
import Subscription from "./modules/Subscription.js";
|
|
14
|
+
import Image from "./modules/Image.js";
|
|
15
|
+
import Audio from "./modules/Audio.js";
|
|
16
|
+
import Svg from "./modules/Svg.js";
|
|
17
|
+
|
|
18
|
+
class ServerKernel {
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
|
|
22
|
+
this.logger = new Logger();
|
|
23
|
+
|
|
24
|
+
this.logger.info("========================================");
|
|
25
|
+
this.logger.info("Starting Taleem Server Kernel");
|
|
26
|
+
this.logger.info("========================================");
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
|
|
30
|
+
// --------------------------------------------------
|
|
31
|
+
// Core
|
|
32
|
+
// --------------------------------------------------
|
|
33
|
+
|
|
34
|
+
this.config = this.initialize("Config", () => new Config());
|
|
35
|
+
|
|
36
|
+
this.db = this.initialize("Prisma", () => new PrismaClient());
|
|
37
|
+
|
|
38
|
+
this.auth = this.initialize("Auth", () => new Auth(this));
|
|
39
|
+
|
|
40
|
+
this.communicationPolicy =
|
|
41
|
+
this.initialize(
|
|
42
|
+
"CommunicationPolicy",
|
|
43
|
+
() => new CommunicationPolicy(this)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// --------------------------------------------------
|
|
47
|
+
// Modules
|
|
48
|
+
// --------------------------------------------------
|
|
49
|
+
this.user = this.initialize("User", () => new User(this));
|
|
50
|
+
this.admin = this.initialize("Admin", () => new Admin(this));
|
|
51
|
+
this.library = this.initialize("Library", () => new Library(this));
|
|
52
|
+
this.course = this.initialize("Course", () => new Course(this));
|
|
53
|
+
this.image = this.initialize("Image", () => new Image(this));
|
|
54
|
+
this.audio = this.initialize("Audio", () => new Audio(this));
|
|
55
|
+
this.svg = this.initialize("Svg", () => new Svg(this));
|
|
56
|
+
this.communication = this.initialize("Communication", () => new Communication(this));
|
|
57
|
+
this.subscription = this.initialize("Subscription", () => new Subscription(this));
|
|
58
|
+
|
|
59
|
+
this.logger.info("Server Kernel started successfully.");
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
|
|
64
|
+
this.logger.error(error.message);
|
|
65
|
+
|
|
66
|
+
throw error;
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
initialize(name, factory) {
|
|
73
|
+
|
|
74
|
+
this.logger.info(`Initializing ${name}...`);
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
|
|
78
|
+
const instance = factory();
|
|
79
|
+
|
|
80
|
+
this.logger.info(`${name} initialized.`);
|
|
81
|
+
|
|
82
|
+
return instance;
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
|
|
87
|
+
throw new Error(
|
|
88
|
+
[
|
|
89
|
+
"",
|
|
90
|
+
"========================================",
|
|
91
|
+
"SERVER KERNEL INITIALIZATION FAILED",
|
|
92
|
+
"----------------------------------------",
|
|
93
|
+
`Component : ${name}`,
|
|
94
|
+
`Reason : ${error.message}`,
|
|
95
|
+
"",
|
|
96
|
+
"Server Kernel startup aborted.",
|
|
97
|
+
"========================================"
|
|
98
|
+
].join("\n")
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async shutdown() {
|
|
106
|
+
|
|
107
|
+
this.logger.info("Shutting down Server Kernel...");
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
|
|
111
|
+
await this.db.$disconnect();
|
|
112
|
+
|
|
113
|
+
this.logger.info("Database disconnected.");
|
|
114
|
+
|
|
115
|
+
this.logger.info("Server Kernel shutdown complete.");
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
|
|
120
|
+
throw new Error(
|
|
121
|
+
[
|
|
122
|
+
"",
|
|
123
|
+
"========================================",
|
|
124
|
+
"SERVER KERNEL SHUTDOWN FAILED",
|
|
125
|
+
"----------------------------------------",
|
|
126
|
+
`Reason : ${error.message}`,
|
|
127
|
+
"========================================"
|
|
128
|
+
].join("\n")
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const kernel = new ServerKernel();
|
|
138
|
+
|
|
139
|
+
export default kernel;
|