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

Site: Attach Database #2572

Merged
merged 29 commits into from
May 5, 2019
Merged

Site: Attach Database #2572

merged 29 commits into from
May 5, 2019

Conversation

lukeed
Copy link
Member

@lukeed lukeed commented Apr 26, 2019

  • Handle GitHub OAuth directly (no passport)
  • Create user records into a users table on PostgreSQL database
  • Handle client<->server authentication through JWT
  • Perform gists' CRUD operations thru database (no more GitHub gists)

All in all, this removes the flakiness of:

  • tunneling public/anonymous traffic through GitHub's free quota
    AKA, at launch, so many people were getting (silent) rate-limit errors. No content appeared.

  • disappearing sessions, as the source of truth has moved to a long-lasting DB instance.
    Currently, sessions get wiped on every new deployment & on new and/or dying GCR instances


I've also added a .env.example file to keep easier track of what's needed. Of course, NODE_ENV and PORT are optional, but still listed anyway.


Finally, I've already provisioned & migrated a DB instance for the website. The ENV vars are also already injected into GCR. Same goes for JWT configuration. Nothing else needs to be done for deployment if/when this is ready to go.

Caution: There's only a "production" DB, so launching a "staging" site will be connected to the same production database.

To run this locally, you need postgres installed. Then you need to create a new database and fill in the details as the DATABASE_URL key within .env file. Then you'll need to migrate/setup the database.

Full instructions may look like this:

# install postgres
$ brew install postgresql
$ brew services start postgresql

# create "svelte_demo" database
# this uses the system's current user, eg "lukeed"
$ createdb svelte_demo -U $(whoami)

# copy ".env.example" to ".env"
$ cp .env.example .env

# EDIT ".env" file~!

# setup db
$ npm install
$ npm run migrate up

# start sapper
$ npm run dev

Here's an example DATABASE_URL value for localhost:

# file: .env

DATABASE_URL=postgres://lukeed@127.0.0.1:5432/svelte_demo

These ENV keys must be filled in in order to have a working app:

  • DATABASE_URL
  • GITHUB_CLIENT_ID
  • GITHUB_CLIENT_SECRET
  • BASEURL
  • JWT_KEY


