Compare commits

..

No commits in common. "f4fd7403ccf1b479c9d717210a8e4768081d75a7" and "24eb27a9498b81a37454198b3104ad165503baa6" have entirely different histories.

13 changed files with 97 additions and 242 deletions

View file

@ -1,61 +0,0 @@
name: Docker image
on:
push:
branches:
- '**'
tags:
- 'v*.*.*'
pull_request:
branches:
- 'master'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=ref,event=branch
type=ref,event=pr
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

2
.gitignore vendored
View file

@ -7,5 +7,3 @@ nsecbunker.json
connection.txt connection.txt
config config
.env .env
.turbo
prisma

View file

@ -1,15 +1,7 @@
{ {
"importOrder": ["^[./]"], "importOrder": ["^[./]"],
"importOrderSeparation": true, "importOrderSeparation": true,
"tabWidth": 4, "tabWidth": 4,
"useTabs": false, "useTabs": false,
"semi": true, "semi": true
"overrides": [
{
"files": "*.handlebar",
"options": {
"tabWidth": 2
}
}
]
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "nsecbunkerd", "name": "nsecbunkerd",
"version": "0.10.5", "version": "0.10.3",
"description": "nsecbunker daemon", "description": "nsecbunker daemon",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
@ -39,7 +39,7 @@
"@fastify/view": "^8.2.0", "@fastify/view": "^8.2.0",
"@inquirer/password": "^1.1.2", "@inquirer/password": "^1.1.2",
"@inquirer/prompts": "^1.2.3", "@inquirer/prompts": "^1.2.3",
"@nostr-dev-kit/ndk": "workspace:*", "@nostr-dev-kit/ndk": "^2.4.2",
"@prisma/client": "^5.4.1", "@prisma/client": "^5.4.1",
"@scure/base": "^1.1.1", "@scure/base": "^1.1.1",
"@types/yargs": "^17.0.24", "@types/yargs": "^17.0.24",

85
pnpm-lock.yaml generated
View file

@ -18,8 +18,8 @@ dependencies:
specifier: ^1.2.3 specifier: ^1.2.3
version: 1.2.3 version: 1.2.3
'@nostr-dev-kit/ndk': '@nostr-dev-kit/ndk':
specifier: ^2.8.1 specifier: ^2.4.2
version: 2.8.1(typescript@5.1.3) version: 2.4.2(typescript@5.1.3)
'@prisma/client': '@prisma/client':
specifier: ^5.4.1 specifier: ^5.4.1
version: 5.4.1(prisma@5.4.1) version: 5.4.1(prisma@5.4.1)
@ -46,10 +46,10 @@ dependencies:
version: 16.3.1 version: 16.3.1
eslint-config-prettier: eslint-config-prettier:
specifier: ^8.8.0 specifier: ^8.8.0
version: 8.8.0(eslint@8.57.0) version: 8.8.0(eslint@8.56.0)
eslint-plugin-import: eslint-plugin-import:
specifier: ^2.27.5 specifier: ^2.27.5
version: 2.27.5(eslint@8.57.0) version: 2.27.5(eslint@8.56.0)
eventemitter3: eventemitter3:
specifier: ^5.0.1 specifier: ^5.0.1
version: 5.0.1 version: 5.0.1
@ -316,13 +316,13 @@ packages:
dev: true dev: true
optional: true optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies: peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies: dependencies:
eslint: 8.57.0 eslint: 8.56.0
eslint-visitor-keys: 3.4.3 eslint-visitor-keys: 3.4.3
dev: false dev: false
@ -348,8 +348,8 @@ packages:
- supports-color - supports-color
dev: false dev: false
/@eslint/js@8.57.0: /@eslint/js@8.56.0:
resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false dev: false
@ -393,7 +393,7 @@ packages:
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
engines: {node: '>=10.10.0'} engines: {node: '>=10.10.0'}
dependencies: dependencies:
'@humanwhocodes/object-schema': 2.0.3 '@humanwhocodes/object-schema': 2.0.2
debug: 4.3.4 debug: 4.3.4
minimatch: 3.1.2 minimatch: 3.1.2
transitivePeerDependencies: transitivePeerDependencies:
@ -405,8 +405,8 @@ packages:
engines: {node: '>=12.22'} engines: {node: '>=12.22'}
dev: false dev: false
/@humanwhocodes/object-schema@2.0.3: /@humanwhocodes/object-schema@2.0.2:
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
dev: false dev: false
/@inquirer/checkbox@1.3.2: /@inquirer/checkbox@1.3.2:
@ -599,12 +599,6 @@ packages:
'@noble/hashes': 1.3.1 '@noble/hashes': 1.3.1
dev: false dev: false
/@noble/curves@1.4.0:
resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==}
dependencies:
'@noble/hashes': 1.4.0
dev: false
/@noble/hashes@1.3.1: /@noble/hashes@1.3.1:
resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==}
engines: {node: '>= 16'} engines: {node: '>= 16'}
@ -615,13 +609,13 @@ packages:
engines: {node: '>= 16'} engines: {node: '>= 16'}
dev: false dev: false
/@noble/hashes@1.4.0: /@noble/hashes@1.3.3:
resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==}
engines: {node: '>= 16'} engines: {node: '>= 16'}
dev: false dev: false
/@noble/secp256k1@2.1.0: /@noble/secp256k1@2.0.0:
resolution: {integrity: sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==} resolution: {integrity: sha512-rUGBd95e2a45rlmFTqQJYEFA4/gdIARFfuTuTqLglz0PZ6AKyzyXsEZZq7UZn8hZsvaBgpCzKKBJizT2cJERXw==}
dev: false dev: false
/@nodelib/fs.scandir@2.1.5: /@nodelib/fs.scandir@2.1.5:
@ -642,15 +636,14 @@ packages:
'@nodelib/fs.scandir': 2.1.5 '@nodelib/fs.scandir': 2.1.5
fastq: 1.15.0 fastq: 1.15.0
/@nostr-dev-kit/ndk@2.8.1(typescript@5.1.3): /@nostr-dev-kit/ndk@2.4.2(typescript@5.1.3):
resolution: {integrity: sha512-2WPN1FVhxcLxFYwva2Ti6XKQUjqU0jfdoHHhqDF+0Mxp8f/uDyLflsUQCXsZym5rOIHY85eCYhAXi6V6q6gpRg==} resolution: {integrity: sha512-78gHKyPVy2u6obnTmOU9dMJMi07LgzoTQtO1RjWkcwlQvPyjbpCQ4EXjDSM1pmdj7narwW+uJibnld6z8dT1Fg==}
dependencies: dependencies:
'@noble/curves': 1.4.0 '@noble/hashes': 1.3.3
'@noble/hashes': 1.4.0 '@noble/secp256k1': 2.0.0
'@noble/secp256k1': 2.1.0
'@scure/base': 1.1.1 '@scure/base': 1.1.1
debug: 4.3.4 debug: 4.3.4
light-bolt11-decoder: 3.1.1 light-bolt11-decoder: 3.0.0
node-fetch: 3.3.2 node-fetch: 3.3.2
nostr-tools: 1.17.0(typescript@5.1.3) nostr-tools: 1.17.0(typescript@5.1.3)
tseep: 1.2.1 tseep: 1.2.1
@ -1471,13 +1464,13 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: false dev: false
/eslint-config-prettier@8.8.0(eslint@8.57.0): /eslint-config-prettier@8.8.0(eslint@8.56.0):
resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
eslint: '>=7.0.0' eslint: '>=7.0.0'
dependencies: dependencies:
eslint: 8.57.0 eslint: 8.56.0
dev: false dev: false
/eslint-import-resolver-node@0.3.7: /eslint-import-resolver-node@0.3.7:
@ -1490,7 +1483,7 @@ packages:
- supports-color - supports-color
dev: false dev: false
/eslint-module-utils@2.8.0(eslint-import-resolver-node@0.3.7)(eslint@8.57.0): /eslint-module-utils@2.8.0(eslint-import-resolver-node@0.3.7)(eslint@8.56.0):
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
engines: {node: '>=4'} engines: {node: '>=4'}
peerDependencies: peerDependencies:
@ -1512,13 +1505,13 @@ packages:
optional: true optional: true
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
eslint: 8.57.0 eslint: 8.56.0
eslint-import-resolver-node: 0.3.7 eslint-import-resolver-node: 0.3.7
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: false dev: false
/eslint-plugin-import@2.27.5(eslint@8.57.0): /eslint-plugin-import@2.27.5(eslint@8.56.0):
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
engines: {node: '>=4'} engines: {node: '>=4'}
peerDependencies: peerDependencies:
@ -1533,9 +1526,9 @@ packages:
array.prototype.flatmap: 1.3.1 array.prototype.flatmap: 1.3.1
debug: 3.2.7 debug: 3.2.7
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 8.57.0 eslint: 8.56.0
eslint-import-resolver-node: 0.3.7 eslint-import-resolver-node: 0.3.7
eslint-module-utils: 2.8.0(eslint-import-resolver-node@0.3.7)(eslint@8.57.0) eslint-module-utils: 2.8.0(eslint-import-resolver-node@0.3.7)(eslint@8.56.0)
has: 1.0.3 has: 1.0.3
is-core-module: 2.12.1 is-core-module: 2.12.1
is-glob: 4.0.3 is-glob: 4.0.3
@ -1563,15 +1556,15 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false dev: false
/eslint@8.57.0: /eslint@8.56.0:
resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true hasBin: true
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
'@eslint-community/regexpp': 4.10.0 '@eslint-community/regexpp': 4.10.0
'@eslint/eslintrc': 2.1.4 '@eslint/eslintrc': 2.1.4
'@eslint/js': 8.57.0 '@eslint/js': 8.56.0
'@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/config-array': 0.11.14
'@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8 '@nodelib/fs.walk': 1.2.8
@ -1826,7 +1819,7 @@ packages:
engines: {node: ^12.20 || >= 14.13} engines: {node: ^12.20 || >= 14.13}
dependencies: dependencies:
node-domexception: 1.0.0 node-domexception: 1.0.0
web-streams-polyfill: 3.3.3 web-streams-polyfill: 3.3.2
dev: false dev: false
/figures@3.2.0: /figures@3.2.0:
@ -1886,13 +1879,13 @@ packages:
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
engines: {node: ^10.12.0 || >=12.0.0} engines: {node: ^10.12.0 || >=12.0.0}
dependencies: dependencies:
flatted: 3.3.1 flatted: 3.2.9
keyv: 4.5.4 keyv: 4.5.4
rimraf: 3.0.2 rimraf: 3.0.2
dev: false dev: false
/flatted@3.3.1: /flatted@3.2.9:
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
dev: false dev: false
/follow-redirects@1.15.3(debug@4.3.4): /follow-redirects@1.15.3(debug@4.3.4):
@ -2436,8 +2429,8 @@ packages:
type-check: 0.4.0 type-check: 0.4.0
dev: false dev: false
/light-bolt11-decoder@3.1.1: /light-bolt11-decoder@3.0.0:
resolution: {integrity: sha512-sLg/KCwYkgsHWkefWd6KqpCHrLFWWaXTOX3cf6yD2hAzL0SLpX+lFcaFK2spkjbgzG6hhijKfORDc9WoUHwX0A==} resolution: {integrity: sha512-AKvOigD2pmC8ktnn2TIqdJu0K0qk6ukUmTvHwF3JNkm8uWCqt18Ijn33A/a7gaRZ4PghJ59X+8+MXrzLKdBTmQ==}
dependencies: dependencies:
'@scure/base': 1.1.1 '@scure/base': 1.1.1
dev: false dev: false
@ -3600,8 +3593,8 @@ packages:
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dev: false dev: false
/web-streams-polyfill@3.3.3: /web-streams-polyfill@3.3.2:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} resolution: {integrity: sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
dev: false dev: false

View file

@ -1,22 +0,0 @@
import { execSync, spawn } from "child_process";
try {
console.log(`Running migrations`);
// check if config folder exists
if (!fs.existsSync('./config')) {
execSync(`mkdir config`);
}
execSync('npm run prisma:migrate');
} catch (error) {
console.log(error);
// Handle any potential migration errors here
}
const args = process.argv.slice(2);
const childProcess = spawn('node', ['./dist/index.js', ...args], {
stdio: 'inherit',
});
childProcess.on('exit', (code) => {
process.exit(code);
});

View file

@ -76,8 +76,6 @@ function loadPrivateKey(): string | undefined {
(async () => { (async () => {
let remoteUser: NDKUser; let remoteUser: NDKUser;
ndk = await createNDK();
// if this is the create_account command and we have something that doesn't look like an npub as the remotePubkey, use NDKUser.fromNip05 to get the npub // if this is the create_account command and we have something that doesn't look like an npub as the remotePubkey, use NDKUser.fromNip05 to get the npub
if (command === 'create_account' && !remotePubkey.startsWith("npub")) { if (command === 'create_account' && !remotePubkey.startsWith("npub")) {
// see if we have a username@domain // see if we have a username@domain
@ -90,7 +88,7 @@ function loadPrivateKey(): string | undefined {
content = `${username},${domain}` content = `${username},${domain}`
const u = await NDKUser.fromNip05(domain, ndk); const u = await NDKUser.fromNip05(domain);
if (!u) { if (!u) {
console.log(`Invalid nip05 ${remotePubkey}`); console.log(`Invalid nip05 ${remotePubkey}`);
process.exit(1); process.exit(1);
@ -112,7 +110,7 @@ function loadPrivateKey(): string | undefined {
} }
} }
ndk = await createNDK();
let localSigner: NDKPrivateKeySigner; let localSigner: NDKPrivateKeySigner;
const pk = loadPrivateKey(); const pk = loadPrivateKey();

View file

@ -27,10 +27,8 @@ async function nip89announcement(configData: IConfig) {
const relays = config.nip89!.relays; const relays = config.nip89!.relays;
const nip05 = `_@${domain}`; const nip05 = `_@${domain}`;
const ndk = new NDK({explicitRelayUrls: relays});
// make sure the nip05 correctly points to this pubkey // make sure the nip05 correctly points to this pubkey
const uservianip05 = await NDKUser.fromNip05(nip05, ndk); const uservianip05 = await NDKUser.fromNip05(nip05);
if (!uservianip05 || uservianip05.pubkey !== signerUser.pubkey) { if (!uservianip05 || uservianip05.pubkey !== signerUser.pubkey) {
console.log(`${nip05} does not point to this nsecbunker's key`); console.log(`${nip05} does not point to this nsecbunker's key`);
if (uservianip05) { if (uservianip05) {
@ -55,6 +53,7 @@ async function nip89announcement(configData: IConfig) {
const hasWallet = !!config.wallet; const hasWallet = !!config.wallet;
const hasNostrdress = !!config.wallet?.lnbits?.nostdressUrl; const hasNostrdress = !!config.wallet?.lnbits?.nostdressUrl;
const ndk = new NDK({explicitRelayUrls: relays});
ndk.signer = signer; ndk.signer = signer;
ndk.connect(5000).then(async () => { ndk.connect(5000).then(async () => {
const event = new NDKAppHandlerEvent(ndk, { const event = new NDKAppHandlerEvent(ndk, {

View file

@ -1,5 +1,5 @@
import "websocket-polyfill"; import "websocket-polyfill";
import NDK, { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKRpcRequest, NDKRpcResponse, NDKUser, NostrEvent } from '@nostr-dev-kit/ndk'; import NDK, { NDKKind, NDKPrivateKeySigner, NDKRpcRequest, NDKRpcResponse, NDKUser } from '@nostr-dev-kit/ndk';
import { NDKNostrRpc } from '@nostr-dev-kit/ndk'; import { NDKNostrRpc } from '@nostr-dev-kit/ndk';
import createDebug from 'debug'; import createDebug from 'debug';
import { Key, KeyUser } from '../run'; import { Key, KeyUser } from '../run';
@ -121,8 +121,6 @@ class AdminInterface {
}); });
this.rpc.on('request', (req) => this.handleRequest(req)); this.rpc.on('request', (req) => this.handleRequest(req));
pingOrDie(this.ndk);
}).catch((err) => { }).catch((err) => {
console.log('❌ admin connection failed'); console.log('❌ admin connection failed');
console.log(err); console.log(err);
@ -395,44 +393,4 @@ class AdminInterface {
} }
} }
async function pingOrDie(ndk: NDK) {
let deathTimer: NodeJS.Timeout | null = null;
function resetDeath() {
if (deathTimer) clearTimeout(deathTimer);
deathTimer = setTimeout(() => {
console.log(`❌ No ping event received in 30 seconds. Exiting.`);
process.exit(1);
}, 50000);
}
const self = await ndk.signer!.user();
const sub = ndk.subscribe({
authors: [self.pubkey],
kinds: [NDKKind.NostrConnect],
"#p": [self.pubkey]
});
sub.on("event", (event: NDKEvent) => {
console.log(`🔔 Received ping event:`, event.created_at);
resetDeath();
});
sub.start();
resetDeath();
setInterval(() => {
const event = new NDKEvent(ndk, {
kind: NDKKind.NostrConnect,
tags: [ ["p", self.pubkey] ],
content: "ping"
} as NostrEvent);
event.publish().then(() => {
console.log(`🔔 Sent ping event:`, event.created_at);
}).catch((e: any) => {
console.log(`❌ Failed to send ping event:`, e.message);
process.exit(1);
});
}, 20000);
}
export default AdminInterface; export default AdminInterface;

View file

@ -1,4 +1,4 @@
import NDK, { NDKNip46Backend, NDKPrivateKeySigner, Nip46PermitCallback } from '@nostr-dev-kit/ndk'; import NDK, { NDKNip46Backend, Nip46PermitCallback } from '@nostr-dev-kit/ndk';
import prisma from '../../db.js'; import prisma from '../../db.js';
import type {FastifyInstance} from "fastify"; import type {FastifyInstance} from "fastify";
@ -13,8 +13,7 @@ export class Backend extends NDKNip46Backend {
cb: Nip46PermitCallback, cb: Nip46PermitCallback,
baseUrl?: string baseUrl?: string
) { ) {
const signer = new NDKPrivateKeySigner(key); super(ndk, key, cb);
super(ndk, signer, cb);
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
this.fastify = fastify; this.fastify = fastify;

View file

@ -226,20 +226,7 @@ class Daemon {
*/ */
async startKey(name: string, nsec: string) { async startKey(name: string, nsec: string) {
const cb = signingAuthorizationCallback(name, this.adminInterface); const cb = signingAuthorizationCallback(name, this.adminInterface);
let hexpk: string; const hexpk = nip19.decode(nsec).data as string;
if (nsec.startsWith('nsec1')) {
try {
const key = new NDKPrivateKeySigner(nsec);
hexpk = key.privateKey!;
} catch(e) {
console.error(`Error loading key ${name}:`, e);
return
}
} else {
hexpk = nsec;
}
const backend = new Backend(this.ndk, this.fastify, hexpk, cb, this.config.baseUrl); const backend = new Backend(this.ndk, this.fastify, hexpk, cb, this.config.baseUrl);
await backend.start(); await backend.start();
} }

View file

@ -1,8 +1,8 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex">
<meta charset="UTF-8" /> <meta charset="UTF-8">
<title>Authorize Request</title> <title>Authorize Request</title>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.tailwindcss.com?plugins=forms"></script> <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
@ -75,12 +75,11 @@
.catch((error) => { .catch((error) => {
console.error('Error:', error); console.error('Error:', error);
}); });
}; }
</script> </script>
<style> <style>
body { body {
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
} }
.border { .border {
@ -89,42 +88,34 @@
</style> </style>
</head> </head>
<body <body
class="flex flex-col justify-center items-center min-h-screen bg-gray-100 dark:bg-neutral-800 text-neutrla-950 dark:text-neutral-50" class="flex flex-col justify-center items-center min-h-screen bg-gray-100 dark:bg-neutral-800 text-neutrla-950 dark:text-neutral-50">
>
<div class="max-w-md mx-auto w-full px-2 md:px-4 lg:px-8"> <div class="max-w-md mx-auto w-full px-2 md:px-4 lg:px-8">
<div id="main"> <div id="main">
<h1 <h1
class="text-neutral-950 dark:text-neutral-50 text-lg font-semibold w-full" class="text-neutral-950 dark:text-neutral-50 text-lg font-semibold w-full">
>
Do you want to allow this client to use account Do you want to allow this client to use account
<br /> <br />
<span class="text-blue-500">{{ record.keyName }}</span <span class="text-blue-500">{{record.keyName}}</span>?
>?
</h1> </h1>
<div <div id="error"
id="error" class="flex flex-col gap-4 bg-red-200 rounded-lg p-4 w-full hidden">
class="flex flex-col gap-4 bg-red-200 rounded-lg p-4 w-full hidden" </div>
></div>
{{#unless authenticated}} {{#unless authenticated}}
<div class="flex flex-col gap-4 mt-10"> <div class="flex flex-col gap-4 mt-10">
<label class="flex flex-col gap-2"> <label class="flex flex-col gap-2">
<span <span
class="text-sm font-medium text-neutral-800 dark:text-neutral-200" class="text-sm font-medium text-neutral-800 dark:text-neutral-200">
>
Enter your password to authenticate this request Enter your password to authenticate this request
</span> </span>
<div <div
class="relative before:pointer-events-none focus-within:before:opacity-100 before:opacity-0 before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight dark:after:shadow-white/5 dark:focus-within:after:shadow-blue-500/20 after:transition" class="relative before:pointer-events-none focus-within:before:opacity-100 before:opacity-0 before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight dark:after:shadow-white/5 dark:focus-within:after:shadow-blue-500/20 after:transition">
>
<input <input
type="password" type="password"
name="password" name="password"
id="password"
required required
class="w-full relative text-sm text-neutral-800 dark:text-neutral-200 bg-white dark:bg-neutral-750 placeholder:text-neutral-400 dark:placeholder:text-neutral-500 px-3.5 py-2 rounded-lg border border-black/5 shadow-input shadow-black/5 dark:shadow-black/10 !outline-none" class="w-full relative text-sm text-neutral-800 dark:text-neutral-200 bg-white dark:bg-neutral-750 placeholder:text-neutral-400 dark:placeholder:text-neutral-500 px-3.5 py-2 rounded-lg border border-black/5 shadow-input shadow-black/5 dark:shadow-black/10 !outline-none" />
/>
</div> </div>
</label> </label>
</div> </div>
@ -133,14 +124,12 @@
<div class="flex flex-col items-center justify-center gap-2 mt-5"> <div class="flex flex-col items-center justify-center gap-2 mt-5">
<button <button
onclick="sendPostRequest()" onclick="sendPostRequest()"
class="px-6 w-full h-9 bg-neutral-900 dark:bg-neutral-100 hover:bg-neutral-800 dark:hover:bg-neutral-200 dark:text-neutral-950 rounded-lg justify-center items-center gap-2 inline-flex text-white text-sm font-semibold" class="px-6 w-full h-9 bg-neutral-900 dark:bg-neutral-100 hover:bg-neutral-800 dark:hover:bg-neutral-200 dark:text-neutral-950 rounded-lg justify-center items-center gap-2 inline-flex text-white text-sm font-semibold">
>
Yes Yes
</button> </button>
<button <button
onclick="window.close()" onclick="window.close()"
class="px-6 h-9 w-full border border-neutral-300 dark:border-neutral-600 dark:text-neutral-50 rounded-lg justify-center items-center gap-2 inline-flex text-neutral-950 text-sm font-semibold" class="px-6 h-9 w-full border border-neutral-300 dark:border-neutral-600 dark:text-neutral-50 rounded-lg justify-center items-center gap-2 inline-flex text-neutral-950 text-sm font-semibold">
>
No No
</button> </button>
</div> </div>
@ -157,7 +146,7 @@
<!-- List all cookies --> <!-- List all cookies -->
<script> <script>
const cookies = document.cookie.split(";"); const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) { for (let i = 0; i < cookies.length; i++) {
console.log(cookies[i]); console.log(cookies[i]);
} }

View file

@ -36,6 +36,7 @@
<script> <script>
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
const form = document.querySelector('form'); const form = document.querySelector('form');
const email = document.querySelector('input[name="email"]');
const password = document.querySelector('input[name="password"]'); const password = document.querySelector('input[name="password"]');
const confirmPassword = document.querySelector('input[name="confirm_password"]'); const confirmPassword = document.querySelector('input[name="confirm_password"]');
@ -51,6 +52,15 @@
valid = false; valid = false;
} }
// Check if email is valid or empty
if (email.value) {
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
if (!emailRegex.test(email.value)) {
alert("Please enter a valid email address or leave it empty!");
valid = false;
}
}
if (!valid) { if (!valid) {
event.preventDefault(); event.preventDefault();
} }
@ -88,7 +98,21 @@
</div> </div>
{{/if}} {{/if}}
<input type="hidden" name="email" value="" /> <label class="flex flex-col gap-2">
<span
class="text-sm font-medium text-neutral-800 dark:text-neutral-200">
Recovery Email
</span>
<div
class="relative before:pointer-events-none focus-within:before:opacity-100 before:opacity-0 before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight dark:after:shadow-white/5 dark:focus-within:after:shadow-blue-500/20 after:transition">
<input
readonly="true"
type="email"
name="email"
value="{{email}}"
class="w-full relative text-sm text-blue-500 dark:text-blue-500 bg-neutral-200 dark:bg-neutral-800 placeholder:text-neutral-400 dark:placeholder:text-neutral-500 px-3.5 py-2 rounded-lg border-none" />
</div>
</label>
<label class="flex flex-col gap-2"> <label class="flex flex-col gap-2">
<span <span
@ -98,6 +122,7 @@
<div <div
class="relative before:pointer-events-none focus-within:before:opacity-100 before:opacity-0 before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight dark:after:shadow-white/5 dark:focus-within:after:shadow-blue-500/20 after:transition"> class="relative before:pointer-events-none focus-within:before:opacity-100 before:opacity-0 before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight dark:after:shadow-white/5 dark:focus-within:after:shadow-blue-500/20 after:transition">
<input <input
readonly="true"
type="text" type="text"
name="username" name="username"
value="{{username}}" value="{{username}}"