better-auth-organization-member 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +245 -0
- package/dist/client-B1z87oFG.d.mts +26 -0
- package/dist/client-B1z87oFG.d.mts.map +1 -0
- package/dist/client-PBBSie1a.mjs +11 -0
- package/dist/client-PBBSie1a.mjs.map +1 -0
- package/dist/client.d.mts +3 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +7 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4 -0
- package/dist/server-Big_p9zG.mjs +1017 -0
- package/dist/server-Big_p9zG.mjs.map +1 -0
- package/dist/server-uBIvbfMk.d.mts +119 -0
- package/dist/server-uBIvbfMk.d.mts.map +1 -0
- package/dist/server.d.mts +2 -0
- package/dist/server.d.ts +115 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +649 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +3 -0
- package/package.json +54 -0
|
@@ -0,0 +1,1017 @@
|
|
|
1
|
+
import { BASE_ERROR_CODES, defineErrorCodes } from "better-auth";
|
|
2
|
+
import { createAuthEndpoint, createAuthMiddleware, sessionMiddleware } from "better-auth/api";
|
|
3
|
+
import { clientSideHasPermission } from "better-auth/client/plugins";
|
|
4
|
+
import { toZodSchema } from "better-auth/db";
|
|
5
|
+
import { getOrgAdapter } from "better-auth/plugins";
|
|
6
|
+
import z from "zod";
|
|
7
|
+
|
|
8
|
+
//#region ../../node_modules/better-call/dist/error.mjs
|
|
9
|
+
function isErrorStackTraceLimitWritable() {
|
|
10
|
+
const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
|
|
11
|
+
if (desc === void 0) return Object.isExtensible(Error);
|
|
12
|
+
return Object.prototype.hasOwnProperty.call(desc, "writable") ? desc.writable : desc.set !== void 0;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Hide internal stack frames from the error stack trace.
|
|
16
|
+
*/
|
|
17
|
+
function hideInternalStackFrames(stack) {
|
|
18
|
+
const lines = stack.split("\n at ");
|
|
19
|
+
if (lines.length <= 1) return stack;
|
|
20
|
+
lines.splice(1, 1);
|
|
21
|
+
return lines.join("\n at ");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates a custom error class that hides stack frames.
|
|
25
|
+
*/
|
|
26
|
+
function makeErrorForHideStackFrame(Base, clazz) {
|
|
27
|
+
class HideStackFramesError extends Base {
|
|
28
|
+
#hiddenStack;
|
|
29
|
+
constructor(...args) {
|
|
30
|
+
if (isErrorStackTraceLimitWritable()) {
|
|
31
|
+
const limit = Error.stackTraceLimit;
|
|
32
|
+
Error.stackTraceLimit = 0;
|
|
33
|
+
super(...args);
|
|
34
|
+
Error.stackTraceLimit = limit;
|
|
35
|
+
} else super(...args);
|
|
36
|
+
const stack = (/* @__PURE__ */ new Error()).stack;
|
|
37
|
+
if (stack) this.#hiddenStack = hideInternalStackFrames(stack.replace(/^Error/, this.name));
|
|
38
|
+
}
|
|
39
|
+
get errorStack() {
|
|
40
|
+
return this.#hiddenStack;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
Object.defineProperty(HideStackFramesError.prototype, "constructor", {
|
|
44
|
+
get() {
|
|
45
|
+
return clazz;
|
|
46
|
+
},
|
|
47
|
+
enumerable: false,
|
|
48
|
+
configurable: true
|
|
49
|
+
});
|
|
50
|
+
return HideStackFramesError;
|
|
51
|
+
}
|
|
52
|
+
const statusCodes = {
|
|
53
|
+
OK: 200,
|
|
54
|
+
CREATED: 201,
|
|
55
|
+
ACCEPTED: 202,
|
|
56
|
+
NO_CONTENT: 204,
|
|
57
|
+
MULTIPLE_CHOICES: 300,
|
|
58
|
+
MOVED_PERMANENTLY: 301,
|
|
59
|
+
FOUND: 302,
|
|
60
|
+
SEE_OTHER: 303,
|
|
61
|
+
NOT_MODIFIED: 304,
|
|
62
|
+
TEMPORARY_REDIRECT: 307,
|
|
63
|
+
BAD_REQUEST: 400,
|
|
64
|
+
UNAUTHORIZED: 401,
|
|
65
|
+
PAYMENT_REQUIRED: 402,
|
|
66
|
+
FORBIDDEN: 403,
|
|
67
|
+
NOT_FOUND: 404,
|
|
68
|
+
METHOD_NOT_ALLOWED: 405,
|
|
69
|
+
NOT_ACCEPTABLE: 406,
|
|
70
|
+
PROXY_AUTHENTICATION_REQUIRED: 407,
|
|
71
|
+
REQUEST_TIMEOUT: 408,
|
|
72
|
+
CONFLICT: 409,
|
|
73
|
+
GONE: 410,
|
|
74
|
+
LENGTH_REQUIRED: 411,
|
|
75
|
+
PRECONDITION_FAILED: 412,
|
|
76
|
+
PAYLOAD_TOO_LARGE: 413,
|
|
77
|
+
URI_TOO_LONG: 414,
|
|
78
|
+
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
79
|
+
RANGE_NOT_SATISFIABLE: 416,
|
|
80
|
+
EXPECTATION_FAILED: 417,
|
|
81
|
+
"I'M_A_TEAPOT": 418,
|
|
82
|
+
MISDIRECTED_REQUEST: 421,
|
|
83
|
+
UNPROCESSABLE_ENTITY: 422,
|
|
84
|
+
LOCKED: 423,
|
|
85
|
+
FAILED_DEPENDENCY: 424,
|
|
86
|
+
TOO_EARLY: 425,
|
|
87
|
+
UPGRADE_REQUIRED: 426,
|
|
88
|
+
PRECONDITION_REQUIRED: 428,
|
|
89
|
+
TOO_MANY_REQUESTS: 429,
|
|
90
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
|
|
91
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: 451,
|
|
92
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
93
|
+
NOT_IMPLEMENTED: 501,
|
|
94
|
+
BAD_GATEWAY: 502,
|
|
95
|
+
SERVICE_UNAVAILABLE: 503,
|
|
96
|
+
GATEWAY_TIMEOUT: 504,
|
|
97
|
+
HTTP_VERSION_NOT_SUPPORTED: 505,
|
|
98
|
+
VARIANT_ALSO_NEGOTIATES: 506,
|
|
99
|
+
INSUFFICIENT_STORAGE: 507,
|
|
100
|
+
LOOP_DETECTED: 508,
|
|
101
|
+
NOT_EXTENDED: 510,
|
|
102
|
+
NETWORK_AUTHENTICATION_REQUIRED: 511
|
|
103
|
+
};
|
|
104
|
+
var InternalAPIError = class extends Error {
|
|
105
|
+
constructor(status = "INTERNAL_SERVER_ERROR", body = void 0, headers = {}, statusCode = typeof status === "number" ? status : statusCodes[status]) {
|
|
106
|
+
super(body?.message, body?.cause ? { cause: body.cause } : void 0);
|
|
107
|
+
this.status = status;
|
|
108
|
+
this.body = body;
|
|
109
|
+
this.headers = headers;
|
|
110
|
+
this.statusCode = statusCode;
|
|
111
|
+
this.name = "APIError";
|
|
112
|
+
this.status = status;
|
|
113
|
+
this.headers = headers;
|
|
114
|
+
this.statusCode = statusCode;
|
|
115
|
+
this.body = body ? {
|
|
116
|
+
code: body?.message?.toUpperCase().replace(/ /g, "_").replace(/[^A-Z0-9_]/g, ""),
|
|
117
|
+
...body
|
|
118
|
+
} : void 0;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var ValidationError = class extends InternalAPIError {
|
|
122
|
+
constructor(message, issues) {
|
|
123
|
+
super(400, {
|
|
124
|
+
message,
|
|
125
|
+
code: "VALIDATION_ERROR"
|
|
126
|
+
});
|
|
127
|
+
this.message = message;
|
|
128
|
+
this.issues = issues;
|
|
129
|
+
this.issues = issues;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var BetterCallError = class extends Error {
|
|
133
|
+
constructor(message) {
|
|
134
|
+
super(message);
|
|
135
|
+
this.name = "BetterCallError";
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const APIError = makeErrorForHideStackFrame(InternalAPIError, Error);
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region ../../node_modules/better-call/dist/utils.mjs
|
|
142
|
+
function isAPIError(error) {
|
|
143
|
+
return error instanceof APIError || error?.name === "APIError";
|
|
144
|
+
}
|
|
145
|
+
function tryDecode(str) {
|
|
146
|
+
try {
|
|
147
|
+
return str.includes("%") ? decodeURIComponent(str) : str;
|
|
148
|
+
} catch {
|
|
149
|
+
return str;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function tryCatch(promise) {
|
|
153
|
+
try {
|
|
154
|
+
return {
|
|
155
|
+
data: await promise,
|
|
156
|
+
error: null
|
|
157
|
+
};
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
data: null,
|
|
161
|
+
error
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Check if an object is a `Request`
|
|
167
|
+
* - `instanceof`: works for native Request instances
|
|
168
|
+
* - `toString`: handles where instanceof check fails but the object is still a valid Request
|
|
169
|
+
*/
|
|
170
|
+
function isRequest(obj) {
|
|
171
|
+
return obj instanceof Request || Object.prototype.toString.call(obj) === "[object Request]";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region ../../node_modules/better-call/dist/to-response.mjs
|
|
176
|
+
function isJSONSerializable(value) {
|
|
177
|
+
if (value === void 0) return false;
|
|
178
|
+
const t = typeof value;
|
|
179
|
+
if (t === "string" || t === "number" || t === "boolean" || t === null) return true;
|
|
180
|
+
if (t !== "object") return false;
|
|
181
|
+
if (Array.isArray(value)) return true;
|
|
182
|
+
if (value.buffer) return false;
|
|
183
|
+
return value.constructor && value.constructor.name === "Object" || typeof value.toJSON === "function";
|
|
184
|
+
}
|
|
185
|
+
function safeStringify(obj, replacer, space) {
|
|
186
|
+
let id = 0;
|
|
187
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
188
|
+
const safeReplacer = (key, value) => {
|
|
189
|
+
if (typeof value === "bigint") return value.toString();
|
|
190
|
+
if (typeof value === "object" && value !== null) {
|
|
191
|
+
if (seen.has(value)) return `[Circular ref-${seen.get(value)}]`;
|
|
192
|
+
seen.set(value, id++);
|
|
193
|
+
}
|
|
194
|
+
if (replacer) return replacer(key, value);
|
|
195
|
+
return value;
|
|
196
|
+
};
|
|
197
|
+
return JSON.stringify(obj, safeReplacer, space);
|
|
198
|
+
}
|
|
199
|
+
function isJSONResponse(value) {
|
|
200
|
+
if (!value || typeof value !== "object") return false;
|
|
201
|
+
return "_flag" in value && value._flag === "json";
|
|
202
|
+
}
|
|
203
|
+
function toResponse(data, init) {
|
|
204
|
+
if (data instanceof Response) {
|
|
205
|
+
if (init?.headers instanceof Headers) init.headers.forEach((value, key) => {
|
|
206
|
+
data.headers.set(key, value);
|
|
207
|
+
});
|
|
208
|
+
return data;
|
|
209
|
+
}
|
|
210
|
+
if (isJSONResponse(data)) {
|
|
211
|
+
const body$1 = data.body;
|
|
212
|
+
const routerResponse = data.routerResponse;
|
|
213
|
+
if (routerResponse instanceof Response) return routerResponse;
|
|
214
|
+
const headers$1 = new Headers();
|
|
215
|
+
if (routerResponse?.headers) {
|
|
216
|
+
const headers$2 = new Headers(routerResponse.headers);
|
|
217
|
+
for (const [key, value] of headers$2.entries()) headers$2.set(key, value);
|
|
218
|
+
}
|
|
219
|
+
if (data.headers) for (const [key, value] of new Headers(data.headers).entries()) headers$1.set(key, value);
|
|
220
|
+
if (init?.headers) for (const [key, value] of new Headers(init.headers).entries()) headers$1.set(key, value);
|
|
221
|
+
headers$1.set("Content-Type", "application/json");
|
|
222
|
+
return new Response(JSON.stringify(body$1), {
|
|
223
|
+
...routerResponse,
|
|
224
|
+
headers: headers$1,
|
|
225
|
+
status: data.status ?? init?.status ?? routerResponse?.status,
|
|
226
|
+
statusText: init?.statusText ?? routerResponse?.statusText
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (isAPIError(data)) return toResponse(data.body, {
|
|
230
|
+
status: init?.status ?? data.statusCode,
|
|
231
|
+
statusText: data.status.toString(),
|
|
232
|
+
headers: init?.headers || data.headers
|
|
233
|
+
});
|
|
234
|
+
let body = data;
|
|
235
|
+
let headers = new Headers(init?.headers);
|
|
236
|
+
if (!data) {
|
|
237
|
+
if (data === null) body = JSON.stringify(null);
|
|
238
|
+
headers.set("content-type", "application/json");
|
|
239
|
+
} else if (typeof data === "string") {
|
|
240
|
+
body = data;
|
|
241
|
+
headers.set("Content-Type", "text/plain");
|
|
242
|
+
} else if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
|
|
243
|
+
body = data;
|
|
244
|
+
headers.set("Content-Type", "application/octet-stream");
|
|
245
|
+
} else if (data instanceof Blob) {
|
|
246
|
+
body = data;
|
|
247
|
+
headers.set("Content-Type", data.type || "application/octet-stream");
|
|
248
|
+
} else if (data instanceof FormData) body = data;
|
|
249
|
+
else if (data instanceof URLSearchParams) {
|
|
250
|
+
body = data;
|
|
251
|
+
headers.set("Content-Type", "application/x-www-form-urlencoded");
|
|
252
|
+
} else if (data instanceof ReadableStream) {
|
|
253
|
+
body = data;
|
|
254
|
+
headers.set("Content-Type", "application/octet-stream");
|
|
255
|
+
} else if (isJSONSerializable(data)) {
|
|
256
|
+
body = safeStringify(data);
|
|
257
|
+
headers.set("Content-Type", "application/json");
|
|
258
|
+
}
|
|
259
|
+
return new Response(body, {
|
|
260
|
+
...init,
|
|
261
|
+
headers
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region ../../node_modules/@better-auth/utils/dist/index.mjs
|
|
267
|
+
function getWebcryptoSubtle() {
|
|
268
|
+
const cr = typeof globalThis !== "undefined" && globalThis.crypto;
|
|
269
|
+
if (cr && typeof cr.subtle === "object" && cr.subtle != null) return cr.subtle;
|
|
270
|
+
throw new Error("crypto.subtle must be defined");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region ../../node_modules/better-call/dist/crypto.mjs
|
|
275
|
+
const algorithm = {
|
|
276
|
+
name: "HMAC",
|
|
277
|
+
hash: "SHA-256"
|
|
278
|
+
};
|
|
279
|
+
const getCryptoKey = async (secret) => {
|
|
280
|
+
const secretBuf = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
|
|
281
|
+
return await getWebcryptoSubtle().importKey("raw", secretBuf, algorithm, false, ["sign", "verify"]);
|
|
282
|
+
};
|
|
283
|
+
const verifySignature = async (base64Signature, value, secret) => {
|
|
284
|
+
try {
|
|
285
|
+
const signatureBinStr = atob(base64Signature);
|
|
286
|
+
const signature = new Uint8Array(signatureBinStr.length);
|
|
287
|
+
for (let i = 0, len = signatureBinStr.length; i < len; i++) signature[i] = signatureBinStr.charCodeAt(i);
|
|
288
|
+
return await getWebcryptoSubtle().verify(algorithm, secret, signature, new TextEncoder().encode(value));
|
|
289
|
+
} catch (e) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
const makeSignature = async (value, secret) => {
|
|
294
|
+
const key = await getCryptoKey(secret);
|
|
295
|
+
const signature = await getWebcryptoSubtle().sign(algorithm.name, key, new TextEncoder().encode(value));
|
|
296
|
+
return btoa(String.fromCharCode(...new Uint8Array(signature)));
|
|
297
|
+
};
|
|
298
|
+
const signCookieValue = async (value, secret) => {
|
|
299
|
+
const signature = await makeSignature(value, secret);
|
|
300
|
+
value = `${value}.${signature}`;
|
|
301
|
+
value = encodeURIComponent(value);
|
|
302
|
+
return value;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region ../../node_modules/better-call/dist/cookies.mjs
|
|
307
|
+
const getCookieKey = (key, prefix) => {
|
|
308
|
+
let finalKey = key;
|
|
309
|
+
if (prefix) if (prefix === "secure") finalKey = "__Secure-" + key;
|
|
310
|
+
else if (prefix === "host") finalKey = "__Host-" + key;
|
|
311
|
+
else return;
|
|
312
|
+
return finalKey;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Parse an HTTP Cookie header string and returning an object of all cookie
|
|
316
|
+
* name-value pairs.
|
|
317
|
+
*
|
|
318
|
+
* Inspired by https://github.com/unjs/cookie-es/blob/main/src/cookie/parse.ts
|
|
319
|
+
*
|
|
320
|
+
* @param str the string representing a `Cookie` header value
|
|
321
|
+
*/
|
|
322
|
+
function parseCookies(str) {
|
|
323
|
+
if (typeof str !== "string") throw new TypeError("argument str must be a string");
|
|
324
|
+
const cookies = /* @__PURE__ */ new Map();
|
|
325
|
+
let index = 0;
|
|
326
|
+
while (index < str.length) {
|
|
327
|
+
const eqIdx = str.indexOf("=", index);
|
|
328
|
+
if (eqIdx === -1) break;
|
|
329
|
+
let endIdx = str.indexOf(";", index);
|
|
330
|
+
if (endIdx === -1) endIdx = str.length;
|
|
331
|
+
else if (endIdx < eqIdx) {
|
|
332
|
+
index = str.lastIndexOf(";", eqIdx - 1) + 1;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const key = str.slice(index, eqIdx).trim();
|
|
336
|
+
if (!cookies.has(key)) {
|
|
337
|
+
let val = str.slice(eqIdx + 1, endIdx).trim();
|
|
338
|
+
if (val.codePointAt(0) === 34) val = val.slice(1, -1);
|
|
339
|
+
cookies.set(key, tryDecode(val));
|
|
340
|
+
}
|
|
341
|
+
index = endIdx + 1;
|
|
342
|
+
}
|
|
343
|
+
return cookies;
|
|
344
|
+
}
|
|
345
|
+
const _serialize = (key, value, opt = {}) => {
|
|
346
|
+
let cookie;
|
|
347
|
+
if (opt?.prefix === "secure") cookie = `${`__Secure-${key}`}=${value}`;
|
|
348
|
+
else if (opt?.prefix === "host") cookie = `${`__Host-${key}`}=${value}`;
|
|
349
|
+
else cookie = `${key}=${value}`;
|
|
350
|
+
if (key.startsWith("__Secure-") && !opt.secure) opt.secure = true;
|
|
351
|
+
if (key.startsWith("__Host-")) {
|
|
352
|
+
if (!opt.secure) opt.secure = true;
|
|
353
|
+
if (opt.path !== "/") opt.path = "/";
|
|
354
|
+
if (opt.domain) opt.domain = void 0;
|
|
355
|
+
}
|
|
356
|
+
if (opt && typeof opt.maxAge === "number" && opt.maxAge >= 0) {
|
|
357
|
+
if (opt.maxAge > 3456e4) throw new Error("Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration.");
|
|
358
|
+
cookie += `; Max-Age=${Math.floor(opt.maxAge)}`;
|
|
359
|
+
}
|
|
360
|
+
if (opt.domain && opt.prefix !== "host") cookie += `; Domain=${opt.domain}`;
|
|
361
|
+
if (opt.path) cookie += `; Path=${opt.path}`;
|
|
362
|
+
if (opt.expires) {
|
|
363
|
+
if (opt.expires.getTime() - Date.now() > 3456e7) throw new Error("Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future.");
|
|
364
|
+
cookie += `; Expires=${opt.expires.toUTCString()}`;
|
|
365
|
+
}
|
|
366
|
+
if (opt.httpOnly) cookie += "; HttpOnly";
|
|
367
|
+
if (opt.secure) cookie += "; Secure";
|
|
368
|
+
if (opt.sameSite) cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`;
|
|
369
|
+
if (opt.partitioned) {
|
|
370
|
+
if (!opt.secure) opt.secure = true;
|
|
371
|
+
cookie += "; Partitioned";
|
|
372
|
+
}
|
|
373
|
+
return cookie;
|
|
374
|
+
};
|
|
375
|
+
const serializeCookie = (key, value, opt) => {
|
|
376
|
+
value = encodeURIComponent(value);
|
|
377
|
+
return _serialize(key, value, opt);
|
|
378
|
+
};
|
|
379
|
+
const serializeSignedCookie = async (key, value, secret, opt) => {
|
|
380
|
+
value = await signCookieValue(value, secret);
|
|
381
|
+
return _serialize(key, value, opt);
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
//#endregion
|
|
385
|
+
//#region ../../node_modules/better-call/dist/validator.mjs
|
|
386
|
+
/**
|
|
387
|
+
* Runs validation on body and query
|
|
388
|
+
* @returns error and data object
|
|
389
|
+
*/
|
|
390
|
+
async function runValidation(options, context = {}) {
|
|
391
|
+
let request = {
|
|
392
|
+
body: context.body,
|
|
393
|
+
query: context.query
|
|
394
|
+
};
|
|
395
|
+
if (options.body) {
|
|
396
|
+
const result = await options.body["~standard"].validate(context.body);
|
|
397
|
+
if (result.issues) return {
|
|
398
|
+
data: null,
|
|
399
|
+
error: fromError(result.issues, "body")
|
|
400
|
+
};
|
|
401
|
+
request.body = result.value;
|
|
402
|
+
}
|
|
403
|
+
if (options.query) {
|
|
404
|
+
const result = await options.query["~standard"].validate(context.query);
|
|
405
|
+
if (result.issues) return {
|
|
406
|
+
data: null,
|
|
407
|
+
error: fromError(result.issues, "query")
|
|
408
|
+
};
|
|
409
|
+
request.query = result.value;
|
|
410
|
+
}
|
|
411
|
+
if (options.requireHeaders && !context.headers) return {
|
|
412
|
+
data: null,
|
|
413
|
+
error: {
|
|
414
|
+
message: "Headers is required",
|
|
415
|
+
issues: []
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
if (options.requireRequest && !context.request) return {
|
|
419
|
+
data: null,
|
|
420
|
+
error: {
|
|
421
|
+
message: "Request is required",
|
|
422
|
+
issues: []
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
return {
|
|
426
|
+
data: request,
|
|
427
|
+
error: null
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function fromError(error, validating) {
|
|
431
|
+
return {
|
|
432
|
+
message: error.map((e) => {
|
|
433
|
+
return `[${e.path?.length ? `${validating}.` + e.path.map((x) => typeof x === "object" ? x.key : x).join(".") : validating}] ${e.message}`;
|
|
434
|
+
}).join("; "),
|
|
435
|
+
issues: error
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region ../../node_modules/better-call/dist/context.mjs
|
|
441
|
+
const createInternalContext = async (context, { options, path }) => {
|
|
442
|
+
const headers = new Headers();
|
|
443
|
+
let responseStatus = void 0;
|
|
444
|
+
const { data, error } = await runValidation(options, context);
|
|
445
|
+
if (error) throw new ValidationError(error.message, error.issues);
|
|
446
|
+
const requestHeaders = "headers" in context ? context.headers instanceof Headers ? context.headers : new Headers(context.headers) : "request" in context && isRequest(context.request) ? context.request.headers : null;
|
|
447
|
+
const requestCookies = requestHeaders?.get("cookie");
|
|
448
|
+
const parsedCookies = requestCookies ? parseCookies(requestCookies) : void 0;
|
|
449
|
+
const internalContext = {
|
|
450
|
+
...context,
|
|
451
|
+
body: data.body,
|
|
452
|
+
query: data.query,
|
|
453
|
+
path: context.path || path || "virtual:",
|
|
454
|
+
context: "context" in context && context.context ? context.context : {},
|
|
455
|
+
returned: void 0,
|
|
456
|
+
headers: context?.headers,
|
|
457
|
+
request: context?.request,
|
|
458
|
+
params: "params" in context ? context.params : void 0,
|
|
459
|
+
method: context.method ?? (Array.isArray(options.method) ? options.method[0] : options.method === "*" ? "GET" : options.method),
|
|
460
|
+
setHeader: (key, value) => {
|
|
461
|
+
headers.set(key, value);
|
|
462
|
+
},
|
|
463
|
+
getHeader: (key) => {
|
|
464
|
+
if (!requestHeaders) return null;
|
|
465
|
+
return requestHeaders.get(key);
|
|
466
|
+
},
|
|
467
|
+
getCookie: (key, prefix) => {
|
|
468
|
+
const finalKey = getCookieKey(key, prefix);
|
|
469
|
+
if (!finalKey) return null;
|
|
470
|
+
return parsedCookies?.get(finalKey) || null;
|
|
471
|
+
},
|
|
472
|
+
getSignedCookie: async (key, secret, prefix) => {
|
|
473
|
+
const finalKey = getCookieKey(key, prefix);
|
|
474
|
+
if (!finalKey) return null;
|
|
475
|
+
const value = parsedCookies?.get(finalKey);
|
|
476
|
+
if (!value) return null;
|
|
477
|
+
const signatureStartPos = value.lastIndexOf(".");
|
|
478
|
+
if (signatureStartPos < 1) return null;
|
|
479
|
+
const signedValue = value.substring(0, signatureStartPos);
|
|
480
|
+
const signature = value.substring(signatureStartPos + 1);
|
|
481
|
+
if (signature.length !== 44 || !signature.endsWith("=")) return null;
|
|
482
|
+
return await verifySignature(signature, signedValue, await getCryptoKey(secret)) ? signedValue : false;
|
|
483
|
+
},
|
|
484
|
+
setCookie: (key, value, options$1) => {
|
|
485
|
+
const cookie = serializeCookie(key, value, options$1);
|
|
486
|
+
headers.append("set-cookie", cookie);
|
|
487
|
+
return cookie;
|
|
488
|
+
},
|
|
489
|
+
setSignedCookie: async (key, value, secret, options$1) => {
|
|
490
|
+
const cookie = await serializeSignedCookie(key, value, secret, options$1);
|
|
491
|
+
headers.append("set-cookie", cookie);
|
|
492
|
+
return cookie;
|
|
493
|
+
},
|
|
494
|
+
redirect: (url) => {
|
|
495
|
+
headers.set("location", url);
|
|
496
|
+
return new APIError("FOUND", void 0, headers);
|
|
497
|
+
},
|
|
498
|
+
error: (status, body, headers$1) => {
|
|
499
|
+
return new APIError(status, body, headers$1);
|
|
500
|
+
},
|
|
501
|
+
setStatus: (status) => {
|
|
502
|
+
responseStatus = status;
|
|
503
|
+
},
|
|
504
|
+
json: (json, routerResponse) => {
|
|
505
|
+
if (!context.asResponse) return json;
|
|
506
|
+
return {
|
|
507
|
+
body: routerResponse?.body || json,
|
|
508
|
+
routerResponse,
|
|
509
|
+
_flag: "json"
|
|
510
|
+
};
|
|
511
|
+
},
|
|
512
|
+
responseHeaders: headers,
|
|
513
|
+
get responseStatus() {
|
|
514
|
+
return responseStatus;
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
for (const middleware of options.use || []) {
|
|
518
|
+
const response = await middleware({
|
|
519
|
+
...internalContext,
|
|
520
|
+
returnHeaders: true,
|
|
521
|
+
asResponse: false
|
|
522
|
+
});
|
|
523
|
+
if (response.response) Object.assign(internalContext.context, response.response);
|
|
524
|
+
/**
|
|
525
|
+
* Apply headers from the middleware to the endpoint headers
|
|
526
|
+
*/
|
|
527
|
+
if (response.headers) response.headers.forEach((value, key) => {
|
|
528
|
+
internalContext.responseHeaders.set(key, value);
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
return internalContext;
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
//#endregion
|
|
535
|
+
//#region ../../node_modules/better-call/dist/endpoint.mjs
|
|
536
|
+
function createEndpoint(pathOrOptions, handlerOrOptions, handlerOrNever) {
|
|
537
|
+
const path = typeof pathOrOptions === "string" ? pathOrOptions : void 0;
|
|
538
|
+
const options = typeof handlerOrOptions === "object" ? handlerOrOptions : pathOrOptions;
|
|
539
|
+
const handler = typeof handlerOrOptions === "function" ? handlerOrOptions : handlerOrNever;
|
|
540
|
+
if ((options.method === "GET" || options.method === "HEAD") && options.body) throw new BetterCallError("Body is not allowed with GET or HEAD methods");
|
|
541
|
+
if (path && /\/{2,}/.test(path)) throw new BetterCallError("Path cannot contain consecutive slashes");
|
|
542
|
+
const internalHandler = async (...inputCtx) => {
|
|
543
|
+
const context = inputCtx[0] || {};
|
|
544
|
+
const { data: internalContext, error: validationError } = await tryCatch(createInternalContext(context, {
|
|
545
|
+
options,
|
|
546
|
+
path
|
|
547
|
+
}));
|
|
548
|
+
if (validationError) {
|
|
549
|
+
if (!(validationError instanceof ValidationError)) throw validationError;
|
|
550
|
+
if (options.onValidationError) await options.onValidationError({
|
|
551
|
+
message: validationError.message,
|
|
552
|
+
issues: validationError.issues
|
|
553
|
+
});
|
|
554
|
+
throw new APIError(400, {
|
|
555
|
+
message: validationError.message,
|
|
556
|
+
code: "VALIDATION_ERROR"
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
const response = await handler(internalContext).catch(async (e) => {
|
|
560
|
+
if (isAPIError(e)) {
|
|
561
|
+
const onAPIError = options.onAPIError;
|
|
562
|
+
if (onAPIError) await onAPIError(e);
|
|
563
|
+
if (context.asResponse) return e;
|
|
564
|
+
}
|
|
565
|
+
throw e;
|
|
566
|
+
});
|
|
567
|
+
const headers = internalContext.responseHeaders;
|
|
568
|
+
const status = internalContext.responseStatus;
|
|
569
|
+
return context.asResponse ? toResponse(response, {
|
|
570
|
+
headers,
|
|
571
|
+
status
|
|
572
|
+
}) : context.returnHeaders ? context.returnStatus ? {
|
|
573
|
+
headers,
|
|
574
|
+
response,
|
|
575
|
+
status
|
|
576
|
+
} : {
|
|
577
|
+
headers,
|
|
578
|
+
response
|
|
579
|
+
} : context.returnStatus ? {
|
|
580
|
+
response,
|
|
581
|
+
status
|
|
582
|
+
} : response;
|
|
583
|
+
};
|
|
584
|
+
internalHandler.options = options;
|
|
585
|
+
internalHandler.path = path;
|
|
586
|
+
return internalHandler;
|
|
587
|
+
}
|
|
588
|
+
createEndpoint.create = (opts) => {
|
|
589
|
+
return (path, options, handler) => {
|
|
590
|
+
return createEndpoint(path, {
|
|
591
|
+
...options,
|
|
592
|
+
use: [...options?.use || [], ...opts?.use || []]
|
|
593
|
+
}, handler);
|
|
594
|
+
};
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
//#endregion
|
|
598
|
+
//#region ../../node_modules/better-call/dist/middleware.mjs
|
|
599
|
+
function createMiddleware(optionsOrHandler, handler) {
|
|
600
|
+
const internalHandler = async (inputCtx) => {
|
|
601
|
+
const context = inputCtx;
|
|
602
|
+
const _handler = typeof optionsOrHandler === "function" ? optionsOrHandler : handler;
|
|
603
|
+
const internalContext = await createInternalContext(context, {
|
|
604
|
+
options: typeof optionsOrHandler === "function" ? {} : optionsOrHandler,
|
|
605
|
+
path: "/"
|
|
606
|
+
});
|
|
607
|
+
if (!_handler) throw new Error("handler must be defined");
|
|
608
|
+
const response = await _handler(internalContext);
|
|
609
|
+
const headers = internalContext.responseHeaders;
|
|
610
|
+
return context.returnHeaders ? {
|
|
611
|
+
headers,
|
|
612
|
+
response
|
|
613
|
+
} : response;
|
|
614
|
+
};
|
|
615
|
+
internalHandler.options = typeof optionsOrHandler === "function" ? {} : optionsOrHandler;
|
|
616
|
+
return internalHandler;
|
|
617
|
+
}
|
|
618
|
+
createMiddleware.create = (opts) => {
|
|
619
|
+
function fn(optionsOrHandler, handler) {
|
|
620
|
+
if (typeof optionsOrHandler === "function") return createMiddleware({ use: opts?.use }, optionsOrHandler);
|
|
621
|
+
if (!handler) throw new Error("Middleware handler is required");
|
|
622
|
+
return createMiddleware({
|
|
623
|
+
...optionsOrHandler,
|
|
624
|
+
method: "*",
|
|
625
|
+
use: [...opts?.use || [], ...optionsOrHandler.use || []]
|
|
626
|
+
}, handler);
|
|
627
|
+
}
|
|
628
|
+
return fn;
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
//#endregion
|
|
632
|
+
//#region src/server.ts
|
|
633
|
+
/** @format */
|
|
634
|
+
const ORGANIZATION_ERROR_CODES = defineErrorCodes({
|
|
635
|
+
NO_ACTIVE_ORGANIZATION: "No active organization",
|
|
636
|
+
MEMBER_NOT_FOUND: "Member not found",
|
|
637
|
+
ORGANIZATION_NOT_FOUND: "Organization not found",
|
|
638
|
+
YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER: "You are not allowed to update this member",
|
|
639
|
+
YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER: "You cannot leave the organization without an owner",
|
|
640
|
+
INVITATION_NOT_FOUND: "Invitation not found",
|
|
641
|
+
YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_INVITATION: "You are not allowed to update this invitation",
|
|
642
|
+
ONLY_PENDING_INVITATIONS_CAN_BE_UPDATED: "Only pending invitations can be updated",
|
|
643
|
+
YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION: "You are not a member of this organization"
|
|
644
|
+
});
|
|
645
|
+
const getOrganizationPlugin = (ctx) => {
|
|
646
|
+
return ctx.options.plugins?.find((plugin) => plugin.id === "organization");
|
|
647
|
+
};
|
|
648
|
+
const orgMiddleware = createAuthMiddleware(async () => {
|
|
649
|
+
return {};
|
|
650
|
+
});
|
|
651
|
+
/**
|
|
652
|
+
* The middleware forces the endpoint to require a valid session by utilizing the `sessionMiddleware`.
|
|
653
|
+
* It also appends additional types to the session type regarding organizations.
|
|
654
|
+
*/
|
|
655
|
+
const orgSessionMiddleware = createAuthMiddleware({ use: [sessionMiddleware] }, async (ctx) => {
|
|
656
|
+
return { session: ctx.context.session };
|
|
657
|
+
});
|
|
658
|
+
/**
|
|
659
|
+
* Organization Member Plugin
|
|
660
|
+
*
|
|
661
|
+
* Extends the organization plugin with:
|
|
662
|
+
* - updateMember endpoint to update member fields (not just role)
|
|
663
|
+
* - Automatically adds afterAcceptInvitation hook to transfer invitation data to member
|
|
664
|
+
*
|
|
665
|
+
* @requires organization plugin
|
|
666
|
+
*/
|
|
667
|
+
const createUpdateMemberEndpoint = (options) => {
|
|
668
|
+
const baseMemberSchema = z.object({
|
|
669
|
+
role: z.union([z.string(), z.array(z.string())]).meta({ description: "The new role to be applied. This can be a string or array of strings representing the roles. Eg: [\"admin\", \"sale\"]" }),
|
|
670
|
+
memberId: z.string().meta({ description: "The member id to apply the role update to. Eg: \"member-id\"" }),
|
|
671
|
+
organizationId: z.string().meta({ description: "An optional organization ID which the member is a part of to apply the role update. If not provided, you must provide session headers to get the active organization. Eg: \"organization-id\"" }).optional(),
|
|
672
|
+
firstName: z.string().optional().meta({ description: "First name of the member" }),
|
|
673
|
+
lastName: z.string().optional().meta({ description: "Last name of the member" }),
|
|
674
|
+
avatar: z.string().optional().meta({ description: "Avatar URL of the member" }),
|
|
675
|
+
status: z.enum(["active", "inactive"]).optional()
|
|
676
|
+
});
|
|
677
|
+
const { status: _status, ...additionalFieldsWithoutStatus } = toZodSchema({
|
|
678
|
+
fields: options?.schema?.member?.additionalFields || {},
|
|
679
|
+
isClientSide: true
|
|
680
|
+
}).shape;
|
|
681
|
+
return createAuthEndpoint("/organization/update-member", {
|
|
682
|
+
method: "POST",
|
|
683
|
+
body: z.object({
|
|
684
|
+
...baseMemberSchema.shape,
|
|
685
|
+
...additionalFieldsWithoutStatus.shape
|
|
686
|
+
}),
|
|
687
|
+
use: [orgMiddleware, orgSessionMiddleware],
|
|
688
|
+
requireHeaders: true,
|
|
689
|
+
metadata: {
|
|
690
|
+
$Infer: { body: {} },
|
|
691
|
+
openapi: {
|
|
692
|
+
operationId: "updateOrganizationMember",
|
|
693
|
+
description: "Update a member in an organization",
|
|
694
|
+
responses: { "200": {
|
|
695
|
+
description: "Success",
|
|
696
|
+
content: { "application/json": { schema: {
|
|
697
|
+
type: "object",
|
|
698
|
+
properties: {
|
|
699
|
+
id: { type: "string" },
|
|
700
|
+
userId: { type: "string" },
|
|
701
|
+
organizationId: { type: "string" },
|
|
702
|
+
firstName: { type: "string" },
|
|
703
|
+
lastName: { type: "string" },
|
|
704
|
+
avatar: { type: "string" },
|
|
705
|
+
status: { type: "string" }
|
|
706
|
+
},
|
|
707
|
+
required: [
|
|
708
|
+
"id",
|
|
709
|
+
"userId",
|
|
710
|
+
"organizationId",
|
|
711
|
+
"role"
|
|
712
|
+
]
|
|
713
|
+
} } }
|
|
714
|
+
} }
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}, async (ctx) => {
|
|
718
|
+
const session = ctx.context.session;
|
|
719
|
+
const organizationPlugin = getOrganizationPlugin(ctx.context);
|
|
720
|
+
if (!organizationPlugin) throw new Error("organization-member plugin requires the organization plugin");
|
|
721
|
+
if (!ctx.body.role) throw new APIError("BAD_REQUEST");
|
|
722
|
+
const organizationId = ctx.body.organizationId || session.session.activeOrganizationId;
|
|
723
|
+
if (!organizationId) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION });
|
|
724
|
+
const adapter = getOrgAdapter(ctx.context, organizationPlugin.options);
|
|
725
|
+
const roleToSet = Array.isArray(ctx.body.role) ? ctx.body.role : ctx.body.role ? [ctx.body.role] : [];
|
|
726
|
+
const member = await adapter.findMemberByOrgId({
|
|
727
|
+
userId: session.user.id,
|
|
728
|
+
organizationId
|
|
729
|
+
});
|
|
730
|
+
if (!member) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND });
|
|
731
|
+
const toBeUpdatedMember = member.id !== ctx.body.memberId ? await adapter.findMemberById(ctx.body.memberId) : member;
|
|
732
|
+
if (!toBeUpdatedMember) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND });
|
|
733
|
+
if (!(toBeUpdatedMember.organizationId === organizationId)) throw new APIError("FORBIDDEN", { message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER });
|
|
734
|
+
const creatorRole = organizationPlugin.options?.creatorRole || "owner";
|
|
735
|
+
const updatingMemberRoles = member.role.split(",");
|
|
736
|
+
const isUpdatingCreator = toBeUpdatedMember.role.split(",").includes(creatorRole);
|
|
737
|
+
const updaterIsCreator = updatingMemberRoles.includes(creatorRole);
|
|
738
|
+
const isSettingCreatorRole = roleToSet.includes(creatorRole);
|
|
739
|
+
const memberIsUpdatingThemselves = member.id === toBeUpdatedMember.id;
|
|
740
|
+
if (isUpdatingCreator && !updaterIsCreator || isSettingCreatorRole && !updaterIsCreator) throw new APIError("FORBIDDEN", { message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER });
|
|
741
|
+
if (updaterIsCreator && memberIsUpdatingThemselves) {
|
|
742
|
+
if ((await ctx.context.adapter.findMany({
|
|
743
|
+
model: "member",
|
|
744
|
+
where: [{
|
|
745
|
+
field: "organizationId",
|
|
746
|
+
value: organizationId
|
|
747
|
+
}]
|
|
748
|
+
})).filter((member$1) => {
|
|
749
|
+
return member$1.role.split(",").includes(creatorRole);
|
|
750
|
+
}).length <= 1 && !isSettingCreatorRole) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER });
|
|
751
|
+
}
|
|
752
|
+
if (!clientSideHasPermission({
|
|
753
|
+
role: member.role,
|
|
754
|
+
options: organizationPlugin.options,
|
|
755
|
+
permissions: { member: ["update"] },
|
|
756
|
+
allowCreatorAllPermissions: true
|
|
757
|
+
})) throw new APIError("FORBIDDEN", { message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER });
|
|
758
|
+
const organization = await adapter.findOrganizationById(organizationId);
|
|
759
|
+
if (!organization) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND });
|
|
760
|
+
const userBeingUpdated = await ctx.context.internalAdapter.findUserById(toBeUpdatedMember.userId);
|
|
761
|
+
if (!userBeingUpdated) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.USER_NOT_FOUND });
|
|
762
|
+
const { memberId: _, organizationId: __, ...updates } = ctx.body;
|
|
763
|
+
let finalUpdates = updates;
|
|
764
|
+
if (options?.organizationMemberHooks?.beforeUpdateMember) {
|
|
765
|
+
const response = await options.organizationMemberHooks.beforeUpdateMember({
|
|
766
|
+
member: toBeUpdatedMember,
|
|
767
|
+
updates,
|
|
768
|
+
user: userBeingUpdated,
|
|
769
|
+
organization
|
|
770
|
+
});
|
|
771
|
+
if (response && typeof response === "object" && "data" in response) finalUpdates = response.data;
|
|
772
|
+
}
|
|
773
|
+
const updatedMember = await ctx.context.adapter.update({
|
|
774
|
+
model: "member",
|
|
775
|
+
where: [{
|
|
776
|
+
field: "id",
|
|
777
|
+
value: ctx.body.memberId
|
|
778
|
+
}],
|
|
779
|
+
update: finalUpdates
|
|
780
|
+
});
|
|
781
|
+
if (!updatedMember) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND });
|
|
782
|
+
if (options?.organizationMemberHooks?.afterUpdateMember) await options.organizationMemberHooks.afterUpdateMember({
|
|
783
|
+
member: updatedMember,
|
|
784
|
+
user: userBeingUpdated,
|
|
785
|
+
organization
|
|
786
|
+
});
|
|
787
|
+
return ctx.json(updatedMember);
|
|
788
|
+
});
|
|
789
|
+
};
|
|
790
|
+
const createGetMemberEndpoint = () => {
|
|
791
|
+
return createAuthEndpoint("/organization/get-member", {
|
|
792
|
+
method: "GET",
|
|
793
|
+
query: z.object({
|
|
794
|
+
userId: z.string().meta({ description: "The user id of the member to retrieve. Eg: \"user-id\"" }).optional(),
|
|
795
|
+
organizationId: z.string().meta({ description: "The organization ID which the member belongs to. If not provided, will default to the user's active organization. Eg: \"organization-id\"" }).optional(),
|
|
796
|
+
organizationSlug: z.string().meta({ description: "The organization slug to get member for. If not provided, will default to the user's active organization. Eg: \"organization-slug\"" }).optional()
|
|
797
|
+
}),
|
|
798
|
+
use: [orgMiddleware, orgSessionMiddleware],
|
|
799
|
+
requireHeaders: true,
|
|
800
|
+
metadata: {
|
|
801
|
+
$Infer: { query: {} },
|
|
802
|
+
openapi: {
|
|
803
|
+
operationId: "getOrganizationMember",
|
|
804
|
+
description: "Get a member from an organization by user ID",
|
|
805
|
+
responses: { "200": {
|
|
806
|
+
description: "Success",
|
|
807
|
+
content: { "application/json": { schema: {
|
|
808
|
+
type: "object",
|
|
809
|
+
properties: {
|
|
810
|
+
id: { type: "string" },
|
|
811
|
+
userId: { type: "string" },
|
|
812
|
+
organizationId: { type: "string" },
|
|
813
|
+
role: { type: "string" },
|
|
814
|
+
firstName: { type: "string" },
|
|
815
|
+
lastName: { type: "string" },
|
|
816
|
+
avatar: { type: "string" },
|
|
817
|
+
status: { type: "string" },
|
|
818
|
+
createdAt: {
|
|
819
|
+
type: "string",
|
|
820
|
+
format: "date-time"
|
|
821
|
+
}
|
|
822
|
+
},
|
|
823
|
+
required: [
|
|
824
|
+
"id",
|
|
825
|
+
"userId",
|
|
826
|
+
"organizationId",
|
|
827
|
+
"role"
|
|
828
|
+
]
|
|
829
|
+
} } }
|
|
830
|
+
} }
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}, async (ctx) => {
|
|
834
|
+
const session = ctx.context.session;
|
|
835
|
+
const organizationPlugin = getOrganizationPlugin(ctx.context);
|
|
836
|
+
if (!organizationPlugin) throw new Error("organization-member plugin requires the organization plugin");
|
|
837
|
+
const adapter = getOrgAdapter(ctx.context, organizationPlugin.options);
|
|
838
|
+
let organizationId = ctx.query?.organizationId || session.session.activeOrganizationId;
|
|
839
|
+
const userId = ctx.query.userId || session.user.id;
|
|
840
|
+
if (!userId) throw new APIError("BAD_REQUEST", { message: BASE_ERROR_CODES.USER_NOT_FOUND });
|
|
841
|
+
if (ctx.query?.organizationSlug) {
|
|
842
|
+
const organization = await adapter.findOrganizationBySlug(ctx.query.organizationSlug);
|
|
843
|
+
if (!organization) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND });
|
|
844
|
+
organizationId = organization.id;
|
|
845
|
+
}
|
|
846
|
+
if (!organizationId) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION });
|
|
847
|
+
const currentUserMember = await adapter.findMemberByOrgId({
|
|
848
|
+
userId: session.user.id,
|
|
849
|
+
organizationId
|
|
850
|
+
});
|
|
851
|
+
if (!currentUserMember) throw new APIError("FORBIDDEN", { message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION });
|
|
852
|
+
if (!ctx.query.userId && (ctx.query.organizationSlug || ctx.query.organizationId)) return ctx.json(currentUserMember);
|
|
853
|
+
const requestedMember = await adapter.findMemberByOrgId({
|
|
854
|
+
userId,
|
|
855
|
+
organizationId
|
|
856
|
+
});
|
|
857
|
+
if (!requestedMember) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND });
|
|
858
|
+
return ctx.json(requestedMember);
|
|
859
|
+
});
|
|
860
|
+
};
|
|
861
|
+
const createUpdateInvitationEndpoint = (options) => {
|
|
862
|
+
const baseInvitationSchema = z.object({
|
|
863
|
+
invitationId: z.string().meta({ description: "The invitation id to update. Eg: \"invitation-id\"" }),
|
|
864
|
+
organizationId: z.string().meta({ description: "An optional organization ID which the invitation belongs to. If not provided, you must provide session headers to get the active organization. Eg: \"organization-id\"" }).optional()
|
|
865
|
+
});
|
|
866
|
+
const additionalFieldsSchema = toZodSchema({
|
|
867
|
+
fields: options?.schema?.invitation?.additionalFields || {},
|
|
868
|
+
isClientSide: true
|
|
869
|
+
});
|
|
870
|
+
return createAuthEndpoint("/organization/update-invitation", {
|
|
871
|
+
method: "POST",
|
|
872
|
+
body: z.object({
|
|
873
|
+
...baseInvitationSchema.shape,
|
|
874
|
+
...additionalFieldsSchema.shape,
|
|
875
|
+
role: z.union([z.string(), z.array(z.string())]).optional().meta({ description: "The new role to be applied. This can be a string or array of strings representing the roles. Eg: [\"admin\", \"sale\"]" })
|
|
876
|
+
}),
|
|
877
|
+
use: [orgMiddleware, orgSessionMiddleware],
|
|
878
|
+
requireHeaders: true,
|
|
879
|
+
metadata: {
|
|
880
|
+
$Infer: { body: {} },
|
|
881
|
+
openapi: {
|
|
882
|
+
operationId: "updateOrganizationInvitation",
|
|
883
|
+
description: "Update a pending invitation in an organization",
|
|
884
|
+
responses: { "200": {
|
|
885
|
+
description: "Success",
|
|
886
|
+
content: { "application/json": { schema: {
|
|
887
|
+
type: "object",
|
|
888
|
+
properties: {
|
|
889
|
+
id: { type: "string" },
|
|
890
|
+
organizationId: { type: "string" },
|
|
891
|
+
email: { type: "string" },
|
|
892
|
+
role: { type: "string" },
|
|
893
|
+
status: { type: "string" },
|
|
894
|
+
createdAt: {
|
|
895
|
+
type: "string",
|
|
896
|
+
format: "date-time",
|
|
897
|
+
description: "Timestamp when the team was created"
|
|
898
|
+
},
|
|
899
|
+
updatedAt: {
|
|
900
|
+
type: "string",
|
|
901
|
+
format: "date-time",
|
|
902
|
+
description: "Timestamp when the team was last updated"
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
required: [
|
|
906
|
+
"id",
|
|
907
|
+
"organizationId",
|
|
908
|
+
"email",
|
|
909
|
+
"role",
|
|
910
|
+
"status"
|
|
911
|
+
]
|
|
912
|
+
} } }
|
|
913
|
+
} }
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}, async (ctx) => {
|
|
917
|
+
const session = ctx.context.session;
|
|
918
|
+
const organizationPlugin = getOrganizationPlugin(ctx.context);
|
|
919
|
+
if (!organizationPlugin) throw new Error("organization-member plugin requires the organization plugin");
|
|
920
|
+
const organizationId = ctx.body.organizationId || session.session.activeOrganizationId;
|
|
921
|
+
if (!organizationId) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION });
|
|
922
|
+
const adapter = getOrgAdapter(ctx.context, organizationPlugin.options);
|
|
923
|
+
const member = await adapter.findMemberByOrgId({
|
|
924
|
+
userId: session.user.id,
|
|
925
|
+
organizationId
|
|
926
|
+
});
|
|
927
|
+
if (!member) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.MEMBER_NOT_FOUND });
|
|
928
|
+
if (!clientSideHasPermission({
|
|
929
|
+
role: member.role,
|
|
930
|
+
options: organizationPlugin.options,
|
|
931
|
+
permissions: { invitation: ["create"] },
|
|
932
|
+
allowCreatorAllPermissions: true
|
|
933
|
+
})) throw new APIError("FORBIDDEN", { message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_INVITATION });
|
|
934
|
+
const invitation = await adapter.findInvitationById(ctx.body.invitationId);
|
|
935
|
+
if (!invitation) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.INVITATION_NOT_FOUND });
|
|
936
|
+
if (invitation.organizationId !== organizationId) throw new APIError("FORBIDDEN", { message: ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_INVITATION });
|
|
937
|
+
if (invitation.status !== "pending") throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.ONLY_PENDING_INVITATIONS_CAN_BE_UPDATED });
|
|
938
|
+
const organization = await adapter.findOrganizationById(organizationId);
|
|
939
|
+
if (!organization) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND });
|
|
940
|
+
const { invitationId: _, organizationId: __, ...updates } = ctx.body;
|
|
941
|
+
let finalUpdates = updates;
|
|
942
|
+
if (options?.organizationMemberHooks?.beforeUpdateInvitation) {
|
|
943
|
+
const response = await options.organizationMemberHooks.beforeUpdateInvitation({
|
|
944
|
+
invitation,
|
|
945
|
+
updates,
|
|
946
|
+
inviter: session.user,
|
|
947
|
+
organization
|
|
948
|
+
});
|
|
949
|
+
if (response && typeof response === "object" && "data" in response) finalUpdates = response.data;
|
|
950
|
+
}
|
|
951
|
+
const updatedInvitation = await ctx.context.adapter.update({
|
|
952
|
+
model: "invitation",
|
|
953
|
+
where: [{
|
|
954
|
+
field: "id",
|
|
955
|
+
value: ctx.body.invitationId
|
|
956
|
+
}],
|
|
957
|
+
update: finalUpdates
|
|
958
|
+
});
|
|
959
|
+
if (!updatedInvitation) throw new APIError("BAD_REQUEST", { message: ORGANIZATION_ERROR_CODES.INVITATION_NOT_FOUND });
|
|
960
|
+
if (options?.organizationMemberHooks?.afterUpdateInvitation) await options.organizationMemberHooks.afterUpdateInvitation({
|
|
961
|
+
invitation: updatedInvitation,
|
|
962
|
+
inviter: session.user,
|
|
963
|
+
organization
|
|
964
|
+
});
|
|
965
|
+
return ctx.json(updatedInvitation);
|
|
966
|
+
});
|
|
967
|
+
};
|
|
968
|
+
const organizationMember = (userConfig) => {
|
|
969
|
+
({ ...userConfig });
|
|
970
|
+
return {
|
|
971
|
+
id: "organization-member",
|
|
972
|
+
init(ctx) {
|
|
973
|
+
const organizationPlugin = getOrganizationPlugin(ctx);
|
|
974
|
+
if (!organizationPlugin) throw new Error("organization-member plugin requires the organization plugin");
|
|
975
|
+
if (!organizationPlugin.options.organizationHooks) organizationPlugin.options.organizationHooks = {};
|
|
976
|
+
const existingAfterAcceptInvitation = organizationPlugin.options.organizationHooks?.afterAcceptInvitation;
|
|
977
|
+
organizationPlugin.options.organizationHooks.afterAcceptInvitation = async (data) => {
|
|
978
|
+
await existingAfterAcceptInvitation?.(data);
|
|
979
|
+
try {
|
|
980
|
+
const { invitation, member } = data;
|
|
981
|
+
const updateData = {};
|
|
982
|
+
for (const field of Object.keys(organizationPlugin.options?.schema?.invitation?.additionalFields ?? {})) {
|
|
983
|
+
const fieldName = organizationPlugin.options?.schema?.invitation?.additionalFields?.[field]?.fieldName || field;
|
|
984
|
+
updateData[fieldName] = invitation[fieldName];
|
|
985
|
+
}
|
|
986
|
+
if (Object.keys(updateData).length > 0) {
|
|
987
|
+
await ctx.adapter.update({
|
|
988
|
+
model: "member",
|
|
989
|
+
where: [{
|
|
990
|
+
field: "id",
|
|
991
|
+
value: member.id
|
|
992
|
+
}],
|
|
993
|
+
update: updateData
|
|
994
|
+
});
|
|
995
|
+
ctx.logger?.info("Member fields updated from invitation:", {
|
|
996
|
+
memberId: member.id,
|
|
997
|
+
fields: Object.keys(updateData)
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
ctx.logger?.error("Failed to update member fields from invitation:", error);
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
},
|
|
1005
|
+
endpoints: {
|
|
1006
|
+
getMember: createGetMemberEndpoint(),
|
|
1007
|
+
updateMember: createUpdateMemberEndpoint(userConfig),
|
|
1008
|
+
updateInvitation: createUpdateInvitationEndpoint(userConfig)
|
|
1009
|
+
},
|
|
1010
|
+
options: userConfig,
|
|
1011
|
+
$ERROR_CODES: ORGANIZATION_ERROR_CODES
|
|
1012
|
+
};
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
//#endregion
|
|
1016
|
+
export { orgSessionMiddleware as n, organizationMember as r, orgMiddleware as t };
|
|
1017
|
+
//# sourceMappingURL=server-Big_p9zG.mjs.map
|