Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add openai app example #195

Merged
merged 21 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/ai-image-generator/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MIRO_CLIENT_ID=""
MIRO_CLIENT_SECRET=""
MIRO_REDIRECT_URL="http://localhost:3000/api/redirect"
OPENAI_API_KEY=""
24 changes: 24 additions & 0 deletions examples/ai-image-generator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.next

# testing
/coverage

# misc
.DS_Store
*.pem
.idea

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env
dist
5 changes: 5 additions & 0 deletions examples/ai-image-generator/APP_SUBMISSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Submission to Miro Marketplace

Congrats! You have finished building your app & you'd like to publish it for
users. You can submit your app on the
[Miro Marketplace](https://developers.miro.com/docs/submit-your-app) for review.
39 changes: 39 additions & 0 deletions examples/ai-image-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Create a Miro app

### How to start locally

1. [Sign in](https://miro.com/login/) to Miro, and then create a
[Developer team](https://developers.miro.com/docs/create-a-developer-team)
under your user account.

2. [Create an app in Miro](https://developers.miro.com/docs/build-your-first-hello-world-app#step-2-create-your-app-in-miro).

- Click the **Create new app** button.
- On the **Create new app** modal, give your app a name, assign it to your
Developer team, and then click **Create**.

3. Configure the app:

- In your account profile, go to **Your apps**, and then select the app you just
created to access its configuration page.
- On the app configuration page, go to **App Credentials**, and copy the app
**Client ID** and **Client secret** values: you'll need to enter these values
in step 4 below.
- Go to **App URL** and enter the following URL: `http://localhost:3000`
- Go to **Redirect URI for OAuth2.0**, and enter the following redirect URL:
`http://localhost:3000/api/redirect/`
- Click **Options**. \
From the drop-down menu select **Use this URI for SDK authorization**.
- Lastly, go to **Permissions**, and select the following permissions:
- `board:read`
- `board:write`

4. Open the [`.env`](.env) file, and enter the app client ID and client secret
values that you saved at the beginning of step 3 above.
5. Run `npm start` to start developing.

When your server is up and running:

- Go to [Miro.com](https://miro.com).
- In your developer team, open a board.
- To start your app, click the app icon in the app toolbar on the left.
17 changes: 17 additions & 0 deletions examples/ai-image-generator/components/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// components/Button.js
horeaporutiu marked this conversation as resolved.
Show resolved Hide resolved

import React from "react";

const Button = ({ label, onClick }) => {
return (
<button
onClick={onClick}
className="cs1 ce12 button button-primary"
type="button"
>
{label}
</button>
);
};

export default Button;
20 changes: 20 additions & 0 deletions examples/ai-image-generator/components/PromptInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// components/TextInput.js

import React, { useState } from "react";

Check warning on line 3 in examples/ai-image-generator/components/PromptInput.jsx

View workflow job for this annotation

GitHub Actions / Run linters

'useState' is defined but never used

Check warning on line 3 in examples/ai-image-generator/components/PromptInput.jsx

View workflow job for this annotation

GitHub Actions / Run linters

'useState' is defined but never used

const TextInput = ({ label, placeholder, value, onChange }) => {

Check warning on line 5 in examples/ai-image-generator/components/PromptInput.jsx

View workflow job for this annotation

GitHub Actions / Run linters

'label' is defined but never used

Check warning on line 5 in examples/ai-image-generator/components/PromptInput.jsx

View workflow job for this annotation

GitHub Actions / Run linters

'label' is defined but never used
return (
<div className="form-group">
<input
className="input"
type="text"
placeholder={placeholder}
value={value}
onChange={(e) => onChange(e.target.value)}
id="example-1"
/>
</div>
);
};

export default TextInput;
41 changes: 41 additions & 0 deletions examples/ai-image-generator/initMiro.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Miro } from "@mirohq/miro-api";
import { serialize } from "cookie";

function getSerializedCookie(name, value) {
return serialize(name, value, {
path: "/",
httpOnly: true,
sameSite: "none",
secure: true,
});
}

export default function initMiro(request, response) {
const tokensCookie = "miro_tokens";

// setup a Miro instance that loads tokens from cookies
return {
miro: new Miro({
storage: {
get: () => {
// Load state (tokens) from a cookie if it's set
try {
return JSON.parse(request.cookies[tokensCookie] || "null");
} catch (err) {
return null;
}
},
set: (_, state) => {
if (!response)
throw new Error(
"initMiro should be invoked with a response object",
);
// store state (tokens) in the cookie
response.setHeader("Set-Cookie", [
getSerializedCookie(tokensCookie, JSON.stringify(state)),
]);
},
},
}),
};
}
7 changes: 7 additions & 0 deletions examples/ai-image-generator/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./node_modules/@mirohq"]
},
"include": ["src"],
"exclude": ["node_modules"]
}
20 changes: 20 additions & 0 deletions examples/ai-image-generator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "ai-image-generator",
"version": "0.1.0",
"license": "MIT",
"scripts": {
"build": "next build",
"start": "next dev",
"lint": "next lint"
},
"dependencies": {
"@mirohq/miro-api": "^2.0.0",
"cookie": "^0.5.0",
"dotenv": "^16.0.3",
"mirotone": "5",
"next": "^13.0.0",
"openai": "^4.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
7 changes: 7 additions & 0 deletions examples/ai-image-generator/pages/_app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import "../src/assets/style.css";

const MyApp = ({ Component, pageProps }) => {
return <Component {...pageProps} />;
};

export default MyApp;
21 changes: 21 additions & 0 deletions examples/ai-image-generator/pages/_document.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Html, Head, Main, NextScript } from "next/document";

export default function Document() {
return (
<Html>
<Head>
<link
rel="stylesheet"
href="https://unpkg.com/mirotone/dist/styles.css"
></link>
<script src="https://miro.com/app/static/sdk/v2/miro.js" />
</Head>
<body>
<div id="root">
<Main />
</div>
<NextScript />
</body>
</Html>
);
}
33 changes: 33 additions & 0 deletions examples/ai-image-generator/pages/api/openai.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import OpenAI from "openai";

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY, // This is also the default, can be omitted
});

export default async (req, res) => {
// grab prompt from the front end
let prompt = await req.body.prompt;
console.log("hello");
console.log(prompt);
horeaporutiu marked this conversation as resolved.
Show resolved Hide resolved

try {
const response = await openai.images.generate({
prompt: prompt,
});
console.log("response after open.img.generate");
console.log(response);
let image_url = response.data[0].url;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let image_url = response.data[0].url;
const image_url = response.data[0].url;

// send url to front end to display the image
res.status(200).json({
success: true,
data: image_url,
});
} catch (error) {
console.log(error);
// send error to front end, so user can easily see that something went wrong
res.status(400).json({
success: false,
error: "The image could not be generated",
});
}
};
16 changes: 16 additions & 0 deletions examples/ai-image-generator/pages/api/redirect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import initMiro from "../../initMiro";

// handle redirect with code and exchange it for the access token
export default async function handler(req, res) {
const { miro } = initMiro(req, res);

// Make sure the code is in query parameters
if (typeof req.query.code !== "string") {
res.status(400);
res.send("Missing code in the query");
return;
}

await miro.exchangeCodeForAccessToken("", req.query.code);
res.redirect("/");
}
Loading
Loading