-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0c8f6d0
Showing
29 changed files
with
1,820 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
DEPLOY_ORG_ID=28306b.... | ||
DEPLOY_ACCESS_TOKEN=ddo_tE5X... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
name: Deploy | ||
on: | ||
push: | ||
branches: main | ||
pull_request: | ||
branches: main | ||
|
||
jobs: | ||
deploy: | ||
name: Deploy | ||
runs-on: ubuntu-latest | ||
|
||
permissions: | ||
id-token: write # Needed for auth with Deno Deploy | ||
contents: read # Needed to clone the repository | ||
|
||
steps: | ||
- name: Clone repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Install Deno | ||
uses: denoland/setup-deno@v1 | ||
with: | ||
deno-version: v1.x | ||
|
||
- name: Install Node.js | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: lts/* | ||
|
||
- name: Build step | ||
run: "deno task build" | ||
|
||
- name: Upload to Deno Deploy | ||
uses: denoland/deployctl@v1 | ||
with: | ||
project: "deno-code-sandbox-hack" | ||
entrypoint: "./server/main.ts" | ||
root: "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
.vite | ||
|
||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
lerna-debug.log* | ||
.pnpm-debug.log* | ||
|
||
# Diagnostic reports (https://nodejs.org/api/report.html) | ||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json | ||
|
||
# Runtime data | ||
pids | ||
*.pid | ||
*.seed | ||
*.pid.lock | ||
|
||
# Directory for instrumented libs generated by jscoverage/JSCover | ||
lib-cov | ||
|
||
# Coverage directory used by tools like istanbul | ||
coverage | ||
*.lcov | ||
|
||
# nyc test coverage | ||
.nyc_output | ||
|
||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) | ||
.grunt | ||
|
||
# Bower dependency directory (https://bower.io/) | ||
bower_components | ||
|
||
# node-waf configuration | ||
.lock-wscript | ||
|
||
# Compiled binary addons (https://nodejs.org/api/addons.html) | ||
build/Release | ||
|
||
# Dependency directories | ||
node_modules/ | ||
jspm_packages/ | ||
|
||
# Snowpack dependency directory (https://snowpack.dev/) | ||
web_modules/ | ||
|
||
# TypeScript cache | ||
*.tsbuildinfo | ||
|
||
# Optional npm cache directory | ||
.npm | ||
|
||
# Optional eslint cache | ||
.eslintcache | ||
|
||
# Optional stylelint cache | ||
.stylelintcache | ||
|
||
# Microbundle cache | ||
.rpt2_cache/ | ||
.rts2_cache_cjs/ | ||
.rts2_cache_es/ | ||
.rts2_cache_umd/ | ||
|
||
# Optional REPL history | ||
.node_repl_history | ||
|
||
# Output of 'npm pack' | ||
*.tgz | ||
|
||
# Yarn Integrity file | ||
.yarn-integrity | ||
|
||
# dotenv environment variable files | ||
.env | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
.env.local | ||
|
||
# parcel-bundler cache (https://parceljs.org/) | ||
.cache | ||
.parcel-cache | ||
|
||
# Next.js build output | ||
.next | ||
out | ||
|
||
# Nuxt.js build / generate output | ||
.nuxt | ||
dist | ||
|
||
# Gatsby files | ||
.cache/ | ||
# Comment in the public line in if your project uses Gatsby and not Next.js | ||
# https://nextjs.org/blog/next-9-1#public-directory-support | ||
# public | ||
|
||
# vuepress build output | ||
.vuepress/dist | ||
|
||
# vuepress v2.x temp and cache directory | ||
.temp | ||
.cache | ||
|
||
# Docusaurus cache and generated files | ||
.docusaurus | ||
|
||
# Serverless directories | ||
.serverless/ | ||
|
||
# FuseBox cache | ||
.fusebox/ | ||
|
||
# DynamoDB Local files | ||
.dynamodb/ | ||
|
||
# TernJS port file | ||
.tern-port | ||
|
||
# Stores VSCode versions used for testing VSCode extensions | ||
.vscode-test | ||
|
||
# yarn v2 | ||
.yarn/cache | ||
.yarn/unplugged | ||
.yarn/build-state.yml | ||
.yarn/install-state.gz | ||
.pnp.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# deno-code-sandbox | ||
|
||
This is a code sandbox example that uses: | ||
|
||
* React - for the WebUI | ||
* Vite - for a react dev server | ||
* Deno - as the backend runtime | ||
* Oak - as the middleware framework for the API | ||
* Deno Deploy - to host the code sandbox | ||
* Deno Subhosting - to host the code *executed using the sandbox* | ||
|
||
This is a stack that'll work if you want to build something like *CodePen*, *Glitch*, *JSFiddle*, or Edge Functions (Cloudflare, Vercel etc). | ||
|
||
## What it does | ||
|
||
The homepage of the app displays a snippet of a program using the `Deno.serve` API to create a Deno web app. When you click `Create Interactive Sandbox` the code you see will be pushed to the backend `API` which will create a `Deno Subhosting Instance` and startup the code. | ||
|
||
You'll be redirected to an editor page at this point, where you can edit and save the code in real-time, each save resulting in a new `Deno Subhosting Deployment` with a distinct URL that you can share with others. | ||
|
||
A real "code sandbox" experience would likely clean up the last n deployments after a certain time period, but this is a simplified example. | ||
|
||
### Pre-Requisites | ||
|
||
You'll need: | ||
|
||
* Deno installed | ||
* A Deno Deploy account | ||
* A Deno Deploy access token | ||
* A Deno Subhosting Account with an Organization created | ||
|
||
Once you have your accounts, you'll need to create a `.env` file in the root of the project with the following contents: | ||
|
||
```text | ||
DEPLOY_ORG_ID=283.... | ||
DEPLOY_ACCESS_TOKEN=ddo_xdR... | ||
``` | ||
|
||
With your `Org Id` and `Access Token` in place so you can run the app. | ||
|
||
### How to run | ||
|
||
```bash | ||
deno task start | ||
``` | ||
|
||
This will start the Vite server and the Oak server, and you can visit `http://localhost:3000` to see the app running. | ||
|
||
### How to deploy | ||
|
||
You can create all the required build artifacts by running | ||
|
||
```bash | ||
deno task build | ||
``` | ||
|
||
This will use `Deno` to execute `Vite` to build the frontend components for the react app. The built react app will be created at `./client/dist` and the backend code will be in `./server`. | ||
|
||
If you're deploying with Deno Deploy, you'll want to configure the following settings: | ||
|
||
* Build - `deno task build` | ||
* Entrypoint - `./server/main.ts` | ||
|
||
In production, the Deno app will serve both the API, and the static files for the react app. | ||
You'll see the API served at `/api` and the static files served at `/`. | ||
|
||
### Development Notes | ||
|
||
In development, the `Vite dev server` is configured to proxy across to the Deno server for the API calls so if you've used `Vite` before, everything will just work as expected. | ||
|
||
If you're looking to diagnose issues, you can run `deno task serve` to start a production-like build that doesn't require a running instance of `Vite` to serve the frontend. | ||
|
||
### React Notes | ||
|
||
This example uses the latest version of `React` and is configured for HMR (Hot Module Replacement) so you can see changes in real-time as you develop. `React Router` is also included to handle the routing for the app, along with appropriate wildcard routing configuration so that refreshing the page will still load the app. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite + React</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/main.tsx"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#root { | ||
max-width: 1280px; | ||
margin: 0 auto; | ||
padding: 2rem; | ||
text-align: left; | ||
} | ||
|
||
.editor { | ||
display: flex; | ||
flex-direction: column; | ||
border-radius: 5px; | ||
|
||
height: 60vh; | ||
width: 100%; | ||
min-width: 50vw; | ||
}; | ||
|
||
textarea { | ||
width: 100%; | ||
min-height: 15rem; | ||
padding: 0.5em; | ||
font-size: 1em; | ||
font-family: 'Courier New', Courier, monospace; | ||
} | ||
|
||
iframe { | ||
width: 100%; | ||
height: 100%; | ||
border: 0; | ||
border-radius-bottom-left: 5px; | ||
border-radius-bottom-right: 5px; | ||
background-color: light-dark(var(--light-primary), var(--dark-primary)); | ||
} | ||
|
||
.url { | ||
margin-top: 1rem; | ||
padding: .5rem 1rem; | ||
font-size: small; | ||
background-color: light-dark(var(--light-tertiary), var(--dark-tertiary)); | ||
a { | ||
text-decoration: none; | ||
color: light-dark(var(--light-text), var(--dark-text)); | ||
&:hover { | ||
text-decoration: underline; | ||
light-dark(var(--light-primary), var(--dark-primary)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React, { useState, useEffect } from 'react' | ||
import './App.css' | ||
|
||
function App(props: { children: React.ReactNode }) { | ||
return ( | ||
<div className="App"> | ||
{ props.children } | ||
<div className="console"></div> | ||
</div> | ||
) | ||
} | ||
|
||
export default App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import doUntil from "./doUntil.ts"; | ||
|
||
export default class ApiClient { | ||
constructor(private readonly baseUrl: string = "") { } | ||
|
||
public async getSampleCode(): Promise<SampleCode> { | ||
const response = await fetch(`${this.baseUrl}/api/sample`); | ||
return (await response.json()).code; | ||
} | ||
|
||
public async deployProject(code: string, id?: string): Promise<ProjectDeployment> { | ||
const idSuffix = id ? `/${id}` : ''; | ||
|
||
const response = await fetch(`${this.baseUrl}/api/project${idSuffix}`, { | ||
method: 'POST', | ||
body: JSON.stringify({ code }) | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error('Failed to deploy project'); | ||
} | ||
|
||
return await response.json(); | ||
} | ||
|
||
public async getDeployment(id: string): Promise<ProjectDeployment> { | ||
const response = await fetch(`${this.baseUrl}/api/deployment/${id}`); | ||
return await response.json(); | ||
} | ||
|
||
public async waitForDeployment(deploymentId: string) { | ||
return await doUntil(async () => { | ||
This comment has been minimized.
Sorry, something went wrong. |
||
return await this.getDeployment(deploymentId); | ||
}, (response) => response.deployment.status !== 'pending'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Rudimentary resilience that retries a task until a condition is met | ||
// Used to poll for deloyment completion in the client without hanging forever. | ||
|
||
export default async function doUntil<TReturns>( | ||
task: () => Promise<TReturns>, | ||
until: (returns: TReturns) => boolean | ||
) { | ||
let tries = 0; | ||
let pushback = 0; | ||
let returns: TReturns; | ||
|
||
do { | ||
if (tries > 10) { | ||
throw new Error("doUntil failed after 10 tries"); | ||
} | ||
|
||
pushback = Math.min(pushback + tries, 10) * 100; | ||
if (pushback > 0) { | ||
await new Promise((resolve) => setTimeout(resolve, pushback)); | ||
} | ||
|
||
returns = await task(); | ||
tries++; | ||
} while (!until(returns)); | ||
|
||
return returns; | ||
} |
Oops, something went wrong.
Since you want to be live code this and need to keep it simple, I think this might be appropriate.
Something that I usually suggest is to stream the build logs. When the API requests that does this is "done" streaming this indicates that the build is done. This would make simple deployments a bit faster too.