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 ADDED
@@ -0,0 +1,5 @@
1
+
2
+ JWT_SECRET=change_this_to_a_long_random_secret
3
+ DATABASE_URL="file:./dev.db"
4
+
5
+ SERVER_URL=https://taleem.help/api
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;