taleem-kernel 1.2.0 → 1.5.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/bin/schema-check.js +21 -10
- package/bin/schema-update.js +1 -0
- package/package.json +4 -4
- package/prisma/dev.db +0 -0
- package/prisma/migrations/20260824182316_add_library_status/migration.sql +24 -0
- package/prisma/migrations/20260829212536_init/migration.sql +96 -0
- package/prisma/schema.prisma +82 -57
- package/readme.md +244 -56
- package/src/{ServerKernel.js → index.js} +4 -2
- package/src/modules/Admin.js +73 -11
- package/src/modules/Communication.js +7 -51
- package/src/modules/Course.js +34 -41
- package/src/modules/Group.js +48 -0
- package/src/modules/Library.js +20 -8
- package/src/modules/Subscription.js +12 -61
- package/src/modules/Svg.js +3 -21
- package/src/modules/User.js +50 -7
- package/src/utils/JWT.js +31 -0
- package/prisma/content.db +0 -0
- package/src/Auth.js +0 -105
package/bin/schema-check.js
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
|
+
// /home/bilal-tariq/00--TALEEM/taleem-kernel/bin/schema-check.js
|
|
1
2
|
import fs from "fs";
|
|
2
3
|
import path from "path";
|
|
3
4
|
import { fileURLToPath } from "url";
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
path.
|
|
7
|
-
"
|
|
6
|
+
const kernelSchemaPath = path.resolve(
|
|
7
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
8
|
+
"../prisma/schema.prisma"
|
|
8
9
|
);
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const appSchemaPath = path.resolve(process.cwd(), "prisma/schema.prisma");
|
|
12
|
+
|
|
13
|
+
function normalize(schema) {
|
|
14
|
+
return schema
|
|
15
|
+
.split("\n")
|
|
16
|
+
.map(line => line.replace(/\/\/.*$/, "").trim())
|
|
17
|
+
.filter(line => line.length > 0)
|
|
18
|
+
.join("\n");
|
|
19
|
+
}
|
|
14
20
|
|
|
15
|
-
if (
|
|
16
|
-
console.error(
|
|
21
|
+
if (!fs.existsSync(appSchemaPath)) {
|
|
22
|
+
console.error(`❌ No schema.prisma found at ${appSchemaPath}.`);
|
|
23
|
+
console.error(`Run this command from the root of a Taleem application.`);
|
|
17
24
|
process.exit(1);
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
const kernelSchema = fs.readFileSync(kernelSchemaPath, "utf8");
|
|
28
|
+
const appSchema = fs.readFileSync(appSchemaPath, "utf8");
|
|
29
|
+
|
|
30
|
+
if (normalize(kernelSchema) !== normalize(appSchema)) {
|
|
31
|
+
console.error("❌ Taleem Prisma schema mismatch.");
|
package/bin/schema-update.js
CHANGED
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taleem-kernel",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Taleem data and business-logic kernel. HTTP is only an adapter.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./src/
|
|
6
|
+
"main": "./src/index.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./src/
|
|
8
|
+
".": "./src/index.js"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
11
|
"taleem-kernel": "./bin/cli.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "vitest run"
|
|
14
|
+
"test": "vitest run --no-file-parallelism"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"src",
|
package/prisma/dev.db
CHANGED
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- RedefineTables
|
|
2
|
+
PRAGMA defer_foreign_keys=ON;
|
|
3
|
+
PRAGMA foreign_keys=OFF;
|
|
4
|
+
CREATE TABLE "new_Library" (
|
|
5
|
+
"slug" TEXT NOT NULL PRIMARY KEY,
|
|
6
|
+
"title" TEXT NOT NULL,
|
|
7
|
+
"description" TEXT,
|
|
8
|
+
"thumbnail" TEXT,
|
|
9
|
+
"type" TEXT NOT NULL,
|
|
10
|
+
"status" TEXT NOT NULL DEFAULT 'DRAFT',
|
|
11
|
+
"body" TEXT,
|
|
12
|
+
"courseSlug" TEXT NOT NULL,
|
|
13
|
+
"groupSlug" TEXT NOT NULL,
|
|
14
|
+
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
|
15
|
+
"allowCommunication" BOOLEAN NOT NULL DEFAULT true,
|
|
16
|
+
"meta" TEXT,
|
|
17
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
18
|
+
"updatedAt" DATETIME NOT NULL
|
|
19
|
+
);
|
|
20
|
+
INSERT INTO "new_Library" ("allowCommunication", "body", "courseSlug", "createdAt", "description", "groupSlug", "meta", "slug", "sortOrder", "thumbnail", "title", "type", "updatedAt") SELECT "allowCommunication", "body", "courseSlug", "createdAt", "description", "groupSlug", "meta", "slug", "sortOrder", "thumbnail", "title", "type", "updatedAt" FROM "Library";
|
|
21
|
+
DROP TABLE "Library";
|
|
22
|
+
ALTER TABLE "new_Library" RENAME TO "Library";
|
|
23
|
+
PRAGMA foreign_keys=ON;
|
|
24
|
+
PRAGMA defer_foreign_keys=OFF;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Warnings:
|
|
3
|
+
|
|
4
|
+
- You are about to drop the column `items` on the `Group` table. All the data in the column will be lost.
|
|
5
|
+
|
|
6
|
+
*/
|
|
7
|
+
-- RedefineTables
|
|
8
|
+
PRAGMA defer_foreign_keys=ON;
|
|
9
|
+
PRAGMA foreign_keys=OFF;
|
|
10
|
+
CREATE TABLE "new_Communication" (
|
|
11
|
+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
12
|
+
"userId" INTEGER NOT NULL,
|
|
13
|
+
"librarySlug" TEXT NOT NULL,
|
|
14
|
+
"type" TEXT NOT NULL,
|
|
15
|
+
"initiator" TEXT NOT NULL DEFAULT 'STUDENT',
|
|
16
|
+
"meta" TEXT,
|
|
17
|
+
"message" TEXT NOT NULL,
|
|
18
|
+
"authorResponse" TEXT,
|
|
19
|
+
"isPublic" BOOLEAN NOT NULL DEFAULT false,
|
|
20
|
+
"readAt" DATETIME,
|
|
21
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
22
|
+
"updatedAt" DATETIME NOT NULL,
|
|
23
|
+
CONSTRAINT "Communication_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
24
|
+
CONSTRAINT "Communication_librarySlug_fkey" FOREIGN KEY ("librarySlug") REFERENCES "Library" ("slug") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
25
|
+
);
|
|
26
|
+
INSERT INTO "new_Communication" ("authorResponse", "createdAt", "id", "isPublic", "librarySlug", "message", "meta", "readAt", "type", "updatedAt", "userId") SELECT "authorResponse", "createdAt", "id", "isPublic", "librarySlug", "message", "meta", "readAt", "type", "updatedAt", "userId" FROM "Communication";
|
|
27
|
+
DROP TABLE "Communication";
|
|
28
|
+
ALTER TABLE "new_Communication" RENAME TO "Communication";
|
|
29
|
+
CREATE INDEX "Communication_userId_idx" ON "Communication"("userId");
|
|
30
|
+
CREATE INDEX "Communication_librarySlug_idx" ON "Communication"("librarySlug");
|
|
31
|
+
CREATE TABLE "new_Course" (
|
|
32
|
+
"slug" TEXT NOT NULL PRIMARY KEY,
|
|
33
|
+
"title" TEXT NOT NULL,
|
|
34
|
+
"description" TEXT,
|
|
35
|
+
"thumbnail" TEXT,
|
|
36
|
+
"access" TEXT NOT NULL DEFAULT 'OPEN',
|
|
37
|
+
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
|
38
|
+
"price" INTEGER NOT NULL DEFAULT 0
|
|
39
|
+
);
|
|
40
|
+
INSERT INTO "new_Course" ("access", "description", "price", "slug", "thumbnail", "title") SELECT "access", "description", "price", "slug", "thumbnail", "title" FROM "Course";
|
|
41
|
+
DROP TABLE "Course";
|
|
42
|
+
ALTER TABLE "new_Course" RENAME TO "Course";
|
|
43
|
+
CREATE TABLE "new_Group" (
|
|
44
|
+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
45
|
+
"courseSlug" TEXT NOT NULL,
|
|
46
|
+
"slug" TEXT NOT NULL,
|
|
47
|
+
"title" TEXT NOT NULL,
|
|
48
|
+
"thumbnail" TEXT,
|
|
49
|
+
"description" TEXT,
|
|
50
|
+
CONSTRAINT "Group_courseSlug_fkey" FOREIGN KEY ("courseSlug") REFERENCES "Course" ("slug") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
51
|
+
);
|
|
52
|
+
INSERT INTO "new_Group" ("courseSlug", "id", "slug", "thumbnail", "title") SELECT "courseSlug", "id", "slug", "thumbnail", "title" FROM "Group";
|
|
53
|
+
DROP TABLE "Group";
|
|
54
|
+
ALTER TABLE "new_Group" RENAME TO "Group";
|
|
55
|
+
CREATE INDEX "Group_courseSlug_idx" ON "Group"("courseSlug");
|
|
56
|
+
CREATE UNIQUE INDEX "Group_courseSlug_slug_key" ON "Group"("courseSlug", "slug");
|
|
57
|
+
CREATE TABLE "new_Library" (
|
|
58
|
+
"slug" TEXT NOT NULL PRIMARY KEY,
|
|
59
|
+
"title" TEXT NOT NULL,
|
|
60
|
+
"description" TEXT,
|
|
61
|
+
"thumbnail" TEXT,
|
|
62
|
+
"type" TEXT NOT NULL,
|
|
63
|
+
"status" TEXT NOT NULL DEFAULT 'DRAFT',
|
|
64
|
+
"body" TEXT,
|
|
65
|
+
"courseSlug" TEXT NOT NULL,
|
|
66
|
+
"groupSlug" TEXT NOT NULL,
|
|
67
|
+
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
|
68
|
+
"allowCommunication" BOOLEAN NOT NULL DEFAULT true,
|
|
69
|
+
"meta" TEXT,
|
|
70
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
71
|
+
"updatedAt" DATETIME NOT NULL,
|
|
72
|
+
CONSTRAINT "Library_courseSlug_groupSlug_fkey" FOREIGN KEY ("courseSlug", "groupSlug") REFERENCES "Group" ("courseSlug", "slug") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
73
|
+
);
|
|
74
|
+
INSERT INTO "new_Library" ("allowCommunication", "body", "courseSlug", "createdAt", "description", "groupSlug", "meta", "slug", "sortOrder", "status", "thumbnail", "title", "type", "updatedAt") SELECT "allowCommunication", "body", "courseSlug", "createdAt", "description", "groupSlug", "meta", "slug", "sortOrder", "status", "thumbnail", "title", "type", "updatedAt" FROM "Library";
|
|
75
|
+
DROP TABLE "Library";
|
|
76
|
+
ALTER TABLE "new_Library" RENAME TO "Library";
|
|
77
|
+
CREATE INDEX "Library_courseSlug_idx" ON "Library"("courseSlug");
|
|
78
|
+
CREATE INDEX "Library_groupSlug_idx" ON "Library"("groupSlug");
|
|
79
|
+
CREATE TABLE "new_Subscription" (
|
|
80
|
+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
81
|
+
"userId" INTEGER NOT NULL,
|
|
82
|
+
"courseSlug" TEXT NOT NULL,
|
|
83
|
+
"startsAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
84
|
+
"endsAt" DATETIME,
|
|
85
|
+
"amount" INTEGER,
|
|
86
|
+
"cancelledAt" DATETIME,
|
|
87
|
+
CONSTRAINT "Subscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
88
|
+
CONSTRAINT "Subscription_courseSlug_fkey" FOREIGN KEY ("courseSlug") REFERENCES "Course" ("slug") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
89
|
+
);
|
|
90
|
+
INSERT INTO "new_Subscription" ("amount", "cancelledAt", "courseSlug", "endsAt", "id", "startsAt", "userId") SELECT "amount", "cancelledAt", "courseSlug", "endsAt", "id", "startsAt", "userId" FROM "Subscription";
|
|
91
|
+
DROP TABLE "Subscription";
|
|
92
|
+
ALTER TABLE "new_Subscription" RENAME TO "Subscription";
|
|
93
|
+
CREATE INDEX "Subscription_userId_idx" ON "Subscription"("userId");
|
|
94
|
+
CREATE INDEX "Subscription_courseSlug_idx" ON "Subscription"("courseSlug");
|
|
95
|
+
PRAGMA foreign_keys=ON;
|
|
96
|
+
PRAGMA defer_foreign_keys=OFF;
|
package/prisma/schema.prisma
CHANGED
|
@@ -6,6 +6,10 @@ datasource db {
|
|
|
6
6
|
provider = "sqlite"
|
|
7
7
|
url = env("DATABASE_URL")
|
|
8
8
|
}
|
|
9
|
+
enum CommunicationInitiator {
|
|
10
|
+
STUDENT
|
|
11
|
+
TEACHER
|
|
12
|
+
}
|
|
9
13
|
|
|
10
14
|
enum ContentType {
|
|
11
15
|
ARTICLE
|
|
@@ -13,6 +17,12 @@ enum ContentType {
|
|
|
13
17
|
MCQ
|
|
14
18
|
}
|
|
15
19
|
|
|
20
|
+
enum LibraryStatus {
|
|
21
|
+
DRAFT
|
|
22
|
+
PUBLISHED
|
|
23
|
+
ARCHIVED
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
enum CourseAccess {
|
|
17
27
|
OPEN
|
|
18
28
|
MEMBERS
|
|
@@ -24,37 +34,67 @@ enum AdminRole {
|
|
|
24
34
|
SUPER_ADMIN
|
|
25
35
|
}
|
|
26
36
|
|
|
37
|
+
model Course {
|
|
38
|
+
slug String @id
|
|
39
|
+
title String
|
|
40
|
+
description String?
|
|
41
|
+
thumbnail String?
|
|
42
|
+
access CourseAccess @default(OPEN)
|
|
43
|
+
isActive Boolean @default(true)
|
|
44
|
+
price Int @default(0)
|
|
45
|
+
|
|
46
|
+
groups Group[]
|
|
47
|
+
subscriptions Subscription[]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
27
51
|
model Group {
|
|
28
|
-
id
|
|
29
|
-
courseSlug
|
|
30
|
-
slug
|
|
31
|
-
title
|
|
32
|
-
thumbnail
|
|
33
|
-
|
|
52
|
+
id Int @id @default(autoincrement())
|
|
53
|
+
courseSlug String
|
|
54
|
+
slug String
|
|
55
|
+
title String
|
|
56
|
+
thumbnail String?
|
|
57
|
+
description String?
|
|
58
|
+
course Course @relation(fields: [courseSlug], references: [slug], onDelete: Restrict)
|
|
59
|
+
library Library[]
|
|
34
60
|
|
|
35
61
|
@@unique([courseSlug, slug])
|
|
36
62
|
@@index([courseSlug])
|
|
37
63
|
}
|
|
38
64
|
|
|
39
|
-
model
|
|
40
|
-
slug
|
|
41
|
-
title
|
|
42
|
-
description
|
|
43
|
-
thumbnail
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
65
|
+
model Library {
|
|
66
|
+
slug String @id
|
|
67
|
+
title String
|
|
68
|
+
description String?
|
|
69
|
+
thumbnail String?
|
|
70
|
+
type ContentType
|
|
71
|
+
status LibraryStatus @default(DRAFT)
|
|
72
|
+
body String?
|
|
73
|
+
courseSlug String
|
|
74
|
+
groupSlug String
|
|
75
|
+
sortOrder Int @default(0)
|
|
76
|
+
allowCommunication Boolean @default(true)
|
|
77
|
+
meta String?
|
|
78
|
+
createdAt DateTime @default(now())
|
|
79
|
+
updatedAt DateTime @updatedAt
|
|
80
|
+
|
|
81
|
+
group Group @relation(fields: [courseSlug, groupSlug], references: [courseSlug, slug], onDelete: Restrict)
|
|
47
82
|
|
|
83
|
+
communications Communication[]
|
|
84
|
+
|
|
85
|
+
@@index([courseSlug])
|
|
86
|
+
@@index([groupSlug])
|
|
87
|
+
}
|
|
48
88
|
model User {
|
|
49
|
-
id
|
|
50
|
-
email
|
|
51
|
-
password
|
|
52
|
-
name
|
|
53
|
-
role
|
|
54
|
-
resource
|
|
55
|
-
createdAt
|
|
56
|
-
subscriptions
|
|
57
|
-
communications
|
|
89
|
+
id Int @id @default(autoincrement())
|
|
90
|
+
email String @unique
|
|
91
|
+
password String
|
|
92
|
+
name String?
|
|
93
|
+
role String @default("student")
|
|
94
|
+
resource String?
|
|
95
|
+
createdAt DateTime @default(now())
|
|
96
|
+
subscriptions Subscription[]
|
|
97
|
+
communications Communication[]
|
|
58
98
|
}
|
|
59
99
|
|
|
60
100
|
model Admin {
|
|
@@ -68,21 +108,6 @@ model Admin {
|
|
|
68
108
|
updatedAt DateTime @updatedAt
|
|
69
109
|
}
|
|
70
110
|
|
|
71
|
-
model Library {
|
|
72
|
-
slug String @id
|
|
73
|
-
title String
|
|
74
|
-
description String?
|
|
75
|
-
thumbnail String?
|
|
76
|
-
type ContentType
|
|
77
|
-
body String?
|
|
78
|
-
courseSlug String
|
|
79
|
-
groupSlug String
|
|
80
|
-
sortOrder Int @default(0)
|
|
81
|
-
allowCommunication Boolean @default(true)
|
|
82
|
-
meta String?
|
|
83
|
-
createdAt DateTime @default(now())
|
|
84
|
-
updatedAt DateTime @updatedAt
|
|
85
|
-
}
|
|
86
111
|
|
|
87
112
|
model Subscription {
|
|
88
113
|
id Int @id @default(autoincrement())
|
|
@@ -91,9 +116,11 @@ model Subscription {
|
|
|
91
116
|
startsAt DateTime @default(now())
|
|
92
117
|
endsAt DateTime?
|
|
93
118
|
amount Int?
|
|
94
|
-
user User @relation(fields: [userId], references: [id])
|
|
95
119
|
cancelledAt DateTime?
|
|
96
120
|
|
|
121
|
+
user User @relation(fields: [userId], references: [id])
|
|
122
|
+
course Course @relation(fields: [courseSlug], references: [slug], onDelete: Restrict)
|
|
123
|
+
|
|
97
124
|
@@index([userId])
|
|
98
125
|
@@index([courseSlug])
|
|
99
126
|
}
|
|
@@ -103,6 +130,7 @@ model Communication {
|
|
|
103
130
|
userId Int
|
|
104
131
|
librarySlug String
|
|
105
132
|
type String
|
|
133
|
+
initiator CommunicationInitiator @default(STUDENT)
|
|
106
134
|
meta String?
|
|
107
135
|
message String
|
|
108
136
|
authorResponse String?
|
|
@@ -110,50 +138,47 @@ model Communication {
|
|
|
110
138
|
readAt DateTime?
|
|
111
139
|
createdAt DateTime @default(now())
|
|
112
140
|
updatedAt DateTime @updatedAt
|
|
113
|
-
user User
|
|
141
|
+
user User @relation(fields: [userId], references: [id])
|
|
142
|
+
library Library @relation(fields: [librarySlug], references: [slug], onDelete: Restrict)
|
|
114
143
|
|
|
115
144
|
@@index([userId])
|
|
116
145
|
@@index([librarySlug])
|
|
117
146
|
}
|
|
118
|
-
model Svg {
|
|
119
147
|
|
|
120
|
-
|
|
148
|
+
model Svg {
|
|
149
|
+
slug String @id
|
|
121
150
|
|
|
122
|
-
title
|
|
151
|
+
title String
|
|
123
152
|
|
|
124
|
-
body
|
|
153
|
+
body String
|
|
125
154
|
|
|
126
|
-
tags
|
|
155
|
+
tags String @default("[]")
|
|
127
156
|
|
|
128
157
|
createdAt DateTime @default(now())
|
|
129
158
|
|
|
130
159
|
updatedAt DateTime @updatedAt
|
|
131
|
-
|
|
132
160
|
}
|
|
133
161
|
|
|
134
162
|
model Image {
|
|
163
|
+
slug String @id
|
|
135
164
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
title String
|
|
165
|
+
title String
|
|
139
166
|
|
|
140
|
-
tags
|
|
167
|
+
tags String @default("[]")
|
|
141
168
|
|
|
142
169
|
createdAt DateTime @default(now())
|
|
143
170
|
|
|
144
171
|
updatedAt DateTime @updatedAt
|
|
145
|
-
|
|
146
172
|
}
|
|
147
|
-
model Audio {
|
|
148
173
|
|
|
149
|
-
|
|
174
|
+
model Audio {
|
|
175
|
+
slug String @id
|
|
150
176
|
|
|
151
|
-
title
|
|
177
|
+
title String
|
|
152
178
|
|
|
153
|
-
tags
|
|
179
|
+
tags String @default("[]")
|
|
154
180
|
|
|
155
181
|
createdAt DateTime @default(now())
|
|
156
182
|
|
|
157
183
|
updatedAt DateTime @updatedAt
|
|
158
|
-
|
|
159
|
-
}
|
|
184
|
+
}
|