neon 3.0.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -5
- package/dist/_chunks/auth_selection-DGgq6ifc.js +83 -0
- package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
- package/dist/_chunks/credentials-MYdHdKah.js +188 -0
- package/dist/_chunks/env-NbA61JR3.js +585 -0
- package/dist/_chunks/env_services-Tz9G4JeT.js +531 -0
- package/dist/_chunks/paths-DMq0Lt7a.js +151 -0
- package/dist/_chunks/profiles-Ir29rqns.js +217 -0
- package/dist/_chunks/psql-DWH-kc69.js +2169 -0
- package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/_chunks/secure_file-BucZj4yQ.js +39 -0
- package/dist/analytics.js +163 -207
- package/dist/api.js +815 -758
- package/dist/auth.js +121 -141
- package/dist/auth_context.js +39 -53
- package/dist/cli.js +4 -7
- package/dist/commands/api.js +220 -250
- package/dist/commands/api_keys.js +251 -314
- package/dist/commands/auth.js +283 -328
- package/dist/commands/bootstrap.js +372 -437
- package/dist/commands/branches.js +304 -455
- package/dist/commands/bucket.js +374 -514
- package/dist/commands/checkout.js +213 -298
- package/dist/commands/config.js +573 -690
- package/dist/commands/connection_string.js +137 -165
- package/dist/commands/data_api.js +238 -260
- package/dist/commands/databases.js +67 -76
- package/dist/commands/deploy.js +31 -25
- package/dist/commands/dev.js +639 -719
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +255 -305
- package/dist/commands/functions.js +275 -355
- package/dist/commands/index.js +70 -65
- package/dist/commands/init.js +84 -119
- package/dist/commands/inspect.js +55 -55
- package/dist/commands/ip_allow.js +88 -120
- package/dist/commands/link.js +874 -1019
- package/dist/commands/logs.js +291 -0
- package/dist/commands/neon_auth.js +725 -933
- package/dist/commands/operations.js +34 -25
- package/dist/commands/orgs.js +28 -18
- package/dist/commands/profile.js +615 -846
- package/dist/commands/projects.js +313 -373
- package/dist/commands/psql.js +60 -58
- package/dist/commands/roles.js +55 -58
- package/dist/commands/schema_diff.js +87 -131
- package/dist/commands/set_context.js +34 -26
- package/dist/commands/snapshots.js +288 -413
- package/dist/commands/status.js +41 -37
- package/dist/commands/user.js +21 -10
- package/dist/commands/vpc_endpoints.js +85 -113
- package/dist/config.js +7 -6
- package/dist/config_format.js +50 -66
- package/dist/config_template.js +128 -157
- package/dist/context.js +183 -235
- package/dist/current_branch_fast_path.js +40 -49
- package/dist/dev/env.js +2 -446
- package/dist/dev/functions.js +54 -68
- package/dist/dev/inputs.js +46 -58
- package/dist/dev/runtime.js +135 -164
- package/dist/dev/websocket.js +766 -959
- package/dist/env.js +27 -33
- package/dist/env_file.js +118 -132
- package/dist/env_services.js +2 -51
- package/dist/errors.js +57 -68
- package/dist/functions_api.js +45 -43
- package/dist/help.js +189 -140
- package/dist/index.js +182 -257
- package/dist/init/agents.js +137 -118
- package/dist/init/auth.js +58 -68
- package/dist/init/bootstrap.js +325 -396
- package/dist/init/build_config.js +4 -2
- package/dist/init/detect_agent.js +56 -101
- package/dist/init/editors.js +35 -52
- package/dist/init/enrich_output.js +51 -66
- package/dist/init/extension.js +134 -171
- package/dist/init/inspect.js +179 -266
- package/dist/init/interactive.js +510 -622
- package/dist/init/neonctl.js +117 -168
- package/dist/init/orchestrate.js +157 -173
- package/dist/init/phases/auth.js +188 -202
- package/dist/init/phases/cleanup.js +23 -23
- package/dist/init/phases/db.js +251 -277
- package/dist/init/phases/getting_started.js +213 -223
- package/dist/init/phases/mcp.js +174 -224
- package/dist/init/phases/migrations.js +247 -248
- package/dist/init/phases/neon_auth.js +114 -133
- package/dist/init/phases/setup.js +546 -703
- package/dist/init/phases/skills.js +75 -86
- package/dist/init/phases/status.js +72 -67
- package/dist/init/resolve_context.js +102 -99
- package/dist/init/route_command.js +91 -98
- package/dist/init/skills.js +174 -218
- package/dist/init/vsix.js +77 -99
- package/dist/log.js +17 -16
- package/dist/neon_services.js +104 -129
- package/dist/parameters.gen.js +481 -471
- package/dist/pkg.js +17 -19
- package/dist/profile_keys.js +44 -47
- package/dist/psql/cli.js +44 -47
- package/dist/psql/command/cmd_cond.js +231 -406
- package/dist/psql/command/cmd_connect.js +557 -764
- package/dist/psql/command/cmd_copy.js +728 -984
- package/dist/psql/command/cmd_describe.js +1499 -1688
- package/dist/psql/command/cmd_format.js +733 -905
- package/dist/psql/command/cmd_io.js +2 -2193
- package/dist/psql/command/cmd_lo.js +297 -359
- package/dist/psql/command/cmd_meta.js +727 -878
- package/dist/psql/command/cmd_misc.js +138 -172
- package/dist/psql/command/cmd_pipeline.js +2 -1148
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +260 -325
- package/dist/psql/command/inputQueue.js +35 -33
- package/dist/psql/command/shared.js +49 -63
- package/dist/psql/complete/filenames.js +90 -133
- package/dist/psql/complete/index.js +59 -97
- package/dist/psql/complete/matcher.js +236 -300
- package/dist/psql/complete/psqlVars.js +218 -223
- package/dist/psql/complete/queries.js +159 -177
- package/dist/psql/complete/rules.js +1493 -2299
- package/dist/psql/core/common.js +2 -1253
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1303
- package/dist/psql/core/prompt.js +391 -408
- package/dist/psql/core/settings.js +429 -644
- package/dist/psql/core/sqlHelp.js +480 -554
- package/dist/psql/core/startup.js +2 -846
- package/dist/psql/core/syncVars.js +67 -110
- package/dist/psql/core/variables.js +156 -278
- package/dist/psql/describe/formatters.js +884 -1285
- package/dist/psql/describe/processNamePattern.js +173 -260
- package/dist/psql/describe/queries.js +1368 -2403
- package/dist/psql/describe/versionGate.js +32 -41
- package/dist/psql/index.js +2 -2030
- package/dist/psql/io/history.js +232 -271
- package/dist/psql/io/input.js +103 -108
- package/dist/psql/io/lineEditor/buffer.js +238 -319
- package/dist/psql/io/lineEditor/complete.js +135 -213
- package/dist/psql/io/lineEditor/filename.js +139 -148
- package/dist/psql/io/lineEditor/index.js +653 -870
- package/dist/psql/io/lineEditor/keymap.js +544 -702
- package/dist/psql/io/lineEditor/vt100.js +294 -341
- package/dist/psql/io/pgpass.js +158 -187
- package/dist/psql/io/pgservice.js +146 -183
- package/dist/psql/io/psqlrc.js +328 -403
- package/dist/psql/print/aligned.js +1020 -1683
- package/dist/psql/print/asciidoc.js +180 -214
- package/dist/psql/print/crosstab.js +281 -442
- package/dist/psql/print/csv.js +48 -70
- package/dist/psql/print/html.js +195 -226
- package/dist/psql/print/json.js +75 -88
- package/dist/psql/print/latex.js +291 -364
- package/dist/psql/print/pager.js +171 -242
- package/dist/psql/print/troff.js +194 -226
- package/dist/psql/print/unaligned.js +69 -95
- package/dist/psql/print/units.js +167 -169
- package/dist/psql/scanner/slash.js +428 -483
- package/dist/psql/scanner/sql.js +445 -889
- package/dist/psql/scanner/stringutils.js +309 -379
- package/dist/psql/types/index.js +8 -7
- package/dist/psql/types/scanner.js +25 -22
- package/dist/psql/wire/connection.js +2042 -2803
- package/dist/psql/wire/copy.js +84 -100
- package/dist/psql/wire/notify.js +39 -59
- package/dist/psql/wire/pipeline.js +305 -518
- package/dist/psql/wire/protocol.js +349 -417
- package/dist/psql/wire/sasl.js +180 -265
- package/dist/psql/wire/tls.js +400 -561
- package/dist/storage_api.js +115 -129
- package/dist/test_utils/fixtures.js +94 -113
- package/dist/test_utils/oauth_server.js +10 -7
- package/dist/test_utils/project_dir.js +33 -0
- package/dist/utils/ai_gateway_notice.js +131 -162
- package/dist/utils/api_enums.js +21 -28
- package/dist/utils/auth.js +10 -4
- package/dist/utils/branch_notice.js +20 -19
- package/dist/utils/branch_picker.js +83 -89
- package/dist/utils/cli_name.js +15 -12
- package/dist/utils/compute_units.js +20 -27
- package/dist/utils/config_diff.js +127 -158
- package/dist/utils/enrichers.js +95 -148
- package/dist/utils/esbuild.js +130 -189
- package/dist/utils/flags.js +35 -47
- package/dist/utils/formats.js +8 -15
- package/dist/utils/git_diff.js +69 -80
- package/dist/utils/inspect_db.js +101 -143
- package/dist/utils/inspect_queries.js +179 -142
- package/dist/utils/middlewares.js +39 -45
- package/dist/utils/openapi.js +87 -99
- package/dist/utils/package_manager.js +312 -110
- package/dist/utils/point_in_time.js +49 -53
- package/dist/utils/psql.js +89 -106
- package/dist/utils/service_picker.js +55 -58
- package/dist/utils/string.js +5 -5
- package/dist/utils/ui.js +38 -55
- package/dist/utils/write_sync.js +26 -35
- package/dist/utils/zip.js +4 -3
- package/dist/writer.js +67 -87
- package/package.json +11 -6
- package/dist/_shared/auth_selection.js +0 -86
- package/dist/_shared/credentials.js +0 -209
- package/dist/_shared/env-core/env.js +0 -558
- package/dist/_shared/env-core/reuse-secrets.js +0 -223
- package/dist/_shared/paths.js +0 -148
- package/dist/_shared/profiles.js +0 -276
- package/dist/_shared/secure_file.js +0 -43
|
@@ -1,1028 +1,820 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { t as __exportAll } from "../_chunks/rolldown-runtime-D7D4PA-g.js";
|
|
2
2
|
import { codeFromBody, isNeonApiError, retryOnLock } from "../api.js";
|
|
3
|
-
import { NeonAuthEmailVerificationMethod, NeonAuthOauthProviderId, NeonAuthOauthProviderType, NeonAuthSupportedAuthProvider, } from "../utils/api_enums.js";
|
|
4
|
-
import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
|
|
5
3
|
import { writer } from "../writer.js";
|
|
6
|
-
|
|
4
|
+
import { NeonAuthEmailVerificationMethod, NeonAuthOauthProviderId, NeonAuthOauthProviderType, NeonAuthSupportedAuthProvider } from "../utils/api_enums.js";
|
|
5
|
+
import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
//#region src/commands/neon_auth.ts
|
|
8
|
+
var neon_auth_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
builder: () => builder,
|
|
10
|
+
command: () => command,
|
|
11
|
+
describe: () => describe,
|
|
12
|
+
handler: () => handler
|
|
13
|
+
});
|
|
7
14
|
const printKvBlock = (title, entries) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
process.stdout.write("\n");
|
|
15
|
+
process.stdout.write(`\n${chalk.green(title)}\n`);
|
|
16
|
+
for (const [key, value] of entries) process.stdout.write(` ${chalk.green(key)} ${value ?? ""}\n`);
|
|
17
|
+
process.stdout.write("\n");
|
|
13
18
|
};
|
|
14
19
|
const printMessage = (message) => {
|
|
15
|
-
|
|
20
|
+
process.stdout.write(`\n${chalk.green(message)}\n\n`);
|
|
16
21
|
};
|
|
17
22
|
const INTEGRATION_RESPONSE_FIELDS = [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
"auth_provider",
|
|
24
|
+
"db_name",
|
|
25
|
+
"base_url",
|
|
26
|
+
"schema_name",
|
|
27
|
+
"table_name",
|
|
28
|
+
"jwks_url"
|
|
24
29
|
];
|
|
25
30
|
const INTEGRATION_STATUS_FIELDS = [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
"auth_provider",
|
|
32
|
+
"branch_id",
|
|
33
|
+
"db_name",
|
|
34
|
+
"base_url",
|
|
35
|
+
"created_at",
|
|
36
|
+
"jwks_url"
|
|
37
|
+
];
|
|
38
|
+
const OAUTH_PROVIDER_FIELDS = [
|
|
39
|
+
"id",
|
|
40
|
+
"type",
|
|
41
|
+
"client_id"
|
|
32
42
|
];
|
|
33
|
-
const OAUTH_PROVIDER_FIELDS = ["id", "type", "client_id"];
|
|
34
43
|
const ALLOW_LOCALHOST_FIELDS = ["allow_localhost"];
|
|
35
44
|
const SUPPORTED_OAUTH_PROVIDERS = [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
NeonAuthOauthProviderId.Google,
|
|
46
|
+
NeonAuthOauthProviderId.Github,
|
|
47
|
+
NeonAuthOauthProviderId.Vercel
|
|
39
48
|
];
|
|
40
49
|
const DOMAIN_FIELDS = ["domain"];
|
|
41
50
|
const EMAIL_PASSWORD_FIELDS = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
"enabled",
|
|
52
|
+
"email_verification_method",
|
|
53
|
+
"require_email_verification",
|
|
54
|
+
"auto_sign_in_after_verification",
|
|
55
|
+
"send_verification_email_on_sign_up",
|
|
56
|
+
"send_verification_email_on_sign_in",
|
|
57
|
+
"disable_sign_up"
|
|
49
58
|
];
|
|
50
59
|
const EMAIL_PROVIDER_FIELDS = [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
"type",
|
|
61
|
+
"host",
|
|
62
|
+
"port",
|
|
63
|
+
"username",
|
|
64
|
+
"sender_email",
|
|
65
|
+
"sender_name"
|
|
57
66
|
];
|
|
58
67
|
const ORGANIZATION_FIELDS = [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
"enabled",
|
|
69
|
+
"organization_limit",
|
|
70
|
+
"creator_role"
|
|
62
71
|
];
|
|
63
72
|
const WEBHOOK_FIELDS = [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
"enabled",
|
|
74
|
+
"webhook_url",
|
|
75
|
+
"enabled_events",
|
|
76
|
+
"timeout_seconds"
|
|
68
77
|
];
|
|
69
78
|
const TEST_EMAIL_FIELDS = ["success", "error_message"];
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
"send.magic_link",
|
|
372
|
-
],
|
|
373
|
-
array: true,
|
|
374
|
-
},
|
|
375
|
-
timeout: {
|
|
376
|
-
describe: "Webhook timeout in seconds (1-10)",
|
|
377
|
-
type: "number",
|
|
378
|
-
},
|
|
379
|
-
}), async (args) => {
|
|
380
|
-
await webhookUpdate(args);
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
})
|
|
384
|
-
.command("plugins", "View Neon Auth plugin configurations", (yargs) => {
|
|
385
|
-
return yargs
|
|
386
|
-
.usage("$0 neon-auth plugins <sub-command> [options]")
|
|
387
|
-
.command("list", "List all plugin configurations", (yargs) => yargs, async (args) => {
|
|
388
|
-
await pluginsList(args);
|
|
389
|
-
})
|
|
390
|
-
.command("get <plugin-name>", "Get a specific plugin configuration", (yargs) => yargs
|
|
391
|
-
.usage("$0 neon-auth plugins get <plugin-name> [options]")
|
|
392
|
-
.positional("plugin-name", {
|
|
393
|
-
describe: "Plugin name (e.g. organization, email_provider, email_and_password, oauth_providers, allow_localhost)",
|
|
394
|
-
type: "string",
|
|
395
|
-
demandOption: true,
|
|
396
|
-
}), async (args) => {
|
|
397
|
-
await pluginsGet(args);
|
|
398
|
-
});
|
|
399
|
-
})
|
|
400
|
-
.command("user", "Manage Neon Auth users", (yargs) => {
|
|
401
|
-
return yargs
|
|
402
|
-
.usage("$0 neon-auth user <sub-command> [options]")
|
|
403
|
-
.command("create", "Create an auth user", (yargs) => yargs.options({
|
|
404
|
-
email: {
|
|
405
|
-
describe: "User email address",
|
|
406
|
-
type: "string",
|
|
407
|
-
demandOption: true,
|
|
408
|
-
},
|
|
409
|
-
name: {
|
|
410
|
-
describe: "User display name (defaults to email if not provided)",
|
|
411
|
-
type: "string",
|
|
412
|
-
},
|
|
413
|
-
}), async (args) => {
|
|
414
|
-
await userCreate(args);
|
|
415
|
-
})
|
|
416
|
-
.command("delete <user-id>", "Delete an auth user", (yargs) => yargs
|
|
417
|
-
.usage("$0 neon-auth user delete <user-id> [options]")
|
|
418
|
-
.positional("user-id", {
|
|
419
|
-
describe: "ID of the user to delete",
|
|
420
|
-
type: "string",
|
|
421
|
-
demandOption: true,
|
|
422
|
-
}), async (args) => {
|
|
423
|
-
await userDelete(args);
|
|
424
|
-
})
|
|
425
|
-
.command("set-role <user-id>", "Set roles for an auth user", (yargs) => yargs
|
|
426
|
-
.usage("$0 neon-auth user set-role <user-id> [options]")
|
|
427
|
-
.positional("user-id", {
|
|
428
|
-
describe: "ID of the user to update",
|
|
429
|
-
type: "string",
|
|
430
|
-
demandOption: true,
|
|
431
|
-
})
|
|
432
|
-
.options({
|
|
433
|
-
roles: {
|
|
434
|
-
describe: "Roles to assign",
|
|
435
|
-
type: "string",
|
|
436
|
-
array: true,
|
|
437
|
-
demandOption: true,
|
|
438
|
-
},
|
|
439
|
-
}), async (args) => {
|
|
440
|
-
await userSetRole(args);
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
};
|
|
444
|
-
export const handler = (args) => {
|
|
445
|
-
return args;
|
|
79
|
+
const command = "neon-auth";
|
|
80
|
+
const describe = "Manage Neon Auth";
|
|
81
|
+
const builder = (argv) => {
|
|
82
|
+
return argv.usage("$0 neon-auth <sub-command> [options]").options({
|
|
83
|
+
"project-id": {
|
|
84
|
+
describe: "Project ID",
|
|
85
|
+
type: "string"
|
|
86
|
+
},
|
|
87
|
+
branch: {
|
|
88
|
+
describe: "Branch ID or name",
|
|
89
|
+
type: "string"
|
|
90
|
+
}
|
|
91
|
+
}).middleware(fillSingleProject).command("enable", "Enable Neon Auth on a branch", (yargs) => yargs.options({ "database-name": {
|
|
92
|
+
describe: "Database name to use for auth data",
|
|
93
|
+
type: "string"
|
|
94
|
+
} }), async (args) => {
|
|
95
|
+
await enable(args);
|
|
96
|
+
}).command("status", "Get Neon Auth status for a branch", (yargs) => yargs, async (args) => {
|
|
97
|
+
await status(args);
|
|
98
|
+
}).command("disable", "Disable Neon Auth on a branch", (yargs) => yargs.options({ "delete-data": {
|
|
99
|
+
describe: "Permanently delete all Neon Auth data and schema from the database",
|
|
100
|
+
type: "boolean",
|
|
101
|
+
default: false
|
|
102
|
+
} }), async (args) => {
|
|
103
|
+
await disable(args);
|
|
104
|
+
}).command("oauth-provider", "Manage OAuth providers", (yargs) => {
|
|
105
|
+
return yargs.usage("$0 neon-auth oauth-provider <sub-command> [options]").command("list", "List OAuth providers", (yargs) => yargs, async (args) => {
|
|
106
|
+
await oauthProviderList(args);
|
|
107
|
+
}).command("add", "Add an OAuth provider", (yargs) => yargs.options({
|
|
108
|
+
"provider-id": {
|
|
109
|
+
describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(", ")}`,
|
|
110
|
+
type: "string",
|
|
111
|
+
choices: SUPPORTED_OAUTH_PROVIDERS,
|
|
112
|
+
demandOption: true
|
|
113
|
+
},
|
|
114
|
+
"oauth-client-id": {
|
|
115
|
+
describe: "OAuth client ID from your provider app. Omit to use Neon's shared OAuth app.",
|
|
116
|
+
type: "string"
|
|
117
|
+
},
|
|
118
|
+
"oauth-client-secret": {
|
|
119
|
+
describe: "OAuth client secret from your provider app. Omit to use Neon's shared OAuth app.",
|
|
120
|
+
type: "string"
|
|
121
|
+
}
|
|
122
|
+
}), async (args) => {
|
|
123
|
+
await oauthProviderAdd(args);
|
|
124
|
+
}).command("update", "Update an OAuth provider", (yargs) => yargs.options({
|
|
125
|
+
"provider-id": {
|
|
126
|
+
describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(", ")}`,
|
|
127
|
+
type: "string",
|
|
128
|
+
choices: SUPPORTED_OAUTH_PROVIDERS,
|
|
129
|
+
demandOption: true
|
|
130
|
+
},
|
|
131
|
+
"oauth-client-id": {
|
|
132
|
+
describe: "OAuth client ID from your provider app. Omit to use Neon's shared OAuth app.",
|
|
133
|
+
type: "string"
|
|
134
|
+
},
|
|
135
|
+
"oauth-client-secret": {
|
|
136
|
+
describe: "OAuth client secret from your provider app. Omit to use Neon's shared OAuth app.",
|
|
137
|
+
type: "string"
|
|
138
|
+
}
|
|
139
|
+
}), async (args) => {
|
|
140
|
+
await oauthProviderUpdate(args);
|
|
141
|
+
}).command("delete", "Delete an OAuth provider", (yargs) => yargs.options({ "provider-id": {
|
|
142
|
+
describe: `OAuth provider ID. Supported values: ${SUPPORTED_OAUTH_PROVIDERS.join(", ")}`,
|
|
143
|
+
type: "string",
|
|
144
|
+
choices: SUPPORTED_OAUTH_PROVIDERS,
|
|
145
|
+
demandOption: true
|
|
146
|
+
} }), async (args) => {
|
|
147
|
+
await oauthProviderDelete(args);
|
|
148
|
+
});
|
|
149
|
+
}).command("domain", "Manage redirect URI trusted domains", (yargs) => {
|
|
150
|
+
return yargs.usage("$0 neon-auth domain <sub-command> [options]").command("list", "List trusted domains", (yargs) => yargs, async (args) => {
|
|
151
|
+
await domainList(args);
|
|
152
|
+
}).command("add <domain>", "Add a trusted domain", (yargs) => yargs.usage("$0 neon-auth domain add <domain> [options]").positional("domain", {
|
|
153
|
+
describe: "Domain to add",
|
|
154
|
+
type: "string",
|
|
155
|
+
demandOption: true
|
|
156
|
+
}), async (args) => {
|
|
157
|
+
await domainAdd(args);
|
|
158
|
+
}).command("delete <domain>", "Delete a trusted domain", (yargs) => yargs.usage("$0 neon-auth domain delete <domain> [options]").positional("domain", {
|
|
159
|
+
describe: "Domain to delete",
|
|
160
|
+
type: "string",
|
|
161
|
+
demandOption: true
|
|
162
|
+
}), async (args) => {
|
|
163
|
+
await domainDelete(args);
|
|
164
|
+
}).command("allow-localhost", "Manage localhost connection settings", (yargs) => yargs.usage("$0 neon-auth domain allow-localhost <sub-command> [options]").command("get", "Get localhost connection setting", (yargs) => yargs, async (args) => {
|
|
165
|
+
await allowLocalhostGet(args);
|
|
166
|
+
}).command("enable", "Allow localhost connections", (yargs) => yargs, async (args) => {
|
|
167
|
+
await allowLocalhostEnable(args);
|
|
168
|
+
}).command("disable", "Restrict localhost connections", (yargs) => yargs, async (args) => {
|
|
169
|
+
await allowLocalhostDisable(args);
|
|
170
|
+
}));
|
|
171
|
+
}).command("config", "Manage Neon Auth configuration", (yargs) => {
|
|
172
|
+
return yargs.usage("$0 neon-auth config <sub-command> [options]").command("email-password", "Manage email and password authentication settings", (yargs) => {
|
|
173
|
+
return yargs.usage("$0 neon-auth config email-password <sub-command> [options]").command("get", "Get email and password config", (yargs) => yargs, async (args) => {
|
|
174
|
+
await emailPasswordGet(args);
|
|
175
|
+
}).command("update", "Update email and password config", (yargs) => yargs.options({
|
|
176
|
+
enabled: {
|
|
177
|
+
describe: "Enable email and password authentication",
|
|
178
|
+
type: "boolean"
|
|
179
|
+
},
|
|
180
|
+
"email-verification-method": {
|
|
181
|
+
describe: "Email verification method",
|
|
182
|
+
type: "string",
|
|
183
|
+
choices: Object.values(NeonAuthEmailVerificationMethod)
|
|
184
|
+
},
|
|
185
|
+
"require-email-verification": {
|
|
186
|
+
describe: "Require email verification before users can sign in",
|
|
187
|
+
type: "boolean"
|
|
188
|
+
},
|
|
189
|
+
"auto-sign-in-after-verification": {
|
|
190
|
+
describe: "Auto sign in users after verifying their email",
|
|
191
|
+
type: "boolean"
|
|
192
|
+
},
|
|
193
|
+
"send-verification-email-on-sign-up": {
|
|
194
|
+
describe: "Send verification email on sign up",
|
|
195
|
+
type: "boolean"
|
|
196
|
+
},
|
|
197
|
+
"send-verification-email-on-sign-in": {
|
|
198
|
+
describe: "Send verification email on sign in",
|
|
199
|
+
type: "boolean"
|
|
200
|
+
},
|
|
201
|
+
"disable-sign-up": {
|
|
202
|
+
describe: "Disable new user sign ups",
|
|
203
|
+
type: "boolean"
|
|
204
|
+
}
|
|
205
|
+
}), async (args) => {
|
|
206
|
+
await emailPasswordUpdate(args);
|
|
207
|
+
});
|
|
208
|
+
}).command("email-provider", "Manage email provider configuration", (yargs) => {
|
|
209
|
+
return yargs.usage("$0 neon-auth config email-provider <sub-command> [options]").command("get", "Get email provider config", (yargs) => yargs, async (args) => {
|
|
210
|
+
await emailProviderGet(args);
|
|
211
|
+
}).command("update", "Update email provider config", (yargs) => yargs.options({
|
|
212
|
+
type: {
|
|
213
|
+
describe: "Email provider type",
|
|
214
|
+
type: "string",
|
|
215
|
+
choices: ["standard", "shared"],
|
|
216
|
+
demandOption: true
|
|
217
|
+
},
|
|
218
|
+
host: {
|
|
219
|
+
describe: "SMTP host (required for standard)",
|
|
220
|
+
type: "string"
|
|
221
|
+
},
|
|
222
|
+
port: {
|
|
223
|
+
describe: "SMTP port (required for standard)",
|
|
224
|
+
type: "number"
|
|
225
|
+
},
|
|
226
|
+
username: {
|
|
227
|
+
describe: "SMTP username (required for standard)",
|
|
228
|
+
type: "string"
|
|
229
|
+
},
|
|
230
|
+
password: {
|
|
231
|
+
describe: "SMTP password (required for standard)",
|
|
232
|
+
type: "string"
|
|
233
|
+
},
|
|
234
|
+
"sender-email": {
|
|
235
|
+
describe: "Sender email address",
|
|
236
|
+
type: "string"
|
|
237
|
+
},
|
|
238
|
+
"sender-name": {
|
|
239
|
+
describe: "Sender display name",
|
|
240
|
+
type: "string"
|
|
241
|
+
}
|
|
242
|
+
}), async (args) => {
|
|
243
|
+
await emailProviderUpdate(args);
|
|
244
|
+
}).command("test", "Send a test email", (yargs) => yargs.options({
|
|
245
|
+
"recipient-email": {
|
|
246
|
+
describe: "Email address to send test email to",
|
|
247
|
+
type: "string",
|
|
248
|
+
demandOption: true
|
|
249
|
+
},
|
|
250
|
+
host: {
|
|
251
|
+
describe: "SMTP host",
|
|
252
|
+
type: "string",
|
|
253
|
+
demandOption: true
|
|
254
|
+
},
|
|
255
|
+
port: {
|
|
256
|
+
describe: "SMTP port",
|
|
257
|
+
type: "number",
|
|
258
|
+
demandOption: true
|
|
259
|
+
},
|
|
260
|
+
username: {
|
|
261
|
+
describe: "SMTP username",
|
|
262
|
+
type: "string",
|
|
263
|
+
demandOption: true
|
|
264
|
+
},
|
|
265
|
+
password: {
|
|
266
|
+
describe: "SMTP password",
|
|
267
|
+
type: "string",
|
|
268
|
+
demandOption: true
|
|
269
|
+
},
|
|
270
|
+
"sender-email": {
|
|
271
|
+
describe: "Sender email address",
|
|
272
|
+
type: "string",
|
|
273
|
+
demandOption: true
|
|
274
|
+
},
|
|
275
|
+
"sender-name": {
|
|
276
|
+
describe: "Sender display name",
|
|
277
|
+
type: "string",
|
|
278
|
+
demandOption: true
|
|
279
|
+
}
|
|
280
|
+
}), async (args) => {
|
|
281
|
+
await emailProviderTest(args);
|
|
282
|
+
});
|
|
283
|
+
}).command("organization", "Manage organization plugin settings", (yargs) => {
|
|
284
|
+
return yargs.usage("$0 neon-auth config organization <sub-command> [options]").command("get", "Get organization plugin config", (yargs) => yargs, async (args) => {
|
|
285
|
+
await organizationGet(args);
|
|
286
|
+
}).command("update", "Update organization plugin config", (yargs) => yargs.options({
|
|
287
|
+
enabled: {
|
|
288
|
+
describe: "Enable the organization plugin",
|
|
289
|
+
type: "boolean"
|
|
290
|
+
},
|
|
291
|
+
limit: {
|
|
292
|
+
describe: "Maximum number of organizations a user can create",
|
|
293
|
+
type: "number"
|
|
294
|
+
},
|
|
295
|
+
"creator-role": {
|
|
296
|
+
describe: "Role assigned to organization creator",
|
|
297
|
+
type: "string",
|
|
298
|
+
choices: ["admin", "owner"]
|
|
299
|
+
}
|
|
300
|
+
}), async (args) => {
|
|
301
|
+
await organizationUpdate(args);
|
|
302
|
+
});
|
|
303
|
+
}).command("webhook", "Manage webhook configuration", (yargs) => {
|
|
304
|
+
return yargs.usage("$0 neon-auth config webhook <sub-command> [options]").command("get", "Get webhook config", (yargs) => yargs, async (args) => {
|
|
305
|
+
await webhookGet(args);
|
|
306
|
+
}).command("update", "Update webhook config", (yargs) => yargs.options({
|
|
307
|
+
enabled: {
|
|
308
|
+
describe: "Enable webhooks",
|
|
309
|
+
type: "boolean",
|
|
310
|
+
demandOption: true
|
|
311
|
+
},
|
|
312
|
+
url: {
|
|
313
|
+
describe: "Webhook endpoint URL",
|
|
314
|
+
type: "string"
|
|
315
|
+
},
|
|
316
|
+
"enabled-events": {
|
|
317
|
+
describe: "Events to enable",
|
|
318
|
+
type: "string",
|
|
319
|
+
choices: [
|
|
320
|
+
"user.before_create",
|
|
321
|
+
"user.created",
|
|
322
|
+
"send.otp",
|
|
323
|
+
"send.magic_link"
|
|
324
|
+
],
|
|
325
|
+
array: true
|
|
326
|
+
},
|
|
327
|
+
timeout: {
|
|
328
|
+
describe: "Webhook timeout in seconds (1-10)",
|
|
329
|
+
type: "number"
|
|
330
|
+
}
|
|
331
|
+
}), async (args) => {
|
|
332
|
+
await webhookUpdate(args);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
}).command("plugins", "View Neon Auth plugin configurations", (yargs) => {
|
|
336
|
+
return yargs.usage("$0 neon-auth plugins <sub-command> [options]").command("list", "List all plugin configurations", (yargs) => yargs, async (args) => {
|
|
337
|
+
await pluginsList(args);
|
|
338
|
+
}).command("get <plugin-name>", "Get a specific plugin configuration", (yargs) => yargs.usage("$0 neon-auth plugins get <plugin-name> [options]").positional("plugin-name", {
|
|
339
|
+
describe: "Plugin name (e.g. organization, email_provider, email_and_password, oauth_providers, allow_localhost)",
|
|
340
|
+
type: "string",
|
|
341
|
+
demandOption: true
|
|
342
|
+
}), async (args) => {
|
|
343
|
+
await pluginsGet(args);
|
|
344
|
+
});
|
|
345
|
+
}).command("user", "Manage Neon Auth users", (yargs) => {
|
|
346
|
+
return yargs.usage("$0 neon-auth user <sub-command> [options]").command("create", "Create an auth user", (yargs) => yargs.options({
|
|
347
|
+
email: {
|
|
348
|
+
describe: "User email address",
|
|
349
|
+
type: "string",
|
|
350
|
+
demandOption: true
|
|
351
|
+
},
|
|
352
|
+
name: {
|
|
353
|
+
describe: "User display name (defaults to email if not provided)",
|
|
354
|
+
type: "string"
|
|
355
|
+
}
|
|
356
|
+
}), async (args) => {
|
|
357
|
+
await userCreate(args);
|
|
358
|
+
}).command("delete <user-id>", "Delete an auth user", (yargs) => yargs.usage("$0 neon-auth user delete <user-id> [options]").positional("user-id", {
|
|
359
|
+
describe: "ID of the user to delete",
|
|
360
|
+
type: "string",
|
|
361
|
+
demandOption: true
|
|
362
|
+
}), async (args) => {
|
|
363
|
+
await userDelete(args);
|
|
364
|
+
}).command("set-role <user-id>", "Set roles for an auth user", (yargs) => yargs.usage("$0 neon-auth user set-role <user-id> [options]").positional("user-id", {
|
|
365
|
+
describe: "ID of the user to update",
|
|
366
|
+
type: "string",
|
|
367
|
+
demandOption: true
|
|
368
|
+
}).options({ roles: {
|
|
369
|
+
describe: "Roles to assign",
|
|
370
|
+
type: "string",
|
|
371
|
+
array: true,
|
|
372
|
+
demandOption: true
|
|
373
|
+
} }), async (args) => {
|
|
374
|
+
await userSetRole(args);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
const handler = (args) => {
|
|
379
|
+
return args;
|
|
446
380
|
};
|
|
447
381
|
const resolveBranch = async (props) => {
|
|
448
|
-
|
|
382
|
+
return props.branchId ?? await branchIdFromProps(props);
|
|
449
383
|
};
|
|
450
384
|
const enable = async (props) => {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
...(d.schema_name
|
|
483
|
-
? [["Schema Name: ", d.schema_name]]
|
|
484
|
-
: []),
|
|
485
|
-
...(d.table_name
|
|
486
|
-
? [["Table Name: ", d.table_name]]
|
|
487
|
-
: []),
|
|
488
|
-
["JWKS URL: ", data.jwks_url],
|
|
489
|
-
]);
|
|
490
|
-
if (data.base_url) {
|
|
491
|
-
process.stdout.write(` ${chalk.green("Set this environment variable in your application:")}\n`);
|
|
492
|
-
process.stdout.write(` NEON_AUTH_BASE_URL=${data.base_url}\n\n`);
|
|
493
|
-
}
|
|
385
|
+
const branchId = await resolveBranch(props);
|
|
386
|
+
let data;
|
|
387
|
+
let alreadyEnabled = false;
|
|
388
|
+
try {
|
|
389
|
+
({data} = await retryOnLock(() => props.apiClient.createNeonAuth(props.projectId, branchId, {
|
|
390
|
+
auth_provider: NeonAuthSupportedAuthProvider.BetterAuth,
|
|
391
|
+
database_name: props.databaseName
|
|
392
|
+
})));
|
|
393
|
+
} catch (err) {
|
|
394
|
+
if (isNeonApiError(err) && err.status === 409) {
|
|
395
|
+
alreadyEnabled = true;
|
|
396
|
+
({data} = await props.apiClient.getNeonAuth(props.projectId, branchId));
|
|
397
|
+
} else throw err;
|
|
398
|
+
}
|
|
399
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
400
|
+
writer(props).end(data, { fields: INTEGRATION_RESPONSE_FIELDS });
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const d = data;
|
|
404
|
+
printKvBlock(alreadyEnabled ? "Neon Auth is already enabled" : "Neon Auth enabled", [
|
|
405
|
+
["Auth Provider:", data.auth_provider],
|
|
406
|
+
...d.db_name ? [["Database: ", d.db_name]] : [],
|
|
407
|
+
["Base URL: ", data.base_url],
|
|
408
|
+
...d.schema_name ? [["Schema Name: ", d.schema_name]] : [],
|
|
409
|
+
...d.table_name ? [["Table Name: ", d.table_name]] : [],
|
|
410
|
+
["JWKS URL: ", data.jwks_url]
|
|
411
|
+
]);
|
|
412
|
+
if (data.base_url) {
|
|
413
|
+
process.stdout.write(` ${chalk.green("Set this environment variable in your application:")}\n`);
|
|
414
|
+
process.stdout.write(` NEON_AUTH_BASE_URL=${data.base_url}\n\n`);
|
|
415
|
+
}
|
|
494
416
|
};
|
|
495
417
|
const status = async (props) => {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
]);
|
|
418
|
+
const branchId = await resolveBranch(props);
|
|
419
|
+
let data;
|
|
420
|
+
try {
|
|
421
|
+
({data} = await props.apiClient.getNeonAuth(props.projectId, branchId));
|
|
422
|
+
} catch (err) {
|
|
423
|
+
if (isNeonApiError(err) && err.status === 404) {
|
|
424
|
+
printMessage("Neon Auth is not configured for this branch");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
throw err;
|
|
428
|
+
}
|
|
429
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
430
|
+
writer(props).end(data, { fields: INTEGRATION_STATUS_FIELDS });
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
printKvBlock("Neon Auth status", [
|
|
434
|
+
["Auth Provider:", data.auth_provider],
|
|
435
|
+
["Branch ID: ", data.branch_id],
|
|
436
|
+
["Database: ", data.db_name],
|
|
437
|
+
["Base URL: ", data.base_url],
|
|
438
|
+
["Created At: ", data.created_at],
|
|
439
|
+
["JWKS URL: ", data.jwks_url]
|
|
440
|
+
]);
|
|
520
441
|
};
|
|
521
442
|
const disable = async (props) => {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}));
|
|
526
|
-
printMessage("Neon Auth has been disabled");
|
|
443
|
+
const branchId = await resolveBranch(props);
|
|
444
|
+
await retryOnLock(() => props.apiClient.disableNeonAuth(props.projectId, branchId, { delete_data: props.deleteData }));
|
|
445
|
+
printMessage("Neon Auth has been disabled");
|
|
527
446
|
};
|
|
528
|
-
|
|
529
|
-
const SHARED_PROVIDER_DISCLAIMER = "Shared keys are created by the Neon team for development only " +
|
|
530
|
-
"and should not be used for production apps. It helps you get started, " +
|
|
531
|
-
"but will show Neon branding (logo and name) on the OAuth consent screen.";
|
|
447
|
+
const SHARED_PROVIDER_DISCLAIMER = "Shared keys are created by the Neon team for development only and should not be used for production apps. It helps you get started, but will show Neon branding (logo and name) on the OAuth consent screen.";
|
|
532
448
|
const oauthProviderList = async (props) => {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (hasShared && props.output === "table") {
|
|
542
|
-
process.stdout.write(`\n${chalk.yellow("Caution:")} ${SHARED_PROVIDER_DISCLAIMER}\n\n`);
|
|
543
|
-
}
|
|
449
|
+
const branchId = await resolveBranch(props);
|
|
450
|
+
const { data } = await props.apiClient.listBranchNeonAuthOauthProviders(props.projectId, branchId);
|
|
451
|
+
if (data.providers.length === 0 && props.output === "table") {
|
|
452
|
+
printMessage("No OAuth providers are configured for this branch.");
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
writer(props).end(data.providers, { fields: OAUTH_PROVIDER_FIELDS });
|
|
456
|
+
if (data.providers.some((p) => p.type === NeonAuthOauthProviderType.Shared) && props.output === "table") process.stdout.write(`\n${chalk.yellow("Caution:")} ${SHARED_PROVIDER_DISCLAIMER}\n\n`);
|
|
544
457
|
};
|
|
545
458
|
const oauthProviderAdd = async (props) => {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
writer(props).end(data, { fields: OAUTH_PROVIDER_FIELDS });
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
printKvBlock("OAuth provider added", [
|
|
569
|
-
["ID: ", data.id],
|
|
570
|
-
["Type: ", data.type],
|
|
571
|
-
...(data.client_id
|
|
572
|
-
? [["Client ID: ", data.client_id]]
|
|
573
|
-
: []),
|
|
574
|
-
]);
|
|
575
|
-
}
|
|
576
|
-
await printCallbackInstructions(props, branchId, props.providerId);
|
|
459
|
+
const branchId = await resolveBranch(props);
|
|
460
|
+
let data;
|
|
461
|
+
try {
|
|
462
|
+
({data} = await props.apiClient.addBranchNeonAuthOauthProvider(props.projectId, branchId, {
|
|
463
|
+
id: props.providerId,
|
|
464
|
+
client_id: props.oauthClientId,
|
|
465
|
+
client_secret: props.oauthClientSecret
|
|
466
|
+
}));
|
|
467
|
+
} catch (err) {
|
|
468
|
+
if (isNeonApiError(err) && codeFromBody(err.data) === "INVALID_SHARED_OAUTH_PROVIDER") throw new Error(`The "${props.providerId}" provider requires your own OAuth app credentials.\nRe-run with --oauth-client-id and --oauth-client-secret to provide them.\nCreate an OAuth app at your provider and use those credentials.`);
|
|
469
|
+
throw err;
|
|
470
|
+
}
|
|
471
|
+
if (props.output === "json" || props.output === "yaml") writer(props).end(data, { fields: OAUTH_PROVIDER_FIELDS });
|
|
472
|
+
else printKvBlock("OAuth provider added", [
|
|
473
|
+
["ID: ", data.id],
|
|
474
|
+
["Type: ", data.type],
|
|
475
|
+
...data.client_id ? [["Client ID: ", data.client_id]] : []
|
|
476
|
+
]);
|
|
477
|
+
await printCallbackInstructions(props, branchId, props.providerId);
|
|
577
478
|
};
|
|
578
479
|
const oauthProviderUpdate = async (props) => {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
writer(props).end(data, { fields: OAUTH_PROVIDER_FIELDS });
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
printKvBlock("OAuth provider updated", [
|
|
601
|
-
["ID: ", data.id],
|
|
602
|
-
["Type: ", data.type],
|
|
603
|
-
...(data.client_id
|
|
604
|
-
? [["Client ID: ", data.client_id]]
|
|
605
|
-
: []),
|
|
606
|
-
]);
|
|
607
|
-
}
|
|
608
|
-
await printCallbackInstructions(props, branchId, props.providerId);
|
|
480
|
+
const branchId = await resolveBranch(props);
|
|
481
|
+
let data;
|
|
482
|
+
try {
|
|
483
|
+
({data} = await props.apiClient.updateBranchNeonAuthOauthProvider(props.projectId, branchId, props.providerId, {
|
|
484
|
+
client_id: props.oauthClientId,
|
|
485
|
+
client_secret: props.oauthClientSecret
|
|
486
|
+
}));
|
|
487
|
+
} catch (err) {
|
|
488
|
+
if (isNeonApiError(err) && codeFromBody(err.data) === "INVALID_SHARED_OAUTH_PROVIDER") throw new Error(`The "${props.providerId}" provider requires your own OAuth app credentials.\nRe-run with --oauth-client-id and --oauth-client-secret to provide them.\nCreate an OAuth app at your provider and use those credentials.`);
|
|
489
|
+
throw err;
|
|
490
|
+
}
|
|
491
|
+
if (props.output === "json" || props.output === "yaml") writer(props).end(data, { fields: OAUTH_PROVIDER_FIELDS });
|
|
492
|
+
else printKvBlock("OAuth provider updated", [
|
|
493
|
+
["ID: ", data.id],
|
|
494
|
+
["Type: ", data.type],
|
|
495
|
+
...data.client_id ? [["Client ID: ", data.client_id]] : []
|
|
496
|
+
]);
|
|
497
|
+
await printCallbackInstructions(props, branchId, props.providerId);
|
|
609
498
|
};
|
|
610
499
|
const CALLBACK_INSTRUCTIONS = {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
500
|
+
github: {
|
|
501
|
+
lead: "Create an OAuth app in the GitHub Developer Portal and add the following authorization callback URL:",
|
|
502
|
+
urlLabel: "callback/github"
|
|
503
|
+
},
|
|
504
|
+
vercel: {
|
|
505
|
+
lead: "Create a Vercel App in your Vercel Dashboard and add the following authorization callback URL:",
|
|
506
|
+
urlLabel: "callback/vercel"
|
|
507
|
+
},
|
|
508
|
+
google: {
|
|
509
|
+
lead: "Get Google credentials by creating an OAuth client in Google Cloud Console > Credentials, and add the following authorized redirect URL:",
|
|
510
|
+
urlLabel: "callback/google"
|
|
511
|
+
}
|
|
623
512
|
};
|
|
624
513
|
const printCallbackInstructions = async (props, branchId, providerId) => {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (!baseUrl)
|
|
639
|
-
return;
|
|
640
|
-
const callbackUrl = `${baseUrl.replace(/\/$/, "")}/${instructions.urlLabel}`;
|
|
641
|
-
printKvBlock(instructions.lead, [["URL: ", callbackUrl]]);
|
|
514
|
+
const instructions = CALLBACK_INSTRUCTIONS[providerId];
|
|
515
|
+
if (!instructions) return;
|
|
516
|
+
if (props.output === "json" || props.output === "yaml") return;
|
|
517
|
+
let baseUrl;
|
|
518
|
+
try {
|
|
519
|
+
const { data } = await props.apiClient.getNeonAuth(props.projectId, branchId);
|
|
520
|
+
baseUrl = data.base_url;
|
|
521
|
+
} catch {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (!baseUrl) return;
|
|
525
|
+
const callbackUrl = `${baseUrl.replace(/\/$/, "")}/${instructions.urlLabel}`;
|
|
526
|
+
printKvBlock(instructions.lead, [["URL: ", callbackUrl]]);
|
|
642
527
|
};
|
|
643
528
|
const oauthProviderDelete = async (props) => {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
529
|
+
const branchId = await resolveBranch(props);
|
|
530
|
+
await props.apiClient.deleteBranchNeonAuthOauthProvider(props.projectId, branchId, props.providerId);
|
|
531
|
+
printMessage(`OAuth provider "${props.providerId}" deleted`);
|
|
647
532
|
};
|
|
648
|
-
// --- Domains ---
|
|
649
533
|
const domainList = async (props) => {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
534
|
+
const branchId = await resolveBranch(props);
|
|
535
|
+
const { data } = await props.apiClient.listBranchNeonAuthTrustedDomains(props.projectId, branchId);
|
|
536
|
+
if (data.domains.length === 0 && props.output === "table") {
|
|
537
|
+
printMessage("No trusted domains are configured for this branch.");
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
writer(props).end(data.domains, { fields: DOMAIN_FIELDS });
|
|
657
541
|
};
|
|
658
542
|
const validateDomainUri = (domain) => {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
if (!["http:", "https:"].includes(url.protocol)) {
|
|
667
|
-
throw new Error(`Invalid domain URI "${domain}". Must use http or https scheme, e.g. https://${url.host}`);
|
|
668
|
-
}
|
|
543
|
+
let url;
|
|
544
|
+
try {
|
|
545
|
+
url = new URL(domain);
|
|
546
|
+
} catch {
|
|
547
|
+
throw new Error(`Invalid domain URI "${domain}". Must be a full URI including scheme, e.g. https://${domain}`);
|
|
548
|
+
}
|
|
549
|
+
if (!["http:", "https:"].includes(url.protocol)) throw new Error(`Invalid domain URI "${domain}". Must use http or https scheme, e.g. https://${url.host}`);
|
|
669
550
|
};
|
|
670
551
|
const domainAdd = async (props) => {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
552
|
+
validateDomainUri(props.domain);
|
|
553
|
+
const branchId = await resolveBranch(props);
|
|
554
|
+
await props.apiClient.addBranchNeonAuthTrustedDomain(props.projectId, branchId, {
|
|
555
|
+
domain: props.domain,
|
|
556
|
+
auth_provider: NeonAuthSupportedAuthProvider.BetterAuth
|
|
557
|
+
});
|
|
558
|
+
printMessage(`Domain "${props.domain}" added`);
|
|
678
559
|
};
|
|
679
560
|
const domainDelete = async (props) => {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
});
|
|
690
|
-
printMessage(`Domain "${props.domain}" deleted`);
|
|
561
|
+
validateDomainUri(props.domain);
|
|
562
|
+
const branchId = await resolveBranch(props);
|
|
563
|
+
const { data: existing } = await props.apiClient.listBranchNeonAuthTrustedDomains(props.projectId, branchId);
|
|
564
|
+
if (!existing.domains.some((d) => d.domain === props.domain)) throw new Error(`Domain "${props.domain}" is not in the trusted domains list.`);
|
|
565
|
+
await props.apiClient.deleteBranchNeonAuthTrustedDomain(props.projectId, branchId, {
|
|
566
|
+
auth_provider: NeonAuthSupportedAuthProvider.BetterAuth,
|
|
567
|
+
domains: [{ domain: props.domain }]
|
|
568
|
+
});
|
|
569
|
+
printMessage(`Domain "${props.domain}" deleted`);
|
|
691
570
|
};
|
|
692
|
-
// --- Allow localhost ---
|
|
693
571
|
const allowLocalhostGet = async (props) => {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
["Allow localhost:", String(data.allow_localhost)],
|
|
702
|
-
]);
|
|
572
|
+
const branchId = await resolveBranch(props);
|
|
573
|
+
const { data } = await props.apiClient.getNeonAuthAllowLocalhost(props.projectId, branchId);
|
|
574
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
575
|
+
writer(props).end(data, { fields: ALLOW_LOCALHOST_FIELDS });
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
printKvBlock("Localhost connection settings", [["Allow localhost:", String(data.allow_localhost)]]);
|
|
703
579
|
};
|
|
704
580
|
const allowLocalhostEnable = async (props) => {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
});
|
|
709
|
-
printMessage("Localhost connections allowed");
|
|
581
|
+
const branchId = await resolveBranch(props);
|
|
582
|
+
await props.apiClient.updateNeonAuthAllowLocalhost(props.projectId, branchId, { allow_localhost: true });
|
|
583
|
+
printMessage("Localhost connections allowed");
|
|
710
584
|
};
|
|
711
585
|
const allowLocalhostDisable = async (props) => {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
});
|
|
716
|
-
printMessage("Localhost connections restricted");
|
|
586
|
+
const branchId = await resolveBranch(props);
|
|
587
|
+
await props.apiClient.updateNeonAuthAllowLocalhost(props.projectId, branchId, { allow_localhost: false });
|
|
588
|
+
printMessage("Localhost connections restricted");
|
|
717
589
|
};
|
|
718
|
-
// --- Email and password ---
|
|
719
590
|
const printEmailPasswordEntries = (data) => [
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
[
|
|
728
|
-
"Send Email On Sign Up: ",
|
|
729
|
-
String(data.send_verification_email_on_sign_up),
|
|
730
|
-
],
|
|
731
|
-
[
|
|
732
|
-
"Send Email On Sign In: ",
|
|
733
|
-
String(data.send_verification_email_on_sign_in),
|
|
734
|
-
],
|
|
735
|
-
["Disable Sign Up: ", String(data.disable_sign_up)],
|
|
591
|
+
["Enabled: ", String(data.enabled)],
|
|
592
|
+
["Verification Method: ", data.email_verification_method],
|
|
593
|
+
["Require Verification: ", String(data.require_email_verification)],
|
|
594
|
+
["Auto Sign In After Verify: ", String(data.auto_sign_in_after_verification)],
|
|
595
|
+
["Send Email On Sign Up: ", String(data.send_verification_email_on_sign_up)],
|
|
596
|
+
["Send Email On Sign In: ", String(data.send_verification_email_on_sign_in)],
|
|
597
|
+
["Disable Sign Up: ", String(data.disable_sign_up)]
|
|
736
598
|
];
|
|
737
599
|
const emailPasswordGet = async (props) => {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
600
|
+
const branchId = await resolveBranch(props);
|
|
601
|
+
const { data } = await props.apiClient.getNeonAuthEmailAndPasswordConfig(props.projectId, branchId);
|
|
602
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
603
|
+
writer(props).end(data, { fields: EMAIL_PASSWORD_FIELDS });
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
printKvBlock("Email & password auth configuration", printEmailPasswordEntries(data));
|
|
745
607
|
};
|
|
746
608
|
const emailPasswordUpdate = async (props) => {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
609
|
+
const branchId = await resolveBranch(props);
|
|
610
|
+
const { data } = await props.apiClient.updateNeonAuthEmailAndPasswordConfig(props.projectId, branchId, {
|
|
611
|
+
enabled: props.enabled,
|
|
612
|
+
email_verification_method: props.emailVerificationMethod,
|
|
613
|
+
require_email_verification: props.requireEmailVerification,
|
|
614
|
+
auto_sign_in_after_verification: props.autoSignInAfterVerification,
|
|
615
|
+
send_verification_email_on_sign_up: props.sendVerificationEmailOnSignUp,
|
|
616
|
+
send_verification_email_on_sign_in: props.sendVerificationEmailOnSignIn,
|
|
617
|
+
disable_sign_up: props.disableSignUp
|
|
618
|
+
});
|
|
619
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
620
|
+
writer(props).end(data, { fields: EMAIL_PASSWORD_FIELDS });
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
printKvBlock("Email & password auth configuration updated", printEmailPasswordEntries(data));
|
|
762
624
|
};
|
|
763
|
-
// --- Email provider ---
|
|
764
625
|
const printEmailProviderEntries = (data) => [
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
["Username: ", data.username],
|
|
774
|
-
]
|
|
775
|
-
: []),
|
|
776
|
-
["Sender Email: ", data.sender_email],
|
|
777
|
-
["Sender Name: ", data.sender_name],
|
|
626
|
+
["Type: ", data.type],
|
|
627
|
+
...data.type === "standard" ? [
|
|
628
|
+
["Host: ", data.host],
|
|
629
|
+
["Port: ", data.port != null ? String(data.port) : void 0],
|
|
630
|
+
["Username: ", data.username]
|
|
631
|
+
] : [],
|
|
632
|
+
["Sender Email: ", data.sender_email],
|
|
633
|
+
["Sender Name: ", data.sender_name]
|
|
778
634
|
];
|
|
779
635
|
const emailProviderGet = async (props) => {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
}
|
|
788
|
-
printKvBlock("Email provider configuration", printEmailProviderEntries(data));
|
|
636
|
+
const branchId = await resolveBranch(props);
|
|
637
|
+
const { data } = await props.apiClient.getNeonAuthEmailProvider(props.projectId, branchId);
|
|
638
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
639
|
+
writer(props).end(data, { fields: EMAIL_PROVIDER_FIELDS });
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
printKvBlock("Email provider configuration", printEmailProviderEntries(data));
|
|
789
643
|
};
|
|
790
644
|
const emailProviderUpdate = async (props) => {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
else {
|
|
810
|
-
config = {
|
|
811
|
-
type: "shared",
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
const { data } = await props.apiClient.updateNeonAuthEmailProvider(props.projectId, branchId, config);
|
|
815
|
-
if (props.output === "json" || props.output === "yaml") {
|
|
816
|
-
writer(props).end(data, {
|
|
817
|
-
fields: EMAIL_PROVIDER_FIELDS,
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
else {
|
|
821
|
-
printKvBlock("Email provider configuration updated", printEmailProviderEntries(data));
|
|
822
|
-
}
|
|
823
|
-
if (warnSharedSender) {
|
|
824
|
-
process.stderr.write(`${chalk.yellow("Warning:")} --sender-email and --sender-name are ignored for the shared email provider. ` +
|
|
825
|
-
`These values only take effect with --type standard.\n\n`);
|
|
826
|
-
}
|
|
645
|
+
if (props.type === "standard" && (!props.host || !props.port || !props.username || !props.password)) throw new Error("--host, --port, --username, and --password are required for standard email provider");
|
|
646
|
+
const warnSharedSender = props.type === "shared" && (props.senderEmail || props.senderName);
|
|
647
|
+
const branchId = await resolveBranch(props);
|
|
648
|
+
let config;
|
|
649
|
+
if (props.type === "standard") config = {
|
|
650
|
+
type: "standard",
|
|
651
|
+
host: props.host,
|
|
652
|
+
port: props.port,
|
|
653
|
+
username: props.username,
|
|
654
|
+
password: props.password,
|
|
655
|
+
sender_email: props.senderEmail,
|
|
656
|
+
sender_name: props.senderName
|
|
657
|
+
};
|
|
658
|
+
else config = { type: "shared" };
|
|
659
|
+
const { data } = await props.apiClient.updateNeonAuthEmailProvider(props.projectId, branchId, config);
|
|
660
|
+
if (props.output === "json" || props.output === "yaml") writer(props).end(data, { fields: EMAIL_PROVIDER_FIELDS });
|
|
661
|
+
else printKvBlock("Email provider configuration updated", printEmailProviderEntries(data));
|
|
662
|
+
if (warnSharedSender) process.stderr.write(`${chalk.yellow("Warning:")} --sender-email and --sender-name are ignored for the shared email provider. These values only take effect with --type standard.\n\n`);
|
|
827
663
|
};
|
|
828
664
|
const emailProviderTest = async (props) => {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
else if (data.success) {
|
|
843
|
-
printMessage("Test email sent successfully");
|
|
844
|
-
}
|
|
845
|
-
else {
|
|
846
|
-
process.stdout.write(`\n${chalk.red("Test email failed")}\n ${data.error_message ?? "Unknown error"}\n\n`);
|
|
847
|
-
}
|
|
665
|
+
const branchId = await resolveBranch(props);
|
|
666
|
+
const { data } = await props.apiClient.sendNeonAuthTestEmail(props.projectId, branchId, {
|
|
667
|
+
recipient_email: props.recipientEmail,
|
|
668
|
+
host: props.host,
|
|
669
|
+
port: props.port,
|
|
670
|
+
username: props.username,
|
|
671
|
+
password: props.password,
|
|
672
|
+
sender_email: props.senderEmail,
|
|
673
|
+
sender_name: props.senderName
|
|
674
|
+
});
|
|
675
|
+
if (props.output === "json" || props.output === "yaml") writer(props).end(data, { fields: TEST_EMAIL_FIELDS });
|
|
676
|
+
else if (data.success) printMessage("Test email sent successfully");
|
|
677
|
+
else process.stdout.write(`\n${chalk.red("Test email failed")}\n ${data.error_message ?? "Unknown error"}\n\n`);
|
|
848
678
|
};
|
|
849
|
-
// --- Organization plugin ---
|
|
850
679
|
const printOrganizationEntries = (data) => [
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
680
|
+
["Enabled: ", String(data.enabled)],
|
|
681
|
+
["Org Limit: ", String(data.organization_limit)],
|
|
682
|
+
["Creator Role: ", data.creator_role]
|
|
854
683
|
];
|
|
855
684
|
const organizationGet = async (props) => {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
printKvBlock("Organization configuration", printOrganizationEntries(org));
|
|
685
|
+
const branchId = await resolveBranch(props);
|
|
686
|
+
const { data } = await props.apiClient.getNeonAuthPluginConfigs(props.projectId, branchId);
|
|
687
|
+
const org = data.organization;
|
|
688
|
+
if (!org) {
|
|
689
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
690
|
+
writer(props).end({}, { fields: ORGANIZATION_FIELDS });
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
printMessage("No organization plugin config found.");
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
697
|
+
writer(props).end(org, { fields: ORGANIZATION_FIELDS });
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
printKvBlock("Organization configuration", printOrganizationEntries(org));
|
|
874
701
|
};
|
|
875
702
|
const organizationUpdate = async (props) => {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
703
|
+
const branchId = await resolveBranch(props);
|
|
704
|
+
const { data } = await props.apiClient.updateNeonAuthOrganizationPlugin(props.projectId, branchId, {
|
|
705
|
+
enabled: props.enabled,
|
|
706
|
+
organization_limit: props.limit,
|
|
707
|
+
creator_role: props.creatorRole
|
|
708
|
+
});
|
|
709
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
710
|
+
writer(props).end(data, { fields: ORGANIZATION_FIELDS });
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
printKvBlock("Organization configuration updated", printOrganizationEntries(data));
|
|
887
714
|
};
|
|
888
|
-
// --- Webhook ---
|
|
889
715
|
const printWebhookEntries = (data) => [
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
"Timeout (sec): ",
|
|
895
|
-
data.timeout_seconds != null ? String(data.timeout_seconds) : "",
|
|
896
|
-
],
|
|
716
|
+
["Enabled: ", String(data.enabled)],
|
|
717
|
+
["URL: ", data.webhook_url ?? ""],
|
|
718
|
+
["Events: ", (data.enabled_events ?? []).join(", ")],
|
|
719
|
+
["Timeout (sec): ", data.timeout_seconds != null ? String(data.timeout_seconds) : ""]
|
|
897
720
|
];
|
|
898
721
|
const webhookGet = async (props) => {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
722
|
+
const branchId = await resolveBranch(props);
|
|
723
|
+
const { data } = await props.apiClient.getNeonAuthWebhookConfig(props.projectId, branchId);
|
|
724
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
725
|
+
writer(props).end(data, { fields: WEBHOOK_FIELDS });
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
printKvBlock("Webhook configuration", printWebhookEntries(data));
|
|
906
729
|
};
|
|
907
730
|
const webhookUpdate = async (props) => {
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
const pluginTitle = (name) => name.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()) +
|
|
923
|
-
" configuration";
|
|
731
|
+
const branchId = await resolveBranch(props);
|
|
732
|
+
const { data } = await props.apiClient.updateNeonAuthWebhookConfig(props.projectId, branchId, {
|
|
733
|
+
enabled: props.enabled,
|
|
734
|
+
webhook_url: props.url,
|
|
735
|
+
enabled_events: props.enabledEvents,
|
|
736
|
+
timeout_seconds: props.timeout
|
|
737
|
+
});
|
|
738
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
739
|
+
writer(props).end(data, { fields: WEBHOOK_FIELDS });
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
printKvBlock("Webhook configuration updated", printWebhookEntries(data));
|
|
743
|
+
};
|
|
744
|
+
const pluginTitle = (name) => name.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()) + " configuration";
|
|
924
745
|
const formatValue = (v) => {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
if (typeof v === "number" || typeof v === "boolean")
|
|
930
|
-
return String(v);
|
|
931
|
-
return JSON.stringify(v);
|
|
746
|
+
if (v == null) return "";
|
|
747
|
+
if (typeof v === "string") return v;
|
|
748
|
+
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
749
|
+
return JSON.stringify(v);
|
|
932
750
|
};
|
|
933
751
|
const pluginsList = async (props) => {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
if (typeof value === "object") {
|
|
956
|
-
const obj = value;
|
|
957
|
-
if ("enabled" in obj)
|
|
958
|
-
return obj.enabled ? "enabled" : "disabled";
|
|
959
|
-
if ("type" in obj)
|
|
960
|
-
return formatValue(obj.type);
|
|
961
|
-
}
|
|
962
|
-
return JSON.stringify(value);
|
|
963
|
-
};
|
|
964
|
-
const entries = Object.entries(data).map(([key, value]) => [key.padEnd(24), summarize(value)]);
|
|
965
|
-
printKvBlock("Neon Auth plugins", entries);
|
|
752
|
+
const branchId = await resolveBranch(props);
|
|
753
|
+
const { data } = await props.apiClient.getNeonAuthPluginConfigs(props.projectId, branchId);
|
|
754
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
755
|
+
writer(props).end(data, { fields: Object.keys(data) });
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
const summarize = (value) => {
|
|
759
|
+
if (value == null) return "not configured";
|
|
760
|
+
if (typeof value === "boolean") return String(value);
|
|
761
|
+
if (typeof value === "string") return value;
|
|
762
|
+
if (typeof value === "number") return String(value);
|
|
763
|
+
if (Array.isArray(value)) return value.length === 1 ? "1 item" : `${String(value.length)} items`;
|
|
764
|
+
if (typeof value === "object") {
|
|
765
|
+
const obj = value;
|
|
766
|
+
if ("enabled" in obj) return obj.enabled ? "enabled" : "disabled";
|
|
767
|
+
if ("type" in obj) return formatValue(obj.type);
|
|
768
|
+
}
|
|
769
|
+
return JSON.stringify(value);
|
|
770
|
+
};
|
|
771
|
+
const entries = Object.entries(data).map(([key, value]) => [key.padEnd(24), summarize(value)]);
|
|
772
|
+
printKvBlock("Neon Auth plugins", entries);
|
|
966
773
|
};
|
|
967
774
|
const pluginsGet = async (props) => {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
}
|
|
988
|
-
else if (Array.isArray(plugin)) {
|
|
989
|
-
const entries = plugin.map((item, i) => [
|
|
990
|
-
`[${i}]:`.padEnd(18),
|
|
991
|
-
typeof item === "object" ? JSON.stringify(item) : String(item),
|
|
992
|
-
]);
|
|
993
|
-
printKvBlock(pluginTitle(props.pluginName), entries);
|
|
994
|
-
}
|
|
995
|
-
else {
|
|
996
|
-
printKvBlock(pluginTitle(props.pluginName), [
|
|
997
|
-
["Value:".padEnd(18), String(plugin)],
|
|
998
|
-
]);
|
|
999
|
-
}
|
|
775
|
+
const branchId = await resolveBranch(props);
|
|
776
|
+
const { data } = await props.apiClient.getNeonAuthPluginConfigs(props.projectId, branchId);
|
|
777
|
+
const plugin = data[props.pluginName];
|
|
778
|
+
if (plugin === void 0) {
|
|
779
|
+
const available = Object.keys(data).join(", ");
|
|
780
|
+
throw new Error(`Unknown plugin "${props.pluginName}". Available plugins: ${available}`);
|
|
781
|
+
}
|
|
782
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
783
|
+
const fields = typeof plugin === "object" && !Array.isArray(plugin) ? Object.keys(plugin) : [];
|
|
784
|
+
writer(props).end(plugin, { fields });
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (typeof plugin === "object" && !Array.isArray(plugin) && plugin != null) {
|
|
788
|
+
const entries = Object.entries(plugin).map(([k, v]) => [`${k}:`.padEnd(18), formatValue(v)]);
|
|
789
|
+
printKvBlock(pluginTitle(props.pluginName), entries);
|
|
790
|
+
} else if (Array.isArray(plugin)) {
|
|
791
|
+
const entries = plugin.map((item, i) => [`[${i}]:`.padEnd(18), typeof item === "object" ? JSON.stringify(item) : String(item)]);
|
|
792
|
+
printKvBlock(pluginTitle(props.pluginName), entries);
|
|
793
|
+
} else printKvBlock(pluginTitle(props.pluginName), [["Value:".padEnd(18), String(plugin)]]);
|
|
1000
794
|
};
|
|
1001
|
-
// --- User ---
|
|
1002
795
|
const userCreate = async (props) => {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
796
|
+
const branchId = await resolveBranch(props);
|
|
797
|
+
const requestBody = {
|
|
798
|
+
email: props.email,
|
|
799
|
+
name: props.name ?? props.email
|
|
800
|
+
};
|
|
801
|
+
const { data } = await props.apiClient.createBranchNeonAuthNewUser(props.projectId, branchId, requestBody);
|
|
802
|
+
const displayName = requestBody.name !== props.email ? requestBody.name : void 0;
|
|
803
|
+
printKvBlock("User created", [
|
|
804
|
+
["ID: ", data.id],
|
|
805
|
+
["Email: ", requestBody.email],
|
|
806
|
+
...displayName ? [["Name: ", displayName]] : []
|
|
807
|
+
]);
|
|
1015
808
|
};
|
|
1016
809
|
const userDelete = async (props) => {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
810
|
+
const branchId = await resolveBranch(props);
|
|
811
|
+
await props.apiClient.deleteBranchNeonAuthUser(props.projectId, branchId, props.userId);
|
|
812
|
+
printMessage(`User "${props.userId}" deleted`);
|
|
1020
813
|
};
|
|
1021
814
|
const userSetRole = async (props) => {
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
["User ID: ", data.id],
|
|
1026
|
-
["Roles: ", props.roles.join(", ")],
|
|
1027
|
-
]);
|
|
815
|
+
const branchId = await resolveBranch(props);
|
|
816
|
+
const { data } = await props.apiClient.updateNeonAuthUserRole(props.projectId, branchId, props.userId, { roles: props.roles });
|
|
817
|
+
printKvBlock("Roles updated", [["User ID: ", data.id], ["Roles: ", props.roles.join(", ")]]);
|
|
1028
818
|
};
|
|
819
|
+
//#endregion
|
|
820
|
+
export { builder, command, describe, handler, neon_auth_exports as t };
|