diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 427bc9ad..90c6cafb 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,13 +1,9 @@
name: Build
-on:
- push:
- tags:
- - "v*"
- workflow_dispatch:
+on: [push]
jobs:
- CI:
+ build:
name: 🔨🚀 Build and deploy
runs-on: ubuntu-latest
steps:
@@ -25,10 +21,10 @@ jobs:
uses: pnpm/action-setup@v4
with:
version: 8
- - name: 🐉 Use Node.js 18.16.x
+ - name: 🐉 Use Node.js 22.4.x
uses: actions/setup-node@v3
with:
- node-version: 18.16.x
+ node-version: 22.4.x
cache: "pnpm"
- name: ⏬ Install Node dependencies
run: pnpm install
@@ -38,17 +34,17 @@ jobs:
- name: 🔨 Build site
run: pnpm build
- - name: 📦 Upload build artifacts
- uses: actions/upload-artifact@v3.1.2
- with:
- name: build
- path: build
- - name: Disable jekyll
- run: touch build/.nojekyll
- - name: Custom domain
- run: echo 'manager.charachorder.com' > build/CNAME
- - run: git config user.name github-actions
- - run: git config user.email github-actions@github.com
- - run: git --work-tree build add --all
- - run: git commit -m "Automatic Deploy action run by github-actions"
- - run: git push origin HEAD:gh-pages --force
+ - name: Setup SSH
+ run: |
+ install -m 600 -D /dev/null ~/.ssh/id_rsa
+ echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_rsa
+ echo "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
+
+ - name: Publish Stable
+ if: ${{ github.ref == 'refs/heads/v*' }}
+ run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/www/
+
+ - name: Publish Branch
+ run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${GITHUB_REF##*/}
+ - name: Publish Commit
+ run: rsync -rav --mkpath --delete build/ deploy@charachorder.io:/home/deploy/ref/${{ github.sha }}
diff --git a/icons.config.js b/icons.config.js
index af1a3e2f..b4d2411c 100644
--- a/icons.config.js
+++ b/icons.config.js
@@ -18,6 +18,8 @@ const config = {
"update",
"offline_pin",
"warning",
+ "dangerous",
+ "check",
"cable",
"person",
"sync",
diff --git a/src/env.d.ts b/src/env.d.ts
index 6c3602f6..94d5f056 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -15,6 +15,7 @@ interface ImportMetaEnv {
readonly VITE_LATEST_FIRMWARE: string;
readonly VITE_STORE_URL: string;
readonly VITE_MATRIX_URL: string;
+ readonly VITE_FIRMWARE_URL: string;
}
interface ImportMeta {
diff --git a/src/routes/(app)/ota-update/+layout.svelte b/src/routes/(app)/ota-update/+layout.svelte
new file mode 100644
index 00000000..edc4d10f
--- /dev/null
+++ b/src/routes/(app)/ota-update/+layout.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+{@render children()}
+
+
diff --git a/src/routes/(app)/ota-update/+layout.ts b/src/routes/(app)/ota-update/+layout.ts
new file mode 100644
index 00000000..ae88a275
--- /dev/null
+++ b/src/routes/(app)/ota-update/+layout.ts
@@ -0,0 +1,2 @@
+export const prerender = false;
+export const ssr = false;
diff --git a/src/routes/(app)/ota-update/+page.svelte b/src/routes/(app)/ota-update/+page.svelte
index 29f70707..92c7ee36 100644
--- a/src/routes/(app)/ota-update/+page.svelte
+++ b/src/routes/(app)/ota-update/+page.svelte
@@ -1,15 +1,91 @@
+
+
+{#if !currentDevice}
+
+{/if}
+
+
+
diff --git a/src/routes/(app)/ota-update/+page.ts b/src/routes/(app)/ota-update/+page.ts
new file mode 100644
index 00000000..66aa787c
--- /dev/null
+++ b/src/routes/(app)/ota-update/+page.ts
@@ -0,0 +1,9 @@
+import type { PageLoad } from "./$types";
+import type { DirectoryListing } from "./listing";
+
+export const load = (async ({ fetch }) => {
+ const result = await fetch(import.meta.env.VITE_FIRMWARE_URL);
+ const data = await result.json();
+
+ return { devices: data as DirectoryListing[] };
+}) satisfies PageLoad;
diff --git a/src/routes/(app)/ota-update/[device]/+page.svelte b/src/routes/(app)/ota-update/[device]/+page.svelte
new file mode 100644
index 00000000..25291894
--- /dev/null
+++ b/src/routes/(app)/ota-update/[device]/+page.svelte
@@ -0,0 +1,85 @@
+
+
+
+
Versions available for {data.device}
+
+
+
+{#if data.versions}
+
+{:else}
+ The device {data.device} does not exist.
+{/if}
+
+
diff --git a/src/routes/(app)/ota-update/[device]/+page.ts b/src/routes/(app)/ota-update/[device]/+page.ts
new file mode 100644
index 00000000..0c1c1575
--- /dev/null
+++ b/src/routes/(app)/ota-update/[device]/+page.ts
@@ -0,0 +1,16 @@
+import type { PageLoad } from "./$types";
+import type { DirectoryListing } from "../listing";
+
+export const load = (async ({ fetch, params }) => {
+ const result = await fetch(
+ `${import.meta.env.VITE_FIRMWARE_URL}/${params.device}/`,
+ );
+ const data = await result.json();
+
+ return {
+ versions: (data as DirectoryListing[]).sort((a, b) =>
+ b.name.localeCompare(a.name),
+ ),
+ device: params.device,
+ };
+}) satisfies PageLoad;
diff --git a/src/routes/(app)/ota-update/[device]/[version]/+page.svelte b/src/routes/(app)/ota-update/[device]/[version]/+page.svelte
new file mode 100644
index 00000000..dde33acd
--- /dev/null
+++ b/src/routes/(app)/ota-update/[device]/[version]/+page.svelte
@@ -0,0 +1,163 @@
+
+
+
+
+ Update {data.device}
+ to {data.version}
+
+
+
+
+ {#if isCorrectDevice === false}
+
+ These files are incompatible with your device
+
+ {/if}
+
+
+ OTA Upate
+ {#if data.ota}
+ OTA update
+ {:else}
+ There are no OTA files for this device.
+ {/if}
+
+
+
+
+
Other options
+
+
+ Via UF2
+
+ - Backup your device
+ - Reboot to bootloader
+ - Save CURRENT.UF2 to the new drive
+ - Restore
+
+
+
+
+
+
diff --git a/src/routes/(app)/ota-update/[device]/[version]/+page.ts b/src/routes/(app)/ota-update/[device]/[version]/+page.ts
new file mode 100644
index 00000000..7c5075de
--- /dev/null
+++ b/src/routes/(app)/ota-update/[device]/[version]/+page.ts
@@ -0,0 +1,20 @@
+import type { PageLoad } from "./$types";
+import type { FileListing, Listing } from "../../listing";
+
+export const load = (async ({ fetch, params }) => {
+ const result = await fetch(
+ `${import.meta.env.VITE_FIRMWARE_URL}/${params.device}/${params.version}/`,
+ );
+ const data: Listing[] = await result.json();
+
+ return {
+ uf2: data.find(
+ (entry) => entry.type === "file" && entry.name === "CURRENT.UF2",
+ ) as FileListing,
+ ota: data.find(
+ (entry) => entry.type === "file" && entry.name === "firmware.bin",
+ ),
+ version: params.version,
+ device: params.device,
+ };
+}) satisfies PageLoad;
diff --git a/src/routes/(app)/ota-update/listing.ts b/src/routes/(app)/ota-update/listing.ts
new file mode 100644
index 00000000..17a61bf8
--- /dev/null
+++ b/src/routes/(app)/ota-update/listing.ts
@@ -0,0 +1,14 @@
+export type Listing = FileListing | DirectoryListing;
+
+export interface DirectoryListing {
+ name: string;
+ type: "directory";
+ mtime: string;
+}
+
+export interface FileListing {
+ name: string;
+ type: "file";
+ mtime: string;
+ size: number;
+}
diff --git a/vite.config.ts b/vite.config.ts
index 536bdc47..97072a81 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -23,6 +23,7 @@ process.env["VITE_LEARN_URL"] = "https://www.iq-eq.io/";
process.env["VITE_LATEST_FIRMWARE"] = "1.1.4";
process.env["VITE_STORE_URL"] = "https://www.charachorder.com/";
process.env["VITE_MATRIX_URL"] = "https://charachorder.io/";
+process.env["VITE_FIRMWARE_URL"] = "https://charachorder.io/firmware/";
export default defineConfig({
build: {
@@ -54,10 +55,11 @@ export default defineConfig({
workbox: {
// https://vite-pwa-org.netlify.app/frameworks/sveltekit.html#globpatterns
globPatterns: [
- "client/**/*.{js,map,css,ico,woff2,csv,png,webp,svg,webmanifest}",
+ "client/**/*.{js,css,ico,woff2,csv,png,webp,svg,webmanifest}",
"prerendered/**/*.html",
],
ignoreURLParametersMatching: [/^import|redirectUrl|loginToken$/],
+ maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
},
manifest: {
name: "CharaChorder Device Manager",