Compare commits

...

4 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
ce54a6886a Use URL-safe base64 for NIP-98 auth encoding (v0.39.2)
Some checks failed
Go / build-and-release (push) Has been cancelled
- Fix base64 encoding to use URL-safe format (- instead of +, _ instead of /)
- Remove padding characters (=) from base64 output
- Apply fix to LogView, BlossomView, and api.js

Files modified:
- app/web/src/LogView.svelte: URL-safe base64 for NIP-98 auth
- app/web/src/BlossomView.svelte: URL-safe base64 for Blossom auth
- app/web/src/api.js: URL-safe base64 for NIP-98 auth
- pkg/version/version: Bump to v0.39.2

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 16:03:46 +01:00
05170db4f7 Fix NIP-98 URL mismatch in log viewer (v0.39.1)
Some checks failed
Go / build-and-release (push) Has been cancelled
- Include query parameters in signed NIP-98 auth URL
- Auth event URL must match actual request URL including ?offset=&limit=

Files modified:
- app/web/src/LogView.svelte: Fix auth URL to include query params
- pkg/version/version: Bump to v0.39.1

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 15:54:30 +01:00
14 changed files with 155 additions and 10 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,7 +76,8 @@
};
const signedEvent = await signer.signEvent(authEvent);
return btoa(JSON.stringify(signedEvent));
// 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,7 +75,8 @@
};
const signedEvent = await userSigner.signEvent(authEvent);
return btoa(JSON.stringify(signedEvent));
// 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;
@@ -94,8 +95,9 @@
}
try {
const authHeader = await createAuthHeader("GET", "/api/logs");
const url = `${window.location.origin}/api/logs?offset=${offset}&limit=${LIMIT}`;
const path = `/api/logs?offset=${offset}&limit=${LIMIT}`;
const authHeader = await createAuthHeader("GET", path);
const url = `${window.location.origin}${path}`;
const response = await fetch(url, {
headers: authHeader ? { Authorization: `Nostr ${authHeader}` } : {},
});

View File

@@ -30,7 +30,8 @@ export async function createNIP98Auth(signer, pubkey, method, url) {
// Sign using the signer
const signedEvent = await signer.signEvent(authEvent);
return btoa(JSON.stringify(signedEvent));
// 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.0
v0.39.4