├── .gitignore ├── README.md ├── app.vue ├── bun.lockb ├── components └── Rocket.vue ├── nuxt.config.ts ├── package.json ├── public ├── app.webmanifest ├── favicon.ico ├── icons │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico └── robots.txt ├── server ├── api │ └── info.ts └── plugins │ └── timing.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | .nuxt 4 | .nitro 5 | .cache 6 | .output 7 | .env 8 | dist 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuxt on PI 2 | 3 | Nuxt hosted on a Raspberry Pi Zero 2W board. ([tweet](https://twitter.com/_pi0_/status/1727774395207135364)) 4 | 5 | ⚡️ Live URL: https://pizero.pi0.io 6 | 7 | ## Development 8 | 9 | Install [bun](https://bun.sh/) 10 | 11 | Start the development server on http://localhost:3000 12 | 13 | ```bash 14 | bun dev 15 | ``` 16 | 17 | ## Production 18 | 19 | ### First time setup 20 | 21 | Install [bun](https://bun.sh/) on your board 22 | 23 | Setup cloudflare tunnel on your board and map to http://localhost:3000 ([learn more](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-local-tunnel/)) 24 | 25 | ### Deploy 26 | 27 | Build app for production from your PC: 28 | 29 | ```bash 30 | bun build --preset bun 31 | ``` 32 | 33 | Copy `.output` directory to your board: 34 | 35 | ```bash 36 | rsync -Lavz .output/ user@ip:app 37 | ``` 38 | 39 | Optionally install and run `tmux` or `screen` 40 | 41 | Start bun server: 42 | 43 | ```bash 44 | bun --watch app/server/index.mjs 45 | ```` 46 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 45 | 46 | 51 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/bun.lockb -------------------------------------------------------------------------------- /components/Rocket.vue: -------------------------------------------------------------------------------- 1 | 101 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtConfig({ 2 | modules: ["@nuxtjs/tailwindcss"], 3 | experimental: { 4 | noScripts: true, 5 | writeEarlyHints: true, 6 | inlineSSRStyles: true 7 | }, 8 | routeRules: { 9 | '/**': { swr: 1 } 10 | }, 11 | appConfig: { 12 | platform: 'Raspberry Pi Zero 2W' 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "nuxt build --preset bun", 5 | "dev": "nuxt dev", 6 | "postinstall": "nuxt prepare", 7 | "preview": "nuxt preview" 8 | }, 9 | "devDependencies": { 10 | "@nuxtjs/tailwindcss": "^6.10.0", 11 | "nuxt": "^3.8.2", 12 | "typescript": "^5.3.2", 13 | "vue": "^3.3.8" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /public/app.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "start_url": "/", 4 | "icons": [ 5 | { 6 | "src": "/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#0e172a", 17 | "background_color": "#0e172a", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/public/favicon.ico -------------------------------------------------------------------------------- /public/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/public/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/public/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/public/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pi0/nuxt-on-pi/784037c9babb5c7044c9e4ab1a653da22398fd00/public/icons/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | User-Agent: * 3 | Allow: / 4 | -------------------------------------------------------------------------------- /server/api/info.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process' 2 | 3 | export default cachedEventHandler(() => { 4 | const temp = getTemp() 5 | return `Temperature: ${temp}` 6 | }) 7 | 8 | function getTemp() { 9 | try { 10 | const tempInfo = execSync('vcgencmd measure_temp').toString().trim() 11 | // const tempInfo = "temp=38.1'C" 12 | return /temp=(\d+\.\d+'C)/.exec(tempInfo)?.[1] || '-' 13 | } catch { 14 | return '-' 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/plugins/timing.ts: -------------------------------------------------------------------------------- 1 | export default defineNitroPlugin((nitroApp) => { 2 | nitroApp.hooks.hook('request', (event) => { 3 | event.context.timing = { start: new Date() } 4 | }) 5 | nitroApp.hooks.hook('render:html', (htmlContext, { event }) => { 6 | const renderTime = Date.now() - event.context.timing.start.getTime() 7 | htmlContext.body.push(` 8 | 12 | `) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://v3.nuxtjs.org/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | --------------------------------------------------------------------------------