Skip to content

Commit 9381f98

Browse files
committed
Initial commit
0 parents  commit 9381f98

38 files changed

+26893
-0
lines changed

.codesandbox/icon.png

1.24 KB
Loading

.codesandbox/tasks.json

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
// These tasks will run in order when initializing your CodeSandbox project.
3+
"setupTasks": [
4+
// For some reason, Nuxt throws a syntax error after multiple installs,
5+
// we're discussing with the Nuxt team how that could happen. For now, we
6+
// always do a clean node_modules installation.
7+
{
8+
"name": "Remove Dependencies",
9+
"command": "rm -rf node_modules"
10+
},
11+
{
12+
"name": "Install Dependencies",
13+
"command": "npm install"
14+
},
15+
{
16+
"name": "Migrate",
17+
"command": "npm run migrate"
18+
}
19+
],
20+
21+
// These tasks can be run from CodeSandbox. Running one will open a log in the app.
22+
"tasks": {
23+
"dev": {
24+
"name": "dev",
25+
"command": "npm run dev",
26+
"runAtStart": true,
27+
"preview": {
28+
"port": 3000
29+
}
30+
},
31+
"build": {
32+
"name": "build",
33+
"command": "npm run build"
34+
},
35+
"preview": {
36+
"name": "preview",
37+
"command": "npm run preview"
38+
},
39+
"migrate": {
40+
"name": "migrate",
41+
"command": "npm run migrate"
42+
},
43+
"lint": {
44+
"name": "lint",
45+
"command": "npm run lint"
46+
},
47+
"postinstall": {
48+
"name": "postinstall",
49+
"command": "npm run postinstall"
50+
},
51+
"install": {
52+
"name": "install dependencies",
53+
"command": "npm install"
54+
}
55+
}
56+
}

.codesandbox/template.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"title": "Nuxt Todos on the Edge",
3+
"description": "Perfect starter for building a Nuxt.js application for the edge with Drizzle and SQLite",
4+
"iconUrl": "https://raw.githubusercontent.com/codesandbox/sandbox-templates/main/nuxt/.codesandbox/icon.png",
5+
"tags": [
6+
"starter",
7+
"javascript",
8+
"typescript",
9+
"vue",
10+
"nuxt",
11+
"drizzle",
12+
"sqlite"
13+
],
14+
"published": true
15+
}

.devcontainer/devcontainer.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "Devcontainer",
3+
"image": "ghcr.io/codesandbox/devcontainers/typescript-node:latest",
4+
"customizations": {
5+
"vscode": {
6+
"extensions": ["Vue.volar"]
7+
}
8+
}
9+
}

.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
NUXT_OAUTH_GITHUB_CLIENT_ID=
2+
NUXT_OAUTH_GITHUB_CLIENT_SECRET=
3+
NUXT_SESSION_PASSWORD=

.eslintignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist
2+
.output
3+
.nuxt

.eslintrc.cjs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
root: true,
3+
extends: ["@nuxt/eslint-config"],
4+
rules: {
5+
// Global
6+
semi: ["error", "never"],
7+
quotes: ["error", "single"],
8+
"quote-props": ["error", "as-needed"],
9+
// Vue
10+
"vue/multi-word-component-names": 0,
11+
"vue/max-attributes-per-line": "off",
12+
"vue/no-v-html": 0,
13+
},
14+
};

.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules
2+
*.log
3+
.nuxt
4+
nuxt.d.ts
5+
.output
6+
.env
7+
.history
8+
db.sqlite
9+
dist
10+
.vercel
11+
.netlify
12+
db.sqlite-*

