@cedarjs/cli 7.0.0-canary.3073 → 7.0.0-canary.3075

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.
@@ -75,12 +75,14 @@ const files = async ({
75
75
  skipReset,
76
76
  skipSignup,
77
77
  webauthn,
78
+ oauth,
78
79
  usernameLabel,
79
80
  passwordLabel
80
81
  }) => {
81
82
  const filesList = [];
82
83
  usernameLabel = usernameLabel || "username";
83
84
  passwordLabel = passwordLabel || "password";
85
+ const oauthEnabled = oauth ?? isOAuthEnabled();
84
86
  const templateVars = {
85
87
  usernameLowerCase: usernameLabel.toLowerCase(),
86
88
  usernameCamelCase: camelCase(usernameLabel),
@@ -110,7 +112,7 @@ const files = async ({
110
112
  extension: typescript ? ".tsx" : ".jsx",
111
113
  webPathSection: "pages",
112
114
  generator: "dbAuth",
113
- templatePath: webauthn ? "login.webAuthn.tsx.template" : "login.tsx.template",
115
+ templatePath: loginTemplatePath(webauthn, oauthEnabled),
114
116
  templateVars
115
117
  })
116
118
  );
@@ -136,7 +138,7 @@ const files = async ({
136
138
  extension: typescript ? ".tsx" : ".jsx",
137
139
  webPathSection: "pages",
138
140
  generator: "dbAuth",
139
- templatePath: "signup.tsx.template",
141
+ templatePath: oauthEnabled ? "signup.oauth.tsx.template" : "signup.tsx.template",
140
142
  templateVars
141
143
  })
142
144
  );
@@ -378,6 +380,23 @@ function isWebAuthnEnabled() {
378
380
  );
379
381
  return webPackageJson.includes('"@simplewebauthn/browser": ');
380
382
  }
