refactor progress

This commit is contained in:
Hampus Kraft
2026-02-17 12:22:36 +00:00
parent cb31608523
commit d5abd1a7e4
8257 changed files with 1190207 additions and 761040 deletions

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {type SpanOptions, withSpan} from '@fluxer/api/src/telemetry/Tracing';
import {recordCounter} from '@fluxer/telemetry/src/Metrics';
export async function withBusinessSpan<T>(
spanName: string,
metricName: string,
attributes: Record<string, string> = {},
fn: () => Promise<T>,
): Promise<T> {
const spanOptions: SpanOptions = {
name: spanName,
attributes,
};
try {
const result = await withSpan(spanOptions, fn);
recordCounter({
name: metricName,
dimensions: {...attributes, status: 'success'},
value: 1,
});
return result;
} catch (error) {
recordCounter({
name: metricName,
dimensions: {
...attributes,
status: 'error',
error_type: error instanceof Error ? error.name : 'unknown',
},
value: 1,
});
throw error;
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import type {CsamResourceType} from '@fluxer/api/src/csam/CsamTypes';
import {recordCounter, recordHistogram} from '@fluxer/api/src/Telemetry';
export type CsamScanStatus = 'success' | 'error' | 'skipped' | 'disabled';
export type CsamApiStatus = 'success' | 'error' | 'timeout';
export type NcmecSubmissionStatus = 'success' | 'error' | 'disabled';
export type EvidenceStorageStatus = 'success' | 'error';
export function recordCsamScan(params: {
resourceType: CsamResourceType;
mediaType: string;
status: CsamScanStatus;
}): void {
recordCounter({
name: 'fluxer.csam.scans.total',
dimensions: {
resource_type: params.resourceType,
media_type: params.mediaType,
status: params.status,
},
});
}
export function recordCsamScanDuration(params: {resourceType: CsamResourceType; durationMs: number}): void {
recordHistogram({
name: 'fluxer.csam.scan.duration_ms',
valueMs: params.durationMs,
dimensions: {
resource_type: params.resourceType,
},
});
}
export function recordCsamMatch(params: {resourceType: CsamResourceType; source: string; matchCount: number}): void {
recordCounter({
name: 'fluxer.csam.matches.total',
value: params.matchCount,
dimensions: {
resource_type: params.resourceType,
source: params.source,
},
});
}
export function recordPhotoDnaApiCall(params: {
operation: 'hash' | 'match';
status: CsamApiStatus;
hashCount?: number;
}): void {
recordCounter({
name: 'fluxer.csam.photodna.api.total',
dimensions: {
operation: params.operation,
status: params.status,
hash_count: String(params.hashCount ?? 0),
},
});
}
export function recordPhotoDnaApiDuration(params: {operation: 'hash' | 'match'; durationMs: number}): void {
recordHistogram({
name: 'fluxer.csam.photodna.api.duration_ms',
valueMs: params.durationMs,
dimensions: {
operation: params.operation,
},
});
}
export type NcmecSubmissionOperation = 'report' | 'evidence' | 'fileinfo' | 'finish' | 'retract';
export function recordNcmecSubmission(params: {
operation: NcmecSubmissionOperation;
status: NcmecSubmissionStatus;
}): void {
recordCounter({
name: 'fluxer.csam.ncmec.submissions',
dimensions: {
operation: params.operation,
status: params.status,
},
});
}
export function recordCsamEvidenceStorage(params: {
status: EvidenceStorageStatus;
evidenceType: 'package' | 'attachment' | 'frame';
}): void {
recordCounter({
name: 'fluxer.csam.evidence.stored',
dimensions: {
status: params.status,
evidence_type: params.evidenceType,
},
});
}
export function recordCsamQueueProcessed(params: {status: 'success' | 'error' | 'timeout'; batchSize: number}): void {
recordCounter({
name: 'fluxer.csam.queue.processed',
dimensions: {
status: params.status,
batch_size: String(params.batchSize),
},
});
}
export function recordCsamQueueWaitTime(params: {waitTimeMs: number}): void {
recordHistogram({
name: 'fluxer.csam.queue.wait_time_ms',
valueMs: params.waitTimeMs,
});
}
export function recordCsamQueueDepth(params: {depth: number}): void {
recordCounter({
name: 'fluxer.csam.queue.depth',
value: params.depth,
});
}
export type ArachnidApiStatus = 'success' | 'error' | 'timeout';
export function recordArachnidApiCall(params: {status: ArachnidApiStatus}): void {
recordCounter({
name: 'fluxer.csam.arachnid.api.total',
dimensions: {
status: params.status,
},
});
}
export function recordArachnidApiDuration(params: {durationMs: number}): void {
recordHistogram({
name: 'fluxer.csam.arachnid.api.duration_ms',
valueMs: params.durationMs,
});
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {recordCounter, recordHistogram} from '@fluxer/api/src/Telemetry';
export type MessageDeleteType = 'single' | 'bulk' | 'moderation';
export type AttachmentOperation = 'upload' | 'process' | 'delete';
export type AttachmentStatus = 'success' | 'error';
export function recordMessageSent(params: {
channelType: string;
hasAttachments: string;
hasEmbeds: string;
messageType: string;
}): void {
recordCounter({
name: 'fluxer.messages.sent.total',
dimensions: {
channel_type: params.channelType,
has_attachments: params.hasAttachments,
has_embeds: params.hasEmbeds,
message_type: params.messageType,
},
});
}
export function recordMessageSendDuration(params: {channelType: string; durationMs: number}): void {
recordHistogram({
name: 'fluxer.messages.send.duration_ms',
valueMs: params.durationMs,
dimensions: {
channel_type: params.channelType,
},
});
}
export function recordMessageEdited(params: {channelType: number}): void {
recordCounter({
name: 'fluxer.messages.edited.total',
dimensions: {
channel_type: String(params.channelType),
},
});
}
export function recordMessageDeleted(params: {channelType: number; deleteType: MessageDeleteType}): void {
recordCounter({
name: 'fluxer.messages.deleted.total',
dimensions: {
channel_type: String(params.channelType),
delete_type: params.deleteType,
},
});
}
export function recordMessageRetrieved(params: {channelType: number; count: number}): void {
recordCounter({
name: 'fluxer.messages.retrieved.total',
dimensions: {
channel_type: String(params.channelType),
count: String(params.count),
},
});
}
export function recordMessageRetrievalDuration(params: {channelType: number; durationMs: number}): void {
recordHistogram({
name: 'fluxer.messages.retrieval.duration_ms',
valueMs: params.durationMs,
dimensions: {
channel_type: String(params.channelType),
},
});
}
export function recordAttachmentOperation(params: {
operation: AttachmentOperation;
contentType: string;
status: AttachmentStatus;
}): void {
recordCounter({
name: 'fluxer.messages.attachments.total',
dimensions: {
operation: params.operation,
content_type: params.contentType,
status: params.status,
},
});
}
export function recordAttachmentUploadDuration(params: {contentType: string; durationMs: number}): void {
recordHistogram({
name: 'fluxer.messages.attachment.upload.duration_ms',
valueMs: params.durationMs,
dimensions: {
content_type: params.contentType,
},
});
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {getActiveSpan, getTracer} from '@fluxer/telemetry/src/Tracing';
import type {Attributes, SpanKind} from '@opentelemetry/api';
import {SpanStatusCode} from '@opentelemetry/api';
const tracer = getTracer('fluxer-api');
export interface SpanOptions {
name: string;
attributes?: Attributes;
kind?: SpanKind;
}
export async function withSpan<T>(options: SpanOptions, fn: () => Promise<T>): Promise<T> {
return await tracer.startActiveSpan(
options.name,
{
attributes: options.attributes ?? {},
kind: options.kind,
},
async (span) => {
try {
const result = await fn();
span.setStatus({code: SpanStatusCode.OK});
return result;
} catch (error) {
const attributes: Attributes = {
error: true,
'error.message': error instanceof Error ? error.message : String(error),
};
if (error instanceof Error) {
attributes['error.type'] = error.name;
if (error.stack) {
attributes['error.stack'] = error.stack;
}
}
span.setAttributes(attributes);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : String(error),
});
throw error;
} finally {
span.end();
}
},
);
}
export function setSpanAttributes(attributes: Attributes): void {
const span = getActiveSpan();
if (span) {
span.setAttributes(attributes);
}
}
export function addSpanEvent(name: string, attributes?: Attributes): void {
const span = getActiveSpan();
if (span) {
span.addEvent(name, attributes);
}
}