Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ MONGO_URI=mongodb://localhost:27017/your-database-name
# Auth Plugin
AUTH_DISCOVERY_URL=https://login.microsoftonline.com/c917f3e2-9322-4926-9bb3-daca730413ca/v2.0/.well-known/openid-configuration
AUTH_CLIENT_ID=b4bc4b9a-7162-44c5-bb50-fe935dce1f5a

# Loki Host
# LOKI_HOST=http://localhost:3100

# Prometheus Key
PROMETHEUS_KEY=prometheus
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@
"@sinclair/typebox": "^0.34.41",
"fastify": "^5.6.2",
"fastify-cli": "7.4.1",
"fastify-metrics": "^12.1.0",
"fastify-plugin": "^5.1.0",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.2.0",
"openid-client": "^6.8.1"
"openid-client": "^6.8.1",
"pino-loki": "^3.0.0"
},
"devDependencies": {
"@commitlint/cli": "^20.1.0",
Expand Down
46 changes: 46 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
RawReplyDefaultExpression,
RawRequestDefaultExpression,
RawServerDefault,
FastifyReply,
FastifyRequest,
RouteOptions,
} from "fastify";
import fastifyMetrics from "fastify-metrics";
import * as path from "path";
import { fileURLToPath } from "url";

Expand All @@ -21,6 +25,8 @@ export type AppOptions = {
// Place your custom options for app below here.
// MongoDB URI (Optional)
// mongoUri: string;
lokiHost?: string;
prometheusKey?: string;
} & FastifyServerOptions &
Partial<AutoloadPluginOptions> &
AuthPluginOptions;
Expand Down Expand Up @@ -49,6 +55,8 @@ const options: AppOptions = {
// mongoUri: getOption("MONGO_URI")!,
authDiscoveryURL: getOption("AUTH_DISCOVERY_URL")!,
authClientID: getOption("AUTH_CLIENT_ID")!,
lokiHost: getOption("LOKI_HOST", false),
prometheusKey: getOption("PROMETHEUS_KEY", false),
authSkip: (() => {
const opt = getOption("AUTH_SKIP", false);
if (opt !== undefined) {
Expand All @@ -59,6 +67,21 @@ const options: AppOptions = {
})(),
};

if (options.lokiHost) {
options.logger = {
level: "info",
transport: {
target: "pino-loki",
options: {
batching: true,
interval: 5, // Logs are sent every 5 seconds, default.
host: options.lokiHost,
labels: { application: packageJson.name },
},
},
};
}

// Support Typebox
export type FastifyTypebox = FastifyInstance<
RawServerDefault,
Expand All @@ -83,6 +106,29 @@ const app: FastifyPluginAsync<AppOptions> = async (
origin: "*",
});

// Register Metrics
const metricsEndpoint: RouteOptions | string | null = opts.prometheusKey
? {
url: "/metrics",
method: "GET",
handler: async () => {}, // Overridden by fastify-metrics
onRequest: async (request: FastifyRequest, reply: FastifyReply) => {
if (
request.headers.authorization !== `Bearer ${opts.prometheusKey}`
) {
reply.code(401).send("Unauthorized");
return reply;
}
},
}
: "/metrics";

await fastify.register(fastifyMetrics.default, {
endpoint: metricsEndpoint,
defaultMetrics: { enabled: true },
clearRegisterOnInit: true,
});

// Register Swagger & Swagger UI & Scalar
await fastify.register(import("@fastify/swagger"), {
openapi: {
Expand Down
6 changes: 4 additions & 2 deletions test/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ async function config(): Promise<AppOptions> {
}

// Automatically build and tear down our instance
async function build(t: TestContext) {
async function build(t: TestContext, options?: Partial<AppOptions>) {
// you can set all the options supported by the fastify CLI command
const argv = [AppPath];

const appOptions = { ...(await config()), ...options };

// fastify-plugin ensures that all decorators
// are exposed for testing purposes, this is
// different from the production setup
const app = await helper.build(argv, await config(), await config());
const app = await helper.build(argv, appOptions, appOptions);

// Tear down our app after we are done
t.after(() => void app.close());
Expand Down
41 changes: 41 additions & 0 deletions test/routes/metrics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { build } from "../helper.js";
import * as assert from "node:assert";
import { test } from "node:test";

test("metrics route without key", async (t) => {
const app = await build(t);

const response = await app.inject({
url: "/metrics",
});

assert.equal(response.statusCode, 200);
});

test("metrics route with key", async (t) => {
const app = await build(t, { prometheusKey: "secret" });

// Without auth header
const response = await app.inject({
url: "/metrics",
});
assert.equal(response.statusCode, 401);

// With correct auth header
const responseAuth = await app.inject({
url: "/metrics",
headers: {
authorization: "Bearer secret",
},
});
assert.equal(responseAuth.statusCode, 200);

// With incorrect auth header
const responseBadAuth = await app.inject({
url: "/metrics",
headers: {
authorization: "Bearer wrong",
},
});
assert.equal(responseBadAuth.statusCode, 401);
});
78 changes: 78 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,13 @@ __metadata:
languageName: node
linkType: hard

"@opentelemetry/api@npm:^1.4.0":
version: 1.9.0
resolution: "@opentelemetry/api@npm:1.9.0"
checksum: 10c0/9aae2fe6e8a3a3eeb6c1fdef78e1939cf05a0f37f8a4fae4d6bf2e09eb1e06f966ece85805626e01ba5fab48072b94f19b835449e58b6d26720ee19a58298add
languageName: node
linkType: hard

"@pinojs/redact@npm:^0.4.0":
version: 0.4.0
resolution: "@pinojs/redact@npm:0.4.0"
Expand Down Expand Up @@ -1342,6 +1349,13 @@ __metadata:
languageName: node
linkType: hard

"bintrees@npm:1.0.2":
version: 1.0.2
resolution: "bintrees@npm:1.0.2"
checksum: 10c0/132944b20c93c1a8f97bf8aa25980a76c6eb4291b7f2df2dbcd01cb5b417c287d3ee0847c7260c9f05f3d5a4233aaa03dec95114e97f308abe9cc3f72bed4a44
languageName: node
linkType: hard

"brace-expansion@npm:^1.1.7":
version: 1.1.12
resolution: "brace-expansion@npm:1.1.12"
Expand Down Expand Up @@ -2080,6 +2094,18 @@ __metadata:
languageName: node
linkType: hard

"fastify-metrics@npm:^12.1.0":
version: 12.1.0
resolution: "fastify-metrics@npm:12.1.0"
dependencies:
fastify-plugin: "npm:^5.0.0"
prom-client: "npm:^15.1.3"
peerDependencies:
fastify: ">=5"
checksum: 10c0/b42940b8c7cbfcd86182b9a85b53dc903727c73523cf14ff465fca7a64ffa966dcf14e9af944efdecfb1be027f2cb356dbaf5126f25c789eed6cd5b6d4767e24
languageName: node
linkType: hard

"fastify-plugin@npm:^4.5.1":
version: 4.5.1
resolution: "fastify-plugin@npm:4.5.1"
Expand Down Expand Up @@ -3611,6 +3637,27 @@ __metadata:
languageName: node
linkType: hard

"pino-abstract-transport@npm:^3.0.0":
version: 3.0.0
resolution: "pino-abstract-transport@npm:3.0.0"
dependencies:
split2: "npm:^4.0.0"
checksum: 10c0/4486e1b9508110aaf963d07741ac98d660b974dd51d8ad42077d215118e27cda20c64da46c07c926898d52540aab7c6b9c37dc0f5355c203bb1d6a72b5bd8d6c
languageName: node
linkType: hard

"pino-loki@npm:^3.0.0":
version: 3.0.0
resolution: "pino-loki@npm:3.0.0"
dependencies:
pino-abstract-transport: "npm:^3.0.0"
pump: "npm:^3.0.3"
bin:
pino-loki: dist/cli.mjs
checksum: 10c0/d7d83b8989366ff73d461f0c39adf1c7000c54a658f282cdb0e035e0fea88ac63933bd0f84dd13fbfcc210581f54b97389ff104f501559ea635c5316a1a6b398
languageName: node
linkType: hard

"pino-pretty@npm:^13.0.0":
version: 13.0.0
resolution: "pino-pretty@npm:13.0.0"
Expand Down Expand Up @@ -3810,6 +3857,16 @@ __metadata:
languageName: node
linkType: hard

"prom-client@npm:^15.1.3":
version: 15.1.3
resolution: "prom-client@npm:15.1.3"
dependencies:
"@opentelemetry/api": "npm:^1.4.0"
tdigest: "npm:^0.1.1"
checksum: 10c0/816525572e5799a2d1d45af78512fb47d073c842dc899c446e94d17cfc343d04282a1627c488c7ca1bcd47f766446d3e49365ab7249f6d9c22c7664a5bce7021
languageName: node
linkType: hard

"pump@npm:^3.0.0":
version: 3.0.0
resolution: "pump@npm:3.0.0"
Expand All @@ -3820,6 +3877,16 @@ __metadata:
languageName: node
linkType: hard

"pump@npm:^3.0.3":
version: 3.0.3
resolution: "pump@npm:3.0.3"
dependencies:
end-of-stream: "npm:^1.1.0"
once: "npm:^1.3.1"
checksum: 10c0/ada5cdf1d813065bbc99aa2c393b8f6beee73b5de2890a8754c9f488d7323ffd2ca5f5a0943b48934e3fcbd97637d0337369c3c631aeb9614915db629f1c75c9
languageName: node
linkType: hard

"punycode@npm:^2.1.0, punycode@npm:^2.3.0":
version: 2.3.1
resolution: "punycode@npm:2.3.1"
Expand Down Expand Up @@ -4160,6 +4227,15 @@ __metadata:
languageName: node
linkType: hard

"tdigest@npm:^0.1.1":
version: 0.1.2
resolution: "tdigest@npm:0.1.2"
dependencies:
bintrees: "npm:1.0.2"
checksum: 10c0/10187b8144b112fcdfd3a5e4e9068efa42c990b1e30cd0d4f35ee8f58f16d1b41bc587e668fa7a6f6ca31308961cbd06cd5d4a4ae1dc388335902ae04f7d57df
languageName: node
linkType: hard

"template-api@workspace:.":
version: 0.0.0-use.local
resolution: "template-api@workspace:."
Expand All @@ -4185,13 +4261,15 @@ __metadata:
eslint-plugin-prettier: "npm:^5.5.4"
fastify: "npm:^5.6.2"
fastify-cli: "npm:7.4.1"
fastify-metrics: "npm:^12.1.0"
fastify-plugin: "npm:^5.1.0"
fastify-tsconfig: "npm:^3.0.0"
globals: "npm:^16.5.0"
husky: "npm:^9.1.7"
jsonwebtoken: "npm:^9.0.2"
jwks-rsa: "npm:^3.2.0"
openid-client: "npm:^6.8.1"
pino-loki: "npm:^3.0.0"
prettier: "npm:3.7.4"
prettier-plugin-jsdoc: "npm:^1.7.0"
typescript: "npm:^5.9.3"
Expand Down
Loading