@canva/cli 0.0.1-beta.2 → 0.0.1-beta.3

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.
Files changed (35) hide show
  1. package/package.json +1 -1
  2. package/templates/base/backend/routers/oauth.ts +393 -0
  3. package/templates/base/package.json +1 -1
  4. package/templates/base/utils/backend/bearer_middleware/bearer_middleware.ts +101 -0
  5. package/templates/base/utils/backend/bearer_middleware/index.ts +1 -0
  6. package/templates/base/utils/backend/bearer_middleware/tests/bearer_middleware.tests.ts +192 -0
  7. package/templates/base/utils/use_add_element.ts +48 -0
  8. package/templates/base/utils/use_feature_support.ts +28 -0
  9. package/templates/common/README.md +0 -67
  10. package/templates/dam/backend/server.ts +0 -7
  11. package/templates/dam/package.json +6 -6
  12. package/templates/dam/src/app.tsx +2 -135
  13. package/templates/gen_ai/README.md +40 -1
  14. package/templates/gen_ai/backend/routers/oauth.ts +393 -0
  15. package/templates/gen_ai/backend/server.ts +1 -1
  16. package/templates/gen_ai/package.json +5 -5
  17. package/templates/gen_ai/src/api/api.ts +44 -27
  18. package/templates/gen_ai/src/components/footer.tsx +9 -5
  19. package/templates/gen_ai/src/components/image_grid.tsx +8 -6
  20. package/templates/gen_ai/src/components/loading_results.tsx +8 -4
  21. package/templates/gen_ai/src/context/app_context.tsx +8 -2
  22. package/templates/gen_ai/src/services/auth.tsx +5 -10
  23. package/templates/gen_ai/utils/backend/bearer_middleware/bearer_middleware.ts +101 -0
  24. package/templates/gen_ai/utils/backend/bearer_middleware/index.ts +1 -0
  25. package/templates/gen_ai/utils/backend/bearer_middleware/tests/bearer_middleware.tests.ts +192 -0
  26. package/templates/hello_world/package.json +3 -2
  27. package/templates/hello_world/src/app.tsx +4 -2
  28. package/templates/hello_world/utils/use_add_element.ts +48 -0
  29. package/templates/hello_world/utils/use_feature_support.ts +28 -0
  30. package/templates/dam/backend/database/database.ts +0 -42
  31. package/templates/dam/backend/routers/auth.ts +0 -285
  32. package/templates/gen_ai/backend/routers/auth.ts +0 -285
  33. package/templates/gen_ai/utils/backend/jwt_middleware/index.ts +0 -1
  34. package/templates/gen_ai/utils/backend/jwt_middleware/jwt_middleware.ts +0 -229
  35. package/templates/gen_ai/utils/backend/jwt_middleware/tests/jwt_middleware.tests.ts +0 -630
