@stackframe/stack-shared 2.8.2 → 2.8.5
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/CHANGELOG.md +18 -0
- package/dist/config/format.d.ts +38 -0
- package/dist/config/format.js +224 -0
- package/dist/config/schema.d.ts +721 -0
- package/dist/config/schema.js +185 -0
- package/dist/crud.js +1 -1
- package/dist/interface/adminInterface.d.ts +7 -7
- package/dist/interface/adminInterface.js +7 -7
- package/dist/interface/clientInterface.d.ts +46 -4
- package/dist/interface/clientInterface.js +66 -2
- package/dist/interface/crud/current-user.d.ts +2 -2
- package/dist/interface/crud/{api-keys.d.ts → internal-api-keys.d.ts} +7 -7
- package/dist/interface/crud/{api-keys.js → internal-api-keys.js} +10 -10
- package/dist/interface/crud/project-api-keys.d.ts +188 -0
- package/dist/interface/crud/project-api-keys.js +76 -0
- package/dist/interface/crud/projects.d.ts +53 -20
- package/dist/interface/crud/projects.js +15 -5
- package/dist/interface/crud/team-member-profiles.d.ts +4 -4
- package/dist/interface/crud/users.d.ts +8 -8
- package/dist/interface/serverInterface.d.ts +2 -1
- package/dist/interface/serverInterface.js +4 -0
- package/dist/interface/webhooks.d.ts +2 -2
- package/dist/known-errors.d.ts +35 -1
- package/dist/known-errors.js +42 -4
- package/dist/schema-fields.d.ts +6 -5
- package/dist/schema-fields.js +36 -3
- package/dist/utils/api-keys.d.ts +23 -0
- package/dist/utils/api-keys.js +76 -0
- package/dist/utils/bytes.d.ts +3 -0
- package/dist/utils/bytes.js +55 -6
- package/dist/utils/caches.d.ts +10 -10
- package/dist/utils/hashes.d.ts +1 -1
- package/dist/utils/hashes.js +1 -3
- package/dist/utils/objects.d.ts +35 -2
- package/dist/utils/objects.js +128 -0
- package/dist/utils/promises.d.ts +1 -1
- package/dist/utils/promises.js +9 -10
- package/dist/utils/stores.d.ts +6 -6
- package/dist/utils/types.d.ts +17 -0
- package/package.json +2 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import * as schemaFields from "../schema-fields";
|
|
2
|
+
import { yupBoolean, yupObject, yupRecord, yupString } from "../schema-fields";
|
|
3
|
+
import { allProviders } from "../utils/oauth";
|
|
4
|
+
import { get, has, isObjectLike, set } from "../utils/objects";
|
|
5
|
+
// NOTE: The validation schemas in here are all schematic validators, not sanity-check validators.
|
|
6
|
+
// For more info, see ./README.md
|
|
7
|
+
export const configLevels = ['project', 'branch', 'environment', 'organization'];
|
|
8
|
+
const permissionRegex = /^\$?[a-z0-9_:]+$/;
|
|
9
|
+
/**
|
|
10
|
+
* All fields that can be overridden at this level.
|
|
11
|
+
*/
|
|
12
|
+
export const projectConfigSchema = yupObject({});
|
|
13
|
+
// --- NEW RBAC Schema ---
|
|
14
|
+
const branchRbacDefaultPermissions = yupRecord(yupString().optional().matches(permissionRegex), yupBoolean().isTrue().optional()).optional();
|
|
15
|
+
const branchRbacSchema = yupObject({
|
|
16
|
+
permissions: yupRecord(yupString().optional().matches(permissionRegex), yupObject({
|
|
17
|
+
description: yupString().optional(),
|
|
18
|
+
scope: yupString().oneOf(['team', 'project']).optional(),
|
|
19
|
+
containedPermissionIds: yupRecord(yupString().optional().matches(permissionRegex), yupBoolean().isTrue().optional()).optional(),
|
|
20
|
+
}).optional()).optional(),
|
|
21
|
+
defaultPermissions: yupObject({
|
|
22
|
+
teamCreator: branchRbacDefaultPermissions,
|
|
23
|
+
teamMember: branchRbacDefaultPermissions,
|
|
24
|
+
signUp: branchRbacDefaultPermissions,
|
|
25
|
+
}).optional(),
|
|
26
|
+
}).optional();
|
|
27
|
+
// --- END NEW RBAC Schema ---
|
|
28
|
+
// --- NEW API Keys Schema ---
|
|
29
|
+
const branchApiKeysSchema = yupObject({
|
|
30
|
+
enabled: yupObject({
|
|
31
|
+
team: yupBoolean().optional(),
|
|
32
|
+
user: yupBoolean().optional(),
|
|
33
|
+
}).optional(),
|
|
34
|
+
}).optional();
|
|
35
|
+
// --- END NEW API Keys Schema ---
|
|
36
|
+
const branchAuthSchema = yupObject({
|
|
37
|
+
allowSignUp: yupBoolean().optional(),
|
|
38
|
+
password: yupObject({
|
|
39
|
+
allowSignIn: yupBoolean().optional(),
|
|
40
|
+
}).optional(),
|
|
41
|
+
otp: yupObject({
|
|
42
|
+
allowSignIn: yupBoolean().optional(),
|
|
43
|
+
}).optional(),
|
|
44
|
+
passkey: yupObject({
|
|
45
|
+
allowSignIn: yupBoolean().optional(),
|
|
46
|
+
}).optional(),
|
|
47
|
+
oauth: yupObject({
|
|
48
|
+
accountMergeStrategy: yupString().oneOf(['link_method', 'raise_error', 'allow_duplicates']).optional(),
|
|
49
|
+
providers: yupRecord(yupString().optional().matches(permissionRegex), yupObject({
|
|
50
|
+
type: yupString().oneOf(allProviders).optional(),
|
|
51
|
+
allowSignIn: yupBoolean().optional(),
|
|
52
|
+
allowConnectedAccounts: yupBoolean().optional(),
|
|
53
|
+
}).defined()).optional(),
|
|
54
|
+
}).optional(),
|
|
55
|
+
}).optional();
|
|
56
|
+
const branchDomain = yupObject({
|
|
57
|
+
allowLocalhost: yupBoolean().optional(),
|
|
58
|
+
}).optional();
|
|
59
|
+
export const branchConfigSchema = projectConfigSchema.concat(yupObject({
|
|
60
|
+
rbac: branchRbacSchema,
|
|
61
|
+
teams: yupObject({
|
|
62
|
+
createPersonalTeamOnSignUp: yupBoolean().optional(),
|
|
63
|
+
allowClientTeamCreation: yupBoolean().optional(),
|
|
64
|
+
}).optional(),
|
|
65
|
+
users: yupObject({
|
|
66
|
+
allowClientUserDeletion: yupBoolean().optional(),
|
|
67
|
+
}).optional(),
|
|
68
|
+
apiKeys: branchApiKeysSchema,
|
|
69
|
+
domains: branchDomain,
|
|
70
|
+
auth: branchAuthSchema,
|
|
71
|
+
emails: yupObject({}),
|
|
72
|
+
}));
|
|
73
|
+
export const environmentConfigSchema = branchConfigSchema.concat(yupObject({
|
|
74
|
+
auth: branchConfigSchema.getNested("auth").concat(yupObject({
|
|
75
|
+
oauth: branchConfigSchema.getNested("auth").getNested("oauth").concat(yupObject({
|
|
76
|
+
providers: yupRecord(yupString().optional().matches(permissionRegex), yupObject({
|
|
77
|
+
type: yupString().oneOf(allProviders).optional(),
|
|
78
|
+
isShared: yupBoolean().optional(),
|
|
79
|
+
clientId: schemaFields.oauthClientIdSchema.optional(),
|
|
80
|
+
clientSecret: schemaFields.oauthClientSecretSchema.optional(),
|
|
81
|
+
facebookConfigId: schemaFields.oauthFacebookConfigIdSchema.optional(),
|
|
82
|
+
microsoftTenantId: schemaFields.oauthMicrosoftTenantIdSchema.optional(),
|
|
83
|
+
allowSignIn: yupBoolean().optional(),
|
|
84
|
+
allowConnectedAccounts: yupBoolean().optional(),
|
|
85
|
+
})).optional(),
|
|
86
|
+
}).optional()),
|
|
87
|
+
})),
|
|
88
|
+
emails: branchConfigSchema.getNested("emails").concat(yupObject({
|
|
89
|
+
server: yupObject({
|
|
90
|
+
isShared: yupBoolean().optional(),
|
|
91
|
+
host: schemaFields.emailHostSchema.optional().nonEmpty(),
|
|
92
|
+
port: schemaFields.emailPortSchema.optional(),
|
|
93
|
+
username: schemaFields.emailUsernameSchema.optional().nonEmpty(),
|
|
94
|
+
password: schemaFields.emailPasswordSchema.optional().nonEmpty(),
|
|
95
|
+
senderName: schemaFields.emailSenderNameSchema.optional().nonEmpty(),
|
|
96
|
+
senderEmail: schemaFields.emailSenderEmailSchema.optional().nonEmpty(),
|
|
97
|
+
}),
|
|
98
|
+
}).optional()),
|
|
99
|
+
domains: branchConfigSchema.getNested("domains").concat(yupObject({
|
|
100
|
+
trustedDomains: yupRecord(yupString().uuid().optional(), yupObject({
|
|
101
|
+
baseUrl: schemaFields.urlSchema.optional(),
|
|
102
|
+
handlerPath: schemaFields.handlerPathSchema.optional(),
|
|
103
|
+
})).optional(),
|
|
104
|
+
})),
|
|
105
|
+
}));
|
|
106
|
+
export const organizationConfigSchema = environmentConfigSchema.concat(yupObject({}));
|
|
107
|
+
// Defaults
|
|
108
|
+
// these are objects that are merged together to form the rendered config (see ./README.md)
|
|
109
|
+
// Wherever an object could be used as a value, a function can instead be used to generate the default values on a per-key basis
|
|
110
|
+
export const projectConfigDefaults = {};
|
|
111
|
+
export const branchConfigDefaults = {};
|
|
112
|
+
export const environmentConfigDefaults = {};
|
|
113
|
+
export const organizationConfigDefaults = {
|
|
114
|
+
rbac: {
|
|
115
|
+
permissions: (key) => ({}),
|
|
116
|
+
defaultPermissions: {
|
|
117
|
+
teamCreator: {},
|
|
118
|
+
teamMember: {},
|
|
119
|
+
signUp: {},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
apiKeys: {
|
|
123
|
+
enabled: {
|
|
124
|
+
team: false,
|
|
125
|
+
user: false,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
teams: {
|
|
129
|
+
createPersonalTeamOnSignUp: false,
|
|
130
|
+
allowClientTeamCreation: false,
|
|
131
|
+
},
|
|
132
|
+
users: {
|
|
133
|
+
allowClientUserDeletion: false,
|
|
134
|
+
},
|
|
135
|
+
domains: {
|
|
136
|
+
allowLocalhost: false,
|
|
137
|
+
trustedDomains: (key) => ({
|
|
138
|
+
handlerPath: '/handler',
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
auth: {
|
|
142
|
+
allowSignUp: true,
|
|
143
|
+
password: {
|
|
144
|
+
allowSignIn: false,
|
|
145
|
+
},
|
|
146
|
+
otp: {
|
|
147
|
+
allowSignIn: false,
|
|
148
|
+
},
|
|
149
|
+
passkey: {
|
|
150
|
+
allowSignIn: false,
|
|
151
|
+
},
|
|
152
|
+
oauth: {
|
|
153
|
+
accountMergeStrategy: 'link_method',
|
|
154
|
+
providers: (key) => ({
|
|
155
|
+
allowSignIn: false,
|
|
156
|
+
allowConnectedAccounts: false,
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
emails: {
|
|
161
|
+
server: {
|
|
162
|
+
isShared: true,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
export function applyDefaults(defaults, config) {
|
|
167
|
+
const res = { ...typeof defaults === 'function' ? {} : defaults };
|
|
168
|
+
for (const [key, mergeValue] of Object.entries(config)) {
|
|
169
|
+
const baseValue = typeof defaults === 'function' ? defaults(key) : (has(defaults, key) ? get(defaults, key) : undefined);
|
|
170
|
+
if (baseValue !== undefined) {
|
|
171
|
+
if (isObjectLike(baseValue) && isObjectLike(mergeValue)) {
|
|
172
|
+
set(res, key, applyDefaults(baseValue, mergeValue));
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
set(res, key, mergeValue);
|
|
177
|
+
}
|
|
178
|
+
return res;
|
|
179
|
+
}
|
|
180
|
+
import.meta.vitest?.test("applyDefaults", ({ expect }) => {
|
|
181
|
+
expect(applyDefaults({ a: 1 }, { a: 2 })).toEqual({ a: 2 });
|
|
182
|
+
expect(applyDefaults({ a: { b: 1 } }, { a: { c: 2 } })).toEqual({ a: { b: 1, c: 2 } });
|
|
183
|
+
expect(applyDefaults((key) => ({ b: key }), { a: {} })).toEqual({ a: { b: "a" } });
|
|
184
|
+
expect(applyDefaults({ a: (key) => ({ b: key }) }, { a: { c: { d: 1 } } })).toEqual({ a: { c: { b: "c", d: 1 } } });
|
|
185
|
+
});
|
package/dist/crud.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { yupObject, yupString } from './schema-fields';
|
|
1
2
|
import { filterUndefined } from './utils/objects';
|
|
2
3
|
export function createCrud(options) {
|
|
3
4
|
const docs = options.docs ?? {};
|
|
@@ -52,7 +53,6 @@ export function createCrud(options) {
|
|
|
52
53
|
hasDelete: !!admin.deleteSchema,
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
|
-
import { yupObject, yupString } from './schema-fields';
|
|
56
56
|
import.meta.vitest?.test("createCrud", ({ expect }) => {
|
|
57
57
|
// Test with empty options
|
|
58
58
|
const emptyCrud = createCrud({});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InternalSession } from "../sessions";
|
|
2
|
-
import { ApiKeysCrud } from "./crud/api-keys";
|
|
3
2
|
import { EmailTemplateCrud, EmailTemplateType } from "./crud/email-templates";
|
|
4
3
|
import { InternalEmailsCrud } from "./crud/emails";
|
|
4
|
+
import { InternalApiKeysCrud } from "./crud/internal-api-keys";
|
|
5
5
|
import { ProjectPermissionDefinitionsCrud } from "./crud/project-permissions";
|
|
6
6
|
import { ProjectsCrud } from "./crud/projects";
|
|
7
7
|
import { SvixTokenCrud } from "./crud/svix-token";
|
|
@@ -12,14 +12,14 @@ export type AdminAuthApplicationOptions = ServerAuthApplicationOptions & ({
|
|
|
12
12
|
} | {
|
|
13
13
|
projectOwnerSession: InternalSession;
|
|
14
14
|
});
|
|
15
|
-
export type
|
|
15
|
+
export type InternalApiKeyCreateCrudRequest = {
|
|
16
16
|
has_publishable_client_key: boolean;
|
|
17
17
|
has_secret_server_key: boolean;
|
|
18
18
|
has_super_secret_admin_key: boolean;
|
|
19
19
|
expires_at_millis: number;
|
|
20
20
|
description: string;
|
|
21
21
|
};
|
|
22
|
-
export type
|
|
22
|
+
export type InternalApiKeyCreateCrudResponse = InternalApiKeysCrud["Admin"]["Read"] & {
|
|
23
23
|
publishable_client_key?: string;
|
|
24
24
|
secret_server_key?: string;
|
|
25
25
|
super_secret_admin_key?: string;
|
|
@@ -35,10 +35,10 @@ export declare class StackAdminInterface extends StackServerInterface {
|
|
|
35
35
|
}>;
|
|
36
36
|
getProject(): Promise<ProjectsCrud["Admin"]["Read"]>;
|
|
37
37
|
updateProject(update: ProjectsCrud["Admin"]["Update"]): Promise<ProjectsCrud["Admin"]["Read"]>;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
createInternalApiKey(options: InternalApiKeyCreateCrudRequest): Promise<InternalApiKeyCreateCrudResponse>;
|
|
39
|
+
listInternalApiKeys(): Promise<InternalApiKeysCrud["Admin"]["Read"][]>;
|
|
40
|
+
revokeInternalApiKeyById(id: string): Promise<void>;
|
|
41
|
+
getInternalApiKey(id: string, session: InternalSession): Promise<InternalApiKeysCrud["Admin"]["Read"]>;
|
|
42
42
|
listEmailTemplates(): Promise<EmailTemplateCrud['Admin']['Read'][]>;
|
|
43
43
|
updateEmailTemplate(type: EmailTemplateType, data: EmailTemplateCrud['Admin']['Update']): Promise<EmailTemplateCrud['Admin']['Read']>;
|
|
44
44
|
resetEmailTemplate(type: EmailTemplateType): Promise<void>;
|
|
@@ -14,13 +14,13 @@ export class StackAdminInterface extends StackServerInterface {
|
|
|
14
14
|
}, session, requestType);
|
|
15
15
|
}
|
|
16
16
|
async getProject() {
|
|
17
|
-
const response = await this.sendAdminRequest("/projects/current", {
|
|
17
|
+
const response = await this.sendAdminRequest("/internal/projects/current", {
|
|
18
18
|
method: "GET",
|
|
19
19
|
}, null);
|
|
20
20
|
return await response.json();
|
|
21
21
|
}
|
|
22
22
|
async updateProject(update) {
|
|
23
|
-
const response = await this.sendAdminRequest("/projects/current", {
|
|
23
|
+
const response = await this.sendAdminRequest("/internal/projects/current", {
|
|
24
24
|
method: "PATCH",
|
|
25
25
|
headers: {
|
|
26
26
|
"content-type": "application/json",
|
|
@@ -29,7 +29,7 @@ export class StackAdminInterface extends StackServerInterface {
|
|
|
29
29
|
}, null);
|
|
30
30
|
return await response.json();
|
|
31
31
|
}
|
|
32
|
-
async
|
|
32
|
+
async createInternalApiKey(options) {
|
|
33
33
|
const response = await this.sendAdminRequest("/internal/api-keys", {
|
|
34
34
|
method: "POST",
|
|
35
35
|
headers: {
|
|
@@ -39,12 +39,12 @@ export class StackAdminInterface extends StackServerInterface {
|
|
|
39
39
|
}, null);
|
|
40
40
|
return await response.json();
|
|
41
41
|
}
|
|
42
|
-
async
|
|
42
|
+
async listInternalApiKeys() {
|
|
43
43
|
const response = await this.sendAdminRequest("/internal/api-keys", {}, null);
|
|
44
44
|
const result = await response.json();
|
|
45
45
|
return result.items;
|
|
46
46
|
}
|
|
47
|
-
async
|
|
47
|
+
async revokeInternalApiKeyById(id) {
|
|
48
48
|
await this.sendAdminRequest(`/internal/api-keys/${id}`, {
|
|
49
49
|
method: "PATCH",
|
|
50
50
|
headers: {
|
|
@@ -55,7 +55,7 @@ export class StackAdminInterface extends StackServerInterface {
|
|
|
55
55
|
}),
|
|
56
56
|
}, null);
|
|
57
57
|
}
|
|
58
|
-
async
|
|
58
|
+
async getInternalApiKey(id, session) {
|
|
59
59
|
const response = await this.sendAdminRequest(`/internal/api-keys/${id}`, {}, session);
|
|
60
60
|
return await response.json();
|
|
61
61
|
}
|
|
@@ -145,7 +145,7 @@ export class StackAdminInterface extends StackServerInterface {
|
|
|
145
145
|
return await response.json();
|
|
146
146
|
}
|
|
147
147
|
async deleteProject() {
|
|
148
|
-
await this.sendAdminRequest("/projects/current", {
|
|
148
|
+
await this.sendAdminRequest("/internal/projects/current", {
|
|
149
149
|
method: "DELETE",
|
|
150
150
|
}, null);
|
|
151
151
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as yup from 'yup';
|
|
1
2
|
import { KnownErrors } from '../known-errors';
|
|
2
3
|
import { AccessToken, InternalSession, RefreshToken } from '../sessions';
|
|
3
4
|
import { ReadonlyJson } from '../utils/json';
|
|
@@ -6,8 +7,9 @@ import { Result } from "../utils/results";
|
|
|
6
7
|
import { ContactChannelsCrud } from './crud/contact-channels';
|
|
7
8
|
import { CurrentUserCrud } from './crud/current-user';
|
|
8
9
|
import { ConnectedAccountAccessTokenCrud } from './crud/oauth';
|
|
10
|
+
import { TeamApiKeysCrud, UserApiKeysCrud, teamApiKeysCreateInputSchema, teamApiKeysCreateOutputSchema, userApiKeysCreateInputSchema, userApiKeysCreateOutputSchema } from './crud/project-api-keys';
|
|
9
11
|
import { ProjectPermissionsCrud } from './crud/project-permissions';
|
|
10
|
-
import {
|
|
12
|
+
import { AdminUserProjectsCrud, ClientProjectsCrud } from './crud/projects';
|
|
11
13
|
import { SessionsCrud } from './crud/sessions';
|
|
12
14
|
import { TeamInvitationCrud } from './crud/team-invitation';
|
|
13
15
|
import { TeamMemberProfilesCrud } from './crud/team-member-profiles';
|
|
@@ -192,10 +194,10 @@ export declare class StackClientInterface {
|
|
|
192
194
|
recursive: boolean;
|
|
193
195
|
}, session: InternalSession): Promise<ProjectPermissionsCrud['Client']['Read'][]>;
|
|
194
196
|
listCurrentUserTeams(session: InternalSession): Promise<TeamsCrud["Client"]["Read"][]>;
|
|
195
|
-
getClientProject(): Promise<Result<
|
|
197
|
+
getClientProject(): Promise<Result<ClientProjectsCrud['Client']['Read'], KnownErrors["ProjectNotFound"]>>;
|
|
196
198
|
updateClientUser(update: CurrentUserCrud["Client"]["Update"], session: InternalSession): Promise<void>;
|
|
197
|
-
listProjects(session: InternalSession): Promise<
|
|
198
|
-
createProject(project:
|
|
199
|
+
listProjects(session: InternalSession): Promise<AdminUserProjectsCrud['Client']['Read'][]>;
|
|
200
|
+
createProject(project: AdminUserProjectsCrud['Client']['Create'], session: InternalSession): Promise<AdminUserProjectsCrud['Client']['Read']>;
|
|
199
201
|
createProviderAccessToken(provider: string, scope: string, session: InternalSession): Promise<ConnectedAccountAccessTokenCrud['Client']['Read']>;
|
|
200
202
|
createClientTeam(data: TeamsCrud['Client']['Create'], session: InternalSession): Promise<TeamsCrud['Client']['Read']>;
|
|
201
203
|
deleteTeam(teamId: string, session: InternalSession): Promise<void>;
|
|
@@ -208,4 +210,44 @@ export declare class StackClientInterface {
|
|
|
208
210
|
listClientContactChannels(session: InternalSession): Promise<ContactChannelsCrud['Client']['Read'][]>;
|
|
209
211
|
sendCurrentUserContactChannelVerificationEmail(contactChannelId: string, callbackUrl: string, session: InternalSession): Promise<Result<undefined, KnownErrors["EmailAlreadyVerified"]>>;
|
|
210
212
|
cliLogin(loginCode: string, refreshToken: string, session: InternalSession): Promise<Result<undefined, KnownErrors["SchemaError"]>>;
|
|
213
|
+
private _getApiKeyRequestInfo;
|
|
214
|
+
listProjectApiKeys(options: {
|
|
215
|
+
user_id: string;
|
|
216
|
+
}, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'][]>;
|
|
217
|
+
listProjectApiKeys(options: {
|
|
218
|
+
team_id: string;
|
|
219
|
+
}, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read'][]>;
|
|
220
|
+
listProjectApiKeys(options: {
|
|
221
|
+
user_id: string;
|
|
222
|
+
} | {
|
|
223
|
+
team_id: string;
|
|
224
|
+
}, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<(UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read'])[]>;
|
|
225
|
+
createProjectApiKey(data: yup.InferType<typeof userApiKeysCreateInputSchema>, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<yup.InferType<typeof userApiKeysCreateOutputSchema>>;
|
|
226
|
+
createProjectApiKey(data: yup.InferType<typeof teamApiKeysCreateInputSchema>, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<yup.InferType<typeof teamApiKeysCreateOutputSchema>>;
|
|
227
|
+
createProjectApiKey(data: yup.InferType<typeof userApiKeysCreateInputSchema> | yup.InferType<typeof teamApiKeysCreateInputSchema>, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<yup.InferType<typeof userApiKeysCreateOutputSchema> | yup.InferType<typeof teamApiKeysCreateOutputSchema>>;
|
|
228
|
+
getProjectApiKey(options: {
|
|
229
|
+
user_id: string | null;
|
|
230
|
+
}, keyId: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read']>;
|
|
231
|
+
getProjectApiKey(options: {
|
|
232
|
+
team_id: string;
|
|
233
|
+
}, keyId: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read']>;
|
|
234
|
+
getProjectApiKey(options: {
|
|
235
|
+
user_id: string | null;
|
|
236
|
+
} | {
|
|
237
|
+
team_id: string;
|
|
238
|
+
}, keyId: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read']>;
|
|
239
|
+
updateProjectApiKey(options: {
|
|
240
|
+
user_id: string;
|
|
241
|
+
}, keyId: string, data: UserApiKeysCrud['Client']['Update'], session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read']>;
|
|
242
|
+
updateProjectApiKey(options: {
|
|
243
|
+
team_id: string;
|
|
244
|
+
}, keyId: string, data: TeamApiKeysCrud['Client']['Update'], session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read']>;
|
|
245
|
+
updateProjectApiKey(options: {
|
|
246
|
+
user_id: string;
|
|
247
|
+
} | {
|
|
248
|
+
team_id: string;
|
|
249
|
+
}, keyId: string, data: UserApiKeysCrud['Client']['Update'] | TeamApiKeysCrud['Client']['Update'], session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read']>;
|
|
250
|
+
checkProjectApiKey(type: "user", apiKey: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | null>;
|
|
251
|
+
checkProjectApiKey(type: "team", apiKey: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read'] | null>;
|
|
252
|
+
checkProjectApiKey(type: "user" | "team", apiKey: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read'] | null>;
|
|
211
253
|
}
|
|
@@ -5,7 +5,7 @@ import { generateSecureRandomString } from '../utils/crypto';
|
|
|
5
5
|
import { StackAssertionError, throwErr } from '../utils/errors';
|
|
6
6
|
import { globalVar } from '../utils/globals';
|
|
7
7
|
import { HTTP_METHODS } from '../utils/http';
|
|
8
|
-
import { filterUndefined } from '../utils/objects';
|
|
8
|
+
import { filterUndefined, filterUndefinedOrNull } from '../utils/objects';
|
|
9
9
|
import { wait } from '../utils/promises';
|
|
10
10
|
import { Result } from "../utils/results";
|
|
11
11
|
import { deindent } from '../utils/strings';
|
|
@@ -173,7 +173,7 @@ export class StackClientInterface {
|
|
|
173
173
|
}
|
|
174
174
|
const params = {
|
|
175
175
|
/**
|
|
176
|
-
* This fetch may
|
|
176
|
+
* This fetch may be cross-origin, in which case we don't want to send cookies of the
|
|
177
177
|
* original origin (this is the default behavior of `credentials`).
|
|
178
178
|
*
|
|
179
179
|
* To help debugging, also omit cookies on same-origin, so we don't accidentally
|
|
@@ -942,4 +942,68 @@ export class StackClientInterface {
|
|
|
942
942
|
}
|
|
943
943
|
return Result.ok(undefined);
|
|
944
944
|
}
|
|
945
|
+
async _getApiKeyRequestInfo(options) {
|
|
946
|
+
if ("user_id" in options && "team_id" in options) {
|
|
947
|
+
throw new StackAssertionError("Cannot specify both user_id and team_id in _getApiKeyRequestInfo");
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
endpoint: "team_id" in options ? "/team-api-keys" : "/user-api-keys",
|
|
951
|
+
queryParams: new URLSearchParams(filterUndefinedOrNull(options)),
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
async listProjectApiKeys(options, session, requestType) {
|
|
955
|
+
const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
|
|
956
|
+
const { endpoint, queryParams } = await this._getApiKeyRequestInfo(options);
|
|
957
|
+
const response = await sendRequest(`${endpoint}?${queryParams.toString()}`, {
|
|
958
|
+
method: "GET",
|
|
959
|
+
}, session, requestType);
|
|
960
|
+
const json = await response.json();
|
|
961
|
+
return json.items;
|
|
962
|
+
}
|
|
963
|
+
async createProjectApiKey(data, session, requestType) {
|
|
964
|
+
const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
|
|
965
|
+
const { endpoint } = await this._getApiKeyRequestInfo(data);
|
|
966
|
+
const response = await sendRequest(`${endpoint}`, {
|
|
967
|
+
method: "POST",
|
|
968
|
+
headers: {
|
|
969
|
+
"content-type": "application/json",
|
|
970
|
+
},
|
|
971
|
+
body: JSON.stringify(data),
|
|
972
|
+
}, session, requestType);
|
|
973
|
+
return await response.json();
|
|
974
|
+
}
|
|
975
|
+
async getProjectApiKey(options, keyId, session, requestType) {
|
|
976
|
+
const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
|
|
977
|
+
const { endpoint, queryParams } = await this._getApiKeyRequestInfo(options);
|
|
978
|
+
const response = await sendRequest(`${endpoint}/${keyId}?${queryParams.toString()}`, {
|
|
979
|
+
method: "GET",
|
|
980
|
+
}, session, requestType);
|
|
981
|
+
return await response.json();
|
|
982
|
+
}
|
|
983
|
+
async updateProjectApiKey(options, keyId, data, session, requestType) {
|
|
984
|
+
const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
|
|
985
|
+
const { endpoint, queryParams } = await this._getApiKeyRequestInfo(options);
|
|
986
|
+
const response = await sendRequest(`${endpoint}/${keyId}?${queryParams.toString()}`, {
|
|
987
|
+
method: "PATCH",
|
|
988
|
+
headers: {
|
|
989
|
+
"content-type": "application/json",
|
|
990
|
+
},
|
|
991
|
+
body: JSON.stringify(data),
|
|
992
|
+
}, session, requestType);
|
|
993
|
+
return await response.json();
|
|
994
|
+
}
|
|
995
|
+
async checkProjectApiKey(type, apiKey, session, requestType) {
|
|
996
|
+
const sendRequest = (requestType === "client" ? this.sendClientRequestAndCatchKnownError : this.sendServerRequestAndCatchKnownError).bind(this);
|
|
997
|
+
const result = await sendRequest(`/${type}-api-keys/check`, {
|
|
998
|
+
method: "POST",
|
|
999
|
+
headers: {
|
|
1000
|
+
"content-type": "application/json",
|
|
1001
|
+
},
|
|
1002
|
+
body: JSON.stringify({ api_key: apiKey }),
|
|
1003
|
+
}, session, [KnownErrors.ApiKeyNotValid]);
|
|
1004
|
+
if (result.status === "error") {
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
return await result.data.json();
|
|
1008
|
+
}
|
|
945
1009
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { CrudTypeOf } from "../../crud";
|
|
2
2
|
export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
3
3
|
clientReadSchema: import("yup").ObjectSchema<{
|
|
4
|
-
primary_email: string | null;
|
|
5
4
|
id: string;
|
|
6
5
|
display_name: string | null;
|
|
7
6
|
oauth_providers: {
|
|
@@ -12,6 +11,7 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
|
|
|
12
11
|
profile_image_url: string | null;
|
|
13
12
|
client_metadata: {} | null;
|
|
14
13
|
client_read_only_metadata: {} | null;
|
|
14
|
+
primary_email: string | null;
|
|
15
15
|
primary_email_verified: boolean;
|
|
16
16
|
passkey_auth_enabled: boolean;
|
|
17
17
|
otp_auth_enabled: boolean;
|
|
@@ -68,8 +68,8 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
|
|
|
68
68
|
client_read_only_metadata?: {} | null | undefined;
|
|
69
69
|
server_metadata?: {} | null | undefined;
|
|
70
70
|
id: string;
|
|
71
|
-
created_at_millis: number;
|
|
72
71
|
display_name: string;
|
|
72
|
+
created_at_millis: number;
|
|
73
73
|
profile_image_url: string | null;
|
|
74
74
|
} | null;
|
|
75
75
|
selected_team_id: string | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CrudTypeOf } from "../../crud";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const internalApiKeysCreateInputSchema: import("yup").ObjectSchema<{
|
|
3
3
|
description: string;
|
|
4
4
|
expires_at_millis: number;
|
|
5
5
|
has_publishable_client_key: boolean;
|
|
@@ -12,7 +12,7 @@ export declare const apiKeysCreateInputSchema: import("yup").ObjectSchema<{
|
|
|
12
12
|
has_secret_server_key: undefined;
|
|
13
13
|
has_super_secret_admin_key: undefined;
|
|
14
14
|
}, "">;
|
|
15
|
-
export declare const
|
|
15
|
+
export declare const internalApiKeysCreateOutputSchema: import("yup").ObjectSchema<{
|
|
16
16
|
id: string;
|
|
17
17
|
description: string;
|
|
18
18
|
expires_at_millis: number;
|
|
@@ -32,7 +32,7 @@ export declare const apiKeysCreateOutputSchema: import("yup").ObjectSchema<{
|
|
|
32
32
|
secret_server_key: undefined;
|
|
33
33
|
super_secret_admin_key: undefined;
|
|
34
34
|
}, "">;
|
|
35
|
-
export declare const
|
|
35
|
+
export declare const internalApiKeysCrudAdminObfuscatedReadSchema: import("yup").ObjectSchema<{
|
|
36
36
|
id: string;
|
|
37
37
|
description: string;
|
|
38
38
|
expires_at_millis: number;
|
|
@@ -64,15 +64,15 @@ export declare const apiKeysCrudAdminObfuscatedReadSchema: import("yup").ObjectS
|
|
|
64
64
|
last_four: undefined;
|
|
65
65
|
};
|
|
66
66
|
}, "">;
|
|
67
|
-
export declare const
|
|
67
|
+
export declare const internalApiKeysCrudAdminUpdateSchema: import("yup").ObjectSchema<{
|
|
68
68
|
description: string | undefined;
|
|
69
69
|
revoked: boolean | undefined;
|
|
70
70
|
}, import("yup").AnyObject, {
|
|
71
71
|
description: undefined;
|
|
72
72
|
revoked: undefined;
|
|
73
73
|
}, "">;
|
|
74
|
-
export declare const
|
|
75
|
-
export declare const
|
|
74
|
+
export declare const internalApiKeysCrudAdminDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
|
|
75
|
+
export declare const internalApiKeysCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
76
76
|
adminReadSchema: import("yup").ObjectSchema<{
|
|
77
77
|
id: string;
|
|
78
78
|
description: string;
|
|
@@ -131,4 +131,4 @@ export declare const apiKeysCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
131
131
|
};
|
|
132
132
|
};
|
|
133
133
|
}>;
|
|
134
|
-
export type
|
|
134
|
+
export type InternalApiKeysCrud = CrudTypeOf<typeof internalApiKeysCrud>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createCrud } from "../../crud";
|
|
2
2
|
import { yupBoolean, yupMixed, yupNumber, yupObject, yupString } from "../../schema-fields";
|
|
3
|
-
const
|
|
3
|
+
const baseInternalApiKeysReadSchema = yupObject({
|
|
4
4
|
id: yupString().defined(),
|
|
5
5
|
description: yupString().defined(),
|
|
6
6
|
expires_at_millis: yupNumber().defined(),
|
|
@@ -8,20 +8,20 @@ const baseApiKeysReadSchema = yupObject({
|
|
|
8
8
|
created_at_millis: yupNumber().defined(),
|
|
9
9
|
});
|
|
10
10
|
// Used for the result of the create endpoint
|
|
11
|
-
export const
|
|
11
|
+
export const internalApiKeysCreateInputSchema = yupObject({
|
|
12
12
|
description: yupString().defined(),
|
|
13
13
|
expires_at_millis: yupNumber().defined(),
|
|
14
14
|
has_publishable_client_key: yupBoolean().defined(),
|
|
15
15
|
has_secret_server_key: yupBoolean().defined(),
|
|
16
16
|
has_super_secret_admin_key: yupBoolean().defined(),
|
|
17
17
|
});
|
|
18
|
-
export const
|
|
18
|
+
export const internalApiKeysCreateOutputSchema = baseInternalApiKeysReadSchema.concat(yupObject({
|
|
19
19
|
publishable_client_key: yupString().optional(),
|
|
20
20
|
secret_server_key: yupString().optional(),
|
|
21
21
|
super_secret_admin_key: yupString().optional(),
|
|
22
22
|
}).defined());
|
|
23
23
|
// Used for list, read and update endpoints after the initial creation
|
|
24
|
-
export const
|
|
24
|
+
export const internalApiKeysCrudAdminObfuscatedReadSchema = baseInternalApiKeysReadSchema.concat(yupObject({
|
|
25
25
|
publishable_client_key: yupObject({
|
|
26
26
|
last_four: yupString().defined(),
|
|
27
27
|
}).optional(),
|
|
@@ -32,15 +32,15 @@ export const apiKeysCrudAdminObfuscatedReadSchema = baseApiKeysReadSchema.concat
|
|
|
32
32
|
last_four: yupString().defined(),
|
|
33
33
|
}).optional(),
|
|
34
34
|
}));
|
|
35
|
-
export const
|
|
35
|
+
export const internalApiKeysCrudAdminUpdateSchema = yupObject({
|
|
36
36
|
description: yupString().optional(),
|
|
37
37
|
revoked: yupBoolean().oneOf([true]).optional(),
|
|
38
38
|
}).defined();
|
|
39
|
-
export const
|
|
40
|
-
export const
|
|
41
|
-
adminReadSchema:
|
|
42
|
-
adminUpdateSchema:
|
|
43
|
-
adminDeleteSchema:
|
|
39
|
+
export const internalApiKeysCrudAdminDeleteSchema = yupMixed();
|
|
40
|
+
export const internalApiKeysCrud = createCrud({
|
|
41
|
+
adminReadSchema: internalApiKeysCrudAdminObfuscatedReadSchema,
|
|
42
|
+
adminUpdateSchema: internalApiKeysCrudAdminUpdateSchema,
|
|
43
|
+
adminDeleteSchema: internalApiKeysCrudAdminDeleteSchema,
|
|
44
44
|
docs: {
|
|
45
45
|
adminList: {
|
|
46
46
|
hidden: true,
|