.npmrc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
shamefully-hoist=true
2+
strict-peer-dependencies=false

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Sébastien Chopin
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Nuxt Todo List on the Edge
2+
3+
A demonstration using [Nuxt](https://nuxt.com) with server-side rendering on the edge, authentication and database querying using SQLite in production.
4+
5+
## Features
6+
7+
- [Server-Side Rendering on the Edge](https://nuxt.com/blog/nuxt-on-the-edge)
8+
- Authentication backed-in using [nuxt-auth-utils](https://github.com/Atinux/nuxt-auth-utils)
9+
- Leverage SQLite as database with migrations using [drizzle ORM](https://orm.drizzle.team/)
10+
- User interface made with [Nuxt UI](https://ui.nuxt.com)
11+
- Embed [Drizzle Studio](https://orm.drizzle.team/drizzle-studio/overview/) in the [Nuxt DevTools](https://devtools.nuxt.com)
12+
13+
## Live demos
14+
15+
- CloudFlare Pages + D1: https://nuxt-todos-edge.pages.dev
16+
- CloudFlare Pages + Turso: https://nuxt-todos-turso.pages.dev
17+
- Lagon.app + Turso: https://nuxt-todos.lagon.dev
18+
- Vercel Edge + Turso: https://nuxt-todos-edge.vercel.app
19+
- Netlify Edge + Turso: https://nuxt-todos-edge.netlify.app
20+
- Deno Deploy + Turso: https://nuxt-todos-edge.deno.dev
21+
22+
https://github.com/Atinux/nuxt-todos-edge/assets/904724/5f3bee55-dbae-4329-8057-7d0e16e92f81
23+
24+
## Setup
25+
26+
Make sure to install the dependencies using [pnpm](https://pnpm.io/):
27+
28+
```bash
29+
pnpm i
30+
```
31+
32+
Create a [GitHub Oauth Application](https://github.com/settings/applications/new) with:
33+
34+
- Homepage url: `http://localhost:3000`
35+
- Callback url: `http://localhost:3000/api/auth/github`
36+
37+
Add the variables in the `.env` file:
38+
39+
```bash
40+
NUXT_OAUTH_GITHUB_CLIENT_ID="my-github-oauth-app-id"
41+
NUXT_OAUTH_GITHUB_CLIENT_SECRET="my-github-oauth-app-secret"
42+
```
43+
44+
To create sealed sessions, you also need to add `NUXT_SESSION_SECRET` in the `.env` with at least 32 characters:
45+
46+
```bash
47+
NUXT_SESSION_SECRET=your-super-long-secret-for-session-encryption
48+
```
49+
50+
## Development
51+
52+
Start the development server on http://localhost:3000
53+
54+
```bash
55+
npm run dev
56+
```
57+
58+
In the Nuxt DevTools, you can see your tables by clicking on the Drizzle Studio tab:
59+
60+
https://github.com/Atinux/nuxt-todos-edge/assets/904724/7ece3f10-aa6f-43d8-a941-7ca549bc208b
61+
62+
## Deploy on CloudFlare Pages
63+
64+
Create a CF pages deployment linked to your GitHub repository. Make sure to select Version 2 (Beta) as the build system version.
65+
66+
### Environment variables
67+
68+
```bash
69+
NUXT_OAUTH_GITHUB_CLIENT_ID=...
70+
NUXT_OAUTH_GITHUB_CLIENT_SECRET=...
71+
NUXT_SESSION_PASSWORD=...
72+
```
73+
74+
### Build command
75+
76+
Set the build command to:
77+
78+
```bash
79+
npm run build
80+
```
81+
82+
And the output directory to `dist/`
83+
84+
### D1 Database
85+
86+
Lastly, in the project settings -> Functions, add the binding between your D1 database and the `DB` variable:
87+
88+
![d1-binding](https://user-images.githubusercontent.com/904724/236021974-d77dfda6-4eb7-4094-ae36-479be73ec35f.png)
89+
90+
Copy the contents from `server/database/migrations/0000_heavy_xorn.sql` into the D1 console to seed the database.
91+
92+
### Turso Database
93+
94+
You can also use [Turso](https://turso.tech/) database instead of CloudFlare D1 by creating a database and adding the following env variables:
95+
96+
```
97+
TURSO_DB_URL=...
98+
TURSO_DB_TOKEN=...
99+
```
100+
101+
You can see a live demo using Turso on https://nuxt-todos-turso.pages.dev
102+
103+
## License
104+
105+
[MIT License](./LICENSE)

app.config.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default defineAppConfig({
2+
ui: {
3+
primary: "emerald",
4+
container: {
5+
constrained: "max-w-2xl",
6+
},
7+
card: {
8+
header: {
9+
base: "flex flex-wrap items-center justify-between",
10+
},
11+
body: {
12+
base: "space-y-4",
13+
},
14+
},
15+
dropdown: {
16+
width: "w-full",
17+
popper: {
18+
strategy: "absolute",
19+
},
20+
},
21+
},
22+
});

app.vue

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<script setup>
2+
const { loggedIn } = useUserSession();
3+
const colorMode = useColorMode();
4+
5+
watch(loggedIn, () => {
6+
if (!loggedIn.value) {
7+
navigateTo("/");
8+
}
9+
});
10+
11+
function toggleColorMode() {
12+
colorMode.preference = colorMode.preference === "dark" ? "light" : "dark";
13+
}
14+
15+
useHead({
16+
htmlAttrs: { lang: "en" },
17+
link: [{ rel: "icon", href: "/icon.png" }],
18+
});
19+
20+
useSeoMeta({
21+
viewport: "width=device-width, initial-scale=1, maximum-scale=1",
22+
title: "Nuxt Todos Edge",
23+
description:
24+
"A Nuxt demo hosted with Edge-side rendering, authentication and queyring a SQLite database",
25+
ogImage: "/social-image.png",
26+
twitterImage: "/social-image.png",
27+
twitterCard: "summary_large_image",
28+
});
29+
</script>
30+
31+
<template>
32+
<UContainer class="min-h-screen flex flex-col justify-center">
33+
<div class="mb-2 text-right">
34+
<UButton
35+
square
36+
variant="ghost"
37+
color="black"
38+
:icon="
39+
$colorMode.preference === 'dark'
40+
? 'i-heroicons-moon'
41+
: 'i-heroicons-sun'
42+
"
43+
@click="toggleColorMode"
44+
/>
45+
</div>
46+
47+
<NuxtPage />
48+
49+
<footer class="text-center mt-2">
50+
<NuxtLink
51+
href="https://github.com/atinux/nuxt-todos-edge"
52+
target="_blank"
53+
class="text-sm text-gray-500 hover:text-gray-700"
54+
>
55+
GitHub
56+
</NuxtLink>
57+
·
58+
<NuxtLink
59+
href="https://twitter.com/Atinux"
60+
target="_blank"
61+
class="text-sm text-gray-500 hover:text-gray-700"
62+
>
63+
Twitter
64+
</NuxtLink>
65+
</footer>
66+
</UContainer>
67+
<UNotifications />
68+
</template>
69+
70+
<style lang="postcss">
71+
body {
72+
@apply font-sans text-gray-950 bg-gray-50 dark:bg-gray-950 dark:text-gray-50;
73+
}
74+
</style>

drizzle.config.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { join } from "pathe";
2+
import type { Config } from "drizzle-kit";
3+
4+
export default {
5+
out: "server/database/migrations",
6+
schema: "server/database/schema.ts",
7+
driver: "better-sqlite",
8+
dbCredentials: {
9+
url: join(process.cwd(), "./db.sqlite"),
10+
},
11+
} satisfies Config;

middleware/auth.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default defineNuxtRouteMiddleware(() => {
2+
const { loggedIn } = useUserSession();
3+
4+
if (!loggedIn.value) {
5+
return navigateTo("/");
6+
}
7+
});

0 commit comments

Comments
 (0)