diff --git a/.gitignore b/.gitignore index 4036c5ece4..3170838755 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ _actual*.* /site/cypress/screenshots/ /site/__sapper__/ +/site/.env +/site/.sessions \ No newline at end of file diff --git a/site/package-lock.json b/site/package-lock.json index f3f9431d0d..d66ae940bf 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -887,6 +887,11 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "bagpipe": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/bagpipe/-/bagpipe-0.3.5.tgz", + "integrity": "sha1-40HRZPyyTN8E6n4Ft2XsEMiupqE=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1265,6 +1270,11 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "crc": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", + "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=" + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -1358,11 +1368,21 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "devalue": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-1.1.0.tgz", + "integrity": "sha512-mKj+DaZuxevfmjI78VdlkBr+NDmwaDAKQz0t5RDSmhwBn6m5z82KDnVRKVFeUvlMOmI1fzkAUx4USdqGGhas6g==" + }, "do-not-zip": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/do-not-zip/-/do-not-zip-1.0.0.tgz", "integrity": "sha512-Pgd81ET43bhAGaN2Hq1zluSX1FmD7kl7KcV9ER/lawiLsRUB9pRA5y8r6us29Xk6BrINZETO8TjhYwtwafWUww==" }, + "dotenv": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", + "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1568,6 +1588,22 @@ "vary": "~1.1.2" } }, + "express-session": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz", + "integrity": "sha512-r0nrHTCYtAMrFwZ0kBzZEXa1vtPVrw0dKvGSrKP4dahwBQ1BJpF2/y1Pp4sCD/0kvxV4zZeclyvfmw0B4RMJQA==", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "crc": "3.4.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "on-headers": "~1.0.1", + "parseurl": "~1.3.2", + "uid-safe": "~2.1.5", + "utils-merge": "1.0.1" + } + }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -1731,6 +1767,16 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fsevents": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", @@ -2362,8 +2408,7 @@ "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, "has": { "version": "1.0.3", @@ -2464,6 +2509,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -2742,6 +2792,14 @@ "minimist": "^1.2.0" } }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -3028,6 +3086,16 @@ "string.prototype.padend": "^3.0.0" } }, + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -3165,6 +3233,39 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-github": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz", + "integrity": "sha1-jOHj/NYa11eOsd9ZWDnkrqEjVdQ=", + "requires": { + "passport-oauth2": "1.x.x" + } + }, + "passport-oauth2": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz", + "integrity": "sha1-9i+BWDy+EmCb585vFguTlaJ7hq0=", + "requires": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", @@ -3203,6 +3304,11 @@ "pify": "^3.0.0" } }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "pidtree": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", @@ -3261,6 +3367,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", @@ -3470,6 +3581,11 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" + }, "rollup": { "version": "0.68.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.68.0.tgz", @@ -3746,6 +3862,18 @@ "send": "0.16.2" } }, + "session-file-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/session-file-store/-/session-file-store-1.2.0.tgz", + "integrity": "sha512-DkYLYFkkK6u9xyraVHemulhlUuuufLukf7SQxOZSx8SPwkswcaIrls882PaQZ72zRKsyhUVNxOUl9w0lQubUFw==", + "requires": { + "bagpipe": "^0.3.5", + "fs-extra": "^4.0.0", + "object-assign": "^4.1.1", + "retry": "^0.10.0", + "write-file-atomic": "1.3.1" + } + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -3817,6 +3945,11 @@ "tiny-glob": "^0.2.0" } }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -4208,6 +4341,19 @@ } } }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, + "uid2": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", + "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=" + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -4271,6 +4417,11 @@ } } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4375,6 +4526,16 @@ "isexe": "^2.0.0" } }, + "write-file-atomic": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.1.tgz", + "integrity": "sha1-fUW6MjFjKN0ex9kPYOvA2EW7dZo=", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, "yootils": { "version": "0.0.14", "resolved": "https://registry.npmjs.org/yootils/-/yootils-0.0.14.tgz", diff --git a/site/package.json b/site/package.json index 4b228fbde0..7489cc5c3b 100644 --- a/site/package.json +++ b/site/package.json @@ -17,11 +17,17 @@ "dependencies": { "codemirror": "^5.42.0", "compression": "^1.7.1", + "devalue": "^1.1.0", "do-not-zip": "^1.0.0", + "dotenv": "^6.2.0", "express": "^4.16.4", + "express-session": "^1.15.6", "golden-fleece": "^1.0.9", "marked": "^0.5.2", + "passport": "^0.4.0", + "passport-github": "^1.1.0", "prismjs": "^1.15.0", + "session-file-store": "^1.2.0", "sirv": "^0.2.0", "yootils": "0.0.14" }, diff --git a/site/src/routes/index.html b/site/src/routes/index.html index 1cf176f23e..994c961163 100644 --- a/site/src/routes/index.html +++ b/site/src/routes/index.html @@ -6,63 +6,6 @@ let sy = 0; - - Svelte • The magical disappearing UI framework - - - - - - -
-

What if web apps could write themselves?

-

Svelte

-
- -
- -
- -
-
-

Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app.

- -

Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.

-
- -
-

TODO a video intro

-
-
- -
-

TODO finish building this page. Ideas: Who's using Svelte? Example code (interactive, ideally). What else?

-
- - - - \ No newline at end of file + + + + Svelte • The magical disappearing UI framework + + + + + + +
+

What if web apps could write themselves?

+

Svelte

+
+ +
+ +
+ +
+
+

Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app.

