Compare commits

...

2 Commits

Author SHA1 Message Date
woikos
80deffb16c Add PWA support with offline-first caching (v0.39.4)
Some checks failed
Go / build-and-release (push) Has been cancelled
- Add web app manifest for standalone installation
- Add service worker with offline-first caching for static assets
- Add network-first caching with fallback for API calls
- Generate PWA icons (192x192, 512x512) from favicon
- Add Apple PWA meta tags for iOS support
- Update rollup config to copy PWA files to dist
- Update release command to use relay.orly.dev instead of 10.0.0.1

Files modified:
- app/web/public/manifest.json: New PWA manifest
- app/web/public/sw.js: New service worker
- app/web/public/icon-192.png: New PWA icon
- app/web/public/icon-512.png: New PWA icon
- app/web/public/index.html: Add manifest link, meta tags, SW registration
- app/web/rollup.config.js: Add PWA files to copy targets
- .claude/commands/release.md: Update VPS SSH address
- pkg/version/version: Bump to v0.39.4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 10:48:25 +01:00
2aa5c16311 Fix base64 encoding to keep padding for Go URLEncoding (v0.39.3)
Some checks failed
Go / build-and-release (push) Has been cancelled
- Remove padding stripping from URL-safe base64 conversion
- Go's base64.URLEncoding expects padding characters
- Fix applied to LogView.svelte, BlossomView.svelte, and api.js

Files modified:
- app/web/src/LogView.svelte: Keep padding in auth header
- app/web/src/BlossomView.svelte: Keep padding in auth header
- app/web/src/api.js: Keep padding in auth header
- pkg/version/version: Bump to v0.39.3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 16:10:47 +01:00
14 changed files with 152 additions and 11 deletions

View File

@@ -50,7 +50,7 @@ If no argument provided, default to `patch`.
11. **Deploy to VPS** by running:
```
ssh 10.0.0.1 'cd ~/src/next.orly.dev && git stash && git pull origin main && export PATH=$PATH:~/go/bin && CGO_ENABLED=0 go build -o ~/.local/bin/next.orly.dev && sudo systemctl restart orly && ~/.local/bin/next.orly.dev version'
ssh relay.orly.dev 'cd ~/src/next.orly.dev && git stash && git pull origin main && export PATH=$PATH:~/go/bin && CGO_ENABLED=0 go build -o ~/.local/bin/next.orly.dev && sudo systemctl restart orly && ~/.local/bin/next.orly.dev version'
```
12. **Report completion** with the new version and commit hash

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -24,6 +24,11 @@
</style>
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/icon-192.png" />
<meta name="theme-color" content="#000000" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="stylesheet" href="/global.css" />
<link rel="stylesheet" href="/bundle.css" />
@@ -31,4 +36,9 @@
</head>
<body></body>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
</html>

BIN
app/web/public/icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
app/web/public/icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

View File

@@ -24,6 +24,11 @@
</style>
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/icon-192.png" />
<meta name="theme-color" content="#000000" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="stylesheet" href="/global.css" />
<link rel="stylesheet" href="/bundle.css" />
@@ -31,4 +36,9 @@
</head>
<body></body>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
</html>

View File

@@ -0,0 +1,22 @@
{
"name": "ORLY Nostr Relay",
"short_name": "ORLY",
"description": "High-performance Nostr relay",
"display": "standalone",
"start_url": "/",
"scope": "/",
"theme_color": "#000000",
"background_color": "#000000",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

95
app/web/public/sw.js Normal file
View File

@@ -0,0 +1,95 @@
const CACHE_VERSION = 'orly-v1';
const STATIC_ASSETS = [
'/',
'/index.html',
'/bundle.js',
'/bundle.css',
'/global.css',
'/favicon.png',
'/icon-192.png',
'/icon-512.png',
'/orly.png'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_VERSION).then((cache) => {
return cache.addAll(STATIC_ASSETS);
})
);
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_VERSION)
.map((name) => caches.delete(name))
);
})
);
self.clients.claim();
});
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
// Skip WebSocket requests
if (url.protocol === 'ws:' || url.protocol === 'wss:') {
return;
}
// Skip non-GET requests
if (event.request.method !== 'GET') {
return;
}
// API calls: network-first with cache fallback
if (url.pathname.startsWith('/api/')) {
event.respondWith(
fetch(event.request)
.then((response) => {
if (response.ok) {
const clone = response.clone();
caches.open(CACHE_VERSION).then((cache) => {
cache.put(event.request, clone);
});
}
return response;
})
.catch(() => {
return caches.match(event.request);
})
);
return;
}
// Static assets: cache-first with network fallback
event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) {
// Update cache in background
fetch(event.request).then((response) => {
if (response.ok) {
caches.open(CACHE_VERSION).then((cache) => {
cache.put(event.request, response);
});
}
}).catch(() => {});
return cached;
}
return fetch(event.request).then((response) => {
if (response.ok) {
const clone = response.clone();
caches.open(CACHE_VERSION).then((cache) => {
cache.put(event.request, clone);
});
}
return response;
});
})
);
});

View File

@@ -84,7 +84,11 @@ export default {
{ src: 'public/global.css', dest: 'dist' },
{ src: 'public/favicon.png', dest: 'dist' },
{ src: 'public/orly.png', dest: 'dist' },
{ src: 'public/orly-favicon.png', dest: 'dist' }
{ src: 'public/orly-favicon.png', dest: 'dist' },
{ src: 'public/manifest.json', dest: 'dist' },
{ src: 'public/sw.js', dest: 'dist' },
{ src: 'public/icon-192.png', dest: 'dist' },
{ src: 'public/icon-512.png', dest: 'dist' }
]
}),
],

View File

@@ -76,8 +76,8 @@
};
const signedEvent = await signer.signEvent(authEvent);
// Use URL-safe base64 encoding
return btoa(JSON.stringify(signedEvent)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
// Use URL-safe base64 encoding (replace + with -, / with _)
return btoa(JSON.stringify(signedEvent)).replace(/\+/g, '-').replace(/\//g, '_');
} catch (err) {
console.error("Error creating Blossom auth:", err);
return null;

View File

@@ -75,8 +75,8 @@
};
const signedEvent = await userSigner.signEvent(authEvent);
// Use URL-safe base64 encoding (replace + with -, / with _, remove padding)
return btoa(JSON.stringify(signedEvent)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
// Use URL-safe base64 encoding (replace + with -, / with _)
return btoa(JSON.stringify(signedEvent)).replace(/\+/g, '-').replace(/\//g, '_');
} catch (err) {
console.error("Error creating auth header:", err);
return null;

View File

@@ -30,8 +30,8 @@ export async function createNIP98Auth(signer, pubkey, method, url) {
// Sign using the signer
const signedEvent = await signer.signEvent(authEvent);
// Use URL-safe base64 encoding
return btoa(JSON.stringify(signedEvent)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
// Use URL-safe base64 encoding (replace + with -, / with _)
return btoa(JSON.stringify(signedEvent)).replace(/\+/g, '-').replace(/\//g, '_');
} catch (error) {
console.error("Error creating NIP-98 auth:", error);
return null;

View File

@@ -1 +1 @@
v0.39.2
v0.39.4