feat: placeholder images, Nostr inbox, order lookup, SEO, bigger logo

- 5 SVG placeholder product images (minimal dark style with watermark initials)
- Seed data updated to reference .svg placeholders
- Nostr DM inbox in admin (Messages tab) with shop npub display
- GET /api/admin/nostr-info endpoint for shop pubkey
- My Orders page: customers look up orders by NIP-07 Nostr identity
- GET /api/orders/by-pubkey/:pubkey endpoint with hex validation
- SeoMeta component for OG/Twitter meta tags
- SEO meta on HomeView and ProductView
- Base OG meta tags in index.html
- "My Orders" link in shop header nav
- Splash logo doubled in size on desktop (680px max)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-17 01:28:36 +00:00
parent 0c7c803aee
commit 52fe7a013f
19 changed files with 342 additions and 6 deletions

View File

@@ -0,0 +1,26 @@
import { Router } from 'express'
import { adminAuth } from '../middleware/adminAuth.js'
export const adminNostrRouter = Router()
adminNostrRouter.use(adminAuth)
adminNostrRouter.get('/nostr-info', async (_req, res) => {
const privkeyHex = process.env.NOSTR_PRIVATE_KEY
if (!privkeyHex) {
res.status(404).json({ error: { code: 'NOT_CONFIGURED', message: 'NOSTR_PRIVATE_KEY not set' } })
return
}
try {
const { getPublicKey } = await import('nostr-tools/pure')
const { npubEncode } = await import('nostr-tools/nip19')
const privkey = Uint8Array.from(Buffer.from(privkeyHex, 'hex'))
const pubkey = getPublicKey(privkey)
const npub = npubEncode(pubkey)
privkey.fill(0)
res.json({ npub, pubkey })
} catch {
res.status(500).json({ error: { code: 'NOSTR_ERROR', message: 'Failed to derive public key' } })
}
})

View File

@@ -0,0 +1,45 @@
import { Router } from 'express'
import { getDb } from '../db/connection.js'
import type { OrderItem, OrderStatus } from '../../shared/types.js'
export const ordersByPubkeyRouter = Router()
interface OrderRow {
id: string
nostr_pubkey: string | null
email: string | null
btcpay_invoice_id: string | null
status: string
items: string
total_sats: number
note: string | null
created_at: string
updated_at: string
}
ordersByPubkeyRouter.get('/:pubkey', (req, res) => {
const pubkey = req.params.pubkey
if (!/^[0-9a-f]{64}$/.test(pubkey)) {
res.status(400).json({ error: { code: 'INVALID_PUBKEY', message: 'Invalid hex pubkey' } })
return
}
const db = getDb()
const rows = db.prepare(
'SELECT * FROM orders WHERE nostr_pubkey = ? ORDER BY created_at DESC'
).all(pubkey) as OrderRow[]
res.json(rows.map((row) => ({
id: row.id,
nostrPubkey: row.nostr_pubkey,
email: row.email,
btcpayInvoiceId: row.btcpay_invoice_id,
status: row.status as OrderStatus,
items: JSON.parse(row.items) as OrderItem[],
totalSats: row.total_sats,
note: row.note,
createdAt: row.created_at,
updatedAt: row.updated_at,
})))
})