repl.set({ components });
} else {
console.warn('TODO: 404 Gist')
Copy link
Member Author

Choose a reason for hiding this comment

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

Didn't see how to plug in to the REPL's error toasts

const storageKey = 'svelte-dev:token';

// On load, get the last-known user token (if any)
// Note: We can skip this all by writing User data?
Copy link
Member Author

Choose a reason for hiding this comment

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

We already have the complete user object. We could write it to localStorage and have the user-data immediately available. If/when this is the case, we fetch the /auth/me.json endpoint which will wipe/reset if the token was expired or invalid.

export function toUser(obj={}) {
const { uid, username, name, avatar } = obj;
const token = sign({ uid, username });
return { uid, username, name, avatar, token };
Copy link
Member Author

@lukeed lukeed Apr 26, 2019

Choose a reason for hiding this comment

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

This is the only user data that ever makes it to the client. The uid is GitHub's internal ID for the user. Not sure how "okay" it to keep this in localStorage.

Currently we don't. See comment above.

@lukeed
Copy link
Member Author

lukeed commented Apr 30, 2019

This is ready to be looked at~

@Conduitry
Copy link
Member

Without looking at anything yet, I guess my main concern is will this mess up people running the site locally and trying to load stuff from the database? When someone posts a gist as part of a bug report, as part of testing my fix I will often run the site locally and check the REPL with my local copy of Svelte to see if the thing is fixed. I can create the secret keys or whatever is needed in my .env, but that's an extra barrier to entry, and I also don't think we want to limit read access when running the site locally to people with trust with all of the secret Google Cloud keys.

@lukeed
Copy link
Member Author

lukeed commented Apr 30, 2019

I would feel that's a very small edge case? Perhaps just you? lol

They'd still post a gist link that can be opened on the live site by anybody. There is a slightly greater barrier of entry for local development, but presumably anyone who's wanting to contribute to the site is already willing to cross that threshold anyway.

@Rich-Harris
Copy link
Member

This looks 😎. Couple of thoughts before merging:

  • Should we call gists something else, to allay confusion? Maybe /gist/create should become /repl/create, etc, and the table could be repl instead of gists. (We could improve the URL scheme around REPLs more generally — for example it'd be nice if you could middle-click on the fork icon, etc — but that needn't be part of this work).
  • So that existing URLs continue to work, maybe it should fall back to querying the gist API if nothing is found in the DB, at least for now? (It could automatically create a DB entry at that point, though one wrinkle: the author of the gist might not exist in the DB yet)
  • Looking ahead, it's conceivable that we'd want to do something like attach a thumbnail to a specific ID. Do we need to factor that sort of thing into the DB design now, or do you think we can worry about that later?

@Rich-Harris
Copy link
Member

If you get an error error: function gen_random_uuid() does not exist, run the following:

CREATE EXTENSION pgcrypto;

@Rich-Harris
Copy link
Member

This is maybe a minor thing but I like the fact that you can double click a gist ID in the URL bar to select the whole thing. With gen_random_uuid() you get something like e6d592ce-54c8-4044-86bc-88ba0af6a493 which doesn't have that quality. Is there a version of that function that doesn't include hyphens?

@Rich-Harris
Copy link
Member

oh wow, I just learned the hyphens are optional. We can just remove them from the URL. Don't mind me

@lukeed
Copy link
Member Author

lukeed commented May 4, 2019

  1. I would want to keep the URLs the same, personally. Though of course you can do anything.

2a) Sure, I would probably want to query GistHub for the gist on server's 404. IMO, the gists table entry should be added on their next "save" or "fork" action

2b) I don't recall the gist's data from GitHub API, but I think the account/owner is included. The only non-public (at least, I think it's not "public") information we need is the id value we're grabbing at login

  1. It's no problem at all to update the table with future migrations

  2. Postgres doesn't care about hyphens. That's a formatting operation for display. I don't think we can opt out of it in pg module directly, but we can have the API always send down values with val.replace(/-/g, '') and it should be fine.

@Rich-Harris
Copy link
Member

Yeah, I just realised that the account info is included, so we could create user objects if necessary when the gist is fetched, and add a token later if that user logs in, if we updated the DB automatically when the gist was requested. I guess it's a question of whether it's worse to increase the risk hitting API rate limits (because of requesting the same gist repeatedly), or to fill up the database unnecessarily.

What's the argument for keeping the URLs the same, if we redirect the old to the new?

@lukeed
Copy link
Member Author

lukeed commented May 4, 2019

Cool. We can create the gist immediately. Filling out our DB is very cheap & more forgiving than GH's rate limit.

Just, familiarity. Avoiding redirects is also nice. They're still "gists" – it's just that they live in our domain now

@Rich-Harris
Copy link
Member

Cool. Working on this stuff atm — doing it in a separate branch off of this one in case any of it turns out controversial

@lukeed
Copy link
Member Author

lukeed commented May 4, 2019

Ah okay. I was thinking I was assigned :P Am around if you wanna ping

@Rich-Harris
Copy link
Member

Ha, I'm being a little selfish — just figured you might have better things to do and that it'd be a good opportunity for me to fully wrap my head round the new stuff

@lukeed
Copy link
Member Author

lukeed commented May 4, 2019

Haha, agreed – good opportunity. Not a problem at all~

@Rich-Harris Rich-Harris merged commit 1d0da9d into master May 5, 2019
@Rich-Harris Rich-Harris deleted the site/database branch May 5, 2019 04:33
@Rich-Harris
Copy link
Member

I can't seem to deploy this... getting the following error:

ERROR: (gcloud.beta.run.deploy) Container failed to start. Failed to start and then listen on the port defined by the PORT environment variable. Logs for this revision might contain more information.

@lukeed any clue how to debug this? There doesn't seem to be anything useful in the logs, but I might be looking in the wrong place

@lukeed
Copy link
Member Author

lukeed commented May 5, 2019

I ran it from the Docker image directly. Here was the message:

Error: Cannot find module 'yootils'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:668:15)
    at Function.Module._load (internal/modules/cjs/loader.js:591:27)
    at Module.require (internal/modules/cjs/loader.js:723:19)
    at require (internal/modules/cjs/helpers.js:14:16)
    at Object.<anonymous> (/app/__sapper__/build/server/index-4f8ee8d0.js:6:1)
    at Module._compile (internal/modules/cjs/loader.js:816:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:827:10)
    at Module.load (internal/modules/cjs/loader.js:685:32)
    at Function.Module._load (internal/modules/cjs/loader.js:620:12)
    at Module.require (internal/modules/cjs/loader.js:723:19)

This was my fault. I removed it in this PR though am not sure where it's being used. Investigate later when not so tired.

In the meantime, I pushed up a fix to master directly am now redeploying the site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants