ncantu 26a53327a4 Enhance: Complete dashboard documentation and new services integration
**Motivations:**
- Complete documentation for dashboard, domains, ports and environment configuration
- Add new services (ClamAV API, Watermark API) to the infrastructure
- Enhance dashboard with new pages and improved functionality
- Improve deployment scripts and service configurations

**Root causes:**
- Missing comprehensive documentation for infrastructure setup
- Need for antivirus scanning service integration
- Need for watermark service integration
- Dashboard required additional pages and features

**Correctifs:**
- Added comprehensive documentation in docs/ (DASHBOARD.md, DOMAINS_AND_PORTS.md, ENVIRONMENT.md)
- Updated systemd service files with proper environment variables
- Enhanced nginx proxy configuration script
- Updated maintenance documentation

**Evolutions:**
- Added new ClamAV API service (api-clamav) for file scanning
- Added new Watermark API service (api-filigrane) for document watermarking
- Enhanced signet-dashboard with new learn.html page
- Improved dashboard UI with better styles and navigation
- Enhanced app.js with new functionality and better error handling
- Updated API documentation page with complete endpoint descriptions
- Added deployment scripts for watermark and nginx configuration
- Updated hash and UTXO lists with latest data
- Enhanced server.js with new routes and improved Bitcoin RPC integration

**Pages affectées:**
- docs/DASHBOARD.md: New comprehensive dashboard documentation
- docs/DOMAINS_AND_PORTS.md: New infrastructure domains and ports documentation
- docs/ENVIRONMENT.md: New environment variables documentation
- docs/MAINTENANCE.md: Updated maintenance procedures
- docs/README.md: Updated main documentation
- signet-dashboard/public/app.js: Enhanced with new features
- signet-dashboard/public/styles.css: Improved styling
- signet-dashboard/public/index.html: Enhanced main page
- signet-dashboard/public/learn.html: New educational page
- signet-dashboard/public/api-docs.html: Enhanced API documentation
- signet-dashboard/public/hash-list.html: Updated hash list page
- signet-dashboard/public/utxo-list.html: Updated UTXO list page
- signet-dashboard/public/join-signet.html: Updated join signet page
- signet-dashboard/src/server.js: Enhanced server with new routes
- signet-dashboard/start.sh: Updated startup script
- signet-dashboard/signet-dashboard.service: Updated systemd service
- api-anchorage/anchorage-api.service: Updated systemd service
- api-faucet/faucet-api.service: Updated systemd service
- configure-nginx-proxy.sh: Enhanced nginx configuration script
- add-watermark-certificate.sh: New watermark certificate script
- deploy-watermark-nginx.sh: New deployment script
- api-clamav/: New ClamAV API service
- api-filigrane/: New Watermark API service
- hash_list.txt, utxo_list.txt: Updated with latest data
- anchor_count.txt: Updated anchor count
2026-01-25 17:43:05 +01:00

342 lines
6.6 KiB
JavaScript

/*!
* finalhandler
* Copyright(c) 2014-2022 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var debug = require('debug')('finalhandler')
var encodeUrl = require('encodeurl')
var escapeHtml = require('escape-html')
var onFinished = require('on-finished')
var parseUrl = require('parseurl')
var statuses = require('statuses')
var unpipe = require('unpipe')
/**
* Module variables.
* @private
*/
var DOUBLE_SPACE_REGEXP = /\x20{2}/g
var NEWLINE_REGEXP = /\n/g
/* istanbul ignore next */
var defer = typeof setImmediate === 'function'
? setImmediate
: function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) }
var isFinished = onFinished.isFinished
/**
* Create a minimal HTML document.
*
* @param {string} message
* @private
*/
function createHtmlDocument (message) {
var body = escapeHtml(message)
.replace(NEWLINE_REGEXP, '<br>')
.replace(DOUBLE_SPACE_REGEXP, ' &nbsp;')
return '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>' + body + '</pre>\n' +
'</body>\n' +
'</html>\n'
}
/**
* Module exports.
* @public
*/
module.exports = finalhandler
/**
* Create a function to handle the final response.
*
* @param {Request} req
* @param {Response} res
* @param {Object} [options]
* @return {Function}
* @public
*/
function finalhandler (req, res, options) {
var opts = options || {}
// get environment
var env = opts.env || process.env.NODE_ENV || 'development'
// get error callback
var onerror = opts.onerror
return function (err) {
var headers
var msg
var status
// ignore 404 on in-flight response
if (!err && headersSent(res)) {
debug('cannot 404 after headers sent')
return
}
// unhandled error
if (err) {
// respect status code from error
status = getErrorStatusCode(err)
if (status === undefined) {
// fallback to status code on response
status = getResponseStatusCode(res)
} else {
// respect headers from error
headers = getErrorHeaders(err)
}
// get error message
msg = getErrorMessage(err, status, env)
} else {
// not found
status = 404
msg = 'Cannot ' + req.method + ' ' + encodeUrl(getResourceName(req))
}
debug('default %s', status)
// schedule onerror callback
if (err && onerror) {
defer(onerror, err, req, res)
}
// cannot actually respond
if (headersSent(res)) {
debug('cannot %d after headers sent', status)
if (req.socket) {
req.socket.destroy()
}
return
}
// send response
send(req, res, status, headers, msg)
}
}
/**
* Get headers from Error object.
*
* @param {Error} err
* @return {object}
* @private
*/
function getErrorHeaders (err) {
if (!err.headers || typeof err.headers !== 'object') {
return undefined
}
var headers = Object.create(null)
var keys = Object.keys(err.headers)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
headers[key] = err.headers[key]
}
return headers
}
/**
* Get message from Error object, fallback to status message.
*
* @param {Error} err
* @param {number} status
* @param {string} env
* @return {string}
* @private
*/
function getErrorMessage (err, status, env) {
var msg
if (env !== 'production') {
// use err.stack, which typically includes err.message
msg = err.stack
// fallback to err.toString() when possible
if (!msg && typeof err.toString === 'function') {
msg = err.toString()
}
}
return msg || statuses.message[status]
}
/**
* Get status code from Error object.
*
* @param {Error} err
* @return {number}
* @private
*/
function getErrorStatusCode (err) {
// check err.status
if (typeof err.status === 'number' && err.status >= 400 && err.status < 600) {
return err.status
}
// check err.statusCode
if (typeof err.statusCode === 'number' && err.statusCode >= 400 && err.statusCode < 600) {
return err.statusCode
}
return undefined
}
/**
* Get resource name for the request.
*
* This is typically just the original pathname of the request
* but will fallback to "resource" is that cannot be determined.
*
* @param {IncomingMessage} req
* @return {string}
* @private
*/
function getResourceName (req) {
try {
return parseUrl.original(req).pathname
} catch (e) {
return 'resource'
}
}
/**
* Get status code from response.
*
* @param {OutgoingMessage} res
* @return {number}
* @private
*/
function getResponseStatusCode (res) {
var status = res.statusCode
// default status code to 500 if outside valid range
if (typeof status !== 'number' || status < 400 || status > 599) {
status = 500
}
return status
}
/**
* Determine if the response headers have been sent.
*
* @param {object} res
* @returns {boolean}
* @private
*/
function headersSent (res) {
return typeof res.headersSent !== 'boolean'
? Boolean(res._header)
: res.headersSent
}
/**
* Send response.
*
* @param {IncomingMessage} req
* @param {OutgoingMessage} res
* @param {number} status
* @param {object} headers
* @param {string} message
* @private
*/
function send (req, res, status, headers, message) {
function write () {
// response body
var body = createHtmlDocument(message)
// response status
res.statusCode = status
if (req.httpVersionMajor < 2) {
res.statusMessage = statuses.message[status]
}
// remove any content headers
res.removeHeader('Content-Encoding')
res.removeHeader('Content-Language')
res.removeHeader('Content-Range')
// response headers
setHeaders(res, headers)
// security headers
res.setHeader('Content-Security-Policy', "default-src 'none'")
res.setHeader('X-Content-Type-Options', 'nosniff')
// standard headers
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
if (req.method === 'HEAD') {
res.end()
return
}
res.end(body, 'utf8')
}
if (isFinished(req)) {
write()
return
}
// unpipe everything from the request
unpipe(req)
// flush the request
onFinished(req, write)
req.resume()
}
/**
* Set response headers from an object.
*
* @param {OutgoingMessage} res
* @param {object} headers
* @private
*/
function setHeaders (res, headers) {
if (!headers) {
return
}
var keys = Object.keys(headers)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
res.setHeader(key, headers[key])
}
}