Update server/status.js
Add paginated address transaction API
This commit is contained in:
+580
-311
@@ -27,6 +27,7 @@ const path = require('path')
|
||||
|
||||
const addressCacheDir = '/data/cache/addresses'
|
||||
const addressCacheMaxAgeMs = 60000
|
||||
const addressTransactionPageSize = 50
|
||||
const addressRefreshes = new Map()
|
||||
|
||||
function ensureAddressCacheDir() {
|
||||
@@ -149,7 +150,11 @@ function refreshAddressCache(
|
||||
return refresh
|
||||
}
|
||||
|
||||
function queryFulcrum(fulcrumUrl, method, params = []) {
|
||||
function queryFulcrum(
|
||||
fulcrumUrl,
|
||||
method,
|
||||
params = [],
|
||||
) {
|
||||
return new Promise((resolve) => {
|
||||
if (!fulcrumUrl) {
|
||||
resolve({
|
||||
@@ -490,34 +495,89 @@ async function getAddressData(address) {
|
||||
}
|
||||
}
|
||||
|
||||
function getAddressTransactionPage(
|
||||
history,
|
||||
offset = 0,
|
||||
limit = addressTransactionPageSize,
|
||||
) {
|
||||
const safeOffset =
|
||||
Math.max(
|
||||
0,
|
||||
Number(offset) || 0,
|
||||
)
|
||||
|
||||
const safeLimit =
|
||||
Math.min(
|
||||
addressTransactionPageSize,
|
||||
Math.max(
|
||||
1,
|
||||
Number(limit) ||
|
||||
addressTransactionPageSize,
|
||||
),
|
||||
)
|
||||
|
||||
return history.slice(
|
||||
safeOffset,
|
||||
safeOffset + safeLimit,
|
||||
)
|
||||
}
|
||||
|
||||
async function getAddressesWithData() {
|
||||
const addresses = await listAddresses()
|
||||
const results = []
|
||||
|
||||
for (const entry of addresses) {
|
||||
try {
|
||||
const data = await getAddressData(entry.address)
|
||||
const data =
|
||||
await getAddressData(
|
||||
entry.address,
|
||||
)
|
||||
|
||||
const history =
|
||||
Array.isArray(data.history)
|
||||
? data.history
|
||||
: []
|
||||
|
||||
results.push({
|
||||
...entry,
|
||||
scripthash: data.scripthash,
|
||||
confirmedBalance: data.confirmedBalance,
|
||||
unconfirmedBalance: data.unconfirmedBalance,
|
||||
utxos: data.utxos,
|
||||
history: data.history,
|
||||
scripthash:
|
||||
data.scripthash,
|
||||
confirmedBalance:
|
||||
data.confirmedBalance,
|
||||
unconfirmedBalance:
|
||||
data.unconfirmedBalance,
|
||||
utxos:
|
||||
data.utxos,
|
||||
history:
|
||||
getAddressTransactionPage(
|
||||
history,
|
||||
),
|
||||
transactionCount:
|
||||
history.length,
|
||||
transactionOffset: 0,
|
||||
transactionLimit:
|
||||
addressTransactionPageSize,
|
||||
lastSynced:
|
||||
data.lastSynced || null,
|
||||
cacheAgeMs:
|
||||
Number(data.cacheAgeMs || 0),
|
||||
Number(
|
||||
data.cacheAgeMs || 0,
|
||||
),
|
||||
error: null,
|
||||
})
|
||||
} catch (error) {
|
||||
results.push({
|
||||
...entry,
|
||||
confirmedBalance: null,
|
||||
unconfirmedBalance: null,
|
||||
confirmedBalance:
|
||||
null,
|
||||
unconfirmedBalance:
|
||||
null,
|
||||
utxos: [],
|
||||
history: [],
|
||||
transactionCount: 0,
|
||||
transactionOffset: 0,
|
||||
transactionLimit:
|
||||
addressTransactionPageSize,
|
||||
lastSynced: null,
|
||||
cacheAgeMs: null,
|
||||
error:
|
||||
@@ -531,245 +591,362 @@ async function getAddressesWithData() {
|
||||
return results
|
||||
}
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
if (req.method === 'POST' && req.url === '/api/login') {
|
||||
let body = ''
|
||||
const server = http.createServer(
|
||||
async (req, res) => {
|
||||
if (
|
||||
req.method === 'POST' &&
|
||||
req.url === '/api/login'
|
||||
) {
|
||||
let body = ''
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const input = JSON.parse(body)
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const input =
|
||||
JSON.parse(body)
|
||||
|
||||
if (!checkPassword(input.password)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type': 'application/json',
|
||||
if (
|
||||
!checkPassword(
|
||||
input.password,
|
||||
)
|
||||
) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Invalid password',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const token =
|
||||
createSession()
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
'Set-Cookie':
|
||||
'munin_session=' +
|
||||
encodeURIComponent(token) +
|
||||
'; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=86400',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Invalid password',
|
||||
ok: true,
|
||||
}),
|
||||
)
|
||||
} catch {
|
||||
res.writeHead(400, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
return
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Invalid request',
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (req.url === '/api/status') {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const status =
|
||||
await getStatus()
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify(status),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to get service status:',
|
||||
error,
|
||||
)
|
||||
|
||||
res.writeHead(500, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
service: 'running',
|
||||
error:
|
||||
'Failed to determine service status',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'GET' &&
|
||||
req.url === '/api/addresses'
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const addresses =
|
||||
await listAddresses()
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
addresses,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to list addresses:',
|
||||
error,
|
||||
)
|
||||
|
||||
res.writeHead(500, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Failed to list addresses',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'GET' &&
|
||||
req.url === '/api/addresses/data'
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const addresses =
|
||||
await getAddressesWithData()
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
addresses,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to get address data:',
|
||||
error,
|
||||
)
|
||||
|
||||
res.writeHead(500, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Failed to get address data',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'GET' &&
|
||||
req.url.startsWith(
|
||||
'/api/addresses/transactions',
|
||||
)
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const url =
|
||||
new URL(
|
||||
req.url,
|
||||
`http://${req.headers.host || 'localhost'}`,
|
||||
)
|
||||
|
||||
const address =
|
||||
url.searchParams.get(
|
||||
'address',
|
||||
)
|
||||
|
||||
if (!address) {
|
||||
throw new Error(
|
||||
'Address is required',
|
||||
)
|
||||
}
|
||||
|
||||
const token = createSession()
|
||||
const offset =
|
||||
Math.max(
|
||||
0,
|
||||
Number(
|
||||
url.searchParams.get(
|
||||
'offset',
|
||||
),
|
||||
) || 0,
|
||||
)
|
||||
|
||||
const limit =
|
||||
Math.min(
|
||||
addressTransactionPageSize,
|
||||
Math.max(
|
||||
1,
|
||||
Number(
|
||||
url.searchParams.get(
|
||||
'limit',
|
||||
),
|
||||
) ||
|
||||
addressTransactionPageSize,
|
||||
),
|
||||
)
|
||||
|
||||
const data =
|
||||
await getAddressData(
|
||||
address,
|
||||
)
|
||||
|
||||
const history =
|
||||
Array.isArray(
|
||||
data.history,
|
||||
)
|
||||
? data.history
|
||||
: []
|
||||
|
||||
const transactions =
|
||||
getAddressTransactionPage(
|
||||
history,
|
||||
offset,
|
||||
limit,
|
||||
)
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
'Set-Cookie':
|
||||
'munin_session=' +
|
||||
encodeURIComponent(token) +
|
||||
'; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=86400',
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
address,
|
||||
scripthash:
|
||||
data.scripthash,
|
||||
transactions,
|
||||
total:
|
||||
history.length,
|
||||
offset,
|
||||
limit,
|
||||
hasMore:
|
||||
offset +
|
||||
transactions.length <
|
||||
history.length,
|
||||
lastSynced:
|
||||
data.lastSynced ||
|
||||
null,
|
||||
cacheAgeMs:
|
||||
Number(
|
||||
data.cacheAgeMs || 0,
|
||||
),
|
||||
}),
|
||||
)
|
||||
} catch {
|
||||
res.writeHead(400, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Invalid request',
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (req.url === '/api/status') {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const status = await getStatus()
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(JSON.stringify(status))
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to get service status:',
|
||||
error,
|
||||
)
|
||||
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
service: 'running',
|
||||
error: 'Failed to determine service status',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (req.method === 'GET' && req.url === '/api/addresses') {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const addresses = await listAddresses()
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
addresses,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to list addresses:',
|
||||
error,
|
||||
)
|
||||
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Failed to list addresses',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'GET' &&
|
||||
req.url === '/api/addresses/data'
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const addresses = await getAddressesWithData()
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
addresses,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to get address data:',
|
||||
error,
|
||||
)
|
||||
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Failed to get address data',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'POST' &&
|
||||
req.url === '/api/addresses'
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let body = ''
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
|
||||
req.on('end', async () => {
|
||||
try {
|
||||
const input = JSON.parse(body)
|
||||
|
||||
const entry = await addAddress(
|
||||
input.address,
|
||||
input.label,
|
||||
)
|
||||
|
||||
res.writeHead(201, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(JSON.stringify(entry))
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to get address transactions:',
|
||||
error,
|
||||
)
|
||||
|
||||
res.writeHead(400, {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
@@ -777,57 +954,197 @@ const server = http.createServer(async (req, res) => {
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to add address',
|
||||
: 'Failed to get address transactions',
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'DELETE' &&
|
||||
req.url === '/api/addresses'
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type': 'application/json',
|
||||
if (
|
||||
req.method === 'POST' &&
|
||||
req.url === '/api/addresses'
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let body = ''
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Authentication required',
|
||||
}),
|
||||
req.on(
|
||||
'end',
|
||||
async () => {
|
||||
try {
|
||||
const input =
|
||||
JSON.parse(body)
|
||||
|
||||
const entry =
|
||||
await addAddress(
|
||||
input.address,
|
||||
input.label,
|
||||
)
|
||||
|
||||
res.writeHead(201, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify(entry),
|
||||
)
|
||||
} catch (error) {
|
||||
res.writeHead(400, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to add address',
|
||||
}),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let body = ''
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
|
||||
req.on('end', async () => {
|
||||
try {
|
||||
const input = JSON.parse(body)
|
||||
|
||||
await removeAddress(input.address)
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
if (
|
||||
req.method === 'DELETE' &&
|
||||
req.url === '/api/addresses'
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
error:
|
||||
'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let body = ''
|
||||
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
|
||||
req.on(
|
||||
'end',
|
||||
async () => {
|
||||
try {
|
||||
const input =
|
||||
JSON.parse(body)
|
||||
|
||||
await removeAddress(
|
||||
input.address,
|
||||
)
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
ok: true,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
res.writeHead(400, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to remove address',
|
||||
}),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'GET' &&
|
||||
req.url.startsWith(
|
||||
'/api/transactions/',
|
||||
)
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const txid =
|
||||
decodeURIComponent(
|
||||
req.url.slice(
|
||||
'/api/transactions/'.length,
|
||||
),
|
||||
)
|
||||
|
||||
try {
|
||||
const transaction =
|
||||
await getTransaction(txid)
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify(
|
||||
transaction,
|
||||
),
|
||||
)
|
||||
} catch (error) {
|
||||
res.writeHead(400, {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Type':
|
||||
'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
@@ -835,66 +1152,18 @@ const server = http.createServer(async (req, res) => {
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to remove address',
|
||||
: 'Failed to get transaction',
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
req.method === 'GET' &&
|
||||
req.url.startsWith('/api/transactions/')
|
||||
) {
|
||||
if (!isValidSession(req)) {
|
||||
res.writeHead(401, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error: 'Authentication required',
|
||||
}),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const txid = decodeURIComponent(
|
||||
req.url.slice('/api/transactions/'.length),
|
||||
)
|
||||
|
||||
try {
|
||||
const transaction = await getTransaction(txid)
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(JSON.stringify(transaction))
|
||||
} catch (error) {
|
||||
res.writeHead(400, {
|
||||
'Content-Type': 'application/json',
|
||||
})
|
||||
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to get transaction',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
res.writeHead(404)
|
||||
res.end('Not found')
|
||||
})
|
||||
res.writeHead(404)
|
||||
res.end('Not found')
|
||||
},
|
||||
)
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(
|
||||
|
||||
Reference in New Issue
Block a user