383
+ function isOAuthEnabled() {
384
+ const apiPackageJsonPath = path.join(getPaths().base, "api", "package.json");
385
+ if (!fs.existsSync(apiPackageJsonPath)) {
386
+ return false;
387
+ }
388
+ const apiPackageJson = fs.readFileSync(apiPackageJsonPath, "utf-8");
389
+ return apiPackageJson.includes('"@cedarjs/auth-dbauth-oauth": ');
390
+ }
391
+ function loginTemplatePath(webauthn, oauth) {
392
+ if (webauthn) {
393
+ return "login.webAuthn.tsx.template";
394
+ }
395
+ if (oauth) {
396
+ return "login.oauth.tsx.template";
397
+ }
398
+ return "login.tsx.template";
399
+ }
381
400
  export {
382
401
  files,
383
402
  handler
@@ -0,0 +1,164 @@
1
+ import { useEffect, useRef } from 'react'
2
+
3
+ import {
4
+ Form,
5
+ Label,
6
+ TextField,
7
+ PasswordField,
8
+ Submit,
9
+ FieldError,
10
+ } from '@cedarjs/forms'
11
+ import { Link, navigate, routes } from '@cedarjs/router'
12
+ import { Metadata } from '@cedarjs/web'
13
+ import { toast, Toaster } from '@cedarjs/web/toast'
14
+
15
+ import { getOAuthUrl, getOAuthError } from '@cedarjs/auth-dbauth-web/oauth'
16
+
17
+ import { useAuth } from 'src/auth'
18
+
19
+ const LoginPage = () => {
20
+ const { isAuthenticated, logIn } = useAuth()
21
+
22
+ useEffect(() => {
23
+ if (isAuthenticated) {
24
+ navigate(routes.home())
25
+ }
26
+ }, [isAuthenticated])
27
+
28
+ // Surface an error that the OAuth provider redirect sent back, e.g.
29
+ // after the user cancels the provider's consent screen.
30
+ useEffect(() => {
31
+ const oauthError = getOAuthError(window.location.search)
32
+
33
+ if (oauthError) {
34
+ toast.error('Could not log in: ' + oauthError)
35
+ }
36
+ }, [])
37
+
38
+ const ${usernameCamelCase}Ref = useRef<HTMLInputElement>(null)
39
+ useEffect(() => {
40
+ ${usernameCamelCase}Ref.current?.focus()
41
+ }, [])
42
+
43
+ const onSubmit = async (data: Record<string, string>) => {
44
+ const response = await logIn({ username: data.${usernameCamelCase}, password: data.${passwordCamelCase} })
45
+
46
+ if (response.message) {
47
+ toast(response.message)
48
+ } else if (response.error) {
49
+ toast.error(response.error)
50
+ } else {
51
+ toast.success('Welcome back!')
52
+ }
53
+ }
54
+
55
+ return (
56
+ <>
57
+ <Metadata title="Login" />
58
+
59
+ <main className="rw-main">
60
+ <Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
61
+ <div className="rw-scaffold rw-login-container">
62
+ <div className="rw-segment">
63
+ <header className="rw-segment-header">
64
+ <h2 className="rw-heading rw-heading-secondary">Login</h2>
65
+ </header>
66
+
67
+ <div className="rw-segment-main">
68
+ <div className="rw-form-wrapper">
69
+ <Form onSubmit={onSubmit} className="rw-form-wrapper">
70
+ <Label
71
+ name="${usernameCamelCase}"
72
+ className="rw-label"
73
+ errorClassName="rw-label rw-label-error"
74
+ >
75
+ ${usernameTitleCase}
76
+ </Label>
77
+ <TextField
78
+ name="${usernameCamelCase}"
79
+ className="rw-input"
80
+ errorClassName="rw-input rw-input-error"
81
+ ref={${usernameCamelCase}Ref}
82
+ validation={{
83
+ required: {
84
+ value: true,
85
+ message: '${usernameTitleCase} is required',
86
+ },
87
+ }}
88
+ />
89
+
90
+ <FieldError name="${usernameCamelCase}" className="rw-field-error" />
91
+
92
+ <Label
93
+ name="${passwordCamelCase}"
94
+ className="rw-label"
95
+ errorClassName="rw-label rw-label-error"
96
+ >
97
+ ${passwordTitleCase}
98
+ </Label>
99
+ <PasswordField
100
+ name="${passwordCamelCase}"
101
+ className="rw-input"
102
+ errorClassName="rw-input rw-input-error"
103
+ autoComplete="current-password"
104
+ validation={{
105
+ required: {
106
+ value: true,
107
+ message: '${passwordTitleCase} is required',
108
+ },
109
+ }}
110
+ />
111
+
112
+ <div className="rw-forgot-link">
113
+ <Link
114
+ to={routes.forgotPassword()}
115
+ className="rw-forgot-link"
116
+ >
117
+ Forgot ${passwordTitleCase}?
118
+ </Link>
119
+ </div>
120
+
121
+ <FieldError name="${passwordCamelCase}" className="rw-field-error" />
122
+
123
+ <div className="rw-button-group">
124
+ <Submit className="rw-button rw-button-blue">Login</Submit>
125
+ </div>
126
+ </Form>
127
+
128
+ {/*
129
+ * OAuth login buttons. Delete the ones for providers you
130
+ * haven't configured in `cedar setup auth dbAuth --oauth`.
131
+ * Each link is a full-page navigation to the provider's
132
+ * authorize route — it must not be turned into a fetch/XHR
133
+ * call.
134
+ */}
135
+ <div className="rw-button-group">
136
+ <a
137
+ href={getOAuthUrl('google', { flow: 'login' })}
138
+ className="rw-button rw-button-blue"
139
+ >
140
+ Continue with Google
141
+ </a>
142
+ <a
143
+ href={getOAuthUrl('github', { flow: 'login' })}
144
+ className="rw-button rw-button-blue"
145
+ >
146
+ Continue with GitHub
147
+ </a>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ <div className="rw-login-link">
153
+ <span>Don&apos;t have an account?</span>{' '}
154
+ <Link to={routes.signup()} className="rw-link">
155
+ Sign up!
156
+ </Link>
157
+ </div>
158
+ </div>
159
+ </main>
160
+ </>
161
+ )
162
+ }
163
+
164
+ export default LoginPage
@@ -0,0 +1,147 @@
1
+ import { useEffect, useRef } from 'react'
2
+
3
+ import {
4
+ Form,
5
+ Label,
6
+ TextField,
7
+ PasswordField,
8
+ FieldError,
9
+ Submit,
10
+ } from '@cedarjs/forms'
11
+ import { Link, navigate, routes } from '@cedarjs/router'
12
+ import { Metadata } from '@cedarjs/web'
13
+ import { toast, Toaster } from '@cedarjs/web/toast'
14
+
15
+ import { getOAuthUrl } from '@cedarjs/auth-dbauth-web/oauth'
16
+
17
+ import { useAuth } from 'src/auth'
18
+
19
+ const SignupPage = () => {
20
+ const { isAuthenticated, signUp } = useAuth()
21
+
22
+ useEffect(() => {
23
+ if (isAuthenticated) {
24
+ navigate(routes.home())
25
+ }
26
+ }, [isAuthenticated])
27
+
28
+ // focus on ${usernameLowerCase} box on page load
29
+ const ${usernameCamelCase}Ref = useRef<HTMLInputElement>(null)
30
+ useEffect(() => {
31
+ ${usernameCamelCase}Ref.current?.focus()
32
+ }, [])
33
+
34
+ const onSubmit = async (data: Record<string, string>) => {
35
+ const response = await signUp({ username: data.${usernameCamelCase}, password: data.${passwordCamelCase} })
36
+
37
+ if (response.message) {
38
+ toast(response.message)
39
+ } else if (response.error) {
40
+ toast.error(response.error)
41
+ } else {
42
+ // user is signed in automatically
43
+ toast.success('Welcome!')
44
+ }
45
+ }
46
+
47
+ return (
48
+ <>
49
+ <Metadata title="Signup" />
50
+
51
+ <main className="rw-main">
52
+ <Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
53
+ <div className="rw-scaffold rw-login-container">
54
+ <div className="rw-segment">
55
+ <header className="rw-segment-header">
56
+ <h2 className="rw-heading rw-heading-secondary">Signup</h2>
57
+ </header>
58
+
59
+ <div className="rw-segment-main">
60
+ <div className="rw-form-wrapper">
61
+ <Form onSubmit={onSubmit} className="rw-form-wrapper">
62
+ <Label
63
+ name="${usernameCamelCase}"
64
+ className="rw-label"
65
+ errorClassName="rw-label rw-label-error"
66
+ >
67
+ ${usernameTitleCase}
68
+ </Label>
69
+ <TextField
70
+ name="${usernameCamelCase}"
71
+ className="rw-input"
72
+ errorClassName="rw-input rw-input-error"
73
+ ref={${usernameCamelCase}Ref}
74
+ validation={{
75
+ required: {
76
+ value: true,
77
+ message: '${usernameTitleCase} is required',
78
+ },
79
+ }}
80
+ />
81
+ <FieldError name="${usernameCamelCase}" className="rw-field-error" />
82
+
83
+ <Label
84
+ name="${passwordCamelCase}"
85
+ className="rw-label"
86
+ errorClassName="rw-label rw-label-error"
87
+ >
88
+ ${passwordTitleCase}
89
+ </Label>
90
+ <PasswordField
91
+ name="${passwordCamelCase}"
92
+ className="rw-input"
93
+ errorClassName="rw-input rw-input-error"
94
+ autoComplete="current-password"
95
+ validation={{
96
+ required: {
97
+ value: true,
98
+ message: '${passwordTitleCase} is required',
99
+ },
100
+ }}
101
+ />
102
+ <FieldError name="${passwordCamelCase}" className="rw-field-error" />
103
+
104
+ <div className="rw-button-group">
105
+ <Submit className="rw-button rw-button-blue">
106
+ Sign Up
107
+ </Submit>
108
+ </div>
109
+ </Form>
110
+
111
+ {/*
112
+ * OAuth signup buttons. Delete the ones for providers you
113
+ * haven't configured in `cedar setup auth dbAuth --oauth`.
114
+ * Each link is a full-page navigation to the provider's
115
+ * authorize route — it must not be turned into a fetch/XHR
116
+ * call.
117
+ */}
118
+ <div className="rw-button-group">
119
+ <a
120
+ href={getOAuthUrl('google', { flow: 'signup' })}
121
+ className="rw-button rw-button-blue"
122
+ >
123
+ Continue with Google
124
+ </a>
125
+ <a
126
+ href={getOAuthUrl('github', { flow: 'signup' })}
127
+ className="rw-button rw-button-blue"
128
+ >
129
+ Continue with GitHub
130
+ </a>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ <div className="rw-login-link">
136
+ <span>Already have an account?</span>{' '}
137
+ <Link to={routes.login()} className="rw-link">
138
+ Log in!
139
+ </Link>
140
+ </div>
141
+ </div>
142
+ </main>
143
+ </>
144
+ )
145
+ }
146
+
147
+ export default SignupPage
@@ -126,9 +126,19 @@ const builder = async (yargs) => {
126
126
  const targetPath = url.pathname.slice(apiUrl.length) || "/";
127
127
  const root = apiRootPath === "/" ? "" : apiRootPath;
128
128
  const targetUrl = `${apiTarget}${root}${targetPath}${url.search}`;
129
+ const forwardedHeaders = new Headers(req.headers);
130
+ if (!forwardedHeaders.has("x-forwarded-host")) {
131
+ forwardedHeaders.set(
132
+ "x-forwarded-host",
133
+ req.headers.get("host") || url.host
134
+ );
135
+ }
136
+ if (!forwardedHeaders.has("x-forwarded-proto")) {
137
+ forwardedHeaders.set("x-forwarded-proto", "http");
138
+ }
129
139
  return fetch(targetUrl, {
130
140
  method: req.method,
131
- headers: req.headers,
141
+ headers: forwardedHeaders,
132
142
  body: req.body,
133
143
  // @ts-expect-error - `duplex` is required when forwarding a
134
144
  // request body via fetch (Node 18+).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cedarjs/cli",
3
- "version": "7.0.0-canary.3073",
3
+ "version": "7.0.0-canary.3075",
4
4
  "description": "The CedarJS Command Line",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,17 +36,17 @@
36
36
  "@babel/preset-typescript": "7.29.7",
37
37
  "@babel/traverse": "7.29.8",
38
38
  "@babel/types": "7.29.8",
39
- "@cedarjs/api-server": "7.0.0-canary.3073",
40
- "@cedarjs/babel-config": "7.0.0-canary.3073",
41
- "@cedarjs/cli-helpers": "7.0.0-canary.3073",
42
- "@cedarjs/internal": "7.0.0-canary.3073",
43
- "@cedarjs/prerender": "7.0.0-canary.3073",
44
- "@cedarjs/project-config": "7.0.0-canary.3073",
45
- "@cedarjs/structure": "7.0.0-canary.3073",
46
- "@cedarjs/telemetry": "7.0.0-canary.3073",
47
- "@cedarjs/utils": "7.0.0-canary.3073",
48
- "@cedarjs/vite": "7.0.0-canary.3073",
49
- "@cedarjs/web-server": "7.0.0-canary.3073",
39
+ "@cedarjs/api-server": "7.0.0-canary.3075",
40
+ "@cedarjs/babel-config": "7.0.0-canary.3075",
41
+ "@cedarjs/cli-helpers": "7.0.0-canary.3075",
42
+ "@cedarjs/internal": "7.0.0-canary.3075",
43
+ "@cedarjs/prerender": "7.0.0-canary.3075",
44
+ "@cedarjs/project-config": "7.0.0-canary.3075",
45
+ "@cedarjs/structure": "7.0.0-canary.3075",
46
+ "@cedarjs/telemetry": "7.0.0-canary.3075",
47
+ "@cedarjs/utils": "7.0.0-canary.3075",
48
+ "@cedarjs/vite": "7.0.0-canary.3075",
49
+ "@cedarjs/web-server": "7.0.0-canary.3075",
50
50
  "@listr2/prompt-adapter-enquirer": "4.3.1",
51
51
  "@opentelemetry/api": "1.9.1",
52
52
  "@opentelemetry/core": "1.30.1",
@@ -94,7 +94,7 @@
94
94
  "yargs": "17.7.3"
95
95
  },
96
96
  "devDependencies": {
97
- "@cedarjs/framework-tools": "7.0.0-canary.3073",
97
+ "@cedarjs/framework-tools": "7.0.0-canary.3075",
98
98
  "@prisma/dmmf": "7.10.0",
99
99
  "@types/archiver": "^7.0.0",
100
100
  "memfs": "4.68.2",