@selvajs/server 1.2.0 → 1.3.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/dist/access/guards.d.ts +213 -0
- package/dist/access/guards.d.ts.map +1 -0
- package/dist/access/guards.js +417 -0
- package/dist/access/guards.js.map +1 -0
- package/dist/access/index.d.ts +2 -0
- package/dist/access/index.d.ts.map +1 -1
- package/dist/access/index.js +6 -0
- package/dist/access/index.js.map +1 -1
- package/dist/access/permissions-scope.d.ts +15 -0
- package/dist/access/permissions-scope.d.ts.map +1 -0
- package/dist/access/permissions-scope.js +23 -0
- package/dist/access/permissions-scope.js.map +1 -0
- package/dist/api/bodies.d.ts +104 -0
- package/dist/api/bodies.d.ts.map +1 -0
- package/dist/api/bodies.js +87 -0
- package/dist/api/bodies.js.map +1 -0
- package/dist/api/deps.d.ts +135 -0
- package/dist/api/deps.d.ts.map +1 -0
- package/dist/api/deps.js +55 -0
- package/dist/api/deps.js.map +1 -0
- package/dist/api/errors.d.ts +35 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +63 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +15 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/map-core-error.d.ts +19 -0
- package/dist/api/map-core-error.d.ts.map +1 -0
- package/dist/api/map-core-error.js +49 -0
- package/dist/api/map-core-error.js.map +1 -0
- package/dist/api/pagination.d.ts +19 -0
- package/dist/api/pagination.d.ts.map +1 -0
- package/dist/api/pagination.js +40 -0
- package/dist/api/pagination.js.map +1 -0
- package/dist/api/request.d.ts +93 -0
- package/dist/api/request.d.ts.map +1 -0
- package/dist/api/request.js +150 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/respond.d.ts +29 -0
- package/dist/api/respond.d.ts.map +1 -0
- package/dist/api/respond.js +59 -0
- package/dist/api/respond.js.map +1 -0
- package/dist/api/responses-schema.d.ts +158 -0
- package/dist/api/responses-schema.d.ts.map +1 -0
- package/dist/api/responses-schema.js +104 -0
- package/dist/api/responses-schema.js.map +1 -0
- package/dist/api/responses.d.ts +19 -0
- package/dist/api/responses.d.ts.map +1 -0
- package/dist/api/responses.js +21 -0
- package/dist/api/responses.js.map +1 -0
- package/dist/api/shaped.d.ts +23 -0
- package/dist/api/shaped.d.ts.map +1 -0
- package/dist/api/shaped.js +29 -0
- package/dist/api/shaped.js.map +1 -0
- package/dist/api/types.d.ts +41 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +12 -0
- package/dist/api/types.js.map +1 -0
- package/dist/compute/errors.d.ts +18 -0
- package/dist/compute/errors.d.ts.map +1 -0
- package/dist/compute/errors.js +21 -0
- package/dist/compute/errors.js.map +1 -0
- package/dist/compute/evict-changed-servers.d.ts +22 -0
- package/dist/compute/evict-changed-servers.d.ts.map +1 -0
- package/dist/compute/evict-changed-servers.js +31 -0
- package/dist/compute/evict-changed-servers.js.map +1 -0
- package/dist/compute/idempotency-http.d.ts +47 -0
- package/dist/compute/idempotency-http.d.ts.map +1 -0
- package/dist/compute/idempotency-http.js +51 -0
- package/dist/compute/idempotency-http.js.map +1 -0
- package/dist/compute/index.d.ts +5 -0
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +5 -0
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/resolve-server.d.ts +25 -0
- package/dist/compute/resolve-server.d.ts.map +1 -0
- package/dist/compute/resolve-server.js +35 -0
- package/dist/compute/resolve-server.js.map +1 -0
- package/dist/compute/server-config-write.d.ts +52 -0
- package/dist/compute/server-config-write.d.ts.map +1 -0
- package/dist/compute/server-config-write.js +100 -0
- package/dist/compute/server-config-write.js.map +1 -0
- package/dist/definitions/index.d.ts +1 -0
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/index.js +1 -0
- package/dist/definitions/index.js.map +1 -1
- package/dist/definitions/visibility.d.ts +74 -0
- package/dist/definitions/visibility.d.ts.map +1 -0
- package/dist/definitions/visibility.js +117 -0
- package/dist/definitions/visibility.js.map +1 -0
- package/dist/handlers/definitionVersions.d.ts +30 -0
- package/dist/handlers/definitionVersions.d.ts.map +1 -0
- package/dist/handlers/definitionVersions.js +93 -0
- package/dist/handlers/definitionVersions.js.map +1 -0
- package/dist/handlers/definitions.d.ts +35 -0
- package/dist/handlers/definitions.d.ts.map +1 -0
- package/dist/handlers/definitions.js +207 -0
- package/dist/handlers/definitions.js.map +1 -0
- package/dist/handlers/index.d.ts +27 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +29 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/invite-lookup.d.ts +11 -0
- package/dist/handlers/invite-lookup.d.ts.map +1 -0
- package/dist/handlers/invite-lookup.js +20 -0
- package/dist/handlers/invite-lookup.js.map +1 -0
- package/dist/handlers/invites.d.ts +31 -0
- package/dist/handlers/invites.d.ts.map +1 -0
- package/dist/handlers/invites.js +148 -0
- package/dist/handlers/invites.js.map +1 -0
- package/dist/handlers/me.d.ts +8 -0
- package/dist/handlers/me.d.ts.map +1 -0
- package/dist/handlers/me.js +21 -0
- package/dist/handlers/me.js.map +1 -0
- package/dist/handlers/me.starred.d.ts +11 -0
- package/dist/handlers/me.starred.d.ts.map +1 -0
- package/dist/handlers/me.starred.js +21 -0
- package/dist/handlers/me.starred.js.map +1 -0
- package/dist/handlers/orgAssets.d.ts +13 -0
- package/dist/handlers/orgAssets.d.ts.map +1 -0
- package/dist/handlers/orgAssets.js +54 -0
- package/dist/handlers/orgAssets.js.map +1 -0
- package/dist/handlers/orgCompute.d.ts +16 -0
- package/dist/handlers/orgCompute.d.ts.map +1 -0
- package/dist/handlers/orgCompute.js +96 -0
- package/dist/handlers/orgCompute.js.map +1 -0
- package/dist/handlers/orgMembers.d.ts +29 -0
- package/dist/handlers/orgMembers.d.ts.map +1 -0
- package/dist/handlers/orgMembers.js +200 -0
- package/dist/handlers/orgMembers.js.map +1 -0
- package/dist/handlers/orgs.d.ts +16 -0
- package/dist/handlers/orgs.d.ts.map +1 -0
- package/dist/handlers/orgs.js +26 -0
- package/dist/handlers/orgs.js.map +1 -0
- package/dist/handlers/projectMembers.d.ts +15 -0
- package/dist/handlers/projectMembers.d.ts.map +1 -0
- package/dist/handlers/projectMembers.js +95 -0
- package/dist/handlers/projectMembers.js.map +1 -0
- package/dist/handlers/projects.d.ts +21 -0
- package/dist/handlers/projects.d.ts.map +1 -0
- package/dist/handlers/projects.js +164 -0
- package/dist/handlers/projects.js.map +1 -0
- package/dist/handlers/reclaim.d.ts +10 -0
- package/dist/handlers/reclaim.d.ts.map +1 -0
- package/dist/handlers/reclaim.js +38 -0
- package/dist/handlers/reclaim.js.map +1 -0
- package/dist/handlers/services.d.ts +22 -0
- package/dist/handlers/services.d.ts.map +1 -0
- package/dist/handlers/services.js +37 -0
- package/dist/handlers/services.js.map +1 -0
- package/dist/handlers/shareLinks.d.ts +23 -0
- package/dist/handlers/shareLinks.d.ts.map +1 -0
- package/dist/handlers/shareLinks.js +85 -0
- package/dist/handlers/shareLinks.js.map +1 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +1 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/login-rate-limit.d.ts +72 -0
- package/dist/http/login-rate-limit.d.ts.map +1 -0
- package/dist/http/login-rate-limit.js +84 -0
- package/dist/http/login-rate-limit.js.map +1 -0
- package/dist/notifications/deliver-invite.d.ts +27 -0
- package/dist/notifications/deliver-invite.d.ts.map +1 -0
- package/dist/notifications/deliver-invite.js +32 -0
- package/dist/notifications/deliver-invite.js.map +1 -0
- package/dist/notifications/index.d.ts +1 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -0
- package/dist/notifications/index.js.map +1 -1
- package/dist/organizations/index.d.ts +3 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +5 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/list-all-members.d.ts +10 -0
- package/dist/organizations/list-all-members.d.ts.map +1 -0
- package/dist/organizations/list-all-members.js +36 -0
- package/dist/organizations/list-all-members.js.map +1 -0
- package/dist/organizations/org-asset-service.d.ts +39 -0
- package/dist/organizations/org-asset-service.d.ts.map +1 -0
- package/dist/organizations/org-asset-service.js +71 -0
- package/dist/organizations/org-asset-service.js.map +1 -0
- package/dist/projects/create-project.d.ts +30 -0
- package/dist/projects/create-project.d.ts.map +1 -0
- package/dist/projects/create-project.js +56 -0
- package/dist/projects/create-project.js.map +1 -0
- package/dist/projects/index.d.ts +2 -0
- package/dist/projects/index.d.ts.map +1 -0
- package/dist/projects/index.js +3 -0
- package/dist/projects/index.js.map +1 -0
- package/dist/providers/auth-lookup.d.ts +14 -0
- package/dist/providers/auth-lookup.d.ts.map +1 -0
- package/dist/providers/auth-lookup.js +34 -0
- package/dist/providers/auth-lookup.js.map +1 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/testing/harness.d.ts +168 -0
- package/dist/testing/harness.d.ts.map +1 -0
- package/dist/testing/harness.js +314 -0
- package/dist/testing/harness.js.map +1 -0
- package/dist/testing/index.d.ts +18 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +15 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/scenarios.d.ts +67 -0
- package/dist/testing/scenarios.d.ts.map +1 -0
- package/dist/testing/scenarios.js +104 -0
- package/dist/testing/scenarios.js.map +1 -0
- package/dist/testing/seed-adapter.d.ts +31 -0
- package/dist/testing/seed-adapter.d.ts.map +1 -0
- package/dist/testing/seed-adapter.js +19 -0
- package/dist/testing/seed-adapter.js.map +1 -0
- package/package.json +30 -2
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Org invites: list, mint, revoke, resend.
|
|
3
|
+
*
|
|
4
|
+
* **The raw token exists only in the response.** The store sees an HMAC digest,
|
|
5
|
+
* so a leak of the `invites` table cannot be replayed without the instance
|
|
6
|
+
* secret — and `tokenHash` is dropped by the response schema, which is why
|
|
7
|
+
* every response here goes through `shaped`/`shapedCollection` rather than
|
|
8
|
+
* `{ body }`.
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { apiError, ApiErrorCode, CreatedInviteResponseSchema, CreateInviteBodySchema, InviteResponseSchema, noContent, parseBody, parseListOptions, requireCaller, requireParams, shaped, shapedCollection } from '../api/index.js';
|
|
12
|
+
import { DEFAULT_ORG_PERMISSIONS, MEMBER_ASSIGNABLE_PERMISSIONS } from '@selvajs/platform';
|
|
13
|
+
import { assertCanGrantPlatformPermissions, canActorChangeOrgRole, requireManageOrgMembers, requireActingOrg, splitFlatPermissions } from '../access/index.js';
|
|
14
|
+
import { deliverInvite } from '../notifications/index.js';
|
|
15
|
+
import { tokenCodec } from './services.js';
|
|
16
|
+
import { findPendingInviteInOrg } from './invite-lookup.js';
|
|
17
|
+
const INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
18
|
+
/**
|
|
19
|
+
* Mail the accept link, or report why not.
|
|
20
|
+
*
|
|
21
|
+
* Mint and resend must send the identical message, so the deps threading lives
|
|
22
|
+
* here rather than at each call site. `not-configured` when the host wired no
|
|
23
|
+
* notification provider: the invite row is already committed and the caller
|
|
24
|
+
* still holds `acceptUrl`, so an instance without mail is a working instance,
|
|
25
|
+
* not a failed write.
|
|
26
|
+
*/
|
|
27
|
+
async function mailInvite(req, invite, acceptUrl) {
|
|
28
|
+
const notifications = req.deps.notifications;
|
|
29
|
+
if (!notifications)
|
|
30
|
+
return 'not-configured';
|
|
31
|
+
return deliverInvite({
|
|
32
|
+
ctx: req.ctx,
|
|
33
|
+
log: req.log,
|
|
34
|
+
invite,
|
|
35
|
+
acceptUrl,
|
|
36
|
+
actor: { profile: req.profile, user: req.user },
|
|
37
|
+
orgs: req.deps.orgs,
|
|
38
|
+
notifications,
|
|
39
|
+
fallbackOrgName: req.deps.instanceName
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Pending and recently accepted invites for the acting org. */
|
|
43
|
+
export const listInvites = async (req) => {
|
|
44
|
+
requireManageOrgMembers(req);
|
|
45
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
46
|
+
// `tokenHash` is the server-side lookup key; the response schema drops it.
|
|
47
|
+
return shapedCollection(InviteResponseSchema, await req.deps.invites.listByOrg(ctx, orgId, parseListOptions(req.url)));
|
|
48
|
+
};
|
|
49
|
+
/** Create an invite and return the accept URL. */
|
|
50
|
+
export const createInvite = async (req) => {
|
|
51
|
+
requireManageOrgMembers(req);
|
|
52
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
53
|
+
const { user } = requireCaller(req);
|
|
54
|
+
const input = await parseBody(req.request, CreateInviteBodySchema);
|
|
55
|
+
const { platform: submittedPlatformPerms, org: submittedOrgPerms } = splitFlatPermissions(input.permissions);
|
|
56
|
+
// An invite is a third write path into platform scope, so it shares the
|
|
57
|
+
// guard with both /api/admin/users handlers rather than restating it.
|
|
58
|
+
assertCanGrantPlatformPermissions(ctx, submittedPlatformPerms);
|
|
59
|
+
// An invite is a second door into `org_members`, so it carries the same
|
|
60
|
+
// owner-only role gate as PATCH /orgs/{orgId}/members/{userId}. Without
|
|
61
|
+
// it an admin mints themselves an `owner` invite, accepts it, and then
|
|
62
|
+
// passes the sole-owner check when removing the founder — `accept-invite`
|
|
63
|
+
// writes `invite.orgRole` verbatim and cannot re-verify the minter.
|
|
64
|
+
if (!(await canActorChangeOrgRole(ctx, orgId, input.orgRole, req))) {
|
|
65
|
+
apiError(403, ApiErrorCode.FORBIDDEN, 'Only the org owner can invite someone as owner or admin.');
|
|
66
|
+
}
|
|
67
|
+
// owner/admin always carry the full set — the checkbox array from the UI is
|
|
68
|
+
// ignored for those roles. `member` takes the caller's selection intersected
|
|
69
|
+
// with MEMBER_ASSIGNABLE_PERMISSIONS, so governance permissions
|
|
70
|
+
// (manage_org_members, manage_org_compute) cannot be granted to a member.
|
|
71
|
+
const orgPermissions = input.orgRole === 'member'
|
|
72
|
+
? submittedOrgPerms.filter((p) => MEMBER_ASSIGNABLE_PERMISSIONS.includes(p))
|
|
73
|
+
: [...DEFAULT_ORG_PERMISSIONS[input.orgRole]];
|
|
74
|
+
const codec = tokenCodec(req.deps, 'invites');
|
|
75
|
+
const rawToken = codec.mintRawToken();
|
|
76
|
+
const now = new Date();
|
|
77
|
+
const invite = {
|
|
78
|
+
id: randomUUID(),
|
|
79
|
+
tokenHash: codec.hashToken(rawToken),
|
|
80
|
+
email: input.email,
|
|
81
|
+
orgId,
|
|
82
|
+
orgRole: input.orgRole,
|
|
83
|
+
orgPermissions,
|
|
84
|
+
platformPermissions: submittedPlatformPerms,
|
|
85
|
+
invitedBy: user.id,
|
|
86
|
+
createdAt: now.toISOString(),
|
|
87
|
+
expiresAt: new Date(now.getTime() + INVITE_TTL_MS).toISOString()
|
|
88
|
+
};
|
|
89
|
+
await req.deps.invites.create(ctx, invite);
|
|
90
|
+
// `acceptUrl` carries the raw token and exists only in this response — it
|
|
91
|
+
// is not a field of the stored invite.
|
|
92
|
+
const acceptUrl = `${req.url.origin}/accept-invite?token=${rawToken}`;
|
|
93
|
+
const delivery = await mailInvite(req, invite, acceptUrl);
|
|
94
|
+
return shaped(CreatedInviteResponseSchema, { ...invite, acceptUrl, delivery }, 201);
|
|
95
|
+
};
|
|
96
|
+
/** Revoke a pending invite. Consumed invites are preserved for audit. */
|
|
97
|
+
export const revokeInvite = async (req) => {
|
|
98
|
+
requireManageOrgMembers(req);
|
|
99
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
100
|
+
const { id } = requireParams(req.params, 'id');
|
|
101
|
+
if (!(await findPendingInviteInOrg(ctx, orgId, id, req.deps.invites))) {
|
|
102
|
+
apiError(404, ApiErrorCode.NOT_FOUND, 'Invite not found');
|
|
103
|
+
}
|
|
104
|
+
await req.deps.invites.revoke(ctx, id);
|
|
105
|
+
return noContent();
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Re-send an invite: mint a replacement and revoke the original.
|
|
109
|
+
*
|
|
110
|
+
* A resend cannot reuse the original link. The raw token is never stored — only
|
|
111
|
+
* its HMAC digest is — so there is nothing to re-read, and rather than weaken
|
|
112
|
+
* that, this issues a fresh token and revokes the row it replaces. The old link
|
|
113
|
+
* stops working the moment this returns, which is also what an admin wants
|
|
114
|
+
* after mailing an invite to the wrong address.
|
|
115
|
+
*
|
|
116
|
+
* Grants are copied verbatim from the original. The role and permission gates
|
|
117
|
+
* ran when it was minted; re-deriving them here would let a since-demoted admin
|
|
118
|
+
* silently change what the invite carries.
|
|
119
|
+
*/
|
|
120
|
+
export const resendInvite = async (req) => {
|
|
121
|
+
requireManageOrgMembers(req);
|
|
122
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
123
|
+
const { id } = requireParams(req.params, 'id');
|
|
124
|
+
const existing = await findPendingInviteInOrg(ctx, orgId, id, req.deps.invites);
|
|
125
|
+
if (!existing)
|
|
126
|
+
apiError(404, ApiErrorCode.NOT_FOUND, 'Invite not found');
|
|
127
|
+
if (existing.acceptedAt) {
|
|
128
|
+
apiError(409, ApiErrorCode.CONFLICT, 'This invite has already been accepted.');
|
|
129
|
+
}
|
|
130
|
+
const codec = tokenCodec(req.deps, 'invites');
|
|
131
|
+
const rawToken = codec.mintRawToken();
|
|
132
|
+
const now = new Date();
|
|
133
|
+
const replacement = {
|
|
134
|
+
...existing,
|
|
135
|
+
id: randomUUID(),
|
|
136
|
+
tokenHash: codec.hashToken(rawToken),
|
|
137
|
+
createdAt: now.toISOString(),
|
|
138
|
+
expiresAt: new Date(now.getTime() + INVITE_TTL_MS).toISOString()
|
|
139
|
+
};
|
|
140
|
+
// Create before revoking: if the revoke fails the invitee holds a working
|
|
141
|
+
// link, whereas the reverse order can leave them with none at all.
|
|
142
|
+
await req.deps.invites.create(ctx, replacement);
|
|
143
|
+
await req.deps.invites.revoke(ctx, existing.id);
|
|
144
|
+
const acceptUrl = `${req.url.origin}/accept-invite?token=${rawToken}`;
|
|
145
|
+
const delivery = await mailInvite(req, replacement, acceptUrl);
|
|
146
|
+
return shaped(CreatedInviteResponseSchema, { ...replacement, acceptUrl, delivery }, 201);
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=invites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invites.js","sourceRoot":"","sources":["../../src/handlers/invites.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whoami. The first call a non-browser client makes, and the only way a token
|
|
3
|
+
* holder can see which org their credential resolved into — `actingOrgId` is
|
|
4
|
+
* derived per request from the caller's membership, not frozen at mint time.
|
|
5
|
+
*/
|
|
6
|
+
import { type ApiHandler } from '../api/index.js';
|
|
7
|
+
export declare const getMe: ApiHandler;
|
|
8
|
+
//# sourceMappingURL=me.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"me.d.ts","sourceRoot":"","sources":["../../src/handlers/me.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEjE,eAAO,MAAM,KAAK,EAAE,UAcnB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whoami. The first call a non-browser client makes, and the only way a token
|
|
3
|
+
* holder can see which org their credential resolved into — `actingOrgId` is
|
|
4
|
+
* derived per request from the caller's membership, not frozen at mint time.
|
|
5
|
+
*/
|
|
6
|
+
import { requireCaller } from '../api/index.js';
|
|
7
|
+
export const getMe = async (req) => {
|
|
8
|
+
const { ctx, user } = requireCaller(req);
|
|
9
|
+
return {
|
|
10
|
+
body: {
|
|
11
|
+
userId: user.id,
|
|
12
|
+
email: user.email,
|
|
13
|
+
displayName: req.profile?.displayName,
|
|
14
|
+
actingOrgId: ctx.actingOrgId,
|
|
15
|
+
platformPermissions: ctx.platformPermissions,
|
|
16
|
+
orgPermissions: ctx.orgPermissions,
|
|
17
|
+
starredDefinitions: req.profile?.starredDefinitions ?? []
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=me.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"me.js","sourceRoot":"","sources":["../../src/handlers/me.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAmB,MAAM,iBAAiB,CAAC;AAEjE,MAAM,CAAC,MAAM,KAAK,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IAC9C,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEzC,OAAO;QACN,IAAI,EAAE;YACL,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW;YACrC,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;YAC5C,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,kBAAkB,EAAE,GAAG,CAAC,OAAO,EAAE,kBAAkB,IAAI,EAAE;SACzD;KACD,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starring a definition for the calling user. Both directions are idempotent.
|
|
3
|
+
*
|
|
4
|
+
* No visibility check: a star is a row on the caller's own profile, keyed by
|
|
5
|
+
* guid, and reveals nothing about whether that guid exists. The read paths that
|
|
6
|
+
* expand stars into definitions filter by visibility there.
|
|
7
|
+
*/
|
|
8
|
+
import { type ApiHandler } from '../api/index.js';
|
|
9
|
+
export declare const starDefinition: ApiHandler;
|
|
10
|
+
export declare const unstarDefinition: ApiHandler;
|
|
11
|
+
//# sourceMappingURL=me.starred.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"me.starred.d.ts","sourceRoot":"","sources":["../../src/handlers/me.starred.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA2C,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE3F,eAAO,MAAM,cAAc,EAAE,UAM5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,UAM9B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starring a definition for the calling user. Both directions are idempotent.
|
|
3
|
+
*
|
|
4
|
+
* No visibility check: a star is a row on the caller's own profile, keyed by
|
|
5
|
+
* guid, and reveals nothing about whether that guid exists. The read paths that
|
|
6
|
+
* expand stars into definitions filter by visibility there.
|
|
7
|
+
*/
|
|
8
|
+
import { noContent, requireCaller, requireParams } from '../api/index.js';
|
|
9
|
+
export const starDefinition = async (req) => {
|
|
10
|
+
const { ctx, user } = requireCaller(req);
|
|
11
|
+
const { guid } = requireParams(req.params, 'guid');
|
|
12
|
+
await req.deps.userProfile.starDefinition(ctx, user.id, guid);
|
|
13
|
+
return noContent();
|
|
14
|
+
};
|
|
15
|
+
export const unstarDefinition = async (req) => {
|
|
16
|
+
const { ctx, user } = requireCaller(req);
|
|
17
|
+
const { guid } = requireParams(req.params, 'guid');
|
|
18
|
+
await req.deps.userProfile.unstarDefinition(ctx, user.id, guid);
|
|
19
|
+
return noContent();
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=me.starred.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"me.starred.js","sourceRoot":"","sources":["../../src/handlers/me.starred.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAmB,MAAM,iBAAiB,CAAC;AAE3F,MAAM,CAAC,MAAM,cAAc,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACvD,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEnD,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9D,OAAO,SAAS,EAAE,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACzD,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEnD,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChE,OAAO,SAAS,EAAE,CAAC;AACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Org branding assets, generic over kind (`logo`, `favicon`, …). Gated on
|
|
3
|
+
* `manage_org_members` — org branding is an org-admin action.
|
|
4
|
+
*
|
|
5
|
+
* Raster images and SVG are both accepted. Every input is rasterized to WebP by
|
|
6
|
+
* the shared transcoder inside the storage provider, so SVG carries no XSS
|
|
7
|
+
* surface: no vector blob is ever stored or served. The allowlist below is the
|
|
8
|
+
* HTTP gate; the rasterization is the security control.
|
|
9
|
+
*/
|
|
10
|
+
import { type ApiHandler } from '../api/index.js';
|
|
11
|
+
export declare const uploadOrgAsset: ApiHandler;
|
|
12
|
+
export declare const removeOrgAsset: ApiHandler;
|
|
13
|
+
//# sourceMappingURL=orgAssets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgAssets.d.ts","sourceRoot":"","sources":["../../src/handlers/orgAssets.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAoD,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAmBpG,eAAO,MAAM,cAAc,EAAE,UAqB5B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,UAO5B,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Org branding assets, generic over kind (`logo`, `favicon`, …). Gated on
|
|
3
|
+
* `manage_org_members` — org branding is an org-admin action.
|
|
4
|
+
*
|
|
5
|
+
* Raster images and SVG are both accepted. Every input is rasterized to WebP by
|
|
6
|
+
* the shared transcoder inside the storage provider, so SVG carries no XSS
|
|
7
|
+
* surface: no vector blob is ever stored or served. The allowlist below is the
|
|
8
|
+
* HTTP gate; the rasterization is the security control.
|
|
9
|
+
*/
|
|
10
|
+
import { apiError, ApiErrorCode, noContent, requireUpload } from '../api/index.js';
|
|
11
|
+
import { OrgAssetKindSchema } from '@selvajs/platform';
|
|
12
|
+
import { requireManageOrgMembers, requireActingOrg } from '../access/index.js';
|
|
13
|
+
import { orgAssetService } from './services.js';
|
|
14
|
+
const ALLOWED_CONTENT_TYPES = new Set([
|
|
15
|
+
'image/png',
|
|
16
|
+
'image/jpeg',
|
|
17
|
+
'image/webp',
|
|
18
|
+
'image/gif',
|
|
19
|
+
'image/svg+xml'
|
|
20
|
+
]);
|
|
21
|
+
function parseKind(params) {
|
|
22
|
+
const kind = OrgAssetKindSchema.safeParse(params.kind);
|
|
23
|
+
if (!kind.success)
|
|
24
|
+
apiError(404, ApiErrorCode.NOT_FOUND, `Unknown asset kind '${params.kind}'`);
|
|
25
|
+
return kind.data;
|
|
26
|
+
}
|
|
27
|
+
export const uploadOrgAsset = async (req) => {
|
|
28
|
+
requireManageOrgMembers(req);
|
|
29
|
+
const kind = parseKind(req.params);
|
|
30
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
31
|
+
// The org must exist — keeps a leaked id from seeding orphan blobs.
|
|
32
|
+
const org = await req.deps.orgs.getOrg(ctx, orgId);
|
|
33
|
+
if (!org)
|
|
34
|
+
apiError(404, ApiErrorCode.NOT_FOUND, 'Organization not found');
|
|
35
|
+
const { file } = requireUpload(await req.request.formData(), 'image', {
|
|
36
|
+
maxBytes: req.deps.uploadLimits.maxImageFileSize,
|
|
37
|
+
contentTypes: {
|
|
38
|
+
allowed: ALLOWED_CONTENT_TYPES,
|
|
39
|
+
message: 'Unsupported image type. Allowed: PNG, JPG, WebP, GIF, SVG'
|
|
40
|
+
},
|
|
41
|
+
label: 'Image'
|
|
42
|
+
});
|
|
43
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
44
|
+
const url = await orgAssetService(req.deps).saveAsset(ctx, orgId, kind, bytes, file.type);
|
|
45
|
+
return { body: { kind, url } };
|
|
46
|
+
};
|
|
47
|
+
export const removeOrgAsset = async (req) => {
|
|
48
|
+
requireManageOrgMembers(req);
|
|
49
|
+
const kind = parseKind(req.params);
|
|
50
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
51
|
+
await orgAssetService(req.deps).removeAsset(ctx, orgId, kind);
|
|
52
|
+
return noContent();
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=orgAssets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgAssets.js","sourceRoot":"","sources":["../../src/handlers/orgAssets.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAmB,MAAM,iBAAiB,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACrC,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,WAAW;IACX,eAAe;CACf,CAAC,CAAC;AAEH,SAAS,SAAS,CAAC,MAAyB;IAC3C,MAAM,IAAI,GAAG,kBAAkB,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvD,IAAI,CAAC,IAAI,CAAC,OAAO;QAAE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,SAAS,EAAE,uBAAuB,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IAChG,OAAO,IAAI,CAAC,IAAI,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACvD,uBAAuB,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE/D,oEAAoE;IACpE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,GAAG;QAAE,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;IAE1E,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE;QACrE,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,gBAAgB;QAChD,YAAY,EAAE;YACb,OAAO,EAAE,qBAAqB;YAC9B,OAAO,EAAE,2DAA2D;SACpE;QACD,KAAK,EAAE,OAAO;KACd,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1F,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;AAChC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAe,KAAK,EAAE,GAAG,EAAE,EAAE;IACvD,uBAAuB,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE/D,MAAM,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC9D,OAAO,SAAS,EAAE,CAAC;AACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-org compute config: this org's own servers (`scope: 'org'`) and its
|
|
3
|
+
* default selection (`orgDefaults[orgId]`). Platform servers and the global
|
|
4
|
+
* default are read-only here — `manage_compute` owns those via `/api/admin/compute`.
|
|
5
|
+
*
|
|
6
|
+
* Authorization: org `owner`/`admin` with `manage_org_compute`, plus the acting-org
|
|
7
|
+
* tenancy check. Gated by `ALLOW_ORG_COMPUTE_OVERRIDE`; when off, both methods 403.
|
|
8
|
+
*
|
|
9
|
+
* **`apiKey` merge semantics are the trap here.** Omitted preserves the stored
|
|
10
|
+
* key, `null` clears it, a string replaces it — and `/api/admin/compute` must
|
|
11
|
+
* agree, which is why the merge lives in `@selvajs/server/compute`, not inline.
|
|
12
|
+
*/
|
|
13
|
+
import type { ApiHandler } from '../api/index.js';
|
|
14
|
+
export declare const getOrgCompute: ApiHandler;
|
|
15
|
+
export declare const updateOrgCompute: ApiHandler;
|
|
16
|
+
//# sourceMappingURL=orgCompute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgCompute.d.ts","sourceRoot":"","sources":["../../src/handlers/orgCompute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,iBAAiB,CAAC;AA0B9D,eAAO,MAAM,aAAa,EAAE,UAiC3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,UA0D9B,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-org compute config: this org's own servers (`scope: 'org'`) and its
|
|
3
|
+
* default selection (`orgDefaults[orgId]`). Platform servers and the global
|
|
4
|
+
* default are read-only here — `manage_compute` owns those via `/api/admin/compute`.
|
|
5
|
+
*
|
|
6
|
+
* Authorization: org `owner`/`admin` with `manage_org_compute`, plus the acting-org
|
|
7
|
+
* tenancy check. Gated by `ALLOW_ORG_COMPUTE_OVERRIDE`; when off, both methods 403.
|
|
8
|
+
*
|
|
9
|
+
* **`apiKey` merge semantics are the trap here.** Omitted preserves the stored
|
|
10
|
+
* key, `null` clears it, a string replaces it — and `/api/admin/compute` must
|
|
11
|
+
* agree, which is why the merge lives in `@selvajs/server/compute`, not inline.
|
|
12
|
+
*/
|
|
13
|
+
import { apiError, ApiErrorCode, noContent, OrgComputePatchBodySchema, OrgComputeResponseSchema, parseBody, shaped } from '../api/index.js';
|
|
14
|
+
import { isOrgServer, isPlatformServer, serversVisibleTo } from '@selvajs/platform';
|
|
15
|
+
import { requireManageOrgCompute, requireActingOrg } from '../access/index.js';
|
|
16
|
+
import { evictChangedServers, validateIncomingServers, resolveApiKey, storedKeysById } from '../compute/index.js';
|
|
17
|
+
function requireFlag(req) {
|
|
18
|
+
if (!req.deps.flag('ALLOW_ORG_COMPUTE_OVERRIDE')) {
|
|
19
|
+
apiError(403, ApiErrorCode.FORBIDDEN, 'Per-org compute override is disabled on this instance (ALLOW_ORG_COMPUTE_OVERRIDE).');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export const getOrgCompute = async (req) => {
|
|
23
|
+
requireFlag(req);
|
|
24
|
+
requireManageOrgCompute(req);
|
|
25
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
26
|
+
// Scoped in the store, so `config.servers` holds only what this org may
|
|
27
|
+
// see and `defaultServerId` is blank unless it is one of them.
|
|
28
|
+
const config = await req.deps.computeServer.getConfig(ctx, { scopeToOrgId: orgId });
|
|
29
|
+
// Servers this org owns and may edit. `apiKey` is dropped by the response
|
|
30
|
+
// schema; `hasApiKey` is what a picker needs to render "key set".
|
|
31
|
+
const owned = config.servers
|
|
32
|
+
.filter((s) => isOrgServer(s) && s.ownerOrgId === orgId)
|
|
33
|
+
.map((s) => ({ ...s, hasApiKey: !!s.hasApiKey }));
|
|
34
|
+
// Platform and own servers visible to this org — the read-only catalog
|
|
35
|
+
// behind the "default selection" dropdown.
|
|
36
|
+
const catalog = config.servers.map((s) => ({
|
|
37
|
+
id: s.id,
|
|
38
|
+
label: s.label,
|
|
39
|
+
serverUrl: s.serverUrl,
|
|
40
|
+
scope: s.scope,
|
|
41
|
+
source: isPlatformServer(s) ? 'platform' : 'org',
|
|
42
|
+
timeoutMs: s.timeoutMs,
|
|
43
|
+
retryCount: s.retryCount
|
|
44
|
+
}));
|
|
45
|
+
return shaped(OrgComputeResponseSchema, {
|
|
46
|
+
servers: owned,
|
|
47
|
+
defaultServerId: config.orgDefaults?.[orgId] ?? null,
|
|
48
|
+
globalDefaultServerId: config.defaultServerId ?? null,
|
|
49
|
+
catalog
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
export const updateOrgCompute = async (req) => {
|
|
53
|
+
requireFlag(req);
|
|
54
|
+
requireManageOrgCompute(req);
|
|
55
|
+
const { ctx, orgId } = requireActingOrg(req, req.params.orgId);
|
|
56
|
+
const incoming = await parseBody(req.request, OrgComputePatchBodySchema);
|
|
57
|
+
// Cross-field rules the schema can't express (URL shape, duplicate ids).
|
|
58
|
+
validateIncomingServers(incoming.servers);
|
|
59
|
+
const provider = req.deps.computeServer;
|
|
60
|
+
// Every key is needed: an unchanged server keeps its stored key across the write.
|
|
61
|
+
const existing = await provider.getConfig(ctx, { includeApiKeys: true });
|
|
62
|
+
const storedKeys = storedKeysById(existing.servers.filter((s) => isOrgServer(s) && s.ownerOrgId === orgId));
|
|
63
|
+
const next = incoming.servers.map((s) => ({
|
|
64
|
+
id: s.id,
|
|
65
|
+
scope: 'org',
|
|
66
|
+
ownerOrgId: orgId,
|
|
67
|
+
label: s.label,
|
|
68
|
+
serverUrl: s.serverUrl,
|
|
69
|
+
timeoutMs: s.timeoutMs,
|
|
70
|
+
retryCount: s.retryCount,
|
|
71
|
+
apiKey: resolveApiKey(s.apiKey, storedKeys.get(s.id))
|
|
72
|
+
}));
|
|
73
|
+
// The requested org default must be visible to this org. The check runs
|
|
74
|
+
// against the projected post-save config so it sees both the new
|
|
75
|
+
// org-private rows and the existing platform ones.
|
|
76
|
+
if (typeof incoming.defaultServerId === 'string') {
|
|
77
|
+
const projected = {
|
|
78
|
+
servers: [
|
|
79
|
+
...existing.servers.filter((s) => !(isOrgServer(s) && s.ownerOrgId === orgId)),
|
|
80
|
+
...next
|
|
81
|
+
],
|
|
82
|
+
defaultServerId: existing.defaultServerId,
|
|
83
|
+
orgDefaults: existing.orgDefaults
|
|
84
|
+
};
|
|
85
|
+
const visibleIds = new Set(serversVisibleTo(projected, orgId).map((s) => s.id));
|
|
86
|
+
if (!visibleIds.has(incoming.defaultServerId)) {
|
|
87
|
+
apiError(400, ApiErrorCode.VALIDATION_FAILED, 'defaultServerId must reference a server visible to this organization');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
await provider.saveOrgServers(ctx, orgId, next, incoming.defaultServerId);
|
|
91
|
+
// Drop warm clients for servers whose URL or key rotated, or that were
|
|
92
|
+
// removed — keyed on `id`, they would not age out on their own.
|
|
93
|
+
evictChangedServers(existing.servers.filter((s) => isOrgServer(s) && s.ownerOrgId === orgId), next, req.deps.evictComputeClient);
|
|
94
|
+
return noContent();
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=orgCompute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgCompute.js","sourceRoot":"","sources":["../../src/handlers/orgCompute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Org member role, permissions, and removal.
|
|
3
|
+
*
|
|
4
|
+
* Two gates run on every request: `requireManageOrgMembers` (owner+admin) and
|
|
5
|
+
* `requireActingOrg` (the URL `orgId` must equal `ctx.actingOrgId`, so
|
|
6
|
+
* cross-org member mutation is blocked even for an actor with permissions in
|
|
7
|
+
* another org).
|
|
8
|
+
*
|
|
9
|
+
* The sole-owner invariant applies to demotion and removal alike — an org with
|
|
10
|
+
* no owner is unadministrable, and removing an owner ends their role exactly as
|
|
11
|
+
* demoting them does.
|
|
12
|
+
*/
|
|
13
|
+
import type { ApiHandler } from '../api/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* Body accepts either or both:
|
|
16
|
+
* - `{ role: OrgRole }` — change the member's role. Owner-only.
|
|
17
|
+
* - `{ permissions: OrgPermission[] }` — replace the member's grantable
|
|
18
|
+
* permissions. Owner/admin via `manage_org_members`. For `member`-role
|
|
19
|
+
* targets, only `MEMBER_ASSIGNABLE_PERMISSIONS` are accepted; promoting a
|
|
20
|
+
* member's permissions does NOT change their role.
|
|
21
|
+
*/
|
|
22
|
+
export declare const updateOrgMember: ApiHandler;
|
|
23
|
+
/**
|
|
24
|
+
* Remove a member from the org. Owner/admin via `manage_org_members`, same
|
|
25
|
+
* tenancy check as PATCH, and the same sole-owner invariant — removing the last
|
|
26
|
+
* owner would leave the org unadministrable.
|
|
27
|
+
*/
|
|
28
|
+
export declare const removeOrgMember: ApiHandler;
|
|
29
|
+
//# sourceMappingURL=orgMembers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgMembers.d.ts","sourceRoot":"","sources":["../../src/handlers/orgMembers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,iBAAiB,CAAC;AA+F9D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,UA8D7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,UAqE7B,CAAC"}
|