• Stars
    star
    122
  • Rank 292,031 (Top 6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Learn to program platform. Being built with Next.js (React), Apollo-GraphQL, and Bulma.

Teach Yourself Code

  • A free platform for learning programming that curates tutorials from Youtube; the main value propositions are that...
  1. Users will be able to watch videos with minimal distraction/in a 'focus' mode
  2. Users will be able to 'subscribe' to tutorials/save them to their profile
  3. Users will be able to add notes to each video

Tech Stack

  • NextJs - a React framework
  • Redux-Toolkit - state container
  • Hasura - a GraphQL engine for querying a Postgres database
  • Auth0 - authentication provider
  • Apollo GraphQL - GraphQL hooks for fetching data from database
  • Bulma - open-source CSS framework
  • Jest - unit-testing library
  • Vercel - serverless hosting

To Get It Up And Running Locally

Install locally

   yarn install
   yarn run dev

Obtain the following .env variables by setting up YouTube API, Hasura, and Auth0:

Create a .env file in your project root with:

AUTH0_DOMAIN =
AUTH0_CLIENT_ID =
AUTH0_CLIENT_SECRET =
REDIRECT_URI= http://localhost:3000/api/callback
POST_LOGOUT_REDIRECT_URI= http://localhost:3000/
SESSION_COOKIE_SECRET =
SESSION_COOKIE_LIFETIME = 7200, // 2 hours
YOUTUBE_API_KEY =

Get the vars by following these:

  1. Sign-up for a YouTube API key. Follow This
YOUTUBE_API_KEY=
  1. Set-up a Hasura GraphQL Engine to obtain the following values. Hasura is used to query our Postgres db. The quickest option for setup is via a free Heroku server =======
HASURA_ADMIN_SECRET=
HASURA_GRAPHQL_JWT_SECRET=
HASURA_ENDPOINT=

Hasura admin secret Docs are here.

Hasura graphql jwt secret can be generated here.

Add jwt Secret to the env vars as you did for HASURA_ADMIN_SECRET.

  1. Create a free account at Auth0 and set-up a test application following the Auth0 config instructions here. It explains where to get the following values:
AUTH0_DOMAIN=
AUTH0_CLIENT_ID=
AUTH0_CLIENT_SECRET=
  1. Create a 32-character secret with a random string generator like this.
SESSION_COOKIE_SECRET=jtftEOwNtDLVwRw0OgrdzsZDeQIeP9yioxPKlgrS5bIVXoPSMP_u-VT4saodFOqN
  1. Add localhost URLs for redirection upon login and logout.
REDIRECT_URI=http://localhost:3000/api/callback
POST_LOGOUT_REDIRECT_URI=http://localhost:3000/

Create the following rules in your Auth0 Dashboard

Directly through Dashbaord, Under the tab Rules. Do remember to change the url: "<your-hasura-graphql-endpoint>" in hasura-user-sync function.

  1. hasura-jwt-claim
function hasuraClaimsRule(user, context, callback) {
  const namespace = "https://hasura.io/jwt/claims";

  context.idToken[namespace] = {
    "x-hasura-default-role": "user",

    // do some custom logic to decide allowed roles

    "x-hasura-allowed-roles": ["user"],
    "x-hasura-user-id": user.user_id
  };

  callback(null, user, context);
}
  1. hasura-user-sync
function userSyncRule(user, context, callback) {
  const userId = user.user_id;
  const email = user.email;

  const mutation = `mutation($userId: String!, $email: String) {
    insert_users(objects: [{
        auth0_id: $userId,
        email: $email
      }],
      on_conflict: {
        constraint: users_pkey,
        update_columns: [last_seen, email]
      }) {
        affected_rows
      }
    }`;

  request.post(
    {
      headers: {
        "content-type": "application/json",
        "x-hasura-admin-secret": configuration.ACCESS_KEY
      },
      url: "<your-hasura-graphql-endpoint>",
      body: JSON.stringify({ query: mutation, variables: { userId, email } })
    },
    function(error, response, body) {
      console.log(body);
      callback(error, user, context);
    }
  );
}

Set up Postgres Database via Hasura Console

Run the following SQL command.

CREATE FUNCTION public.set_current_timestamp_updated_at() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
DECLARE
  _new record;
BEGIN
  _new := NEW;
  _new."updated_at" = NOW();
  RETURN _new;
END;
$$;
CREATE TABLE public.users (
    id integer NOT NULL,
    auth0_id text NOT NULL,
    email text NOT NULL,
    created_at timestamp with time zone DEFAULT now() NOT NULL,
    last_seen timestamp with time zone DEFAULT now() NOT NULL,
    current_playlist_id integer
);
CREATE TABLE public.notes (
    id integer NOT NULL,
    note text NOT NULL,
    video_id text NOT NULL,
    created_at timestamp with time zone DEFAULT now() NOT NULL,
    updated_at timestamp with time zone DEFAULT now() NOT NULL,
    user_id text NOT NULL,
    "timestamp" integer
);
CREATE SEQUENCE public.notes_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE public.notes_id_seq OWNED BY public.notes.id;
CREATE VIEW public.online_users AS
 SELECT users.email,
    users.last_seen
   FROM public.users
  WHERE (users.last_seen >= (now() - '00:00:30'::interval));
CREATE TABLE public.playlists (
    id integer NOT NULL,
    title text NOT NULL,
    description text,
    thumbnail text,
    topic_id integer NOT NULL,
    playlist_id text NOT NULL,
    channel text
);
CREATE SEQUENCE public.playlists_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE public.playlists_id_seq OWNED BY public.playlists.id;
CREATE TABLE public.topics (
    id integer NOT NULL,
    title text NOT NULL
);
CREATE SEQUENCE public.topics_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE public.topics_id_seq OWNED BY public.topics.id;
CREATE TABLE public.user_playlists (
    id integer NOT NULL,
    user_id integer NOT NULL,
    playlist_id integer NOT NULL,
    current_video_id text
);
CREATE SEQUENCE public.user_playlists_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE public.user_playlists_id_seq OWNED BY public.user_playlists.id;
CREATE SEQUENCE public.users_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id;
ALTER TABLE ONLY public.notes ALTER COLUMN id SET DEFAULT nextval('public.notes_id_seq'::regclass);
ALTER TABLE ONLY public.playlists ALTER COLUMN id SET DEFAULT nextval('public.playlists_id_seq'::regclass);
ALTER TABLE ONLY public.topics ALTER COLUMN id SET DEFAULT nextval('public.topics_id_seq'::regclass);
ALTER TABLE ONLY public.user_playlists ALTER COLUMN id SET DEFAULT nextval('public.user_playlists_id_seq'::regclass);
ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass);
ALTER TABLE ONLY public.notes
    ADD CONSTRAINT notes_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.playlists
    ADD CONSTRAINT playlists_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.topics
    ADD CONSTRAINT topics_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.user_playlists
    ADD CONSTRAINT user_playlists_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.users
    ADD CONSTRAINT users_auth0_id_key UNIQUE (auth0_id);
ALTER TABLE ONLY public.users
    ADD CONSTRAINT users_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.notes
    ADD CONSTRAINT notes_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(auth0_id) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.playlists
    ADD CONSTRAINT playlists_topic_id_fkey FOREIGN KEY (topic_id) REFERENCES public.topics(id) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.user_playlists
    ADD CONSTRAINT user_playlists_playlist_id_fkey FOREIGN KEY (playlist_id) REFERENCES public.playlists(id) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.user_playlists
    ADD CONSTRAINT user_playlists_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE RESTRICT ON DELETE RESTRICT;
ALTER TABLE ONLY public.users
    ADD CONSTRAINT users_current_playlist_id_fkey FOREIGN KEY (current_playlist_id) REFERENCES public.playlists(id) ON UPDATE RESTRICT ON DELETE RESTRICT;

Build for Production

  yarn run build