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 qwik examples #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions qwik/github-oauth/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GITHUB_CLIENT_ID=""
GITHUB_CLIENT_SECRET=""
41 changes: 41 additions & 0 deletions qwik/github-oauth/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Build
/dist
/lib
/lib-types
/server

# Development
node_modules
*.local

# Cache
.cache
.mf
.rollup.cache
tsconfig.tsbuildinfo

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

# Editor
.vscode/*
!.vscode/launch.json
!.vscode/*.code-snippets

.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# Yarn
.yarn/*
!.yarn/releases
26 changes: 26 additions & 0 deletions qwik/github-oauth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# GitHub OAuth example with Lucia and Qwik

This example uses `better-sqlite3`.

```bash
# install dependencies
pnpm i

# run dev server
pnpm dev
```

## Runtime

This example is built for Node.js 20. If you're using Node.js 16/18, un-comment the following lines in `auth/lucia.ts`:

```ts
// import "lucia/polyfill/node";
```

## User schema

| id | type | unique |
| ---------- | -------- | :----: |
| `id` | `string` | |
| `username` | `string` | ✓ |
40 changes: 40 additions & 0 deletions qwik/github-oauth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "github-oauth",
"description": "GitHub OAuth example with Lucia and Qwik",
"engines": {
"node": ">=15.0.0"
},
"scripts": {
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
"build.types": "tsc --incremental --noEmit",
"dev": "vite --mode ssr",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
"lint": "eslint \"src/**/*.ts*\"",
"preview": "qwik build preview && vite preview --open",
"start": "vite --open --mode ssr",
"qwik": "qwik"
},
"devDependencies": {
"@builder.io/qwik": "^1.2.10",
"@builder.io/qwik-city": "^1.2.10",
"@libsql/client": "^0.3.4",
"@lucia-auth/adapter-sqlite": "latest",
"@lucia-auth/oauth": "latest",
"@types/better-sqlite3": "^7.6.4",
"@types/eslint": "8.44.1",
"@types/node": "^20.4.5",
"@typescript-eslint/eslint-plugin": "6.2.0",
"@typescript-eslint/parser": "6.2.0",
"better-sqlite3": "^8.4.0",
"eslint": "8.45.0",
"eslint-plugin-qwik": "^1.2.10",
"lucia": "latest",
"prettier": "3.0.0",
"typescript": "5.1.6",
"undici": "5.22.1",
"vite": "4.4.7",
"vite-tsconfig-paths": "4.2.0"
}
}
1 change: 1 addition & 0 deletions qwik/github-oauth/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions qwik/github-oauth/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/web-manifest-combined.json",
"name": "qwik-project-name",
"short_name": "Welcome to Qwik",
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"description": "A Qwik project app."
}
Empty file.
17 changes: 17 additions & 0 deletions qwik/github-oauth/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE TABLE user (
id VARCHAR(15) PRIMARY KEY,
username VARCHAR(31) NOT NULL UNIQUE
);
CREATE TABLE user_key (
id VARCHAR(255) PRIMARY KEY,
user_id VARCHAR(15) NOT NULL,
hashed_password VARCHAR(255),
FOREIGN KEY (user_id) REFERENCES user(id)
);
CREATE TABLE user_session (
id VARCHAR(127) PRIMARY KEY,
user_id VARCHAR(15) NOT NULL,
active_expires BIGINT NOT NULL,
idle_expires BIGINT NOT NULL,
FOREIGN KEY (user_id) REFERENCES user(id)
);
8 changes: 8 additions & 0 deletions qwik/github-oauth/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// <reference types="lucia" />
declare namespace Lucia {
type Auth = import("./auth/lucia").Auth;
type DatabaseUserAttributes = {
username: string;
};
type DatabaseSessionAttributes = {};
}
35 changes: 35 additions & 0 deletions qwik/github-oauth/src/auth/lucia.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import fs from "node:fs";
import { betterSqlite3 } from "@lucia-auth/adapter-sqlite";
import { github } from "@lucia-auth/oauth/providers";
import sqlite from "better-sqlite3";
import { lucia } from "lucia";
import { qwik } from "lucia/middleware";
// import "lucia/polyfill/node";

const db = sqlite(":memory:");
db.exec(fs.readFileSync("schema.sql", "utf8"));

export const auth = lucia({
adapter: betterSqlite3(db, {
user: "user",
session: "user_session",
key: "user_key"
}),
env: process.env.NODE_ENV === "development" ? "DEV" : "PROD",
middleware: qwik(),
sessionCookie: {
expires: false
},
getUserAttributes: (data) => {
return {
username: data.username
};
}
});

export const githubAuth = github(auth, {
clientId: process.env.GITHUB_CLIENT_ID ?? "",
clientSecret: process.env.GITHUB_CLIENT_SECRET ?? ""
});

