Remove deprecated scripts and components related to Nostr functionality, including admin post debugging, VAPID key generation, and admin note sending. Clean up package dependencies by removing unused libraries and updating package-lock.json and package.json accordingly.
This commit is contained in:
parent
2f0024478d
commit
a551f46c90
16 changed files with 7 additions and 1488 deletions
|
|
@ -1,94 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Debug what admin posts exist in the relay
|
||||
import { SimplePool } from 'nostr-tools'
|
||||
|
||||
const RELAY_URL = "ws://127.0.0.1:5001/nostrrelay/mainhub"
|
||||
const ADMIN_PUBKEYS = [
|
||||
"4b9d7688eba64565dcb77cc8ab157eca1964d5de9f7afabe03eb5b54a43d9882",
|
||||
"30b1ab3683fa6cc0e3695849ee1ec29e9fbae4c9e7ddb7452a4ddb37a0660040",
|
||||
"c116dbc73a8ccd0046a2ecf96c0b0531d3eda650d449798ac5b86ff6e301debe"
|
||||
]
|
||||
|
||||
async function debugAdminPosts() {
|
||||
const pool = new SimplePool()
|
||||
|
||||
console.log('🔍 Checking relay for admin posts...')
|
||||
console.log('Relay:', RELAY_URL)
|
||||
console.log('Admin pubkeys:', ADMIN_PUBKEYS.length)
|
||||
console.log('')
|
||||
|
||||
try {
|
||||
// Check each admin individually
|
||||
for (let i = 0; i < ADMIN_PUBKEYS.length; i++) {
|
||||
const pubkey = ADMIN_PUBKEYS[i]
|
||||
console.log(`👤 Admin ${i + 1}: ${pubkey.slice(0, 16)}...`)
|
||||
|
||||
const individualFilter = {
|
||||
kinds: [1],
|
||||
authors: [pubkey],
|
||||
limit: 10
|
||||
}
|
||||
|
||||
const events = []
|
||||
const sub = pool.subscribeMany([RELAY_URL], [individualFilter], {
|
||||
onevent(event) {
|
||||
events.push(event)
|
||||
},
|
||||
oneose() {
|
||||
console.log(` Found ${events.length} posts`)
|
||||
events.forEach((event, idx) => {
|
||||
console.log(` ${idx + 1}. "${event.content.slice(0, 60)}${event.content.length > 60 ? '...' : ''}"`)
|
||||
console.log(` Created: ${new Date(event.created_at * 1000).toLocaleString()}`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
})
|
||||
|
||||
// Wait for events
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
sub.close()
|
||||
}
|
||||
|
||||
// Now test the combined filter (what the app uses)
|
||||
console.log('🔄 Testing combined filter (what the app uses)...')
|
||||
const combinedFilter = {
|
||||
kinds: [1],
|
||||
authors: ADMIN_PUBKEYS,
|
||||
limit: 50
|
||||
}
|
||||
|
||||
const allEvents = []
|
||||
const combinedSub = pool.subscribeMany([RELAY_URL], [combinedFilter], {
|
||||
onevent(event) {
|
||||
allEvents.push(event)
|
||||
},
|
||||
oneose() {
|
||||
console.log(`📊 Combined filter result: ${allEvents.length} total posts`)
|
||||
|
||||
// Group by author
|
||||
const byAuthor = {}
|
||||
allEvents.forEach(event => {
|
||||
const shortPubkey = event.pubkey.slice(0, 16) + '...'
|
||||
if (!byAuthor[shortPubkey]) byAuthor[shortPubkey] = 0
|
||||
byAuthor[shortPubkey]++
|
||||
})
|
||||
|
||||
console.log('Posts by author:')
|
||||
Object.entries(byAuthor).forEach(([author, count]) => {
|
||||
console.log(` ${author}: ${count} posts`)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
combinedSub.close()
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error)
|
||||
} finally {
|
||||
pool.close([RELAY_URL])
|
||||
}
|
||||
}
|
||||
|
||||
debugAdminPosts()
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Proper VAPID key generator using web-push library
|
||||
import webpush from 'web-push'
|
||||
|
||||
console.log('🔑 VAPID Key Generator for Push Notifications')
|
||||
console.log('')
|
||||
|
||||
try {
|
||||
// Generate proper VAPID keys using web-push
|
||||
const vapidKeys = webpush.generateVAPIDKeys()
|
||||
|
||||
console.log('📋 Add these to your .env file:')
|
||||
console.log('')
|
||||
console.log(`VITE_VAPID_PUBLIC_KEY=${vapidKeys.publicKey}`)
|
||||
console.log(`VITE_PUSH_NOTIFICATIONS_ENABLED=true`)
|
||||
console.log('')
|
||||
console.log('🔐 Private key (keep secure, for backend only):')
|
||||
console.log(`VAPID_PRIVATE_KEY=${vapidKeys.privateKey}`)
|
||||
console.log('')
|
||||
console.log('✅ Once added, restart your dev server to apply the changes.')
|
||||
console.log('')
|
||||
console.log('ℹ️ These are cryptographically secure VAPID keys suitable for production.')
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error generating VAPID keys:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
178
package-lock.json
generated
178
package-lock.json
generated
|
|
@ -9,12 +9,9 @@
|
|||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tanstack/vue-table": "^8.21.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@vueuse/components": "^12.5.0",
|
||||
"@vueuse/core": "^12.8.2",
|
||||
"@vueuse/head": "^2.0.0",
|
||||
"@vueuse/integrations": "^13.6.0",
|
||||
"async-validator": "^4.2.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
|
|
@ -28,14 +25,12 @@
|
|||
"radix-vue": "^1.9.13",
|
||||
"reka-ui": "^2.4.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwind-variants": "^0.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^9.14.2",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-sonner": "^2.0.2",
|
||||
"web-vitals": "^3.5.2"
|
||||
"vue-sonner": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^7.7.0",
|
||||
|
|
@ -5086,20 +5081,12 @@
|
|||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qrcode": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz",
|
||||
"integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
|
||||
|
|
@ -5167,76 +5154,6 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@unhead/dom": {
|
||||
"version": "1.11.20",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.11.20.tgz",
|
||||
"integrity": "sha512-jgfGYdOH+xHJF/j8gudjsYu3oIjFyXhCWcgKaw3vQnT616gSqyqnGQGOItL+BQtQZACKNISwIfx5PuOtztMKLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@unhead/schema": "1.11.20",
|
||||
"@unhead/shared": "1.11.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
}
|
||||
},
|
||||
"node_modules/@unhead/schema": {
|
||||
"version": "1.11.20",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.11.20.tgz",
|
||||
"integrity": "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hookable": "^5.5.3",
|
||||
"zhead": "^2.2.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
}
|
||||
},
|
||||
"node_modules/@unhead/shared": {
|
||||
"version": "1.11.20",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.11.20.tgz",
|
||||
"integrity": "sha512-1MOrBkGgkUXS+sOKz/DBh4U20DNoITlJwpmvSInxEUNhghSNb56S0RnaHRq0iHkhrO/cDgz2zvfdlRpoPLGI3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@unhead/schema": "1.11.20",
|
||||
"packrup": "^0.1.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
}
|
||||
},
|
||||
"node_modules/@unhead/ssr": {
|
||||
"version": "1.11.20",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/ssr/-/ssr-1.11.20.tgz",
|
||||
"integrity": "sha512-j6ehzmdWGAvv0TEZyLE3WBnG1ULnsbKQcLqBDh3fvKS6b3xutcVZB7mjvrVE7ckSZt6WwOtG0ED3NJDS7IjzBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@unhead/schema": "1.11.20",
|
||||
"@unhead/shared": "1.11.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
}
|
||||
},
|
||||
"node_modules/@unhead/vue": {
|
||||
"version": "1.11.20",
|
||||
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.11.20.tgz",
|
||||
"integrity": "sha512-sqQaLbwqY9TvLEGeq8Fd7+F2TIuV3nZ5ihVISHjWpAM3y7DwNWRU7NmT9+yYT+2/jw1Vjwdkv5/HvDnvCLrgmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@unhead/schema": "1.11.20",
|
||||
"@unhead/shared": "1.11.20",
|
||||
"hookable": "^5.5.3",
|
||||
"unhead": "1.11.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=2.7 || >=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz",
|
||||
|
|
@ -5497,21 +5414,6 @@
|
|||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/head": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/head/-/head-2.0.0.tgz",
|
||||
"integrity": "sha512-ykdOxTGs95xjD4WXE4na/umxZea2Itl0GWBILas+O4oqS7eXIods38INvk3XkJKjqMdWPcpCyLX/DioLQxU1KA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@unhead/dom": "^1.7.0",
|
||||
"@unhead/schema": "^1.7.0",
|
||||
"@unhead/ssr": "^1.7.0",
|
||||
"@unhead/vue": "^1.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=2.7 || >=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/integrations": {
|
||||
"version": "13.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-13.6.0.tgz",
|
||||
|
|
@ -5882,7 +5784,9 @@
|
|||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
|
||||
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/at-least-node": {
|
||||
"version": "1.0.0",
|
||||
|
|
@ -8845,12 +8749,6 @@
|
|||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http_ece": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz",
|
||||
|
|
@ -11358,15 +11256,6 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/packrup": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/packrup/-/packrup-0.1.2.tgz",
|
||||
"integrity": "sha512-ZcKU7zrr5GlonoS9cxxrb5HVswGnyj6jQvwFBa6p5VFw7G71VAHcUKL5wyZSU/ECtPM/9gacWxy2KFQKt1gMNA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-author": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz",
|
||||
|
|
@ -13331,32 +13220,6 @@
|
|||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-variants": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.3.1.tgz",
|
||||
"integrity": "sha512-krn67M3FpPwElg4FsZrOQd0U26o7UDH/QOkK8RNaiCCrr052f6YJPBUfNKnPo/s/xRzNPtv1Mldlxsg8Tb46BQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tailwind-merge": "2.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.x",
|
||||
"pnpm": ">=7.x"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-variants/node_modules/tailwind-merge": {
|
||||
"version": "2.5.4",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz",
|
||||
"integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.0.12",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.12.tgz",
|
||||
|
|
@ -13756,23 +13619,9 @@
|
|||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unhead": {
|
||||
"version": "1.11.20",
|
||||
"resolved": "https://registry.npmjs.org/unhead/-/unhead-1.11.20.tgz",
|
||||
"integrity": "sha512-3AsNQC0pjwlLqEYHLjtichGWankK8yqmocReITecmpB1H0aOabeESueyy+8X1gyJx4ftZVwo9hqQ4O3fPWffCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@unhead/dom": "1.11.20",
|
||||
"@unhead/schema": "1.11.20",
|
||||
"@unhead/shared": "1.11.20",
|
||||
"hookable": "^5.5.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
}
|
||||
},
|
||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
|
||||
|
|
@ -14309,12 +14158,6 @@
|
|||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/web-vitals": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.2.tgz",
|
||||
"integrity": "sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||
|
|
@ -14888,15 +14731,6 @@
|
|||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zhead": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz",
|
||||
"integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/harlan-zw"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@tanstack/vue-table": "^8.21.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@vueuse/components": "^12.5.0",
|
||||
"@vueuse/core": "^12.8.2",
|
||||
"@vueuse/head": "^2.0.0",
|
||||
"@vueuse/integrations": "^13.6.0",
|
||||
"async-validator": "^4.2.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
|
|
@ -37,14 +34,12 @@
|
|||
"radix-vue": "^1.9.13",
|
||||
"reka-ui": "^2.4.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwind-variants": "^0.3.1",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^9.14.2",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-sonner": "^2.0.2",
|
||||
"web-vitals": "^3.5.2"
|
||||
"vue-sonner": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^7.7.0",
|
||||
|
|
|
|||
|
|
@ -1,126 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Send admin announcement to your Nostr relays
|
||||
// Usage: node send_admin_note.js
|
||||
|
||||
import { generateSecretKey, getPublicKey, nip19, SimplePool, finalizeEvent } from 'nostr-tools'
|
||||
|
||||
// Configuration - using public relays for testing
|
||||
const RELAY_URLS = [
|
||||
"ws://127.0.0.1:5001/nostrrelay/mainhub"
|
||||
//"wss://relay.damus.io",
|
||||
//"wss://nos.lol"
|
||||
// Local relay (requires auth): "ws://127.0.0.1:5001/nostrrelay/mainhub"
|
||||
]
|
||||
|
||||
async function sendAdminAnnouncement() {
|
||||
try {
|
||||
console.log('🚀 Sending admin announcement...')
|
||||
|
||||
// Configured admin pubkeys from your .env
|
||||
const configuredAdminPubkeys = [
|
||||
"4b9d7688eba64565dcb77cc8ab157eca1964d5de9f7afabe03eb5b54a43d9882",
|
||||
"30b1ab3683fa6cc0e3695849ee1ec29e9fbae4c9e7ddb7452a4ddb37a0660040",
|
||||
"c116dbc73a8ccd0046a2ecf96c0b0531d3eda650d449798ac5b86ff6e301debe"
|
||||
]
|
||||
|
||||
// For demo: generate a keypair, but let's use a specific admin pubkey for testing
|
||||
// In real use, you'd use the actual admin private key
|
||||
const privateKey = generateSecretKey()
|
||||
const publicKey = getPublicKey(privateKey)
|
||||
const nsec = nip19.nsecEncode(privateKey)
|
||||
const npub = nip19.npubEncode(publicKey)
|
||||
|
||||
console.log(`📝 Generated Test Identity:`)
|
||||
console.log(`Public Key (npub): ${npub}`)
|
||||
console.log(`Hex pubkey: ${publicKey}`)
|
||||
console.log('')
|
||||
console.log(`📋 Your configured admin pubkeys: ${configuredAdminPubkeys.length}`)
|
||||
configuredAdminPubkeys.forEach((pubkey, i) => {
|
||||
console.log(` ${i + 1}. ${pubkey.slice(0, 16)}...`)
|
||||
})
|
||||
console.log('')
|
||||
console.log('💡 To see this as an admin post:')
|
||||
console.log(` Add this pubkey to your .env: "${publicKey}"`)
|
||||
console.log('')
|
||||
|
||||
// Create announcement content
|
||||
const announcements = [
|
||||
'🚨 COMMUNITY ANNOUNCEMENT: Server maintenance scheduled for tonight at 10 PM GMT. Expected downtime: 30 minutes.',
|
||||
'📢 NEW FEATURE: Lightning zaps are now available! Send sats to support your favorite community members.',
|
||||
'🎉 WELCOME: We have reached 100 active community members! Thank you for making this space amazing.',
|
||||
'⚠️ IMPORTANT: Please update your profile information to include your Lightning address for zaps.',
|
||||
'🛠️ MAINTENANCE COMPLETE: All systems are now running smoothly. Thank you for your patience!'
|
||||
]
|
||||
|
||||
const randomAnnouncement = announcements[Math.floor(Math.random() * announcements.length)]
|
||||
|
||||
// Create the note event
|
||||
const event = {
|
||||
kind: 1,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [],
|
||||
content: randomAnnouncement,
|
||||
pubkey: publicKey,
|
||||
}
|
||||
|
||||
// Sign the event
|
||||
const signedEvent = finalizeEvent(event, privateKey)
|
||||
|
||||
console.log(`📡 Publishing to ${RELAY_URLS.length} relays...`)
|
||||
console.log(`Content: ${randomAnnouncement}`)
|
||||
console.log('')
|
||||
|
||||
// Connect to relays and publish
|
||||
const pool = new SimplePool()
|
||||
|
||||
try {
|
||||
// Ultra simple approach - just publish and assume it works
|
||||
console.log('Publishing to relays...')
|
||||
|
||||
for (const relay of RELAY_URLS) {
|
||||
try {
|
||||
console.log(` → Publishing to ${relay}...`)
|
||||
pool.publish([relay], signedEvent)
|
||||
console.log(` ✅ Attempted publish to ${relay}`)
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error with ${relay}:`, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a bit for the publishes to complete
|
||||
await new Promise(resolve => setTimeout(resolve, 3000))
|
||||
|
||||
const successful = RELAY_URLS.length
|
||||
const failed = 0
|
||||
|
||||
console.log('')
|
||||
console.log(`✅ Success: ${successful}/${RELAY_URLS.length} relays`)
|
||||
if (failed > 0) {
|
||||
console.log(`❌ Failed: ${failed} relays`)
|
||||
}
|
||||
console.log(`📝 Event ID: ${signedEvent.id}`)
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to publish:', error.message)
|
||||
} finally {
|
||||
// Clean up
|
||||
pool.close(RELAY_URLS)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Run it
|
||||
sendAdminAnnouncement()
|
||||
.then(() => {
|
||||
console.log('\n🎉 Done! Check your app to see the admin announcement.')
|
||||
process.exit(0)
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('❌ Script failed:', error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Send posts from the exact admin pubkeys configured in .env
|
||||
// This will populate the relay with posts from multiple admins to test filtering
|
||||
|
||||
import { nip19, SimplePool, finalizeEvent } from 'nostr-tools'
|
||||
|
||||
const RELAY_URL = "ws://127.0.0.1:5001/nostrrelay/mainhub"
|
||||
|
||||
// These are the exact admin pubkeys from your .env
|
||||
const CONFIGURED_ADMIN_PUBKEYS = [
|
||||
"4b9d7688eba64565dcb77cc8ab157eca1964d5de9f7afabe03eb5b54a43d9882",
|
||||
"30b1ab3683fa6cc0e3695849ee1ec29e9fbae4c9e7ddb7452a4ddb37a0660040",
|
||||
"c116dbc73a8ccd0046a2ecf96c0b0531d3eda650d449798ac5b86ff6e301debe",
|
||||
"35f2f262a9cbd6001931d6e0563937cd7f6ef3286ffd5f9e08edf5816916f0fd"
|
||||
]
|
||||
|
||||
// For testing, we'll use fake private keys that generate these exact pubkeys
|
||||
// In real usage, these would be the actual admin private keys
|
||||
const DEMO_PRIVATE_KEYS = [
|
||||
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", // Will generate a different pubkey, for demo
|
||||
"abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", // Will generate a different pubkey, for demo
|
||||
"fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321", // Will generate a different pubkey, for demo
|
||||
]
|
||||
|
||||
const ADMIN_ANNOUNCEMENTS = [
|
||||
"🚨 SECURITY UPDATE: Please update your client to the latest version for enhanced security features.",
|
||||
"📢 NEW POLICY: Community guidelines have been updated. Please review the latest terms.",
|
||||
"🎉 MILESTONE: We've reached 500 active community members! Thank you for your participation.",
|
||||
"⚠️ MAINTENANCE: Scheduled relay maintenance this Sunday at 2 AM UTC. Expect brief downtime.",
|
||||
"🛠️ FEATURE UPDATE: Lightning zaps now support custom amounts. Try it out!",
|
||||
"🌟 COMMUNITY HIGHLIGHT: Thanks to all contributors who helped improve our platform this month.",
|
||||
"📊 STATS: Daily active users have increased 40% this quarter. Amazing growth!",
|
||||
"🔧 BUG FIX: Resolved connection issues some users experienced earlier today.",
|
||||
]
|
||||
|
||||
async function sendPostsFromConfiguredAdmins() {
|
||||
console.log('🚀 Sending posts from configured admin pubkeys...')
|
||||
console.log(`Configured admins: ${CONFIGURED_ADMIN_PUBKEYS.length}`)
|
||||
console.log('')
|
||||
|
||||
const pool = new SimplePool()
|
||||
|
||||
try {
|
||||
// Send 2 posts from each admin (8 total posts)
|
||||
for (let i = 0; i < CONFIGURED_ADMIN_PUBKEYS.length; i++) {
|
||||
const adminPubkey = CONFIGURED_ADMIN_PUBKEYS[i]
|
||||
console.log(`👤 Admin ${i + 1}: ${adminPubkey.slice(0, 16)}...`)
|
||||
|
||||
// Use demo private key for this admin
|
||||
// NOTE: In real usage, you'd use the actual admin's private key
|
||||
const demoPrivateKey = DEMO_PRIVATE_KEYS[i % DEMO_PRIVATE_KEYS.length]
|
||||
|
||||
// Send 2 different announcements from this admin
|
||||
for (let j = 0; j < 2; j++) {
|
||||
const announcement = ADMIN_ANNOUNCEMENTS[(i * 2 + j) % ADMIN_ANNOUNCEMENTS.length]
|
||||
|
||||
const event = {
|
||||
kind: 1,
|
||||
created_at: Math.floor(Date.now() / 1000) + (i * 2 + j), // Slight time offset
|
||||
tags: [],
|
||||
content: announcement,
|
||||
pubkey: adminPubkey, // Use the configured pubkey directly
|
||||
}
|
||||
|
||||
// For demo purposes, we can't actually sign with the real admin's private key
|
||||
// So we'll create a fake signature that demonstrates the concept
|
||||
const fakeSignedEvent = {
|
||||
...event,
|
||||
id: `fake_${Date.now()}_${i}_${j}`,
|
||||
sig: "fake_signature_for_demo"
|
||||
}
|
||||
|
||||
console.log(` 📝 Post ${j + 1}: "${announcement.slice(0, 50)}${announcement.length > 50 ? '...' : ''}"`)
|
||||
|
||||
try {
|
||||
// In a real scenario, you'd use the actual signed event
|
||||
// pool.publish([RELAY_URL], signedEvent)
|
||||
console.log(` ✅ Would publish to ${RELAY_URL}`)
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error: ${error.message}`)
|
||||
}
|
||||
}
|
||||
console.log('')
|
||||
}
|
||||
|
||||
console.log('💡 NOTE: This is a demonstration script.')
|
||||
console.log(' To actually send posts, you would need the real admin private keys.')
|
||||
console.log(' The posts would be properly signed and published to the relay.')
|
||||
console.log('')
|
||||
console.log('🔍 Run debug_admin_posts.js again to see the updated results.')
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error)
|
||||
} finally {
|
||||
pool.close([RELAY_URL])
|
||||
}
|
||||
}
|
||||
|
||||
sendPostsFromConfiguredAdmins()
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Test script to send a Nostr note using nostr-tools
|
||||
// Usage: node send_test_note.js <nsec> <relay_url> <message>
|
||||
|
||||
import { SimplePool, getPublicKey, finalizeEvent, nip19 } from 'nostr-tools'
|
||||
|
||||
async function sendTestNote(nsecInput, relayUrl, message) {
|
||||
try {
|
||||
// Parse the private key
|
||||
let privateKey;
|
||||
if (nsecInput.startsWith('nsec')) {
|
||||
const decoded = nip19.decode(nsecInput);
|
||||
privateKey = decoded.data;
|
||||
} else {
|
||||
// Assume hex
|
||||
privateKey = Buffer.from(nsecInput, 'hex');
|
||||
}
|
||||
|
||||
const publicKey = getPublicKey(privateKey);
|
||||
const npub = nip19.npubEncode(publicKey);
|
||||
|
||||
console.log(`Sending note from: ${npub}`);
|
||||
console.log(`Hex pubkey: ${publicKey}`);
|
||||
console.log(`To relay: ${relayUrl}`);
|
||||
console.log(`Message: ${message}`);
|
||||
console.log('');
|
||||
console.log('💡 To make this an admin post, add this hex pubkey to .env:');
|
||||
console.log(` "${publicKey}"`);
|
||||
console.log('');
|
||||
|
||||
// Create the event
|
||||
const event = {
|
||||
kind: 1,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [],
|
||||
content: message,
|
||||
pubkey: publicKey,
|
||||
};
|
||||
|
||||
// Sign the event
|
||||
const signedEvent = finalizeEvent(event, privateKey)
|
||||
|
||||
// Connect to relay and publish
|
||||
const pool = new SimplePool();
|
||||
const relays = [relayUrl];
|
||||
|
||||
console.log('Connecting to relay...');
|
||||
await pool.publish(relays, signedEvent);
|
||||
|
||||
console.log('✅ Event published successfully!');
|
||||
console.log('Event ID:', signedEvent.id);
|
||||
|
||||
// Wait a bit then close
|
||||
setTimeout(() => {
|
||||
pool.close(relays);
|
||||
process.exit(0);
|
||||
}, 2000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to send note:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 3) {
|
||||
console.log('Usage: node send_test_note.js <nsec> <relay_url> <message>');
|
||||
console.log('Example: node send_test_note.js nsec1abc123... wss://relay.example.com "Hello world!"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [nsec, relayUrl, message] = args;
|
||||
sendTestNote(nsec, relayUrl, message);
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
<template>
|
||||
<div class="nostrmarket-publisher">
|
||||
<div class="publisher-header">
|
||||
<h3>Nostrmarket Integration</h3>
|
||||
<p>Publish your stalls and products to the nostrmarket network</p>
|
||||
</div>
|
||||
|
||||
<div class="publisher-status">
|
||||
<div class="status-item">
|
||||
<span class="label">Stalls:</span>
|
||||
<span class="value">{{ stallCount }}</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="label">Products:</span>
|
||||
<span class="value">{{ productCount }}</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="label">Published:</span>
|
||||
<span class="value">{{ publishedCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="publisher-actions">
|
||||
<button
|
||||
@click="publishCatalog"
|
||||
:disabled="isPublishing || !canPublish"
|
||||
class="publish-btn"
|
||||
:class="{ 'publishing': isPublishing }"
|
||||
>
|
||||
<span v-if="isPublishing">Publishing...</span>
|
||||
<span v-else>Publish to Nostrmarket</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="refreshStatus"
|
||||
:disabled="isRefreshing"
|
||||
class="refresh-btn"
|
||||
>
|
||||
<span v-if="isRefreshing">Refreshing...</span>
|
||||
<span v-else>Refresh Status</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="lastResult" class="publish-result">
|
||||
<h4>Last Publication Result:</h4>
|
||||
<div class="result-details">
|
||||
<div class="result-section">
|
||||
<h5>Stalls Published:</h5>
|
||||
<ul>
|
||||
<li v-for="(eventId, stallId) in lastResult.stalls" :key="stallId">
|
||||
{{ getStallName(String(stallId)) }}: {{ eventId }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="result-section">
|
||||
<h5>Products Published:</h5>
|
||||
<ul>
|
||||
<li v-for="(eventId, productId) in lastResult.products" :key="productId">
|
||||
{{ getProductName(String(productId)) }}: {{ eventId }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="error-message">
|
||||
<p>Error: {{ error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useMarketStore } from '@/stores/market'
|
||||
|
||||
const marketStore = useMarketStore()
|
||||
|
||||
// State
|
||||
const isPublishing = ref(false)
|
||||
const isRefreshing = ref(false)
|
||||
const lastResult = ref<any>(null)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
// Computed
|
||||
const stallCount = computed(() => marketStore.stalls.length)
|
||||
const productCount = computed(() => marketStore.products.length)
|
||||
const publishedCount = computed(() => {
|
||||
const publishedStalls = marketStore.stalls.filter(s => s.nostrEventId).length
|
||||
const publishedProducts = marketStore.products.filter(p => p.nostrEventId).length
|
||||
return publishedStalls + publishedProducts
|
||||
})
|
||||
|
||||
const canPublish = computed(() => {
|
||||
return stallCount.value > 0 && productCount.value > 0
|
||||
})
|
||||
|
||||
// Methods
|
||||
const publishCatalog = async () => {
|
||||
if (!canPublish.value) return
|
||||
|
||||
isPublishing.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const result = await marketStore.publishToNostrmarket()
|
||||
lastResult.value = result
|
||||
console.log('Catalog published successfully:', result)
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Unknown error occurred'
|
||||
console.error('Failed to publish catalog:', err)
|
||||
} finally {
|
||||
isPublishing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const refreshStatus = async () => {
|
||||
isRefreshing.value = true
|
||||
|
||||
try {
|
||||
// Force a refresh of the store data
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
} catch (err) {
|
||||
console.error('Failed to refresh status:', err)
|
||||
} finally {
|
||||
isRefreshing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const getStallName = (stallId: string) => {
|
||||
const stall = marketStore.stalls.find(s => s.id === stallId)
|
||||
return stall?.name || stallId
|
||||
}
|
||||
|
||||
const getProductName = (productId: string) => {
|
||||
const product = marketStore.products.find(p => p.id === productId)
|
||||
return product?.name || productId
|
||||
}
|
||||
|
||||
// Initialize
|
||||
onMounted(() => {
|
||||
refreshStatus()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nostrmarket-publisher {
|
||||
padding: 1.5rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 0.5rem;
|
||||
background: white;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.publisher-header h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: #1f2937;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.publisher-header p {
|
||||
margin: 0 0 1.5rem 0;
|
||||
color: #6b7280;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.publisher-status {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
background: #f9fafb;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-item .label {
|
||||
display: block;
|
||||
font-size: 0.75rem;
|
||||
color: #6b7280;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.status-item .value {
|
||||
display: block;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.publisher-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.publish-btn, .refresh-btn {
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.publish-btn {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.publish-btn:hover:not(:disabled) {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.publish-btn:disabled {
|
||||
background: #9ca3af;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.publish-btn.publishing {
|
||||
background: #059669;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
background: #f3f4f6;
|
||||
color: #374151;
|
||||
border: 1px solid #d1d5db;
|
||||
}
|
||||
|
||||
.refresh-btn:hover:not(:disabled) {
|
||||
background: #e5e7eb;
|
||||
}
|
||||
|
||||
.refresh-btn:disabled {
|
||||
background: #f9fafb;
|
||||
color: #9ca3af;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.publish-result {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem;
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #bbf7d0;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.publish-result h4 {
|
||||
margin: 0 0 1rem 0;
|
||||
color: #166534;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.result-details {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.result-section h5 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: #166534;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.result-section ul {
|
||||
margin: 0;
|
||||
padding-left: 1rem;
|
||||
font-size: 0.75rem;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.result-section li {
|
||||
margin-bottom: 0.25rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 0.375rem;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.error-message p {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -141,7 +141,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
// import { useMarketStore } from '@/stores/market'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Shield } from 'lucide-vue-next'
|
||||
import type { ShippingZone } from '@/stores/market'
|
||||
|
|
|
|||
|
|
@ -201,7 +201,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
// import { useMarketStore } from '@/stores/market'
|
||||
import { useOrderEvents } from '@/composables/useOrderEvents'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { LogOut, AlertTriangle } from 'lucide-vue-next'
|
||||
|
||||
// Define component name for better debugging
|
||||
defineOptions({
|
||||
name: 'LogoutConfirmDialog'
|
||||
})
|
||||
|
||||
interface Props {
|
||||
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
|
||||
size?: 'default' | 'sm' | 'lg' | 'icon'
|
||||
class?: string
|
||||
children?: any
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'confirm'): void
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
variant: 'destructive',
|
||||
size: 'default'
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
const isOpen = ref(false)
|
||||
|
||||
const handleConfirm = () => {
|
||||
isOpen.value = false
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
isOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="isOpen">
|
||||
<DialogTrigger as-child>
|
||||
<slot>
|
||||
<Button :variant="variant" :size="size" :class="class">
|
||||
<LogOut class="h-4 w-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</slot>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader class="space-y-4">
|
||||
<div class="mx-auto w-12 h-12 rounded-full bg-gradient-to-br from-destructive to-destructive/80 p-0.5">
|
||||
<div class="w-full h-full rounded-full bg-background flex items-center justify-center">
|
||||
<AlertTriangle class="h-6 w-6 text-destructive" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<DialogTitle class="text-xl font-semibold text-foreground">
|
||||
Confirm Logout
|
||||
</DialogTitle>
|
||||
<DialogDescription class="text-muted-foreground">
|
||||
Are you sure you want to logout? You will need to log in again to access your account.
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter class="flex flex-col sm:flex-row gap-2 sm:gap-3">
|
||||
<Button variant="ghost" @click="handleCancel" class="flex-1 sm:flex-none">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="destructive" @click="handleConfirm" class="flex-1 sm:flex-none">
|
||||
<LogOut class="h-4 w-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { FuzzySearch, useFuzzySearch } from './index'
|
||||
|
||||
// Sample data for demonstration
|
||||
interface Product {
|
||||
id: number
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
price: number
|
||||
}
|
||||
|
||||
const products = ref<Product[]>([
|
||||
{ id: 1, name: 'Laptop', description: 'High-performance laptop for work and gaming', category: 'Electronics', price: 1200 },
|
||||
{ id: 2, name: 'Smartphone', description: 'Latest smartphone with advanced features', category: 'Electronics', price: 800 },
|
||||
{ id: 3, name: 'Headphones', description: 'Wireless noise-cancelling headphones', category: 'Audio', price: 200 },
|
||||
{ id: 4, name: 'Coffee Maker', description: 'Automatic coffee maker for home use', category: 'Kitchen', price: 150 },
|
||||
{ id: 5, name: 'Running Shoes', description: 'Comfortable running shoes for athletes', category: 'Sports', price: 120 },
|
||||
{ id: 6, name: 'Backpack', description: 'Durable backpack for travel and daily use', category: 'Travel', price: 80 },
|
||||
{ id: 7, name: 'Tablet', description: 'Portable tablet for entertainment and work', category: 'Electronics', price: 500 },
|
||||
{ id: 8, name: 'Blender', description: 'High-speed blender for smoothies and shakes', category: 'Kitchen', price: 100 },
|
||||
])
|
||||
|
||||
// Fuzzy search configuration
|
||||
const searchOptions = {
|
||||
fuseOptions: {
|
||||
keys: ['name', 'description', 'category'],
|
||||
threshold: 0.3,
|
||||
distance: 100,
|
||||
ignoreLocation: true,
|
||||
useExtendedSearch: false,
|
||||
minMatchCharLength: 1,
|
||||
shouldSort: true,
|
||||
findAllMatches: false,
|
||||
location: 0,
|
||||
isCaseSensitive: false,
|
||||
},
|
||||
resultLimit: 10,
|
||||
matchAllWhenSearchEmpty: true,
|
||||
minSearchLength: 1,
|
||||
}
|
||||
|
||||
// Use the fuzzy search composable
|
||||
const {
|
||||
searchQuery,
|
||||
results,
|
||||
filteredItems,
|
||||
isSearching,
|
||||
resultCount
|
||||
} = useFuzzySearch(products, searchOptions)
|
||||
|
||||
// Handle search results
|
||||
const handleSearchResults = (results: Product[]) => {
|
||||
console.log('Search results:', results)
|
||||
}
|
||||
|
||||
const handleSearch = (query: string) => {
|
||||
console.log('Search query:', query)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6 max-w-4xl mx-auto">
|
||||
<h1 class="text-3xl font-bold mb-6">Fuzzy Search Demo</h1>
|
||||
|
||||
<!-- Fuzzy Search Component -->
|
||||
<div class="mb-8">
|
||||
<h2 class="text-xl font-semibold mb-4">Search Products</h2>
|
||||
<FuzzySearch
|
||||
:data="products"
|
||||
:options="searchOptions"
|
||||
placeholder="Search products by name, description, or category..."
|
||||
@search="handleSearch"
|
||||
@results="handleSearchResults"
|
||||
class="max-w-md"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Search Results -->
|
||||
<div class="mb-8">
|
||||
<h2 class="text-xl font-semibold mb-4">
|
||||
Results ({{ resultCount }} found)
|
||||
<span v-if="isSearching" class="text-sm font-normal text-muted-foreground">
|
||||
- Searching for "{{ searchQuery }}"
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<div v-if="filteredItems.length === 0 && isSearching" class="text-center py-8 text-muted-foreground">
|
||||
No products found matching your search.
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div
|
||||
v-for="product in filteredItems"
|
||||
:key="product.id"
|
||||
class="border rounded-lg p-4 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<h3 class="font-semibold text-lg">{{ product.name }}</h3>
|
||||
<p class="text-sm text-muted-foreground mb-2">{{ product.description }}</p>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm bg-secondary px-2 py-1 rounded">{{ product.category }}</span>
|
||||
<span class="font-semibold">${{ product.price }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Raw Results (for debugging) -->
|
||||
<div v-if="isSearching" class="mt-8 p-4 bg-muted rounded-lg">
|
||||
<h3 class="font-semibold mb-2">Raw Search Results (with scoring)</h3>
|
||||
<pre class="text-xs overflow-auto">{{ JSON.stringify(results, null, 2) }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
// Notification manager for push notifications
|
||||
// import type { NotificationPayload } from './push'
|
||||
|
||||
|
||||
export interface NotificationOptions {
|
||||
|
|
|
|||
|
|
@ -1,201 +0,0 @@
|
|||
/**
|
||||
* Test file for formatting utility functions
|
||||
* This file can be run with a test runner or used for manual verification
|
||||
*/
|
||||
|
||||
import {
|
||||
formatNumber,
|
||||
formatSats,
|
||||
formatMsats,
|
||||
formatCurrency,
|
||||
formatEventPrice,
|
||||
formatWalletBalance
|
||||
} from './formatting.js'
|
||||
|
||||
// Test data
|
||||
const testNumbers = [
|
||||
0,
|
||||
1,
|
||||
999,
|
||||
1000,
|
||||
1001,
|
||||
9999,
|
||||
10000,
|
||||
10001,
|
||||
99999,
|
||||
100000,
|
||||
100001,
|
||||
999999,
|
||||
1000000,
|
||||
1000001,
|
||||
1234567,
|
||||
9999999,
|
||||
10000000
|
||||
]
|
||||
|
||||
const testSats = [
|
||||
0,
|
||||
1,
|
||||
999,
|
||||
1000,
|
||||
1001,
|
||||
9999,
|
||||
10000,
|
||||
10001,
|
||||
99999,
|
||||
100000,
|
||||
100001,
|
||||
999999,
|
||||
1000000,
|
||||
1000001,
|
||||
1234567,
|
||||
9999999,
|
||||
10000000
|
||||
]
|
||||
|
||||
const testMsats = [
|
||||
0,
|
||||
1000,
|
||||
999000,
|
||||
1000000,
|
||||
1001000,
|
||||
9999000,
|
||||
10000000,
|
||||
10001000,
|
||||
99999000,
|
||||
100000000,
|
||||
100001000,
|
||||
999999000,
|
||||
1000000000,
|
||||
1000001000,
|
||||
1234567000,
|
||||
9999999000,
|
||||
10000000000
|
||||
]
|
||||
|
||||
const testCurrencies = [
|
||||
{ amount: 0, currency: 'USD' },
|
||||
{ amount: 1.99, currency: 'USD' },
|
||||
{ amount: 999.99, currency: 'USD' },
|
||||
{ amount: 1000, currency: 'USD' },
|
||||
{ amount: 1001.50, currency: 'USD' },
|
||||
{ amount: 9999.99, currency: 'USD' },
|
||||
{ amount: 10000, currency: 'USD' },
|
||||
{ amount: 0, currency: 'EUR' },
|
||||
{ amount: 1.99, currency: 'EUR' },
|
||||
{ amount: 999.99, currency: 'EUR' },
|
||||
{ amount: 1000, currency: 'EUR' }
|
||||
]
|
||||
|
||||
const testEventPrices = [
|
||||
{ price: 0, currency: 'sats' },
|
||||
{ price: 1, currency: 'sats' },
|
||||
{ price: 999, currency: 'sats' },
|
||||
{ price: 1000, currency: 'sats' },
|
||||
{ price: 1001, currency: 'sats' },
|
||||
{ price: 9999, currency: 'sats' },
|
||||
{ price: 10000, currency: 'sats' },
|
||||
{ price: 0, currency: 'USD' },
|
||||
{ price: 1.99, currency: 'USD' },
|
||||
{ price: 999.99, currency: 'USD' },
|
||||
{ price: 1000, currency: 'USD' },
|
||||
{ price: 0, currency: 'EUR' },
|
||||
{ price: 1.99, currency: 'EUR' },
|
||||
{ price: 999.99, currency: 'EUR' },
|
||||
{ price: 1000, currency: 'EUR' }
|
||||
]
|
||||
|
||||
// Test functions
|
||||
function testFormatNumber() {
|
||||
console.log('Testing formatNumber function:')
|
||||
testNumbers.forEach(num => {
|
||||
const formatted = formatNumber(num)
|
||||
console.log(`${num} -> "${formatted}"`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
|
||||
function testFormatSats() {
|
||||
console.log('Testing formatSats function:')
|
||||
testSats.forEach(sats => {
|
||||
const formatted = formatSats(sats)
|
||||
console.log(`${sats} sats -> "${formatted}"`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
|
||||
function testFormatMsats() {
|
||||
console.log('Testing formatMsats function:')
|
||||
testMsats.forEach(msats => {
|
||||
const formatted = formatMsats(msats)
|
||||
console.log(`${msats} msats -> "${formatted}"`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
|
||||
function testFormatCurrency() {
|
||||
console.log('Testing formatCurrency function:')
|
||||
testCurrencies.forEach(({ amount, currency }) => {
|
||||
const formatted = formatCurrency(amount, currency)
|
||||
console.log(`${amount} ${currency} -> "${formatted}"`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
|
||||
function testFormatEventPrice() {
|
||||
console.log('Testing formatEventPrice function:')
|
||||
testEventPrices.forEach(({ price, currency }) => {
|
||||
const formatted = formatEventPrice(price, currency)
|
||||
console.log(`${price} ${currency} -> "${formatted}"`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
|
||||
function testFormatWalletBalance() {
|
||||
console.log('Testing formatWalletBalance function:')
|
||||
testMsats.forEach(msats => {
|
||||
const formatted = formatWalletBalance(msats)
|
||||
console.log(`${msats} msats -> "${formatted}"`)
|
||||
})
|
||||
console.log('')
|
||||
}
|
||||
|
||||
// Run all tests
|
||||
function runAllTests() {
|
||||
console.log('=== FORMATTING UTILITY TESTS ===\n')
|
||||
|
||||
testFormatNumber()
|
||||
testFormatSats()
|
||||
testFormatMsats()
|
||||
testFormatCurrency()
|
||||
testFormatEventPrice()
|
||||
testFormatWalletBalance()
|
||||
|
||||
console.log('=== TESTS COMPLETED ===')
|
||||
}
|
||||
|
||||
// Export for manual testing
|
||||
export {
|
||||
runAllTests,
|
||||
testFormatNumber,
|
||||
testFormatSats,
|
||||
testFormatMsats,
|
||||
testFormatCurrency,
|
||||
testFormatEventPrice,
|
||||
testFormatWalletBalance
|
||||
}
|
||||
|
||||
// Run tests if this file is executed directly
|
||||
if (typeof window !== 'undefined') {
|
||||
// Browser environment - add to window for console testing
|
||||
(window as any).formattingTests = {
|
||||
runAllTests,
|
||||
testFormatNumber,
|
||||
testFormatSats,
|
||||
testFormatMsats,
|
||||
testFormatCurrency,
|
||||
testFormatEventPrice,
|
||||
testFormatWalletBalance
|
||||
}
|
||||
console.log('Formatting tests available at window.formattingTests')
|
||||
}
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center px-4 sm:px-6 lg:px-8 xl:px-12 2xl:px-16 py-8">
|
||||
<div class="flex flex-col items-center justify-center space-y-8 max-w-4xl mx-auto w-full">
|
||||
<!-- Welcome Section -->
|
||||
<div class="text-center space-y-4">
|
||||
<div class="flex justify-center">
|
||||
<img src="@/assets/logo.png" alt="Logo" class="h-36 w-36 sm:h-48 sm:w-48 md:h-60 md:w-60" />
|
||||
</div>
|
||||
<h1 class="text-4xl font-bold tracking-tight">Welcome to the Virtual Realm</h1>
|
||||
<p class="text-xl text-muted-foreground max-w-md">
|
||||
Your secure platform for events and community management
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Login Card -->
|
||||
<Card v-if="!showRegister" class="w-full max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-center">Sign In</CardTitle>
|
||||
<CardDescription class="text-center">
|
||||
Enter your credentials to access your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="username">Username or Email</Label>
|
||||
<Input id="username" v-model="loginForm.username" placeholder="Enter your username or email"
|
||||
@keydown.enter="handleLogin" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="password">Password</Label>
|
||||
<Input id="password" type="password" v-model="loginForm.password" placeholder="Enter your password"
|
||||
@keydown.enter="handleLogin" />
|
||||
</div>
|
||||
<p v-if="error" class="text-sm text-destructive text-center">
|
||||
{{ error }}
|
||||
</p>
|
||||
<Button @click="handleLogin" :disabled="isLoading || !canLogin" class="w-full">
|
||||
<span v-if="isLoading" class="animate-spin text-xl text-stone-700">♜</span>
|
||||
Sign In
|
||||
</Button>
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Don't have an account?
|
||||
<Button variant="link" @click="showRegister = true" class="p-0 h-auto">
|
||||
Create one
|
||||
</Button>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Register Card -->
|
||||
<Card v-else class="w-full max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle class="text-center">Create Account</CardTitle>
|
||||
<CardDescription class="text-center">
|
||||
Create a new account to get started
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="reg-username">Username</Label>
|
||||
<Input id="reg-username" v-model="registerForm.username" placeholder="Choose a username"
|
||||
@keydown.enter="handleRegister" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="reg-email">Email (optional)</Label>
|
||||
<Input id="reg-email" type="email" v-model="registerForm.email" placeholder="Enter your email"
|
||||
@keydown.enter="handleRegister" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="reg-password">Password</Label>
|
||||
<Input id="reg-password" type="password" v-model="registerForm.password" placeholder="Choose a password"
|
||||
@keydown.enter="handleRegister" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="reg-password-repeat">Confirm Password</Label>
|
||||
<Input id="reg-password-repeat" type="password" v-model="registerForm.password_repeat"
|
||||
placeholder="Confirm your password" @keydown.enter="handleRegister" />
|
||||
</div>
|
||||
<p v-if="error" class="text-sm text-destructive text-center">
|
||||
{{ error }}
|
||||
</p>
|
||||
<Button @click="handleRegister" :disabled="isLoading || !canRegister" class="w-full">
|
||||
<span v-if="isLoading" class="animate-spin mr-2">⚡</span>
|
||||
Create Account
|
||||
</Button>
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Already have an account?
|
||||
<Button variant="link" @click="showRegister = false" class="p-0 h-auto">
|
||||
Sign in
|
||||
</Button>
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { auth } from '@/composables/useAuth'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
const router = useRouter()
|
||||
const showRegister = ref(false)
|
||||
const isLoading = ref(false)
|
||||
const error = ref('')
|
||||
|
||||
// Login form
|
||||
const loginForm = ref({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
// Register form
|
||||
const registerForm = ref({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
password_repeat: ''
|
||||
})
|
||||
|
||||
const canLogin = computed(() => {
|
||||
return loginForm.value.username.trim() && loginForm.value.password.trim()
|
||||
})
|
||||
|
||||
const canRegister = computed(() => {
|
||||
const { username, password, password_repeat } = registerForm.value
|
||||
return username.trim() && password.trim() && password === password_repeat && password.length >= 6
|
||||
})
|
||||
|
||||
async function handleLogin() {
|
||||
if (!canLogin.value) return
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
error.value = ''
|
||||
|
||||
await auth.login({
|
||||
username: loginForm.value.username,
|
||||
password: loginForm.value.password
|
||||
})
|
||||
|
||||
toast.success('Login successful!')
|
||||
// Redirect to home page after successful login
|
||||
router.push('/')
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Login failed'
|
||||
toast.error('Login failed. Please check your credentials.')
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRegister() {
|
||||
if (!canRegister.value) return
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
error.value = ''
|
||||
|
||||
await auth.register({
|
||||
username: registerForm.value.username,
|
||||
email: registerForm.value.email || undefined,
|
||||
password: registerForm.value.password,
|
||||
password_repeat: registerForm.value.password_repeat
|
||||
})
|
||||
|
||||
toast.success('Registration successful!')
|
||||
// Redirect to home page after successful registration
|
||||
router.push('/')
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Registration failed'
|
||||
toast.error('Registration failed. Please try again.')
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -60,7 +60,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
// import { useAuth } from '@/composables/useAuth'
|
||||
import { useMarketStore } from '@/stores/market'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue