diff --git a/Justfile b/Justfile index 6a884a6..a5736f1 100644 --- a/Justfile +++ b/Justfile @@ -13,7 +13,7 @@ set dotenv-load # To launch all apis @apis: - node src/dev.js + node src/start.js # To launch the front end @front: diff --git a/docker/ELK/compose.yml b/docker/ELK/compose.yml new file mode 100644 index 0000000..46182c7 --- /dev/null +++ b/docker/ELK/compose.yml @@ -0,0 +1,4 @@ +include: + - ./logstash/compose.yml + - ./kibana/compose.yml + - ./elasticsearch/compose.yml diff --git a/docker/ELK/elasticsearch/Dockerfile b/docker/ELK/elasticsearch/Dockerfile new file mode 100644 index 0000000..74b087a --- /dev/null +++ b/docker/ELK/elasticsearch/Dockerfile @@ -0,0 +1,4 @@ +FROM docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.0.4 + +COPY --chown=elasticsearch:elasticsearch elasticsearch.yml /usr/share/elasticsearch/config/ +COPY --chown=elasticsearch:elasticsearch jvm.options /usr/share/elasticsearch/config/jvm.options.d/custom.options diff --git a/docker/ELK/elasticsearch/compose.yml b/docker/ELK/elasticsearch/compose.yml new file mode 100644 index 0000000..36ab9d2 --- /dev/null +++ b/docker/ELK/elasticsearch/compose.yml @@ -0,0 +1,10 @@ +services: + elasticsearch: + container_name: transcendence-elasticsearch + build: + dockerfile: Dockerfile + context: . + environment: + - LOG_LEVEL=info + networks: + - elk diff --git a/docker/ELK/elasticsearch/elasticsearch.yml b/docker/ELK/elasticsearch/elasticsearch.yml new file mode 100644 index 0000000..30b5f04 --- /dev/null +++ b/docker/ELK/elasticsearch/elasticsearch.yml @@ -0,0 +1,9 @@ +cluster.name: docker-cluster + +node.name: transcendence-elasticsearch + +discovery.type: single-node + +xpack.security.enabled: false + +network.host: 0.0.0.0 diff --git a/docker/ELK/elasticsearch/jvm.options b/docker/ELK/elasticsearch/jvm.options new file mode 100644 index 0000000..e292d3f --- /dev/null +++ b/docker/ELK/elasticsearch/jvm.options @@ -0,0 +1,2 @@ +-Xms1g +-Xmx1g diff --git a/docker/ELK/kibana/Dockerfile b/docker/ELK/kibana/Dockerfile new file mode 100644 index 0000000..0ab577c --- /dev/null +++ b/docker/ELK/kibana/Dockerfile @@ -0,0 +1,3 @@ +FROM docker.elastic.co/kibana/kibana-wolfi:9.0.4 + +COPY --chmod=777 kibana.yml /etc/kibana/kibana.yml diff --git a/docker/ELK/kibana/compose.yml b/docker/ELK/kibana/compose.yml new file mode 100644 index 0000000..7a4cb52 --- /dev/null +++ b/docker/ELK/kibana/compose.yml @@ -0,0 +1,12 @@ +services: + kibana: + container_name: transcendence-kibana + build: + dockerfile: Dockerfile + context: . + ports: + - ${ELK_PORT}:5601 + environment: + - LOG_LEVEL=info + networks: + - elk diff --git a/docker/ELK/kibana/kibana.yml b/docker/ELK/kibana/kibana.yml new file mode 100644 index 0000000..a24e16a --- /dev/null +++ b/docker/ELK/kibana/kibana.yml @@ -0,0 +1,6 @@ +server.name: kibana +server.host: "0.0.0.0" + +elasticsearch.hosts: ["https://transcendence-elasticsearch:9200"] + +telemetry.enabled: false diff --git a/docker/ELK/logstash/Dockerfile b/docker/ELK/logstash/Dockerfile new file mode 100644 index 0000000..58d1167 --- /dev/null +++ b/docker/ELK/logstash/Dockerfile @@ -0,0 +1,6 @@ +FROM docker.elastic.co/logstash/logstash-wolfi:9.0.4 + +RUN rm -f /usr/share/logstash/pipeline/logstash.conf + +COPY --chmod=777 pipeline/ /usr/share/logstash/pipeline/ +COPY --chmod=777 config/ /usr/share/logstash/config/ diff --git a/docker/ELK/logstash/compose.yml b/docker/ELK/logstash/compose.yml new file mode 100644 index 0000000..be01856 --- /dev/null +++ b/docker/ELK/logstash/compose.yml @@ -0,0 +1,15 @@ +services: + logstash: + container_name: transcendence-logstash + build: + dockerfile: Dockerfile + context: . + volumes: + - log-user:/var/log/user-api + - log-auth:/var/log/auth-api + - log-nginx:/var/log/nginx + environment: + - LOG_LEVEL=info + networks: + - elk + - logstash diff --git a/docker/ELK/logstash/config/logstash.yml b/docker/ELK/logstash/config/logstash.yml new file mode 100644 index 0000000..b9838aa --- /dev/null +++ b/docker/ELK/logstash/config/logstash.yml @@ -0,0 +1,2 @@ +api: + environment: production diff --git a/docker/ELK/logstash/pipeline/logstash.conf b/docker/ELK/logstash/pipeline/logstash.conf new file mode 100644 index 0000000..1a53ae5 --- /dev/null +++ b/docker/ELK/logstash/pipeline/logstash.conf @@ -0,0 +1,27 @@ +input { + file { + path => "/var/log/user-api/log.log" + start_position => "beginning" + tags => [ "api", "user" ] + } + file { + path => "/var/log/auth-api/log.log" + start_position => "beginning" + tags => [ "api", "auth" ] + } + file { + path => "/var/log/nginx/log.log" + start_position => "beginning" + tags => [ "nginx", "front" ] + } + file { + path => "/var/log/nginx/err.log" + start_position => "beginning" + tags => [ "nginx", "front", "error" ] + } +} + +output { + elasticsearch { hosts => ["transcendence-elasticsearch:9200"] } + stdout { codec => rubydebug } +} diff --git a/docker/api-base/Dockerfile b/docker/api-base/Dockerfile index 8513be8..142d056 100644 --- a/docker/api-base/Dockerfile +++ b/docker/api-base/Dockerfile @@ -24,5 +24,7 @@ EXPOSE 3000 RUN mkdir /db STOPSIGNAL SIGINT +ENV LOG_TARGET=/var/log/log.log +RUN touch /var/log/log.log CMD [ "node", "/app/src/start.js" ] diff --git a/docker/api-base/compose.yml b/docker/api-base/compose.yml index 3aa20a8..9ce5386 100644 --- a/docker/api-base/compose.yml +++ b/docker/api-base/compose.yml @@ -6,6 +6,7 @@ services: context: ../../ volumes: - db-user:/db + - log-user:/var/log networks: - front - back @@ -13,6 +14,7 @@ services: environment: - TZ=Europe/Paris - API_TARGET=user + - LOG_FILE_PATH=/var/log/log.log - JWT_SECRET=${JWT_SECRET} restart: unless-stopped auth-api: @@ -22,12 +24,17 @@ services: context: ../../ volumes: - db-auth:/db + - log-auth:/var/log networks: - front - back - prom-exporter environment: - TZ=Europe/Paris + - GOOGLE_CALLBACK_URL=${GOOGLE_CALLBACK_URL} + - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} + - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} - API_TARGET=auth + - LOG_FILE_PATH=/var/log/log.log - JWT_SECRET=${JWT_SECRET} restart: unless-stopped diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8a890f4..9e49825 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -6,3 +6,4 @@ include: - ./monitoring/compose.yml - ./api-base/compose.yml - ./front/compose.yml + - ./ELK/compose.yml diff --git a/docker/front/Dockerfile b/docker/front/Dockerfile index f5e1207..a60b3e9 100644 --- a/docker/front/Dockerfile +++ b/docker/front/Dockerfile @@ -25,5 +25,11 @@ COPY --chmod=755 docker/front/entry/ssl-cert.sh /docker-entrypoint.d/ssl-cert.s COPY --from=builder /app/dist /usr/share/nginx/html +USER root +RUN mkdir -p /var/log/front +RUN touch /var/log/front/err.log /var/log/front/log.log +RUN chmod -R 777 /var/log/front +USER nginx + EXPOSE 80 443 STOPSIGNAL SIGINT diff --git a/docker/front/compose.yml b/docker/front/compose.yml index c748cff..22b500e 100644 --- a/docker/front/compose.yml +++ b/docker/front/compose.yml @@ -6,8 +6,9 @@ services: context: ../../ ports: - ${OUT_PORT}:443 + volumes: + - log-nginx:/var/log/front environment: - - SERVER_NAME=localhost - TZ=Europe/Paris depends_on: user-api: diff --git a/docker/front/config/default.conf.template b/docker/front/config/default.conf.template index b199b9b..8574aee 100644 --- a/docker/front/config/default.conf.template +++ b/docker/front/config/default.conf.template @@ -1,4 +1,7 @@ server { + error_log /var/log/front/err.log warn; + access_log /var/log/front/log.log; + listen 443 ssl; server_name example.com; # Replace with your domain or handle env vars externally diff --git a/docker/monitoring/prometheus/config/prometheus.yml b/docker/monitoring/prometheus/config/prometheus.yml index 84ec958..e3789e5 100644 --- a/docker/monitoring/prometheus/config/prometheus.yml +++ b/docker/monitoring/prometheus/config/prometheus.yml @@ -24,7 +24,7 @@ scrape_configs: static_configs: - targets: ['node-exporter:9100'] - - job_name: 'nodejs' - static_configs: - - targets: ['transcendence-api-auth:3000'] - - targets: ['transcendence-api-user:3000'] + # - job_name: 'nodejs' + # static_configs: + # - targets: ['transcendence-api-auth:3000'] + # - targets: ['transcendence-api-user:3000'] diff --git a/docker/networks.yml b/docker/networks.yml index 9ce0fe6..65a1c1e 100644 --- a/docker/networks.yml +++ b/docker/networks.yml @@ -7,3 +7,7 @@ networks: name: transcendence-prom prom-exporter: name: transcendence-prom-exporter + elk: + name: transcendence-elk + logstash: + name: transcendence-logstash diff --git a/docker/volumes.yml b/docker/volumes.yml index e67d472..f05e822 100644 --- a/docker/volumes.yml +++ b/docker/volumes.yml @@ -5,3 +5,9 @@ volumes: name: transcendence-api-auth-db db-user: name: transcendence-api-user-db + log-auth: + name: transcendence-api-auth-log + log-user: + name: transcendence-api-user-log + log-nginx: + name: transcendence-front-log diff --git a/package.json b/package.json index 149bcc0..0c43524 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "better-sqlite3": "^12.2.0", "fastify": "^5.4.0", "fastify-cli": "^7.4.0", - "google-auth-library": "^10.1.0", + "pino": "^9.7.0", + "pino-logstash": "^1.0.0", "prom-client": "^15.1.3", "solhint": "^6.0.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c12749..75dd8c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,9 +35,12 @@ importers: fastify-cli: specifier: ^7.4.0 version: 7.4.0 - google-auth-library: - specifier: ^10.1.0 - version: 10.1.0 + pino: + specifier: ^9.7.0 + version: 9.7.0 + pino-logstash: + specifier: ^1.0.0 + version: 1.0.0 prom-client: specifier: ^15.1.3 version: 15.1.3 @@ -512,10 +515,6 @@ packages: abstract-logging@2.0.1: resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - ajv-errors@1.0.1: resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==} peerDependencies: @@ -597,9 +596,6 @@ packages: resolution: {integrity: sha512-eGbYq2CT+tos1fBwLQ/tkBt9J5M3JEHjku4hbvQUePCckkvVf14xWj+1m7dGoK81M/fOjFT7yM9UMeKT/+vFLQ==} engines: {node: 20.x || 22.x || 23.x || 24.x} - bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} @@ -615,9 +611,6 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -692,22 +685,9 @@ packages: typescript: optional: true - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -788,9 +768,6 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - fast-copy@3.0.2: resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} @@ -857,10 +834,6 @@ packages: picomatch: optional: true - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -889,10 +862,6 @@ packages: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -911,14 +880,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gaxios@7.1.1: - resolution: {integrity: sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==} - engines: {node: '>=18'} - - gcp-metadata@7.0.1: - resolution: {integrity: sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==} - engines: {node: '>=18'} - generify@4.2.0: resolution: {integrity: sha512-b4cVhbPfbgbCZtK0dcUc1lASitXGEAIqukV5DDAyWm25fomWnV+C+a1yXvqikcRZXHN2j0pSDyj3cTfzq8pC7Q==} hasBin: true @@ -943,14 +904,6 @@ packages: engines: {node: '>=12'} deprecated: Glob versions prior to v9 are no longer supported - google-auth-library@10.1.0: - resolution: {integrity: sha512-GspVjZj1RbyRWpQ9FbAXMKjFGzZwDKnUHi66JJ+tcjcu5/xYAP1pdlWotCuIkMwjfVsxxDvsGZXGLzRt72D0sQ==} - engines: {node: '>=18'} - - google-logging-utils@1.1.1: - resolution: {integrity: sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==} - engines: {node: '>=14'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -965,10 +918,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - gtoken@8.0.0: - resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} - engines: {node: '>=18'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -995,10 +944,6 @@ packages: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1055,9 +1000,6 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - json-bigint@1.0.0: - resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1080,12 +1022,6 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1236,9 +1172,6 @@ packages: mnemonist@0.40.3: resolution: {integrity: sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==} - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1255,15 +1188,6 @@ packages: resolution: {integrity: sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==} engines: {node: ^18 || ^20 || >= 21} - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -1328,6 +1252,9 @@ packages: pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + pino-logstash@1.0.0: + resolution: {integrity: sha512-v8UCUxGROClKZW6mB0GumsfCi3gnmupMNDzh86TX9Oab9ijDjIW3vJdMjlcNaAyJK9GiJxUdLtI6amzLhpuARg==} + pino-pretty@13.0.0: resolution: {integrity: sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==} hasBin: true @@ -1634,10 +1561,6 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -1970,8 +1893,6 @@ snapshots: abstract-logging@2.0.1: {} - agent-base@7.1.4: {} - ajv-errors@1.0.1(ajv@6.12.6): dependencies: ajv: 6.12.6 @@ -2057,8 +1978,6 @@ snapshots: bindings: 1.5.0 prebuild-install: 7.1.3 - bignumber.js@9.3.1: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -2077,8 +1996,6 @@ snapshots: dependencies: balanced-match: 1.0.2 - buffer-equal-constant-time@1.0.1: {} - buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -2150,14 +2067,8 @@ snapshots: optionalDependencies: typescript: 5.8.3 - data-uri-to-buffer@4.0.1: {} - dateformat@4.6.3: {} - debug@4.4.1: - dependencies: - ms: 2.1.3 - decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -2253,8 +2164,6 @@ snapshots: expand-template@2.0.3: {} - extend@3.0.2: {} - fast-copy@3.0.2: {} fast-decode-uri-component@1.0.1: {} @@ -2352,11 +2261,6 @@ snapshots: optionalDependencies: picomatch: 4.0.2 - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - file-uri-to-path@1.0.0: {} find-my-way@9.3.0: @@ -2381,10 +2285,6 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - fs-constants@1.0.0: {} fs-extra@11.3.0: @@ -2400,22 +2300,6 @@ snapshots: function-bind@1.1.2: {} - gaxios@7.1.1: - dependencies: - extend: 3.0.2 - https-proxy-agent: 7.0.6 - node-fetch: 3.3.2 - transitivePeerDependencies: - - supports-color - - gcp-metadata@7.0.1: - dependencies: - gaxios: 7.1.1 - google-logging-utils: 1.1.1 - json-bigint: 1.0.0 - transitivePeerDependencies: - - supports-color - generify@4.2.0: dependencies: isbinaryfile: 4.0.10 @@ -2453,20 +2337,6 @@ snapshots: minimatch: 5.1.6 once: 1.4.0 - google-auth-library@10.1.0: - dependencies: - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - gaxios: 7.1.1 - gcp-metadata: 7.0.1 - google-logging-utils: 1.1.1 - gtoken: 8.0.0 - jws: 4.0.0 - transitivePeerDependencies: - - supports-color - - google-logging-utils@1.1.1: {} - gopd@1.2.0: {} got@12.6.1: @@ -2487,13 +2357,6 @@ snapshots: graceful-fs@4.2.11: {} - gtoken@8.0.0: - dependencies: - gaxios: 7.1.1 - jws: 4.0.0 - transitivePeerDependencies: - - supports-color - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -2515,13 +2378,6 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - ieee754@1.2.1: {} ignore@5.3.2: {} @@ -2560,10 +2416,6 @@ snapshots: dependencies: argparse: 2.0.1 - json-bigint@1.0.0: - dependencies: - bignumber.js: 9.3.1 - json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} @@ -2584,17 +2436,6 @@ snapshots: jsonpointer@5.0.1: {} - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.0: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -2711,8 +2552,6 @@ snapshots: dependencies: obliterator: 2.0.5 - ms@2.1.3: {} - nanoid@3.3.11: {} napi-build-utils@2.0.0: {} @@ -2723,14 +2562,6 @@ snapshots: node-addon-api@8.4.0: {} - node-domexception@1.0.0: {} - - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - node-gyp-build@4.8.4: {} normalize-url@8.0.2: {} @@ -2785,6 +2616,8 @@ snapshots: dependencies: split2: 4.2.0 + pino-logstash@1.0.0: {} + pino-pretty@13.0.0: dependencies: colorette: 2.0.20 @@ -3128,8 +2961,6 @@ snapshots: dependencies: makeerror: 1.0.12 - web-streams-polyfill@3.3.3: {} - wrappy@1.0.2: {} xtend@4.0.2: {} diff --git a/src/api/auth/default.js b/src/api/auth/default.js index b8a9063..6a15651 100644 --- a/src/api/auth/default.js +++ b/src/api/auth/default.js @@ -1,6 +1,5 @@ import fastifyJWT from '@fastify/jwt'; import fastifyCookie from '@fastify/cookie'; -import client from 'prom-client'; import { register } from './register.js'; import { login } from './login.js'; @@ -14,7 +13,6 @@ import { totpVerify } from './totpVerify.js'; const saltRounds = 10; export const appName = process.env.APP_NAME || 'knl_meowscendence'; -const collectDefaultMetrics = client.collectDefaultMetrics authDB.prepareDB(); @@ -24,29 +22,6 @@ authDB.prepareDB(); */ export default async function(fastify, options) { - collectDefaultMetrics({ labels: { service: "auth-api" } }) - client.register.setDefaultLabels({ service: "auth-api" }) - - const httpRequestCounter = new client.Counter({ - name: 'http_requests_total', - help: 'Total number of HTTP requests', - labelNames: ['method', 'route', 'status_code'], - }) - - fastify.addHook('onResponse', (req, res, done) => { - httpRequestCounter.inc({ - method: req.method, - route: req.routerPath || req.url, - status_code: res.statusCode, - }) - done() - }) - fastify.get('/metrics', async (req, reply) => { - reply - .header('Content-Type', client.register.contentType) - .send(await client.register.metrics()) - }) - fastify.register(fastifyJWT, { secret: process.env.JWT_SECRET || '123456789101112131415161718192021', cookie: { diff --git a/src/api/user/default.js b/src/api/user/default.js index 34ef89d..a5efe65 100644 --- a/src/api/user/default.js +++ b/src/api/user/default.js @@ -1,10 +1,8 @@ import fastifyJWT from '@fastify/jwt'; import fastifyCookie from '@fastify/cookie'; import Database from 'better-sqlite3'; -import client from 'prom-client'; var env = process.env.NODE_ENV || 'development'; -const collectDefaultMetrics = client.collectDefaultMetrics let database; @@ -58,31 +56,6 @@ const deleteFriends = database.prepare('DELETE FROM friends WHERE username = ?;' * @param {import('fastify').FastifyPluginOptions} options */ export default async function(fastify, options) { - - collectDefaultMetrics({ labels: { service: "auth-api" } }) - client.register.setDefaultLabels({ service: "auth-api" }) - - const httpRequestCounter = new client.Counter({ - name: 'http_requests_total', - help: 'Total number of HTTP requests', - labelNames: ['method', 'route', 'status_code'], - }) - - fastify.addHook('onResponse', (req, res, done) => { - httpRequestCounter.inc({ - method: req.method, - route: req.routerPath || req.url, - status_code: res.statusCode, - }) - done() - }) - fastify.get('/metrics', async (req, reply) => { - reply - .header('Content-Type', client.register.contentType) - .send(await client.register.metrics()) - }) - - fastify.register(fastifyJWT, { secret: process.env.JWT_SECRET || '123456789101112131415161718192021', cookie: { diff --git a/src/start.js b/src/start.js index 6111c96..e76f7d9 100644 --- a/src/start.js +++ b/src/start.js @@ -1,52 +1,71 @@ import Fastify from 'fastify'; import authApi from './api/auth/default.js'; import userApi from './api/user/default.js'; +import fs from 'fs'; +import path from 'path'; -const loggerOption = { - transport: { - target: 'pino-pretty', - options: { - colorize: true, - translateTime: 'HH:MM:ss', - ignore: 'pid,hostname' - } - } +const isProduction = process.env.NODE_ENV === 'production'; +const logFilePath = process.env.LOG_FILE_PATH || './logs/api.log'; + +const loggerOption = () => { + if (!isProduction) { + return { + transport: { + target: 'pino-pretty', + options: { + colorize: true, + translateTime: 'HH:MM:ss', + ignore: 'pid,hostname', + }, + }, + }; + } else { + // Make sure the directory exists + const logDir = path.dirname(logFilePath); + fs.mkdirSync(logDir, { recursive: true }); + + const logStream = fs.createWriteStream(logFilePath, { flags: 'a' }); // append mode + return { + level: 'info', + stream: logStream, + }; + } }; -function sigHandle(signal) { - process.exit(0); -} - -process.on('SIGINT', sigHandle); - async function start() { const target = process.env.API_TARGET || 'all'; + const servers = []; + if (target === 'auth' || target === 'all') { - const auth = Fastify({ logger: loggerOption }); + const auth = Fastify({ logger: loggerOption('auth') }); auth.register(authApi); - if (target !== 'all') { - await auth.listen({ port: 3000, host: '0.0.0.0' }); - console.log('Auth API listening on http://0.0.0.0:3000'); - } - else { - await auth.listen({ port: 3001, host: '127.0.0.1'}); - console.log('Auth API listening on http://localhost:3001'); - } + const port = target === 'all' ? 3001 : 3000; + const host = target === 'all' ? '127.0.0.1' : '0.0.0.0'; + await auth.listen({ port, host }); + console.log(`Auth API listening on http://${host}:${port}`); + servers.push(auth); } if (target === 'user' || target === 'all') { - const user = Fastify({ logger: loggerOption }); + const user = Fastify({ logger: loggerOption('user') }); user.register(userApi); - if (target !== 'all') { - await user.listen({ port: 3000, host: '0.0.0.0' }); - console.log('User API listening on http://0.0.0.0:3000'); - } - else { - await user.listen({ port: 3002, host: '127.0.0.1'}); - console.log('User API listening on http://localhost:3002'); - } + const port = target === 'all' ? 3002 : 3000; + const host = target === 'all' ? '127.0.0.1' : '0.0.0.0'; + await user.listen({ port, host }); + console.log(`User API listening on http://${host}:${port}`); + servers.push(user); } + + // Graceful shutdown on SIGINT + process.on('SIGINT', async () => { + console.log('SIGINT received, closing servers...'); + await Promise.all(servers.map((srv) => srv.close())); + process.exit(0); + }); } -start().catch(console.error); +start().catch((err) => { + console.error(err); + process.exit(1); +});