@m5kdev/backend 0.1.1 → 0.1.3
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/src/lib/posthog.js +7 -0
- package/dist/src/lib/sentry.js +9 -0
- package/dist/src/modules/access/access.repository.js +32 -0
- package/dist/src/modules/access/access.service.js +51 -0
- package/dist/src/modules/access/access.test.js +182 -0
- package/dist/src/modules/access/access.utils.js +20 -0
- package/dist/src/modules/ai/ai.db.js +39 -0
- package/dist/src/modules/ai/ai.prompt.js +30 -0
- package/dist/src/modules/ai/ai.repository.js +26 -0
- package/dist/src/modules/ai/ai.router.js +132 -0
- package/dist/src/modules/ai/ai.service.js +207 -0
- package/dist/src/modules/ai/ai.trpc.d.ts +5 -5
- package/dist/src/modules/ai/ai.trpc.js +20 -0
- package/dist/src/modules/ai/ideogram/ideogram.constants.js +167 -0
- package/dist/src/modules/ai/ideogram/ideogram.dto.js +49 -0
- package/dist/src/modules/ai/ideogram/ideogram.prompt.js +860 -0
- package/dist/src/modules/ai/ideogram/ideogram.repository.js +46 -0
- package/dist/src/modules/ai/ideogram/ideogram.service.js +11 -0
- package/dist/src/modules/auth/auth.db.js +215 -0
- package/dist/src/modules/auth/auth.dto.js +38 -0
- package/dist/src/modules/auth/auth.lib.d.ts +4 -4
- package/dist/src/modules/auth/auth.lib.js +284 -0
- package/dist/src/modules/auth/auth.middleware.js +52 -0
- package/dist/src/modules/auth/auth.repository.js +541 -0
- package/dist/src/modules/auth/auth.service.js +201 -0
- package/dist/src/modules/auth/auth.trpc.d.ts +18 -18
- package/dist/src/modules/auth/auth.trpc.js +157 -0
- package/dist/src/modules/auth/auth.utils.js +97 -0
- package/dist/src/modules/base/base.abstract.js +53 -0
- package/dist/src/modules/base/base.dto.js +112 -0
- package/dist/src/modules/base/base.grants.js +123 -0
- package/dist/src/modules/base/base.grants.test.js +668 -0
- package/dist/src/modules/base/base.repository.js +307 -0
- package/dist/src/modules/base/base.service.js +109 -0
- package/dist/src/modules/base/base.types.js +2 -0
- package/dist/src/modules/billing/billing.db.js +29 -0
- package/dist/src/modules/billing/billing.repository.js +235 -0
- package/dist/src/modules/billing/billing.router.js +56 -0
- package/dist/src/modules/billing/billing.service.js +147 -0
- package/dist/src/modules/billing/billing.trpc.d.ts +5 -5
- package/dist/src/modules/billing/billing.trpc.js +17 -0
- package/dist/src/modules/clay/clay.repository.js +26 -0
- package/dist/src/modules/clay/clay.service.js +24 -0
- package/dist/src/modules/connect/connect.db.js +30 -0
- package/dist/src/modules/connect/connect.dto.js +36 -0
- package/dist/src/modules/connect/connect.linkedin.js +53 -0
- package/dist/src/modules/connect/connect.oauth.js +198 -0
- package/dist/src/modules/connect/connect.repository.d.ts +7 -7
- package/dist/src/modules/connect/connect.repository.js +54 -0
- package/dist/src/modules/connect/connect.router.js +54 -0
- package/dist/src/modules/connect/connect.service.d.ts +14 -14
- package/dist/src/modules/connect/connect.service.js +114 -0
- package/dist/src/modules/connect/connect.trpc.d.ts +10 -10
- package/dist/src/modules/connect/connect.trpc.js +21 -0
- package/dist/src/modules/connect/connect.types.js +2 -0
- package/dist/src/modules/crypto/crypto.db.js +17 -0
- package/dist/src/modules/crypto/crypto.repository.js +10 -0
- package/dist/src/modules/crypto/crypto.service.js +52 -0
- package/dist/src/modules/email/email.service.js +107 -0
- package/dist/src/modules/file/file.repository.js +79 -0
- package/dist/src/modules/file/file.router.js +99 -0
- package/dist/src/modules/file/file.service.js +150 -0
- package/dist/src/modules/recurrence/recurrence.db.js +66 -0
- package/dist/src/modules/recurrence/recurrence.repository.js +39 -0
- package/dist/src/modules/recurrence/recurrence.service.js +70 -0
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts +15 -15
- package/dist/src/modules/recurrence/recurrence.trpc.js +65 -0
- package/dist/src/modules/social/social.dto.js +18 -0
- package/dist/src/modules/social/social.linkedin.js +427 -0
- package/dist/src/modules/social/social.linkedin.test.js +235 -0
- package/dist/src/modules/social/social.service.js +76 -0
- package/dist/src/modules/social/social.types.js +2 -0
- package/dist/src/modules/tag/tag.db.js +42 -0
- package/dist/src/modules/tag/tag.dto.js +9 -0
- package/dist/src/modules/tag/tag.repository.js +154 -0
- package/dist/src/modules/tag/tag.service.js +31 -0
- package/dist/src/modules/tag/tag.trpc.d.ts +5 -5
- package/dist/src/modules/tag/tag.trpc.js +47 -0
- package/dist/src/modules/utils/applyPagination.js +16 -0
- package/dist/src/modules/utils/applySorting.js +18 -0
- package/dist/src/modules/utils/getConditionsFromFilters.js +200 -0
- package/dist/src/modules/video/video.service.js +84 -0
- package/dist/src/modules/webhook/webhook.constants.js +10 -0
- package/dist/src/modules/webhook/webhook.db.js +17 -0
- package/dist/src/modules/webhook/webhook.dto.js +7 -0
- package/dist/src/modules/webhook/webhook.repository.js +56 -0
- package/dist/src/modules/webhook/webhook.router.js +30 -0
- package/dist/src/modules/webhook/webhook.service.js +68 -0
- package/dist/src/modules/workflow/workflow.db.js +30 -0
- package/dist/src/modules/workflow/workflow.repository.js +105 -0
- package/dist/src/modules/workflow/workflow.service.js +37 -0
- package/dist/src/modules/workflow/workflow.trpc.d.ts +5 -5
- package/dist/src/modules/workflow/workflow.trpc.js +21 -0
- package/dist/src/modules/workflow/workflow.types.js +2 -0
- package/dist/src/modules/workflow/workflow.utils.js +173 -0
- package/dist/src/test/stubs/utils.js +5 -0
- package/dist/src/trpc/context.d.ts +5 -5
- package/dist/src/trpc/context.js +17 -0
- package/dist/src/trpc/index.js +6 -0
- package/dist/src/trpc/procedures.d.ts +56 -56
- package/dist/src/trpc/procedures.js +32 -0
- package/dist/src/trpc/utils.js +20 -0
- package/dist/src/types.d.ts +33 -33
- package/dist/src/types.js +13 -0
- package/dist/src/utils/errors.js +104 -0
- package/dist/src/utils/logger.js +11 -0
- package/dist/src/utils/posthog.js +31 -0
- package/dist/src/utils/types.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/tsconfig.json +2 -0
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @m5kdev/backend
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- build js
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @m5kdev/commons@0.1.3
|
|
10
|
+
- @m5kdev/config@0.1.3
|
|
11
|
+
|
|
12
|
+
## 0.1.2
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- build
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @m5kdev/commons@0.1.2
|
|
19
|
+
- @m5kdev/config@0.1.2
|
|
20
|
+
|
|
3
21
|
## 0.1.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.posthogClient = void 0;
|
|
4
|
+
const posthog_node_1 = require("posthog-node");
|
|
5
|
+
exports.posthogClient = new posthog_node_1.PostHog(process.env.VITE_PUBLIC_POSTHOG_KEY, {
|
|
6
|
+
host: process.env.VITE_PUBLIC_POSTHOG_HOST,
|
|
7
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const Sentry = tslib_1.__importStar(require("@sentry/node"));
|
|
5
|
+
Sentry.init({
|
|
6
|
+
dsn: process.env.SENTRY_SERVER_DNS,
|
|
7
|
+
// Set sampling rate for profiling - this is evaluated only once per SDK.init
|
|
8
|
+
profileSessionSampleRate: 1.0,
|
|
9
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccessRepository = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
6
|
+
const neverthrow_1 = require("neverthrow");
|
|
7
|
+
const auth = tslib_1.__importStar(require("#modules/auth/auth.db"));
|
|
8
|
+
const base_repository_1 = require("#modules/base/base.repository");
|
|
9
|
+
const schema = { ...auth };
|
|
10
|
+
class AccessRepository extends base_repository_1.BaseRepository {
|
|
11
|
+
async getOrganizationRole(userId, organizationId) {
|
|
12
|
+
return this.throwableAsync(async () => {
|
|
13
|
+
const [member] = await this.orm
|
|
14
|
+
.select({ role: schema.members.role })
|
|
15
|
+
.from(schema.members)
|
|
16
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.members.organizationId, organizationId), (0, drizzle_orm_1.eq)(schema.members.userId, userId)))
|
|
17
|
+
.limit(1);
|
|
18
|
+
return (0, neverthrow_1.ok)(member?.role ?? "");
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async getTeamRole(userId, teamId) {
|
|
22
|
+
return this.throwableAsync(async () => {
|
|
23
|
+
const [member] = await this.orm
|
|
24
|
+
.select({ role: schema.teamMembers.role })
|
|
25
|
+
.from(schema.teamMembers)
|
|
26
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.teamMembers.teamId, teamId), (0, drizzle_orm_1.eq)(schema.teamMembers.userId, userId)))
|
|
27
|
+
.limit(1);
|
|
28
|
+
return (0, neverthrow_1.ok)(member?.role ?? "");
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.AccessRepository = AccessRepository;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccessService = void 0;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const base_service_1 = require("#modules/base/base.service");
|
|
6
|
+
class AccessService extends base_service_1.BaseService {
|
|
7
|
+
acr;
|
|
8
|
+
constructor(repositories, acr) {
|
|
9
|
+
super(repositories);
|
|
10
|
+
this.acr = acr;
|
|
11
|
+
}
|
|
12
|
+
authorize(level, role, request, connector = "AND") {
|
|
13
|
+
try {
|
|
14
|
+
return !!this.acr[level][role].authorize(request, connector).success;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
console.error(error);
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async checkAccess(user, level, levelId, request, connector = "AND") {
|
|
22
|
+
const role = level === "organization"
|
|
23
|
+
? await this.repository.access.getOrganizationRole(user.id, levelId)
|
|
24
|
+
: await this.repository.access.getTeamRole(user.id, levelId);
|
|
25
|
+
if (role.isErr())
|
|
26
|
+
return (0, neverthrow_1.err)(role.error);
|
|
27
|
+
return (0, neverthrow_1.ok)(this.authorize(level, role.value, request, connector));
|
|
28
|
+
}
|
|
29
|
+
async hasAccess(user, level, levelId, request, connector = "AND") {
|
|
30
|
+
// FIXME: catch all admin user access for now
|
|
31
|
+
if (user.role === "admin")
|
|
32
|
+
return (0, neverthrow_1.ok)(true);
|
|
33
|
+
const userAccess = this.authorize("user", user.role, request, connector);
|
|
34
|
+
if (level === "user")
|
|
35
|
+
return (0, neverthrow_1.ok)(userAccess && user.id === levelId);
|
|
36
|
+
if (userAccess)
|
|
37
|
+
return (0, neverthrow_1.ok)(true);
|
|
38
|
+
const organizationAccess = await this.checkAccess(user, "organization", levelId, request, connector);
|
|
39
|
+
if (organizationAccess.isErr())
|
|
40
|
+
return (0, neverthrow_1.err)(organizationAccess.error);
|
|
41
|
+
if (level === "organization")
|
|
42
|
+
return organizationAccess;
|
|
43
|
+
if (organizationAccess.value)
|
|
44
|
+
return (0, neverthrow_1.ok)(true);
|
|
45
|
+
const teamAccess = await this.checkAccess(user, "team", levelId, request, connector);
|
|
46
|
+
if (teamAccess.isErr())
|
|
47
|
+
return (0, neverthrow_1.err)(teamAccess.error);
|
|
48
|
+
return teamAccess;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.AccessService = AccessService;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const client_1 = require("@libsql/client");
|
|
5
|
+
const libsql_1 = require("drizzle-orm/libsql");
|
|
6
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
7
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
8
|
+
const access_repository_1 = require("#modules/access/access.repository");
|
|
9
|
+
const access_service_1 = require("#modules/access/access.service");
|
|
10
|
+
const access_utils_1 = require("#modules/access/access.utils");
|
|
11
|
+
const authSchema = tslib_1.__importStar(require("#modules/auth/auth.db"));
|
|
12
|
+
describe("AccessService", () => {
|
|
13
|
+
const statements = {
|
|
14
|
+
project: ["create", "share", "update", "delete"],
|
|
15
|
+
};
|
|
16
|
+
const roleDefinitions = {
|
|
17
|
+
user: {
|
|
18
|
+
admin: { project: ["create", "share", "update"] },
|
|
19
|
+
},
|
|
20
|
+
team: {
|
|
21
|
+
admin: { project: ["create", "share", "update"] },
|
|
22
|
+
},
|
|
23
|
+
organization: {
|
|
24
|
+
admin: { project: ["create", "share", "update"] },
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const acr = (0, access_utils_1.createAccessRoles)(statements, roleDefinitions);
|
|
28
|
+
// Minimal repository stub; not used by authorize() unit tests
|
|
29
|
+
const accessService = new access_service_1.AccessService({ access: {} }, acr);
|
|
30
|
+
describe("user level", () => {
|
|
31
|
+
it("allows defined action", () => {
|
|
32
|
+
expect(accessService.authorize("user", "admin", { project: ["create"] })).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it("denies undefined action", () => {
|
|
35
|
+
expect(accessService.authorize("user", "admin", { project: ["delete"] })).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
it("handles AND vs OR connectors for multiple actions", () => {
|
|
38
|
+
// admin has create but not delete
|
|
39
|
+
expect(accessService.authorize("user", "admin", { project: ["create", "delete"] }, "AND")).toBe(false);
|
|
40
|
+
expect(accessService.authorize("user", "admin", { project: { actions: ["create", "delete"], connector: "OR" } }, "OR")).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("team level", () => {
|
|
44
|
+
it("allows defined action", () => {
|
|
45
|
+
expect(accessService.authorize("team", "admin", { project: ["share"] })).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
it("denies undefined action", () => {
|
|
48
|
+
expect(accessService.authorize("team", "admin", { project: ["delete"] })).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe("organization level", () => {
|
|
52
|
+
it("allows defined action", () => {
|
|
53
|
+
expect(accessService.authorize("organization", "admin", { project: ["update"] })).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it("denies undefined action", () => {
|
|
56
|
+
expect(accessService.authorize("organization", "admin", { project: ["delete"] })).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("AccessRepository (libsql local)", () => {
|
|
61
|
+
const dbFile = path_1.default.join(__dirname, "access.test.sqlite");
|
|
62
|
+
const url = `file:${dbFile}`;
|
|
63
|
+
const client = (0, client_1.createClient)({ url });
|
|
64
|
+
const orm = (0, libsql_1.drizzle)(client, { schema: authSchema });
|
|
65
|
+
const repo = new access_repository_1.AccessRepository({ orm, schema: authSchema }, {});
|
|
66
|
+
beforeAll(async () => {
|
|
67
|
+
// Create minimal tables required for tests
|
|
68
|
+
await client.execute(`
|
|
69
|
+
CREATE TABLE IF NOT EXISTS members (
|
|
70
|
+
id TEXT PRIMARY KEY,
|
|
71
|
+
organization_id TEXT NOT NULL,
|
|
72
|
+
user_id TEXT NOT NULL,
|
|
73
|
+
role TEXT NOT NULL
|
|
74
|
+
);
|
|
75
|
+
`);
|
|
76
|
+
await client.execute(`
|
|
77
|
+
CREATE TABLE IF NOT EXISTS teammembers (
|
|
78
|
+
id TEXT PRIMARY KEY,
|
|
79
|
+
team_id TEXT NOT NULL,
|
|
80
|
+
user_id TEXT NOT NULL,
|
|
81
|
+
role TEXT NOT NULL
|
|
82
|
+
);
|
|
83
|
+
`);
|
|
84
|
+
});
|
|
85
|
+
beforeEach(async () => {
|
|
86
|
+
await client.execute({
|
|
87
|
+
sql: "INSERT INTO members (id, organization_id, user_id, role) VALUES (?, ?, ?, ?)",
|
|
88
|
+
args: ["m1", "org1", "user1", "admin"],
|
|
89
|
+
});
|
|
90
|
+
await client.execute({
|
|
91
|
+
sql: "INSERT INTO teammembers (id, team_id, user_id, role) VALUES (?, ?, ?, ?)",
|
|
92
|
+
args: ["tm1", "team1", "user1", "admin"],
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
afterEach(async () => {
|
|
96
|
+
await client.execute("DELETE FROM members;");
|
|
97
|
+
await client.execute("DELETE FROM teammembers;");
|
|
98
|
+
});
|
|
99
|
+
afterAll(async () => {
|
|
100
|
+
try {
|
|
101
|
+
await client.close();
|
|
102
|
+
}
|
|
103
|
+
catch { }
|
|
104
|
+
try {
|
|
105
|
+
if (fs_1.default.existsSync(dbFile))
|
|
106
|
+
fs_1.default.unlinkSync(dbFile);
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
});
|
|
110
|
+
it("returns organization role for user", async () => {
|
|
111
|
+
const res = await repo.getOrganizationRole("user1", "org1");
|
|
112
|
+
expect(res.isOk()).toBe(true);
|
|
113
|
+
if (res.isOk())
|
|
114
|
+
expect(res.value).toBe("admin");
|
|
115
|
+
});
|
|
116
|
+
it("returns team role for user", async () => {
|
|
117
|
+
const res = await repo.getTeamRole("user1", "team1");
|
|
118
|
+
expect(res.isOk()).toBe(true);
|
|
119
|
+
if (res.isOk())
|
|
120
|
+
expect(res.value).toBe("admin");
|
|
121
|
+
});
|
|
122
|
+
describe("AccessService.hasAccess (with repo)", () => {
|
|
123
|
+
const hasAccessStatements = {
|
|
124
|
+
project: ["create", "share", "update", "delete"],
|
|
125
|
+
};
|
|
126
|
+
const hasAccessRoles = {
|
|
127
|
+
user: {
|
|
128
|
+
member: { project: ["create"] },
|
|
129
|
+
},
|
|
130
|
+
team: {
|
|
131
|
+
manager: { project: ["share"] },
|
|
132
|
+
},
|
|
133
|
+
organization: {
|
|
134
|
+
manager: { project: ["update"] },
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const acr2 = (0, access_utils_1.createAccessRoles)(hasAccessStatements, hasAccessRoles);
|
|
138
|
+
const service = new access_service_1.AccessService({ access: repo }, acr2);
|
|
139
|
+
it("admin override grants access", async () => {
|
|
140
|
+
const result = await service.hasAccess({ id: "any", role: "admin" }, "organization", "whatever", { project: ["delete"] });
|
|
141
|
+
expect(result.isOk()).toBe(true);
|
|
142
|
+
if (result.isOk())
|
|
143
|
+
expect(result.value).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
it("user level access only for self and allowed action", async () => {
|
|
146
|
+
const allowSelf = await service.hasAccess({ id: "user2", role: "member" }, "user", "user2", {
|
|
147
|
+
project: ["create"],
|
|
148
|
+
});
|
|
149
|
+
expect(allowSelf.isOk()).toBe(true);
|
|
150
|
+
if (allowSelf.isOk())
|
|
151
|
+
expect(allowSelf.value).toBe(true);
|
|
152
|
+
const denyOther = await service.hasAccess({ id: "user2", role: "member" }, "user", "other", {
|
|
153
|
+
project: ["create"],
|
|
154
|
+
});
|
|
155
|
+
expect(denyOther.isOk()).toBe(true);
|
|
156
|
+
if (denyOther.isOk())
|
|
157
|
+
expect(denyOther.value).toBe(false);
|
|
158
|
+
});
|
|
159
|
+
it("organization membership grants access based on repo role", async () => {
|
|
160
|
+
await client.execute({
|
|
161
|
+
sql: "INSERT INTO members (id, organization_id, user_id, role) VALUES (?, ?, ?, ?)",
|
|
162
|
+
args: ["m2", "org2", "user2", "manager"],
|
|
163
|
+
});
|
|
164
|
+
const result = await service.hasAccess({ id: "user2", role: "member" }, "organization", "org2", { project: ["update"] });
|
|
165
|
+
expect(result.isOk()).toBe(true);
|
|
166
|
+
if (result.isOk())
|
|
167
|
+
expect(result.value).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
it("team membership grants access based on repo role", async () => {
|
|
170
|
+
await client.execute({
|
|
171
|
+
sql: "INSERT INTO teammembers (id, team_id, user_id, role) VALUES (?, ?, ?, ?)",
|
|
172
|
+
args: ["tm2", "team2", "user2", "manager"],
|
|
173
|
+
});
|
|
174
|
+
const result = await service.hasAccess({ id: "user2", role: "member" }, "team", "team2", {
|
|
175
|
+
project: ["share"],
|
|
176
|
+
});
|
|
177
|
+
expect(result.isOk()).toBe(true);
|
|
178
|
+
if (result.isOk())
|
|
179
|
+
expect(result.value).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAccessRoles = createAccessRoles;
|
|
4
|
+
const access_1 = require("better-auth/plugins/access");
|
|
5
|
+
function createAccessRoles(statements, roleDefinitions) {
|
|
6
|
+
const ac = (0, access_1.createAccessControl)(statements);
|
|
7
|
+
const user = {};
|
|
8
|
+
const team = {};
|
|
9
|
+
const organization = {};
|
|
10
|
+
for (const [roleName, roleStatements] of Object.entries(roleDefinitions.user)) {
|
|
11
|
+
user[roleName] = ac.newRole(roleStatements);
|
|
12
|
+
}
|
|
13
|
+
for (const [roleName, roleStatements] of Object.entries(roleDefinitions.team)) {
|
|
14
|
+
team[roleName] = ac.newRole(roleStatements);
|
|
15
|
+
}
|
|
16
|
+
for (const [roleName, roleStatements] of Object.entries(roleDefinitions.organization)) {
|
|
17
|
+
organization[roleName] = ac.newRole(roleStatements);
|
|
18
|
+
}
|
|
19
|
+
return { ac, user, team, organization };
|
|
20
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.aiUsage = exports.chats = void 0;
|
|
4
|
+
const sqlite_core_1 = require("drizzle-orm/sqlite-core");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
6
|
+
const auth_db_1 = require("#modules/auth/auth.db");
|
|
7
|
+
exports.chats = (0, sqlite_core_1.sqliteTable)("chats", {
|
|
8
|
+
id: (0, sqlite_core_1.text)("id").primaryKey().$default(uuid_1.v4),
|
|
9
|
+
userId: (0, sqlite_core_1.text)("user_id")
|
|
10
|
+
.notNull()
|
|
11
|
+
.references(() => auth_db_1.users.id, { onDelete: "cascade" }),
|
|
12
|
+
title: (0, sqlite_core_1.text)("title"),
|
|
13
|
+
type: (0, sqlite_core_1.text)("type"),
|
|
14
|
+
conversation: (0, sqlite_core_1.text)("conversation", { mode: "json" }),
|
|
15
|
+
createdAt: (0, sqlite_core_1.integer)("created_at", { mode: "timestamp" }).$default(() => new Date()),
|
|
16
|
+
updatedAt: (0, sqlite_core_1.integer)("updated_at", { mode: "timestamp" })
|
|
17
|
+
.notNull()
|
|
18
|
+
.$default(() => new Date()),
|
|
19
|
+
});
|
|
20
|
+
exports.aiUsage = (0, sqlite_core_1.sqliteTable)("ai_usage", {
|
|
21
|
+
id: (0, sqlite_core_1.text)("id").primaryKey().$default(uuid_1.v4),
|
|
22
|
+
userId: (0, sqlite_core_1.text)("user_id").references(() => auth_db_1.users.id, { onDelete: "cascade" }),
|
|
23
|
+
teamId: (0, sqlite_core_1.text)("team_id").references(() => auth_db_1.teams.id, { onDelete: "cascade" }),
|
|
24
|
+
organizationId: (0, sqlite_core_1.text)("organization_id").references(() => auth_db_1.organizations.id, {
|
|
25
|
+
onDelete: "cascade",
|
|
26
|
+
}),
|
|
27
|
+
feature: (0, sqlite_core_1.text)("feature").notNull(),
|
|
28
|
+
provider: (0, sqlite_core_1.text)("provider").notNull(),
|
|
29
|
+
model: (0, sqlite_core_1.text)("model").notNull(),
|
|
30
|
+
inputTokens: (0, sqlite_core_1.integer)("input_tokens"),
|
|
31
|
+
outputTokens: (0, sqlite_core_1.integer)("output_tokens"),
|
|
32
|
+
totalTokens: (0, sqlite_core_1.integer)("total_tokens"),
|
|
33
|
+
cost: (0, sqlite_core_1.real)("cost"),
|
|
34
|
+
traceId: (0, sqlite_core_1.text)("trace_id"),
|
|
35
|
+
createdAt: (0, sqlite_core_1.integer)("created_at", { mode: "timestamp" })
|
|
36
|
+
.notNull()
|
|
37
|
+
.$default(() => new Date()),
|
|
38
|
+
metadata: (0, sqlite_core_1.text)("metadata", { mode: "json" }),
|
|
39
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Prompt = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const mustache_1 = tslib_1.__importDefault(require("mustache"));
|
|
6
|
+
const logger_1 = require("#utils/logger");
|
|
7
|
+
class Prompt {
|
|
8
|
+
prompt;
|
|
9
|
+
name;
|
|
10
|
+
type;
|
|
11
|
+
config;
|
|
12
|
+
version;
|
|
13
|
+
labels;
|
|
14
|
+
tags;
|
|
15
|
+
constructor(prompt, settings) {
|
|
16
|
+
this.prompt = prompt;
|
|
17
|
+
this.name = settings?.name;
|
|
18
|
+
this.type = settings?.type ?? "text";
|
|
19
|
+
this.config = settings?.config;
|
|
20
|
+
this.version = settings?.version;
|
|
21
|
+
this.labels = settings?.labels;
|
|
22
|
+
this.tags = settings?.tags;
|
|
23
|
+
}
|
|
24
|
+
compile(context) {
|
|
25
|
+
const result = mustache_1.default.render(this.prompt.trim(), context);
|
|
26
|
+
logger_1.logger.debug(`[PROMPT]: ${result.trim()}`);
|
|
27
|
+
return result.trim();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.Prompt = Prompt;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AiUsageRepository = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
6
|
+
const neverthrow_1 = require("neverthrow");
|
|
7
|
+
const ai = tslib_1.__importStar(require("#modules/ai/ai.db"));
|
|
8
|
+
const base_repository_1 = require("#modules/base/base.repository");
|
|
9
|
+
const schema = { ...ai };
|
|
10
|
+
class AiUsageRepository extends base_repository_1.BaseTableRepository {
|
|
11
|
+
getUsage(userId) {
|
|
12
|
+
return this.throwableAsync(async () => {
|
|
13
|
+
const [usage] = await this.orm
|
|
14
|
+
.select({
|
|
15
|
+
inputTokens: (0, drizzle_orm_1.sql) `SUM(${this.table.inputTokens})`,
|
|
16
|
+
outputTokens: (0, drizzle_orm_1.sql) `SUM(${this.table.outputTokens})`,
|
|
17
|
+
totalTokens: (0, drizzle_orm_1.sql) `SUM(${this.table.totalTokens})`,
|
|
18
|
+
cost: (0, drizzle_orm_1.sql) `SUM(${this.table.cost})`,
|
|
19
|
+
})
|
|
20
|
+
.from(this.table)
|
|
21
|
+
.where((0, drizzle_orm_1.eq)(this.table.userId, userId));
|
|
22
|
+
return (0, neverthrow_1.ok)(usage);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.AiUsageRepository = AiUsageRepository;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// import { openai } from "@ai-sdk/openai";
|
|
3
|
+
// import { open}
|
|
4
|
+
// import { withTracing } from "@posthog/ai";
|
|
5
|
+
// import {
|
|
6
|
+
// appendClientMessage,
|
|
7
|
+
// appendResponseMessages,
|
|
8
|
+
// type Message,
|
|
9
|
+
// streamText,
|
|
10
|
+
// type Tool,
|
|
11
|
+
// } from "ai";
|
|
12
|
+
// import bodyParser from "body-parser";
|
|
13
|
+
// import { and, eq } from "drizzle-orm";
|
|
14
|
+
// import express, { type Response, type Router } from "express";
|
|
15
|
+
// import { v4 as uuidv4 } from "uuid";
|
|
16
|
+
// import type { AuthRequest, createAuthMiddleware } from "../auth/auth.middleware";
|
|
17
|
+
// import { type Orm, schema } from "../db";
|
|
18
|
+
// import { logger } from "../logger";
|
|
19
|
+
// import { posthogClient } from "../posthog";
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
// export type Tooling = Record<
|
|
22
|
+
// string,
|
|
23
|
+
// {
|
|
24
|
+
// getMesseges: (chatId: string) => Promise<Message[]>;
|
|
25
|
+
// getTools: (chatId: string) => Promise<
|
|
26
|
+
// Record<
|
|
27
|
+
// string,
|
|
28
|
+
// {
|
|
29
|
+
// tool: Tool;
|
|
30
|
+
// handler?: (
|
|
31
|
+
// chatId: string,
|
|
32
|
+
// args: unknown,
|
|
33
|
+
// user: NonNullable<AuthRequest["user"]>
|
|
34
|
+
// ) => Promise<void>;
|
|
35
|
+
// }
|
|
36
|
+
// >
|
|
37
|
+
// >;
|
|
38
|
+
// }
|
|
39
|
+
// >;
|
|
40
|
+
// export function tracedOpenAiModel(
|
|
41
|
+
// model: Parameters<typeof openai>[0],
|
|
42
|
+
// clientOptions: Parameters<typeof withTracing>[2]
|
|
43
|
+
// ) {
|
|
44
|
+
// return withTracing(openai(model), posthogClient, clientOptions);
|
|
45
|
+
// }
|
|
46
|
+
// async function getRouteSettings(id: string, name: string, settings: Tooling) {
|
|
47
|
+
// const entity = settings[name as keyof typeof settings];
|
|
48
|
+
// if (!entity) return { tools: {}, entityMesseges: [], entityTools: {} };
|
|
49
|
+
// const entityTools = await entity.getTools(id);
|
|
50
|
+
// const entityMesseges = await entity.getMesseges(id);
|
|
51
|
+
// const tools = Object.entries(entityTools).reduce<
|
|
52
|
+
// Record<string, (typeof entityTools)[keyof typeof entityTools]["tool"]>
|
|
53
|
+
// >((acc, [key, value]) => {
|
|
54
|
+
// acc[key] = value.tool as (typeof entityTools)[keyof typeof entityTools]["tool"];
|
|
55
|
+
// return acc;
|
|
56
|
+
// }, {});
|
|
57
|
+
// return { tools, entityMesseges, entityTools };
|
|
58
|
+
// }
|
|
59
|
+
// export function createAiRouter(
|
|
60
|
+
// orm: Orm,
|
|
61
|
+
// authMiddleware: ReturnType<typeof createAuthMiddleware>,
|
|
62
|
+
// settings: Tooling
|
|
63
|
+
// ) {
|
|
64
|
+
// const aiRouter: Router = express.Router();
|
|
65
|
+
// aiRouter.use(bodyParser.json());
|
|
66
|
+
// aiRouter.post("/completion/:name", authMiddleware, async (req: AuthRequest, res: Response) => {
|
|
67
|
+
// try {
|
|
68
|
+
// const { id, message } = req.body as {
|
|
69
|
+
// id: string;
|
|
70
|
+
// message: Message;
|
|
71
|
+
// };
|
|
72
|
+
// logger.info(req.body, "body");
|
|
73
|
+
// const { name } = req.params;
|
|
74
|
+
// const user = req.user!;
|
|
75
|
+
// logger.info(message, "Received message:");
|
|
76
|
+
// const { tools, entityMesseges, entityTools } = await getRouteSettings(id, name, settings);
|
|
77
|
+
// const [chat] = await orm
|
|
78
|
+
// .select()
|
|
79
|
+
// .from(schema.chats)
|
|
80
|
+
// .where(and(eq(schema.chats.id, id), eq(schema.chats.userId, user.id)));
|
|
81
|
+
// if (!chat) throw new Error("Chat not found");
|
|
82
|
+
// const messages = appendClientMessage({
|
|
83
|
+
// messages: (chat.conversation || []) as Message[],
|
|
84
|
+
// message,
|
|
85
|
+
// });
|
|
86
|
+
// // Process any tool invocations in the message
|
|
87
|
+
// const toolInvocation = message?.parts?.find((p) => p.type === "tool-invocation");
|
|
88
|
+
// if (toolInvocation) {
|
|
89
|
+
// logger.info(toolInvocation, "Processing tool invocation:");
|
|
90
|
+
// const tool =
|
|
91
|
+
// entityTools[toolInvocation.toolInvocation.toolName as keyof typeof entityTools];
|
|
92
|
+
// if (tool?.handler) {
|
|
93
|
+
// const result = await tool.handler(id, toolInvocation.toolInvocation.args, user);
|
|
94
|
+
// logger.info({ result }, "Tool handler result:");
|
|
95
|
+
// }
|
|
96
|
+
// }
|
|
97
|
+
// logger.info([...entityMesseges, ...messages], "Processed messages");
|
|
98
|
+
// const result = streamText({
|
|
99
|
+
// model: tracedOpenAiModel("gpt-4o", {
|
|
100
|
+
// posthogDistinctId: user.id,
|
|
101
|
+
// posthogProperties: { conversation_id: id, paid: true },
|
|
102
|
+
// posthogPrivacyMode: false,
|
|
103
|
+
// }),
|
|
104
|
+
// experimental_generateMessageId: uuidv4,
|
|
105
|
+
// messages: [...entityMesseges, ...messages],
|
|
106
|
+
// async onFinish({ response }) {
|
|
107
|
+
// logger.info(response, "Final response:");
|
|
108
|
+
// try {
|
|
109
|
+
// await orm
|
|
110
|
+
// .update(schema.chats)
|
|
111
|
+
// .set({
|
|
112
|
+
// conversation: appendResponseMessages({
|
|
113
|
+
// messages,
|
|
114
|
+
// responseMessages: response.messages,
|
|
115
|
+
// }),
|
|
116
|
+
// })
|
|
117
|
+
// .where(eq(schema.chats.id, id));
|
|
118
|
+
// } catch (error) {
|
|
119
|
+
// logger.error("Error in onFinish handler:", error);
|
|
120
|
+
// }
|
|
121
|
+
// },
|
|
122
|
+
// tools,
|
|
123
|
+
// });
|
|
124
|
+
// result.consumeStream();
|
|
125
|
+
// result.pipeDataStreamToResponse(res);
|
|
126
|
+
// } catch (error) {
|
|
127
|
+
// logger.error(error, "Error in ai handler");
|
|
128
|
+
// res.status(500).send({ error: "Internal Server Error" });
|
|
129
|
+
// }
|
|
130
|
+
// });
|
|
131
|
+
// return aiRouter;
|
|
132
|
+
// }
|