weifuwu 0.24.0 → 0.24.1
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/README.md +200 -92
- package/dist/agent/types.d.ts +2 -1
- package/dist/analytics.d.ts +2 -2
- package/dist/cache.d.ts +4 -4
- package/dist/cors.d.ts +2 -2
- package/dist/deploy/types.d.ts +2 -2
- package/dist/helmet.d.ts +2 -2
- package/dist/hub.d.ts +2 -1
- package/dist/iii/register-worker.d.ts +1 -1
- package/dist/iii/types.d.ts +4 -4
- package/dist/index.js +119 -97
- package/dist/kb/types.d.ts +8 -0
- package/dist/logdb/types.d.ts +2 -1
- package/dist/mailer.d.ts +2 -1
- package/dist/messager/types.d.ts +2 -1
- package/dist/opencode/types.d.ts +2 -1
- package/dist/permissions.d.ts +2 -2
- package/dist/postgres/module.d.ts +2 -1
- package/dist/postgres/types.d.ts +2 -2
- package/dist/queue/types.d.ts +2 -2
- package/dist/rate-limit.d.ts +1 -1
- package/dist/react.js +6 -6
- package/dist/redis/types.d.ts +2 -2
- package/dist/seo.d.ts +2 -2
- package/dist/serve.d.ts +1 -1
- package/dist/session.d.ts +9 -5
- package/dist/tailwind.d.ts +9 -0
- package/dist/tenant/types.d.ts +3 -3
- package/dist/types.d.ts +8 -0
- package/dist/upload.d.ts +4 -2
- package/dist/user/types.d.ts +2 -2
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -183,8 +183,8 @@ async function sendResponse(res, response, opts) {
|
|
|
183
183
|
}
|
|
184
184
|
res.end();
|
|
185
185
|
}
|
|
186
|
-
async function createTestServer(handler) {
|
|
187
|
-
const server = serve(handler, { port: 0, shutdown: false });
|
|
186
|
+
async function createTestServer(handler, options) {
|
|
187
|
+
const server = serve(handler, { ...options, port: options?.port ?? 0, shutdown: false });
|
|
188
188
|
await server.ready;
|
|
189
189
|
return { server, url: `http://localhost:${server.port}` };
|
|
190
190
|
}
|
|
@@ -2695,9 +2695,10 @@ var Table = class {
|
|
|
2695
2695
|
_buildSET(data) {
|
|
2696
2696
|
const sets = [];
|
|
2697
2697
|
const values = [];
|
|
2698
|
+
const d = data;
|
|
2698
2699
|
for (const { prop, db } of this.colEntries) {
|
|
2699
|
-
if (prop in
|
|
2700
|
-
const val =
|
|
2700
|
+
if (prop in d && d[prop] !== void 0) {
|
|
2701
|
+
const val = d[prop];
|
|
2701
2702
|
if (val instanceof SQL) {
|
|
2702
2703
|
sets.push(`"${db}" = ${val.toSQL()}`);
|
|
2703
2704
|
} else {
|
|
@@ -2706,7 +2707,7 @@ var Table = class {
|
|
|
2706
2707
|
}
|
|
2707
2708
|
}
|
|
2708
2709
|
}
|
|
2709
|
-
if (this.hasColumn("updated_at") && !
|
|
2710
|
+
if (this.hasColumn("updated_at") && !d.updated_at) {
|
|
2710
2711
|
sets.push('"updated_at" = NOW()');
|
|
2711
2712
|
}
|
|
2712
2713
|
return { sets, values };
|
|
@@ -3231,7 +3232,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
|
|
|
3231
3232
|
const redirectUri = form.get("redirect_uri") || "";
|
|
3232
3233
|
const scope = form.get("scope") || "";
|
|
3233
3234
|
const state = form.get("state") || "";
|
|
3234
|
-
const
|
|
3235
|
+
const userId2 = parseInt(form.get("user_id") || "0", 10);
|
|
3235
3236
|
const codeChallenge = form.get("code_challenge") || "";
|
|
3236
3237
|
const codeChallengeMethod = form.get("code_challenge_method") || "";
|
|
3237
3238
|
if (!approve) {
|
|
@@ -3242,7 +3243,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
|
|
|
3242
3243
|
const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
|
|
3243
3244
|
await pg.sql`
|
|
3244
3245
|
INSERT INTO "_oauth2_codes" ("code", "client_id", "user_id", "redirect_uri", "code_challenge", "code_challenge_method", "scope", "expires_at")
|
|
3245
|
-
VALUES (${code}, ${clientId}, ${
|
|
3246
|
+
VALUES (${code}, ${clientId}, ${userId2}, ${redirectUri}, ${codeChallenge || null}, ${codeChallengeMethod || null}, ${scope || null}, ${expiresAt})
|
|
3246
3247
|
`;
|
|
3247
3248
|
const loc = `${redirectUri}?code=${code}${state ? `&state=${state}` : ""}`;
|
|
3248
3249
|
return Response.redirect(loc, 302);
|
|
@@ -3418,12 +3419,12 @@ function registerOAuthLoginRoutes(router, deps, providers) {
|
|
|
3418
3419
|
);
|
|
3419
3420
|
return row ?? null;
|
|
3420
3421
|
}
|
|
3421
|
-
async function linkProvider(
|
|
3422
|
+
async function linkProvider(userId2, provider, providerId, email, name, avatarUrl) {
|
|
3422
3423
|
await sql2.unsafe(
|
|
3423
3424
|
`INSERT INTO ${escapeIdent(providerTable)} (user_id, provider, provider_id, email, name, avatar_url)
|
|
3424
3425
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
3425
3426
|
ON CONFLICT (provider, provider_id) DO NOTHING`,
|
|
3426
|
-
[
|
|
3427
|
+
[userId2, provider, providerId, email, name, avatarUrl]
|
|
3427
3428
|
);
|
|
3428
3429
|
}
|
|
3429
3430
|
async function findOrCreateUser(provider, providerId, email, name, avatarUrl) {
|
|
@@ -3765,7 +3766,7 @@ function user(options) {
|
|
|
3765
3766
|
const payload = jwt2.verify(token, secret);
|
|
3766
3767
|
if (payload.token_type === "client_credentials") return null;
|
|
3767
3768
|
if (!hasDb || !findById) return null;
|
|
3768
|
-
const row = await findById(payload.sub);
|
|
3769
|
+
const row = await findById(Number(payload.sub));
|
|
3769
3770
|
if (!row) return null;
|
|
3770
3771
|
return stripPassword(row);
|
|
3771
3772
|
} catch {
|
|
@@ -3774,27 +3775,26 @@ function user(options) {
|
|
|
3774
3775
|
}
|
|
3775
3776
|
const headerName = options.header ?? "Authorization";
|
|
3776
3777
|
async function resolveUser(req, ctx) {
|
|
3777
|
-
const
|
|
3778
|
+
const s = ctx;
|
|
3779
|
+
const sessionUserId = s.session?.userId;
|
|
3778
3780
|
if (sessionUserId !== void 0 && sessionUserId !== null) {
|
|
3779
3781
|
if (hasDb) {
|
|
3780
3782
|
const row = await findById(sessionUserId);
|
|
3781
3783
|
if (row) {
|
|
3782
3784
|
return stripPassword(row);
|
|
3783
3785
|
}
|
|
3784
|
-
if (typeof
|
|
3785
|
-
;
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
delete ctx.session?.userId;
|
|
3786
|
+
if (typeof s.session?.destroy === "function") {
|
|
3787
|
+
s.session.destroy();
|
|
3788
|
+
} else if (s.session) {
|
|
3789
|
+
delete s.session.userId;
|
|
3789
3790
|
}
|
|
3790
3791
|
} else if (options.resolveUser) {
|
|
3791
3792
|
const userData = await options.resolveUser(sessionUserId);
|
|
3792
3793
|
if (userData) {
|
|
3793
3794
|
return userData;
|
|
3794
3795
|
}
|
|
3795
|
-
if (typeof
|
|
3796
|
-
;
|
|
3797
|
-
ctx.session.destroy();
|
|
3796
|
+
if (typeof s.session?.destroy === "function") {
|
|
3797
|
+
s.session.destroy();
|
|
3798
3798
|
}
|
|
3799
3799
|
console.warn(`[${currentTraceId()}] user: session userId ${sessionUserId} resolved to null`);
|
|
3800
3800
|
} else {
|
|
@@ -3854,7 +3854,7 @@ function user(options) {
|
|
|
3854
3854
|
try {
|
|
3855
3855
|
const payload = jwt2.verify(token, secret);
|
|
3856
3856
|
if (payload.token_type === "client_credentials") return null;
|
|
3857
|
-
const row = await findById(payload.sub);
|
|
3857
|
+
const row = await findById(Number(payload.sub));
|
|
3858
3858
|
if (row) return stripPassword(row);
|
|
3859
3859
|
} catch {
|
|
3860
3860
|
}
|
|
@@ -3915,13 +3915,13 @@ function user(options) {
|
|
|
3915
3915
|
try {
|
|
3916
3916
|
const body = await parseBody2(req);
|
|
3917
3917
|
const result = await login(body);
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3918
|
+
const s = ctx;
|
|
3919
|
+
if (s.session) {
|
|
3920
|
+
s.session.userId = result.user.id;
|
|
3921
|
+
s.session.role = result.user.role;
|
|
3922
3922
|
}
|
|
3923
3923
|
const res = Response.json(result);
|
|
3924
|
-
if (!
|
|
3924
|
+
if (!s.session) {
|
|
3925
3925
|
res.headers.set("Set-Cookie", `session=${result.token}; HttpOnly; SameSite=Lax; Path=/`);
|
|
3926
3926
|
}
|
|
3927
3927
|
return res;
|
|
@@ -4695,6 +4695,27 @@ function buildColumnDDL(tenantId, field) {
|
|
|
4695
4695
|
}
|
|
4696
4696
|
|
|
4697
4697
|
// tenant/rest.ts
|
|
4698
|
+
function userId(ctx) {
|
|
4699
|
+
return ctx.user?.id ?? null;
|
|
4700
|
+
}
|
|
4701
|
+
function extractCount(rows) {
|
|
4702
|
+
return Number(rows[0]?.count ?? 0);
|
|
4703
|
+
}
|
|
4704
|
+
function asJson(val) {
|
|
4705
|
+
return val;
|
|
4706
|
+
}
|
|
4707
|
+
function tableRef(s, name) {
|
|
4708
|
+
return s(name);
|
|
4709
|
+
}
|
|
4710
|
+
function withTenant(ctx, data) {
|
|
4711
|
+
;
|
|
4712
|
+
data.tenant_id = ctx.tenant.id;
|
|
4713
|
+
return data;
|
|
4714
|
+
}
|
|
4715
|
+
function withoutTenant(data) {
|
|
4716
|
+
delete data.tenant_id;
|
|
4717
|
+
return data;
|
|
4718
|
+
}
|
|
4698
4719
|
function zodType(field) {
|
|
4699
4720
|
let t;
|
|
4700
4721
|
switch (field.type) {
|
|
@@ -4752,7 +4773,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4752
4773
|
`;
|
|
4753
4774
|
await sql2`
|
|
4754
4775
|
INSERT INTO "_tenant_members" ("tenant_id", "user_id", "role")
|
|
4755
|
-
VALUES (${tenant2.id}, ${ctx
|
|
4776
|
+
VALUES (${tenant2.id}, ${userId(ctx)}, 'admin')
|
|
4756
4777
|
`;
|
|
4757
4778
|
return Response.json(tenant2, { status: 201 });
|
|
4758
4779
|
});
|
|
@@ -4760,7 +4781,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4760
4781
|
const rows = await sql2`
|
|
4761
4782
|
SELECT t.*, tm.role FROM "_tenants" t
|
|
4762
4783
|
JOIN "_tenant_members" tm ON tm.tenant_id = t.id
|
|
4763
|
-
WHERE tm.user_id = ${ctx
|
|
4784
|
+
WHERE tm.user_id = ${userId(ctx)}
|
|
4764
4785
|
`;
|
|
4765
4786
|
return Response.json(rows);
|
|
4766
4787
|
});
|
|
@@ -4769,7 +4790,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4769
4790
|
if (err) return err;
|
|
4770
4791
|
const { email, role = "member" } = await req.json();
|
|
4771
4792
|
const [user2] = await sql2`
|
|
4772
|
-
SELECT id FROM ${sql2
|
|
4793
|
+
SELECT id FROM ${tableRef(sql2, usersTable)} WHERE "email" = ${email} LIMIT 1
|
|
4773
4794
|
`;
|
|
4774
4795
|
if (!user2) return Response.json({ error: "User not found" }, { status: 404 });
|
|
4775
4796
|
const [existing] = await sql2`
|
|
@@ -4786,10 +4807,10 @@ function buildRouter(sql2, usersTable) {
|
|
|
4786
4807
|
r.delete("/sys/tenants/members/:userId", async (req, ctx) => {
|
|
4787
4808
|
const err = requireAdmin(ctx);
|
|
4788
4809
|
if (err) return err;
|
|
4789
|
-
const
|
|
4810
|
+
const userId2 = parseInt(ctx.params.userId, 10);
|
|
4790
4811
|
await sql2`
|
|
4791
4812
|
DELETE FROM "_tenant_members"
|
|
4792
|
-
WHERE tenant_id = ${ctx.tenant.id} AND user_id = ${
|
|
4813
|
+
WHERE tenant_id = ${ctx.tenant.id} AND user_id = ${userId2}
|
|
4793
4814
|
`;
|
|
4794
4815
|
return Response.json({ ok: true });
|
|
4795
4816
|
});
|
|
@@ -4815,7 +4836,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4815
4836
|
}
|
|
4816
4837
|
const [row] = await sql2`
|
|
4817
4838
|
INSERT INTO "_user_tables" ("tenant_id", "slug", "label", "fields")
|
|
4818
|
-
VALUES (${ctx.tenant.id}, ${body.slug}, ${body.label || ""}, ${body.fields})
|
|
4839
|
+
VALUES (${ctx.tenant.id}, ${body.slug}, ${body.label || ""}, ${asJson(body.fields)})
|
|
4819
4840
|
RETURNING *
|
|
4820
4841
|
`;
|
|
4821
4842
|
return Response.json(row, { status: 201 });
|
|
@@ -4850,7 +4871,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4850
4871
|
const merged = [...table.fields, ...newFields];
|
|
4851
4872
|
await sql2`
|
|
4852
4873
|
UPDATE "_user_tables"
|
|
4853
|
-
SET fields = ${merged}
|
|
4874
|
+
SET fields = ${asJson(merged)}
|
|
4854
4875
|
WHERE id = ${table.id}
|
|
4855
4876
|
`;
|
|
4856
4877
|
return Response.json({ ...table, fields: merged });
|
|
@@ -4898,7 +4919,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4898
4919
|
[ctx.tenant.id]
|
|
4899
4920
|
)
|
|
4900
4921
|
]);
|
|
4901
|
-
return Response.json({ rows: rows2, count:
|
|
4922
|
+
return Response.json({ rows: rows2, count: extractCount(countResult2) });
|
|
4902
4923
|
} catch {
|
|
4903
4924
|
return Response.json({ error: "Invalid search_vector" }, { status: 400 });
|
|
4904
4925
|
}
|
|
@@ -4914,7 +4935,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4914
4935
|
[ctx.tenant.id]
|
|
4915
4936
|
)
|
|
4916
4937
|
]);
|
|
4917
|
-
return Response.json({ rows, count:
|
|
4938
|
+
return Response.json({ rows, count: extractCount(countResult) });
|
|
4918
4939
|
});
|
|
4919
4940
|
r.post("/:_slug", async (req, ctx) => {
|
|
4920
4941
|
const table = await resolveTable(ctx);
|
|
@@ -4925,11 +4946,10 @@ function buildRouter(sql2, usersTable) {
|
|
|
4925
4946
|
shape[f.name] = zodType(f);
|
|
4926
4947
|
}
|
|
4927
4948
|
const zodSchema = z3.object(shape);
|
|
4928
|
-
const parsed = zodSchema.parse(data);
|
|
4929
|
-
parsed.tenant_id = ctx.tenant.id;
|
|
4949
|
+
const parsed = withTenant(ctx, zodSchema.parse(data));
|
|
4930
4950
|
delete parsed.id;
|
|
4931
4951
|
const name = internalName(ctx);
|
|
4932
|
-
const [row] = await sql2`INSERT INTO ${sql2
|
|
4952
|
+
const [row] = await sql2`INSERT INTO ${tableRef(sql2, name)} ${sql2(parsed)} RETURNING *`;
|
|
4933
4953
|
return Response.json(row, { status: 201 });
|
|
4934
4954
|
});
|
|
4935
4955
|
r.get("/:_slug/:id", async (_req, ctx) => {
|
|
@@ -4937,7 +4957,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4937
4957
|
if (!table) return Response.json({ error: "Table not found" }, { status: 404 });
|
|
4938
4958
|
const name = internalName(ctx);
|
|
4939
4959
|
const [row] = await sql2`
|
|
4940
|
-
SELECT * FROM ${sql2
|
|
4960
|
+
SELECT * FROM ${tableRef(sql2, name)}
|
|
4941
4961
|
WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
|
|
4942
4962
|
LIMIT 1
|
|
4943
4963
|
`;
|
|
@@ -4953,13 +4973,12 @@ function buildRouter(sql2, usersTable) {
|
|
|
4953
4973
|
shape[f.name] = zodType(f);
|
|
4954
4974
|
}
|
|
4955
4975
|
const zodSchema = z3.object(shape).partial();
|
|
4956
|
-
const parsed = zodSchema.parse(data);
|
|
4976
|
+
const parsed = withoutTenant(zodSchema.parse(data));
|
|
4957
4977
|
delete parsed.id;
|
|
4958
|
-
delete parsed.tenant_id;
|
|
4959
4978
|
if (Object.keys(parsed).length === 0) {
|
|
4960
4979
|
const name2 = internalName(ctx);
|
|
4961
4980
|
const [row2] = await sql2`
|
|
4962
|
-
SELECT * FROM ${sql2
|
|
4981
|
+
SELECT * FROM ${tableRef(sql2, name2)}
|
|
4963
4982
|
WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
|
|
4964
4983
|
LIMIT 1
|
|
4965
4984
|
`;
|
|
@@ -4967,7 +4986,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4967
4986
|
}
|
|
4968
4987
|
const name = internalName(ctx);
|
|
4969
4988
|
const [row] = await sql2`
|
|
4970
|
-
UPDATE ${sql2
|
|
4989
|
+
UPDATE ${tableRef(sql2, name)} SET ${sql2(parsed)}
|
|
4971
4990
|
WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
|
|
4972
4991
|
RETURNING *
|
|
4973
4992
|
`;
|
|
@@ -4977,7 +4996,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
4977
4996
|
r.delete("/:_slug/:id", async (_req, ctx) => {
|
|
4978
4997
|
const name = internalName(ctx);
|
|
4979
4998
|
const result = await sql2`
|
|
4980
|
-
DELETE FROM ${sql2
|
|
4999
|
+
DELETE FROM ${tableRef(sql2, name)}
|
|
4981
5000
|
WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
|
|
4982
5001
|
RETURNING 1
|
|
4983
5002
|
`;
|
|
@@ -5018,7 +5037,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
5018
5037
|
[parentId2, ctx.tenant.id]
|
|
5019
5038
|
)
|
|
5020
5039
|
]);
|
|
5021
|
-
return Response.json({ rows, count:
|
|
5040
|
+
return Response.json({ rows, count: extractCount(countResult) });
|
|
5022
5041
|
}
|
|
5023
5042
|
return Response.json({ error: "POST not supported on M2M nested routes" }, { status: 400 });
|
|
5024
5043
|
}
|
|
@@ -5035,7 +5054,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
5035
5054
|
[parentId, ctx.tenant.id]
|
|
5036
5055
|
)
|
|
5037
5056
|
]);
|
|
5038
|
-
return Response.json({ rows, count:
|
|
5057
|
+
return Response.json({ rows, count: extractCount(countResult) });
|
|
5039
5058
|
}
|
|
5040
5059
|
const body = await req.json();
|
|
5041
5060
|
const shape = {};
|
|
@@ -5047,7 +5066,7 @@ function buildRouter(sql2, usersTable) {
|
|
|
5047
5066
|
parsed.tenant_id = ctx.tenant.id;
|
|
5048
5067
|
parsed[relField.name] = parentId;
|
|
5049
5068
|
delete parsed.id;
|
|
5050
|
-
const [row] = await sql2`INSERT INTO ${sql2
|
|
5069
|
+
const [row] = await sql2`INSERT INTO ${tableRef(sql2, childName)} ${sql2(parsed)} RETURNING *`;
|
|
5051
5070
|
return Response.json(row, { status: 201 });
|
|
5052
5071
|
}
|
|
5053
5072
|
r.get("/:_slug/:id/:_nested", async (req, ctx) => handleNested(req, ctx, "GET"));
|
|
@@ -5117,7 +5136,6 @@ function buildObjectType(table, ctx) {
|
|
|
5117
5136
|
if (other.id === table.id) continue;
|
|
5118
5137
|
const relField = findRelation(other.fields, table.slug);
|
|
5119
5138
|
if (relField) {
|
|
5120
|
-
const otherName = pascalCase(other.slug);
|
|
5121
5139
|
fields[other.slug] = {
|
|
5122
5140
|
type: new GraphQLList(new GraphQLNonNull(buildObjectType(other, ctx))),
|
|
5123
5141
|
args: {
|
|
@@ -5632,7 +5650,7 @@ async function loadAgent(agents, agentId) {
|
|
|
5632
5650
|
return row ?? null;
|
|
5633
5651
|
}
|
|
5634
5652
|
function createRunner(deps) {
|
|
5635
|
-
const { sql: sql2, agents, runs, provider,
|
|
5653
|
+
const { sql: sql2, agents, runs, provider, userTools } = deps;
|
|
5636
5654
|
function truncate(s, max = 200) {
|
|
5637
5655
|
return s.length > max ? s.slice(0, max) + "..." : s;
|
|
5638
5656
|
}
|
|
@@ -5942,32 +5960,32 @@ function createWSHandler(deps) {
|
|
|
5942
5960
|
prefix: "messager:"
|
|
5943
5961
|
});
|
|
5944
5962
|
const userConnections = /* @__PURE__ */ new Map();
|
|
5945
|
-
function trackConnection(
|
|
5946
|
-
let conns = userConnections.get(
|
|
5963
|
+
function trackConnection(userId2, ws) {
|
|
5964
|
+
let conns = userConnections.get(userId2);
|
|
5947
5965
|
if (!conns) {
|
|
5948
5966
|
conns = /* @__PURE__ */ new Set();
|
|
5949
|
-
userConnections.set(
|
|
5967
|
+
userConnections.set(userId2, conns);
|
|
5950
5968
|
}
|
|
5951
5969
|
conns.add(ws);
|
|
5952
5970
|
}
|
|
5953
5971
|
function untrackConnection(ws) {
|
|
5954
|
-
for (const [
|
|
5972
|
+
for (const [userId2, conns] of userConnections) {
|
|
5955
5973
|
conns.delete(ws);
|
|
5956
|
-
if (conns.size === 0) userConnections.delete(
|
|
5974
|
+
if (conns.size === 0) userConnections.delete(userId2);
|
|
5957
5975
|
}
|
|
5958
5976
|
}
|
|
5959
5977
|
return {
|
|
5960
5978
|
handler: {
|
|
5961
5979
|
open(ws, ctx) {
|
|
5962
|
-
const
|
|
5963
|
-
if (!
|
|
5980
|
+
const userId2 = ctx.user?.id;
|
|
5981
|
+
if (!userId2) {
|
|
5964
5982
|
ws.close(4001, "Unauthorized");
|
|
5965
5983
|
return;
|
|
5966
5984
|
}
|
|
5967
5985
|
},
|
|
5968
5986
|
async message(ws, ctx, data) {
|
|
5969
|
-
const
|
|
5970
|
-
if (!
|
|
5987
|
+
const userId2 = ctx.user?.id;
|
|
5988
|
+
if (!userId2) return;
|
|
5971
5989
|
let msg;
|
|
5972
5990
|
try {
|
|
5973
5991
|
msg = JSON.parse(data.toString());
|
|
@@ -5981,12 +5999,12 @@ function createWSHandler(deps) {
|
|
|
5981
5999
|
if (!content || !channel_id) return;
|
|
5982
6000
|
const [row] = await sql2`
|
|
5983
6001
|
INSERT INTO "_messages" ("channel_id", "sender_id", "sender_type", "content")
|
|
5984
|
-
VALUES (${channel_id}, ${
|
|
6002
|
+
VALUES (${channel_id}, ${userId2}, 'user', ${content})
|
|
5985
6003
|
RETURNING *
|
|
5986
6004
|
`;
|
|
5987
6005
|
const message = row;
|
|
5988
6006
|
hub.join(`messager:${channel_id}`, ws);
|
|
5989
|
-
trackConnection(
|
|
6007
|
+
trackConnection(userId2, ws);
|
|
5990
6008
|
broadcastToChannel(hub, channel_id, { type: "message", data: message });
|
|
5991
6009
|
if (agents) {
|
|
5992
6010
|
const insertMsg = (data2) => sql2`
|
|
@@ -6005,7 +6023,7 @@ function createWSHandler(deps) {
|
|
|
6005
6023
|
broadcastToChannel(hub, channel_id, {
|
|
6006
6024
|
type: "typing",
|
|
6007
6025
|
channel_id,
|
|
6008
|
-
user_id:
|
|
6026
|
+
user_id: userId2,
|
|
6009
6027
|
is_typing: is_typing ?? false
|
|
6010
6028
|
});
|
|
6011
6029
|
break;
|
|
@@ -6016,12 +6034,12 @@ function createWSHandler(deps) {
|
|
|
6016
6034
|
await sql2`
|
|
6017
6035
|
UPDATE "_channel_members"
|
|
6018
6036
|
SET last_read_id = ${last_message_id}, last_read_at = NOW()
|
|
6019
|
-
WHERE channel_id = ${channel_id} AND member_id = ${
|
|
6037
|
+
WHERE channel_id = ${channel_id} AND member_id = ${userId2} AND member_type = 'user'
|
|
6020
6038
|
`;
|
|
6021
6039
|
broadcastToChannel(hub, channel_id, {
|
|
6022
6040
|
type: "read",
|
|
6023
6041
|
channel_id,
|
|
6024
|
-
user_id:
|
|
6042
|
+
user_id: userId2,
|
|
6025
6043
|
last_message_id
|
|
6026
6044
|
});
|
|
6027
6045
|
break;
|
|
@@ -6075,7 +6093,7 @@ function buildRouter3(deps) {
|
|
|
6075
6093
|
return Response.json(channel, { status: 201 });
|
|
6076
6094
|
});
|
|
6077
6095
|
r.get("/channels", async (_req, ctx) => {
|
|
6078
|
-
const
|
|
6096
|
+
const userId2 = ctx.user?.id ?? 1;
|
|
6079
6097
|
const rows = await sql2`
|
|
6080
6098
|
SELECT c.*, (
|
|
6081
6099
|
SELECT content FROM "_messages"
|
|
@@ -6084,7 +6102,7 @@ function buildRouter3(deps) {
|
|
|
6084
6102
|
) AS last_message
|
|
6085
6103
|
FROM "_channels" c
|
|
6086
6104
|
JOIN "_channel_members" m ON m.channel_id = c.id
|
|
6087
|
-
WHERE m.member_id = ${
|
|
6105
|
+
WHERE m.member_id = ${userId2} AND m.member_type = 'user'
|
|
6088
6106
|
ORDER BY c.created_at DESC
|
|
6089
6107
|
`;
|
|
6090
6108
|
return Response.json(rows);
|
|
@@ -6157,9 +6175,9 @@ function buildRouter3(deps) {
|
|
|
6157
6175
|
r.post("/channels/:id/read", async (req, ctx) => {
|
|
6158
6176
|
const channelId = parseInt(ctx.params.id, 10);
|
|
6159
6177
|
const body = await req.json();
|
|
6160
|
-
const
|
|
6178
|
+
const userId2 = body.user_id ?? ctx.user?.id ?? 1;
|
|
6161
6179
|
await members.updateMany(
|
|
6162
|
-
[eq("channel_id", channelId), eq("member_id",
|
|
6180
|
+
[eq("channel_id", channelId), eq("member_id", userId2), eq("member_type", "user")],
|
|
6163
6181
|
{ last_read_id: body.last_message_id }
|
|
6164
6182
|
);
|
|
6165
6183
|
return Response.json({ ok: true });
|
|
@@ -7349,8 +7367,9 @@ function errorBoundary(errorPath) {
|
|
|
7349
7367
|
const mod = await compile(errorPath);
|
|
7350
7368
|
const ErrorComponent = mod.default;
|
|
7351
7369
|
if (!ErrorComponent) throw err;
|
|
7352
|
-
const
|
|
7353
|
-
const
|
|
7370
|
+
const ctx2 = ctx;
|
|
7371
|
+
const layouts = (ctx2.layoutStack || []).map((l) => l.component);
|
|
7372
|
+
const base = (ctx2.mountPath || "").replace(/\/$/, "");
|
|
7354
7373
|
let element = createElement2(ErrorComponent, {
|
|
7355
7374
|
error: err instanceof Error ? err : new Error(String(err)),
|
|
7356
7375
|
reset: () => {
|
|
@@ -7360,10 +7379,10 @@ function errorBoundary(errorPath) {
|
|
|
7360
7379
|
const { renderToReadableStream } = await import("react-dom/server");
|
|
7361
7380
|
const stream = await renderToReadableStream(element);
|
|
7362
7381
|
return streamResponse(stream, {
|
|
7363
|
-
ctx,
|
|
7382
|
+
ctx: ctx2,
|
|
7364
7383
|
base,
|
|
7365
7384
|
isDev: isDev(),
|
|
7366
|
-
tailwind:
|
|
7385
|
+
tailwind: ctx2.tailwind,
|
|
7367
7386
|
status: 500
|
|
7368
7387
|
});
|
|
7369
7388
|
}
|
|
@@ -7743,10 +7762,10 @@ async function getSession(sql2, id2) {
|
|
|
7743
7762
|
const { data: rows } = await sessions.readMany(sql2, { id: id2, active: true });
|
|
7744
7763
|
return rows[0] ?? null;
|
|
7745
7764
|
}
|
|
7746
|
-
async function listSessions(sql2,
|
|
7765
|
+
async function listSessions(sql2, userId2) {
|
|
7747
7766
|
const opts = { orderBy: { updated_at: "desc" } };
|
|
7748
|
-
if (
|
|
7749
|
-
const { data: rows2 } = await sessions.readMany(sql2, { user_id:
|
|
7767
|
+
if (userId2 !== void 0) {
|
|
7768
|
+
const { data: rows2 } = await sessions.readMany(sql2, { user_id: userId2, active: true }, opts);
|
|
7750
7769
|
return rows2;
|
|
7751
7770
|
}
|
|
7752
7771
|
const { data: rows } = await sessions.readMany(sql2, { active: true }, opts);
|
|
@@ -8367,9 +8386,9 @@ function createWSHandler2(deps) {
|
|
|
8367
8386
|
const { sql: sql2, model, workspace, systemPrompt, skills, skillsRegistry, permissions: permissions2, pendingQuestions } = deps;
|
|
8368
8387
|
return {
|
|
8369
8388
|
open(ws, ctx) {
|
|
8370
|
-
const
|
|
8389
|
+
const userId2 = ctx.user?.id ?? 0;
|
|
8371
8390
|
const mountPath = ctx.mountPath ?? "";
|
|
8372
|
-
clients2.set(ws, { userId, mountPath });
|
|
8391
|
+
clients2.set(ws, { userId: userId2, mountPath });
|
|
8373
8392
|
},
|
|
8374
8393
|
async message(ws, ctx, data) {
|
|
8375
8394
|
const client = clients2.get(ws);
|
|
@@ -8878,7 +8897,7 @@ function analytics(options) {
|
|
|
8878
8897
|
if (pg) await migratePg(pg.sql, pg.table);
|
|
8879
8898
|
};
|
|
8880
8899
|
const close = async () => {
|
|
8881
|
-
|
|
8900
|
+
store2?.stopCleanup();
|
|
8882
8901
|
};
|
|
8883
8902
|
const mod = r;
|
|
8884
8903
|
mod.middleware = middleware;
|
|
@@ -10193,7 +10212,7 @@ function iii(opts = {}) {
|
|
|
10193
10212
|
mod.migrate = async () => {
|
|
10194
10213
|
await stream.migrate();
|
|
10195
10214
|
};
|
|
10196
|
-
mod.
|
|
10215
|
+
mod.close = async () => {
|
|
10197
10216
|
for (const [, p] of pending) {
|
|
10198
10217
|
clearTimeout(p.timer);
|
|
10199
10218
|
p.reject(new Error("Engine shutting down"));
|
|
@@ -10205,7 +10224,6 @@ function iii(opts = {}) {
|
|
|
10205
10224
|
triggers.clear();
|
|
10206
10225
|
await stream.close();
|
|
10207
10226
|
};
|
|
10208
|
-
mod.close = mod.shutdown;
|
|
10209
10227
|
return mod;
|
|
10210
10228
|
}
|
|
10211
10229
|
|
|
@@ -10410,7 +10428,7 @@ function registerWorker(url) {
|
|
|
10410
10428
|
onStream(handler) {
|
|
10411
10429
|
handlers.set("__stream__", handler);
|
|
10412
10430
|
},
|
|
10413
|
-
|
|
10431
|
+
close() {
|
|
10414
10432
|
intentionalClose = true;
|
|
10415
10433
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
10416
10434
|
ws?.close();
|
|
@@ -10459,7 +10477,7 @@ var MemoryStore = class {
|
|
|
10459
10477
|
if (entry.expires < now) this.store.delete(key);
|
|
10460
10478
|
}
|
|
10461
10479
|
}
|
|
10462
|
-
close() {
|
|
10480
|
+
async close() {
|
|
10463
10481
|
clearInterval(this.interval);
|
|
10464
10482
|
this.store.clear();
|
|
10465
10483
|
}
|
|
@@ -10495,6 +10513,9 @@ var RedisStore = class {
|
|
|
10495
10513
|
async destroy(sid) {
|
|
10496
10514
|
await this.redis.del(this.key(sid));
|
|
10497
10515
|
}
|
|
10516
|
+
async close() {
|
|
10517
|
+
this.redis.disconnect();
|
|
10518
|
+
}
|
|
10498
10519
|
};
|
|
10499
10520
|
var COOKIE_SEPARATOR = ".";
|
|
10500
10521
|
function signSessionId(sid, secret) {
|
|
@@ -10568,6 +10589,7 @@ function session(options) {
|
|
|
10568
10589
|
} else if (options?.store === "redis") {
|
|
10569
10590
|
if (!options.redis) throw new Error('session: redis client required when store: "redis"');
|
|
10570
10591
|
store2 = new RedisStore(options.redis);
|
|
10592
|
+
closeStore = () => store2.close();
|
|
10571
10593
|
} else {
|
|
10572
10594
|
const mem = new MemoryStore();
|
|
10573
10595
|
store2 = mem;
|
|
@@ -10647,8 +10669,8 @@ function session(options) {
|
|
|
10647
10669
|
}
|
|
10648
10670
|
return res;
|
|
10649
10671
|
});
|
|
10650
|
-
mw.close = () => {
|
|
10651
|
-
closeStore?.();
|
|
10672
|
+
mw.close = async () => {
|
|
10673
|
+
await closeStore?.();
|
|
10652
10674
|
};
|
|
10653
10675
|
mw.store = store2;
|
|
10654
10676
|
return mw;
|
|
@@ -10742,7 +10764,7 @@ var MemoryCache = class {
|
|
|
10742
10764
|
}
|
|
10743
10765
|
}
|
|
10744
10766
|
}
|
|
10745
|
-
close() {
|
|
10767
|
+
async close() {
|
|
10746
10768
|
clearInterval(this.interval);
|
|
10747
10769
|
this.store.clear();
|
|
10748
10770
|
this.tagIndex.clear();
|
|
@@ -10883,8 +10905,8 @@ function cache2(options) {
|
|
|
10883
10905
|
mw.store = store2;
|
|
10884
10906
|
mw.invalidate = async (tag) => store2.invalidate(tag);
|
|
10885
10907
|
mw.flush = async () => store2.flush();
|
|
10886
|
-
mw.close = () => {
|
|
10887
|
-
closeStore?.();
|
|
10908
|
+
mw.close = async () => {
|
|
10909
|
+
await closeStore?.();
|
|
10888
10910
|
};
|
|
10889
10911
|
return mw;
|
|
10890
10912
|
}
|
|
@@ -11468,17 +11490,17 @@ function permissions(options) {
|
|
|
11468
11490
|
);
|
|
11469
11491
|
return created.id;
|
|
11470
11492
|
}
|
|
11471
|
-
async function assignRole(
|
|
11493
|
+
async function assignRole(userId2, role) {
|
|
11472
11494
|
const roleId = await ensureRole(role);
|
|
11473
11495
|
await sql2.unsafe(
|
|
11474
11496
|
`INSERT INTO ${escapeIdent6(userRolesTable)} (user_id, role_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
|
|
11475
|
-
[
|
|
11497
|
+
[userId2, roleId]
|
|
11476
11498
|
);
|
|
11477
11499
|
}
|
|
11478
|
-
async function removeRole(
|
|
11500
|
+
async function removeRole(userId2, role) {
|
|
11479
11501
|
await sql2.unsafe(
|
|
11480
11502
|
`DELETE FROM ${escapeIdent6(userRolesTable)} WHERE user_id = $1 AND role_id = (SELECT id FROM ${escapeIdent6(rolesTable)} WHERE name = $2)`,
|
|
11481
|
-
[
|
|
11503
|
+
[userId2, role]
|
|
11482
11504
|
);
|
|
11483
11505
|
}
|
|
11484
11506
|
async function grantPermission(role, permission) {
|
|
@@ -11494,31 +11516,31 @@ function permissions(options) {
|
|
|
11494
11516
|
[role, permission]
|
|
11495
11517
|
);
|
|
11496
11518
|
}
|
|
11497
|
-
async function getUserRoles(
|
|
11519
|
+
async function getUserRoles(userId2) {
|
|
11498
11520
|
const rows = await sql2.unsafe(
|
|
11499
11521
|
`SELECT r.name FROM ${escapeIdent6(userRolesTable)} ur
|
|
11500
11522
|
JOIN ${escapeIdent6(rolesTable)} r ON r.id = ur.role_id
|
|
11501
11523
|
WHERE ur.user_id = $1 ORDER BY r.name`,
|
|
11502
|
-
[
|
|
11524
|
+
[userId2]
|
|
11503
11525
|
);
|
|
11504
11526
|
return rows.map((r) => r.name);
|
|
11505
11527
|
}
|
|
11506
|
-
async function getUserPermissions(
|
|
11528
|
+
async function getUserPermissions(userId2) {
|
|
11507
11529
|
const rows = await sql2.unsafe(
|
|
11508
11530
|
`SELECT DISTINCT rp.permission FROM ${escapeIdent6(userRolesTable)} ur
|
|
11509
11531
|
JOIN ${escapeIdent6(rolePermsTable)} rp ON rp.role_id = ur.role_id
|
|
11510
11532
|
WHERE ur.user_id = $1 ORDER BY rp.permission`,
|
|
11511
|
-
[
|
|
11533
|
+
[userId2]
|
|
11512
11534
|
);
|
|
11513
11535
|
return rows.map((r) => r.permission);
|
|
11514
11536
|
}
|
|
11515
11537
|
const mw = (async (req, ctx, next) => {
|
|
11516
|
-
const
|
|
11538
|
+
const userId2 = ctx.user?.id;
|
|
11517
11539
|
let roles = /* @__PURE__ */ new Set();
|
|
11518
11540
|
let perms = /* @__PURE__ */ new Set();
|
|
11519
|
-
if (
|
|
11520
|
-
const userRoles = await getUserRoles(
|
|
11521
|
-
const userPerms =
|
|
11541
|
+
if (userId2) {
|
|
11542
|
+
const userRoles = await getUserRoles(userId2);
|
|
11543
|
+
const userPerms = userId2 ? await getUserPermissions(userId2) : [];
|
|
11522
11544
|
roles = new Set(userRoles);
|
|
11523
11545
|
perms = new Set(userPerms);
|
|
11524
11546
|
const hasWildcard = userPerms.includes("*");
|
package/dist/kb/types.d.ts
CHANGED
|
@@ -41,6 +41,14 @@ export interface KBListEntry {
|
|
|
41
41
|
title: string;
|
|
42
42
|
chunks: number;
|
|
43
43
|
}
|
|
44
|
+
export interface KBInjected {
|
|
45
|
+
search(query: string, searchOptions?: KBSearchOptions): Promise<KBSearchResult[]>;
|
|
46
|
+
}
|
|
47
|
+
declare module '../types.ts' {
|
|
48
|
+
interface Context {
|
|
49
|
+
kb?: KBInjected;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
44
52
|
export interface KBModule {
|
|
45
53
|
/**
|
|
46
54
|
* Ingest a document: chunk → embed → store.
|
package/dist/logdb/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PostgresClient } from '../postgres/types.ts';
|
|
2
2
|
import type { Router } from '../router.ts';
|
|
3
|
+
import type { Closeable } from '../types.ts';
|
|
3
4
|
export interface LogdbOptions {
|
|
4
5
|
pg: PostgresClient;
|
|
5
6
|
table?: string;
|
|
@@ -18,7 +19,7 @@ export interface LogEntryInput {
|
|
|
18
19
|
message: string;
|
|
19
20
|
metadata?: Record<string, unknown>;
|
|
20
21
|
}
|
|
21
|
-
export interface LogdbModule extends Router {
|
|
22
|
+
export interface LogdbModule extends Router, Closeable {
|
|
22
23
|
log(input: LogEntryInput): Promise<LogEntry>;
|
|
23
24
|
migrate(): Promise<void>;
|
|
24
25
|
clean(retentionMonths: number): Promise<number>;
|