+ +

Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.

+
+ +
+

TODO a video intro

+
+
+ +
+

TODO finish building this page. Ideas: Who's using Svelte? Example code (interactive, ideally). What else?

+
+ + diff --git a/site/src/routes/repl/_components/AppControls/index.html b/site/src/routes/repl/_components/AppControls/index.html index f4392b060c..3dfab728d0 100644 --- a/site/src/routes/repl/_components/AppControls/index.html +++ b/site/src/routes/repl/_components/AppControls/index.html @@ -54,6 +54,9 @@ async function fork(intentWasSave) { saving = true; + const { components, values } = repl.toJSON(); + const json5 = fleece.stringify(values); // TODO preserve the original code + try { const r = await fetch(`gist/create`, { method: 'POST', @@ -103,6 +106,9 @@ saving = true; + const { components, values } = repl.toJSON(); + const json5 = fleece.stringify(values); // TODO preserve the original code + try { const files = {}; diff --git a/site/src/routes/repl/_components/Output/PropEditor.html b/site/src/routes/repl/_components/Output/PropEditor.html index 2d2cf7f393..a42612fb2d 100644 --- a/site/src/routes/repl/_components/Output/PropEditor.html +++ b/site/src/routes/repl/_components/Output/PropEditor.html @@ -39,8 +39,6 @@ function stringify(value) { if (value === undefined) return ''; - console.log(value); - const code = previous_code ? fleece.patch(previous_code, value) : fleece.stringify(value); diff --git a/site/src/routes/repl/index.html b/site/src/routes/repl/index.html index 9c38bc4e96..d7d761cb31 100644 --- a/site/src/routes/repl/index.html +++ b/site/src/routes/repl/index.html @@ -19,7 +19,6 @@ let name = 'loading...'; let zen_mode = false; - let repl; $: if (typeof history !== 'undefined') { diff --git a/site/src/server.js b/site/src/server.js index 143a6a1bf9..49b46d1c4c 100644 --- a/site/src/server.js +++ b/site/src/server.js @@ -1,17 +1,116 @@ -import sirv from 'sirv'; +import dotenv from 'dotenv'; import express from 'express'; import compression from 'compression'; +import session from 'express-session'; +import passport from 'passport'; +import { Strategy } from 'passport-github'; +import sessionFileStore from 'session-file-store'; +import serve from 'serve-static'; +import devalue from 'devalue'; import * as sapper from '../__sapper__/server.js'; -const { PORT, NODE_ENV } = process.env; -const dev = NODE_ENV === 'development'; - -express() - .use( - compression({ threshold: 0 }), - sirv('static', { dev }), - sapper.middleware() - ) - .listen(PORT, err => { - if (err) console.log('error', err); +dotenv.config(); + +const app = express(); + +if (process.env.GITHUB_CLIENT_ID) { + const FileStore = sessionFileStore(session); + + passport.use(new Strategy({ + clientID: process.env.GITHUB_CLIENT_ID, + clientSecret: process.env.GITHUB_CLIENT_SECRET, + callbackURL: `${process.env.BASEURL}/auth/callback`, + userAgent: 'svelte.technology' + }, (accessToken, refreshToken, profile, callback) => { + return callback(null, { + token: accessToken, + id: profile.id, + username: profile.username, + displayName: profile.displayName, + photo: profile.photos && profile.photos[0] && profile.photos[0].value + }); + })); + + passport.serializeUser((user, cb) => { + cb(null, user); }); + + passport.deserializeUser((obj, cb) => { + cb(null, obj); + }); + + app + .use(session({ + secret: 'svelte', + resave: true, + saveUninitialized: true, + cookie: { + maxAge: 31536000 + }, + store: new FileStore({ + path: process.env.NOW ? `/tmp/sessions` : `.sessions` + }) + })) + + .use(passport.initialize()) + .use(passport.session()) + + .get('/auth/login', (req, res, next) => { + const { returnTo } = req.query; + req.session.returnTo = returnTo ? decodeURIComponent(returnTo) : '/'; + next(); + }, passport.authenticate('github', { scope: ['gist', 'read:user'] })) + + .post('/auth/logout', (req, res) => { + req.logout(); + res.end('ok'); + }) + + .get('/auth/callback', passport.authenticate('github', { failureRedirect: '/auth/error' }), (req, res) => { + const { id, username, displayName, photo } = req.session.passport && req.session.passport.user; + + res.set({ 'Content-Type': 'text/html; charset=utf-8' }); + res.end(` + + `); + }); +} else { + app.get('/auth/login', (req, res) => { + res.writeHead(500); + res.end(` + +

Missing .env file

+

In order to use GitHub authentication, you will need to register an OAuth application with gist and read:user scopes, and create a .env file:

+ +
GITHUB_CLIENT_ID=[YOUR_APP_ID]\nGITHUB_CLIENT_SECRET=[YOUR_APP_SECRET]\nBASEURL=http://localhost:3000
+ +

The BASEURL variable should match the callback URL specified for your app.

+ + `); + }); +} + +app.use( + compression({ threshold: 0 }), + serve('static'), + sapper.middleware({ + // TODO update Sapper so that we can pass props to the client + props: req => { + const user = req.session && req.session.passport && req.session.passport.user; + + return { + user: user && { + // strip access token + id: user.id, + username: user.username, + displayName: user.displayName, + photo: user.photo + } + }; + } + }) +).listen(process.env.PORT); \ No newline at end of file