export type Auth = typeof auth;
18 changes: 18 additions & 0 deletions qwik/github-oauth/src/entry.dev.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* WHAT IS THIS FILE?
*
* Development entry point using only client-side modules:
* - Do not use this mode in production!
* - No SSR
* - No portion of the application is pre-rendered on the server.
* - All of the application is running eagerly in the browser.
* - More code is transferred to the browser than in SSR mode.
* - Optimizer/Serialization/Deserialization code is not exercised!
*/
import { type RenderOptions, render } from "@builder.io/qwik";

import Root from "./root";

export default function (opts: RenderOptions) {
return render(document, <Root />, opts);
}
21 changes: 21 additions & 0 deletions qwik/github-oauth/src/entry.preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* WHAT IS THIS FILE?
*
* It's the bundle entry point for `npm run preview`.
* That is, serving your app built in production mode.
*
* Feel free to modify this file, but don't remove it!
*
* Learn more about Vite's preview command:
* - https://vitejs.dev/config/preview-options.html#preview-options
*
*/
import { createQwikCity } from "@builder.io/qwik-city/middleware/node";
import qwikCityPlan from "@qwik-city-plan";

import render from "./entry.ssr";

/**
* The default export is the QwikCity adapter used by Vite preview.
*/
export default createQwikCity({ render, qwikCityPlan });
31 changes: 31 additions & 0 deletions qwik/github-oauth/src/entry.ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* WHAT IS THIS FILE?
*
* SSR entry point, in all cases the application is rendered outside the browser, this
* entry point will be the common one.
*
* - Server (express, cloudflare...)
* - npm run start
* - npm run preview
* - npm run build
*
*/
import {
type RenderToStreamOptions,
renderToStream
} from "@builder.io/qwik/server";
import { manifest } from "@qwik-client-manifest";

import Root from "./root";

export default function (opts: RenderToStreamOptions) {
return renderToStream(<Root />, {
manifest,
...opts,
// Use container attributes to set attributes on the html tag.
containerAttributes: {
lang: "en-us",
...opts.containerAttributes
}
});
}
42 changes: 42 additions & 0 deletions qwik/github-oauth/src/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* WHAT IS THIS FILE?
*
* Globally applied styles. No matter which components are in the page or matching route,
* the styles in here will be applied to the Document, without any sort of CSS scoping.
*
*/
html {
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
font-family:
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial,
"Noto Sans",
sans-serif,
"Apple Color Emoji",
"Segoe UI Emoji",
"Segoe UI Symbol",
"Noto Color Emoji";
}

body {
padding: 2rem;
line-height: inherit;
}

* {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}

.error {
color: red;
}
31 changes: 31 additions & 0 deletions qwik/github-oauth/src/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { component$ } from "@builder.io/qwik";
import {
QwikCityProvider,
RouterOutlet,
ServiceWorkerRegister
} from "@builder.io/qwik-city";

import "./global.css";

export default component$(() => {
/**
* The root of a QwikCity site always start with the <QwikCityProvider> component,
* immediately followed by the document's <head> and <body>.
*
* Don't remove the `<head>` and `<body>` elements.
*/

return (
<QwikCityProvider>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="manifest" href="/manifest.json" />
</head>
<body lang="en">
<RouterOutlet />
<ServiceWorkerRegister />
</body>
</QwikCityProvider>
);
});
52 changes: 52 additions & 0 deletions qwik/github-oauth/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { component$ } from "@builder.io/qwik";
import {
type DocumentHead,
Form,
routeAction$,
routeLoader$
} from "@builder.io/qwik-city";

import { auth } from "~/auth/lucia";

export const useSessionUser = routeLoader$(async (event) => {
const authRequest = auth.handleRequest(event);
const session = await authRequest.validate();
if (!session) throw event.redirect(302, "/signin/");
return session.user;
});

export const useSignOutAction = routeAction$(async (_, event) => {
const authRequest = auth.handleRequest(event);
const session = await authRequest.validate();
if (!session) throw event.error(401, "Unauthorized");

await auth.invalidateSession(session.sessionId);
const { name, value, attributes } = auth.createSessionCookie(null);
event.cookie.set(name, value, attributes);
throw event.redirect(302, "/login/");
});

export default component$(() => {
const user = useSessionUser();
const signout = useSignOutAction();
return (
<>
<h1>Profile</h1>
<p>User id: {user.value.userId}</p>
<p>GitHub username: {user.value.username}</p>
<Form action={signout}>
<button>Sign out</button>
</Form>
</>
);
});

export const head: DocumentHead = {
title: "GitHub OAuth with Lucia",
meta: [
{
name: "description",
content: "Qwik site description"
}
]
};
Loading