@@ -0,0 +1,48 @@
1
+ import type {
2
+ EmbedElement,
3
+ ImageElement,
4
+ RichtextElement,
5
+ TableElement,
6
+ TextElement,
7
+ VideoElement,
8
+ } from "@canva/design";
9
+ import { addElementAtCursor, addElementAtPoint } from "@canva/design";
10
+ import { useFeatureSupport } from "./use_feature_support";
11
+ import { features } from "@canva/platform";
12
+ import { useEffect, useState } from "react";
13
+
14
+ type AddElementParams =
15
+ | ImageElement
16
+ | VideoElement
17
+ | EmbedElement
18
+ | TextElement
19
+ | RichtextElement
20
+ | TableElement;
21
+
22
+ export const useAddElement = () => {
23
+ const isSupported = useFeatureSupport();
24
+
25
+ // Store a wrapped addElement function that checks feature support
26
+ const [addElement, setAddElement] = useState(() => {
27
+ return (element: AddElementParams) => {
28
+ if (features.isSupported(addElementAtPoint)) {
29
+ return addElementAtPoint(element);
30
+ } else if (features.isSupported(addElementAtCursor)) {
31
+ return addElementAtCursor(element);
32
+ }
33
+ };
34
+ });
35
+
36
+ useEffect(() => {
37
+ const addElement = (element: AddElementParams) => {
38
+ if (isSupported(addElementAtPoint)) {
39
+ return addElementAtPoint(element);
40
+ } else if (isSupported(addElementAtCursor)) {
41
+ return addElementAtCursor(element);
42
+ }
43
+ };
44
+ setAddElement(() => addElement);
45
+ }, [isSupported]);
46
+
47
+ return addElement;
48
+ };
@@ -0,0 +1,28 @@
1
+ import { features } from "@canva/platform";
2
+ import type { Feature } from "@canva/platform";
3
+ import { useState, useEffect } from "react";
4
+
5
+ /**
6
+ * This hook allows re-rendering of a React component whenever
7
+ * the state of feature support changes in Canva.
8
+ *
9
+ * @returns isSupported - callback to inspect a Canva SDK method.
10
+ **/
11
+ export function useFeatureSupport() {
12
+ // Store a wrapped function that checks feature support
13
+ const [isSupported, setIsSupported] = useState(() => {
14
+ return (...args: Feature[]) => features.isSupported(...args);
15
+ });
16
+
17
+ useEffect(() => {
18
+ // create new function ref when feature support changes to trigger
19
+ // re-render
20
+ return features.registerOnSupportChange(() => {
21
+ setIsSupported(() => {
22
+ return (...args: Feature[]) => features.isSupported(...args);
23
+ });
24
+ });
25
+ }, []);
26
+
27
+ return isSupported;
28
+ }
@@ -181,70 +181,3 @@ To use ngrok, you'll need to do the following:
181
181
  ```
182
182
 
183
183
  This environment variable is available for the current terminal session, so the command must be re-run for each new session. Alternatively, you can add the variable to your terminal's default parameters.
184
-
185
- ## Run the development server with ngrok and add authentication to the app
186
-
187
- These steps demonstrate how to start the local development server with ngrok.
188
-
189
- From your app's root directory
190
-
191
- 1. Stop any running scripts, and run the following command to launch the backend and frontend development servers. The `--ngrok` parameter exposes the backend server via a publicly accessible URL.
192
-
193
- ```bash
194
- npm start --ngrok
195
- ```
196
-
197
- 2. After ngrok is running, copy your ngrok url
198
- (e.g. `https://0000-0000.ngrok-free.app`) to the clipboard.
199
-
200
- 1. Go to your app in the [Developer Portal](https://www.canva.com/developers/apps).
201
- 2. Navigate to the "Add authentication" section of your app.
202
- 3. Check "This app requires authentication"
203
- 4. In the "Redirect URL" text box, enter your ngrok url followed by `/redirect-url` e.g.
204
- `https://0000-0000.ngrok-free.app/redirect-url`
205
- 5. In the "Authentication base URL" text box, enter your ngrok url followed by `/` e.g.
206
- `https://0000-0000.ngrok-free.app/`
207
- Note: Your ngrok URL changes each time you restart ngrok. Keep these fields up to
208
- date to ensure your example authentication step will run.
209
-
210
- 3. Make sure the app is authenticating users by making the following changes:
211
-
212
- 1. Replace
213
-
214
- `router.post("/resources/find", async (req, res) => {`
215
-
216
- with
217
-
218
- `router.post("/api/resources/find", async (req, res) => {`
219
-
220
- in [./backend/routers/auth.ts](./backend/routers/auth.ts). Adding `/api/` to the route ensures
221
- the JWT middleware authenticates requests.
222
-
223
- 2. Replace
224
-
225
- ``const url = new URL(`${BACKEND_HOST}/resources/find`);``
226
-
227
- with
228
-
229
- ``const url = new URL(`${BACKEND_HOST}/api/resources/find`);``
230
-
231
- in [./adapter.ts](./adapter.ts)
232
-
233
- 3. Comment out these lines in [./app.tsx](./app.tsx)
234
-
235
- ```typescript
236
- // Comment this next line out for production apps
237
- setAuthState("authenticated");
238
- ```
239
-
240
- 4. Navigate to your app at `https://www.canva.com/developers/apps`, and click **Preview** to preview the app.
241
- 1. A new screen will appear asking if you want to authenticate.
242
- Press **Connect** to start the authentication flow.
243
- 2. A ngrok screen may appear. If it does, select **Visit Site**
244
- 3. An authentication popup will appear. For the username, enter `username`, and
245
- for the password enter `password`.
246
- 4. If successful, you will be redirected back to your app.
247
- 5. You can now modify the `/redirect-url` function in `server.ts` to authenticate with your third-party
248
- asset manager, and `/api/resources/find` to pull assets from your third-party asset manager.
249
-
250
- See `https://www.canva.dev/docs/apps/authenticating-users/` for more details.
@@ -2,7 +2,6 @@ import * as express from "express";
2
2
  import * as cors from "cors";
3
3
  import { createBaseServer } from "../utils/backend/base_backend/create";
4
4
  import { createDamRouter } from "./routers/dam";
5
- import { createAuthRouter } from "./routers/auth";
6
5
 
7
6
  async function main() {
8
7
  // TODO: Set the CANVA_APP_ID environment variable in the project's .env file
@@ -46,12 +45,6 @@ async function main() {
46
45
  */
47
46
  router.use(cors());
48
47
 
49
- /**
50
- * Add routes for authorisation.
51
- */
52
- const authRouter = createAuthRouter();
53
- router.use(authRouter);
54
-
55
48
  /**
56
49
  * Add routes for digital asset management.
57
50
  */
@@ -16,13 +16,13 @@
16
16
  "test:watch": "jest --watchAll"
17
17
  },
18
18
  "dependencies": {
19
+ "@canva/app-components": "^1.0.0-beta.28",
19
20
  "@canva/app-i18n-kit": "^0.0.1-beta.5",
20
- "@canva/app-components": "^1.0.0-beta.22",
21
- "@canva/app-ui-kit": "^3.8.0",
22
- "@canva/asset": "^1.7.1",
23
- "@canva/design": "^1.10.0",
24
- "@canva/platform": "^1.1.0",
25
- "@canva/user": "^1.0.0",
21
+ "@canva/app-ui-kit": "^4.0.0",
22
+ "@canva/asset": "^2.0.0",
23
+ "@canva/design": "^2.1.0",
24
+ "@canva/platform": "^2.0.0",
25
+ "@canva/user": "^2.0.0",
26
26
  "cookie-parser": "1.4.6",
27
27
  "cors": "2.8.5",
28
28
  "react": "18.3.1",
@@ -1,147 +1,14 @@
1
- import { useEffect, useState } from "react";
2
1
  import { SearchableListView } from "@canva/app-components";
3
- import { Alert, Box, Button, LoadingIndicator, Rows } from "@canva/app-ui-kit";
4
- import type { Authentication } from "@canva/user";
5
- import { auth } from "@canva/user";
2
+ import { Box } from "@canva/app-ui-kit";
6
3
  import { findResources } from "./adapter";
7
4
  import { config } from "./config";
8
5
  import * as styles from "./index.css";
9
6
  import "@canva/app-ui-kit/styles.css";
10
7
 
11
- type AuthenticationState =
12
- | "authenticated"
13
- | "not_authenticated"
14
- | "checking"
15
- | "cancelled"
16
- | "error";
17
-
18
- /**
19
- * This endpoint is defined in the ./backend/server.ts file. You need to
20
- * register the endpoint in the Developer Portal before sending requests.
21
- *
22
- * BACKEND_HOST is configured in the root .env file, for more information,
23
- * refer to the README.md.
24
- */
25
- const AUTHENTICATION_CHECK_URL = `${BACKEND_HOST}/api/authentication/status`;
26
-
27
- const checkAuthenticationStatus = async (
28
- auth: Authentication,
29
- ): Promise<AuthenticationState> => {
30
- /**
31
- * Send a request to an endpoint that checks if the user is authenticated.
32
- * This is example code, intended to convey the basic idea. When implementing this in your app, you might want more advanced checks.
33
- *
34
- * Note: You must register the provided endpoint via the Developer Portal.
35
- */
36
- try {
37
- const token = await auth.getCanvaUserToken();
38
- const res = await fetch(AUTHENTICATION_CHECK_URL, {
39
- headers: {
40
- Authorization: `Bearer ${token}`,
41
- },
42
- method: "POST",
43
- });
44
- const body = await res.json();
45
-
46
- if (body?.isAuthenticated) {
47
- return "authenticated";
48
- } else {
49
- return "not_authenticated";
50
- }
51
- } catch (error) {
52
- // eslint-disable-next-line no-console
53
- console.error(error);
54
- return "error";
55
- }
56
- };
57
-
58
8
  export function App() {
59
- // Keep track of the user's authentication status.
60
- const [authState, setAuthState] = useState<AuthenticationState>("checking");
61
-
62
- useEffect(() => {
63
- setAuthState("checking");
64
- checkAuthenticationStatus(auth).then((status) => {
65
- setAuthState(status);
66
- });
67
- }, []);
68
-
69
- useEffect(() => {
70
- if (authState === "not_authenticated") {
71
- startAuthenticationFlow();
72
- }
73
- }, [authState]);
74
-
75
- const startAuthenticationFlow = async () => {
76
- try {
77
- const response = await auth.requestAuthentication();
78
- switch (response.status) {
79
- case "COMPLETED":
80
- setAuthState("authenticated");
81
- break;
82
- case "ABORTED":
83
- // eslint-disable-next-line no-console
84
- console.warn("Authentication aborted by user.");
85
- setAuthState("cancelled");
86
- break;
87
- case "DENIED":
88
- // eslint-disable-next-line no-console
89
- console.warn("Authentication denied by user", response.details);
90
- setAuthState("cancelled");
91
- break;
92
- default:
93
- // eslint-disable-next-line no-console
94
- console.log("Unknown auth state");
95
- break;
96
- }
97
- } catch (e) {
98
- // eslint-disable-next-line no-console
99
- console.error(e);
100
- setAuthState("error");
101
- }
102
- };
103
-
104
- if (authState === "error") {
105
- // eslint-disable-next-line no-console
106
- console.warn(
107
- "Warning: authentication not enabled on this app. Please enable auth with the instructions in README",
108
- );
109
- // Comment this next line out for production apps
110
- setAuthState("authenticated");
111
- }
112
-
113
- // If user has denied or aborted auth flow
114
- if (authState === "cancelled") {
115
- return (
116
- <Box paddingEnd="2u" height="full" className={styles.centerInPage}>
117
- <Rows spacing="2u" align="center">
118
- <Alert tone="critical">
119
- Something went wrong while authenticating
120
- </Alert>
121
- <Button
122
- variant="primary"
123
- onClick={startAuthenticationFlow}
124
- stretch={true}
125
- >
126
- Start authentication flow
127
- </Button>
128
- </Rows>
129
- </Box>
130
- );
131
- }
132
-
133
- return authState === "authenticated" ? (
9
+ return (
134
10
  <Box className={styles.rootWrapper}>
135
11
  <SearchableListView config={config} findResources={findResources} />
136
12
  </Box>
137
- ) : (
138
- <Box
139
- width="full"
140
- height="full"
141
- paddingTop="1u"
142
- className={styles.centerInPage}
143
- >
144
- <LoadingIndicator size="large" />
145
- </Box>
146
13
  );
147
14
  }
@@ -1,3 +1,42 @@
1
+ ## Run the development server with ngrok to use authentication with the app
2
+
3
+ These steps demonstrate how to start the local development server with ngrok and https.
4
+
5
+ From your app's root directory
6
+
7
+ 1. Stop any running scripts, and run the following command to launch the backend and frontend development servers. The `--ngrok` parameter exposes the backend server via a publicly accessible URL.
8
+
9
+ ```bash
10
+ npm start --ngrok
11
+ ```
12
+
13
+ 2. After ngrok is running, copy your ngrok url
14
+ (e.g. `https://0000-0000.ngrok-free.app`) to the clipboard.
15
+
16
+ 1. Go to your app in the [Developer Portal](https://www.canva.com/developers/apps).
17
+ 2. Navigate to the "Authentication" section of your app.
18
+ 3. Click the "Add provider" button under the "OAuth 2.0" tab
19
+ 4. Configure the provider as follows:
20
+ - Provider: local (or name of your choice)
21
+ - Client ID: `client_id` (exactly this string, in a real app you'd have some opaque identifier)
22
+ - Client Secret: `client_secret` (same here)
23
+ - Authorization server URL: Your ngrok url followed by `/auth/authorize` e.g. `https://0000-0000.ngrok-free.app/auth/authorize`
24
+ - Token exchange URL: Your ngrok url followed by `/auth/token` e.g. `https://0000-0000.ngrok-free.app/auth/token`
25
+ - Revocation exchange URL: (leave blank)
26
+ - Proof of Key for Code Exchange (PKCE): off
27
+ Note: Your ngrok URL changes each time you restart ngrok. Keep these fields up to
28
+ date to ensure your example authentication step will run.
29
+
30
+ 3. Navigate to your app at `https://www.canva.com/developers/apps` and make sure that under Configuration, App source is pointing to your localhost url.
31
+
32
+ 4. Navigate to your app at `https://www.canva.com/developers/apps`, and click **Preview** to preview the app.
33
+ 1. A new screen will appear asking if you want to authenticate.
34
+ Press **Connect** to start the authentication flow.
35
+ 2. A ngrok screen may appear. If it does, select **Visit Site**
36
+ 3. An authentication popup will appear. For the username, enter `username`, and
37
+ for the password enter `password`.
38
+ 4. If successful, you will be redirected back to your app.
39
+
1
40
  ## Generative AI Template
2
41
 
3
42
  This template captures best practices for improving user experience in your application.
@@ -20,7 +59,7 @@ In this template, we've included a basic obscenity filter to stop users from cre
20
59
 
21
60
  ### Backend
22
61
 
23
- This template includes a simple Express server as a sample backend. Please note that this server is not production-ready, and we advise using it solely for instructional purposes to demonstrate API calls.
62
+ This template includes a simple Express server as a sample backend. Please note that this server is not production-ready, and we advise using it solely for instructional purposes to demonstrate API calls. This backend implements an [OAuth 2](https://datatracker.ietf.org/doc/html/rfc6749) Authorization Server to authorize and authenticate users. To use this, you will need to set up authentication above.
24
63
 
25
64
  ### Thumbnails
26
65