mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
Compare commits
5 commits
03940cd8fd
...
aff51b651c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aff51b651c | ||
|
|
e1bd389bf1 | ||
|
|
2310e8b33d | ||
|
|
129bc97b09 | ||
|
|
1a666e8371 |
|
|
@ -1,9 +0,0 @@
|
|||
# Bun doesn't run well on Musl but this seems to work
|
||||
FROM oven/bun:1.2.15-alpine as base
|
||||
|
||||
# Switch to Bash by editing /etc/passwd
|
||||
RUN apk add --no-cache libstdc++ git bash curl openssh cloc && \
|
||||
sed -i -e 's|/bin/ash|/bin/bash|g' /etc/passwd
|
||||
|
||||
# Extract Node from its docker image (node:22-alpine)
|
||||
COPY --from=node:22-alpine /usr/local/bin/node /usr/local/bin/node
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "versia Dev Container",
|
||||
"dockerFile": "Dockerfile",
|
||||
"runArgs": [
|
||||
"-v",
|
||||
"${localWorkspaceFolder}/config:/workspace/config",
|
||||
"-v",
|
||||
"${localWorkspaceFolder}/logs:/workspace/logs",
|
||||
"-v",
|
||||
"${localWorkspaceFolder}/uploads:/workspace/uploads",
|
||||
"--network=host"
|
||||
],
|
||||
"mounts": [
|
||||
"source=node_modules,target=/workspace/node_modules,type=bind,consistency=cached",
|
||||
"type=bind,source=/home/${localEnv:USER}/.ssh,target=/root/.ssh,readonly"
|
||||
],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash"
|
||||
},
|
||||
"extensions": [
|
||||
"biomejs.biome",
|
||||
"ms-vscode-remote.remote-containers",
|
||||
"oven.bun-vscode",
|
||||
"vivaxy.vscode-conventional-commits",
|
||||
"EditorConfig.EditorConfig",
|
||||
"tamasfe.even-better-toml",
|
||||
"YoavBls.pretty-ts-errors",
|
||||
"eamodio.gitlens"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
25
.github/config.workflow.toml
vendored
25
.github/config.workflow.toml
vendored
|
|
@ -429,31 +429,28 @@ text = "No spam"
|
|||
|
||||
[logging]
|
||||
|
||||
# Available levels: debug, info, warning, error, fatal
|
||||
log_level = "debug"
|
||||
|
||||
log_file_path = "logs/versia.log"
|
||||
|
||||
[logging.types]
|
||||
# Either pass a boolean
|
||||
# requests = true
|
||||
# Or a table with the following keys:
|
||||
# requests_content = { level = "debug", log_file_path = "logs/requests.log" }
|
||||
# Available types are: requests, responses, requests_content, filters
|
||||
# Available levels: trace, debug, info, warning, error, fatal
|
||||
log_level = "info" # For console output
|
||||
|
||||
# [logging.file]
|
||||
# path = "logs/versia.log"
|
||||
# log_level = "info"
|
||||
#
|
||||
# [logging.file.rotation]
|
||||
# max_size = 10_000_000 # 10 MB
|
||||
# max_files = 10 # Keep 10 rotated files
|
||||
#
|
||||
# https://sentry.io support
|
||||
# Uncomment to enable
|
||||
# [logging.sentry]
|
||||
# Sentry DSN for error logging
|
||||
# dsn = "https://example.com"
|
||||
# debug = false
|
||||
|
||||
# sample_rate = 1.0
|
||||
# traces_sample_rate = 1.0
|
||||
# Can also be regex
|
||||
# trace_propagation_targets = []
|
||||
# max_breadcrumbs = 100
|
||||
# environment = "production"
|
||||
# log_level = "info"
|
||||
|
||||
[plugins]
|
||||
# Whether to automatically load all plugins in the plugins directory
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
### Backend
|
||||
|
||||
- [x] 🚀 Upgraded Bun to `1.2.15`
|
||||
- [x] 🚀 Upgraded Bun to `1.2.17`
|
||||
|
||||
# `0.8.0` • Federation 2: Electric Boogaloo
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ RUN bun run build
|
|||
WORKDIR /temp/dist
|
||||
|
||||
# Copy production dependencies and source code into final image
|
||||
FROM oven/bun:1.2.15-alpine
|
||||
FROM oven/bun:1.2.17-alpine
|
||||
|
||||
# Install libstdc++ for Bun and create app directory
|
||||
RUN apk add --no-cache libstdc++ && \
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ RUN bun run build:worker
|
|||
WORKDIR /temp/dist
|
||||
|
||||
# Copy production dependencies and source code into final image
|
||||
FROM oven/bun:1.2.15-alpine
|
||||
FROM oven/bun:1.2.17-alpine
|
||||
|
||||
# Install libstdc++ for Bun and create app directory
|
||||
RUN apk add --no-cache libstdc++ && \
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ import {
|
|||
} from "@versia-server/tests";
|
||||
import { bench, run } from "mitata";
|
||||
import type { z } from "zod";
|
||||
import { configureLoggers } from "@/loggers";
|
||||
|
||||
await configureLoggers(true);
|
||||
|
||||
const { users, tokens, deleteUsers } = await getTestUsers(5);
|
||||
await getTestStatuses(40, users[0]);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.0-beta.5/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.4/schema.json",
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": {
|
||||
|
|
@ -49,7 +49,6 @@
|
|||
}
|
||||
},
|
||||
"useLiteralEnumMembers": "error",
|
||||
"noCommaOperator": "error",
|
||||
"noNegationElse": "error",
|
||||
"noYodaExpression": "error",
|
||||
"useBlockStatements": "error",
|
||||
|
|
@ -89,7 +88,6 @@
|
|||
"accessibility": "explicit"
|
||||
}
|
||||
},
|
||||
"noArguments": "error",
|
||||
"useImportType": "error",
|
||||
"useExportType": "error",
|
||||
"noUselessElse": "error",
|
||||
|
|
@ -143,7 +141,9 @@
|
|||
"noUselessEscapeInRegex": "warn",
|
||||
"useSimplifiedLogicExpression": "error",
|
||||
"useWhile": "error",
|
||||
"useNumericLiterals": "error"
|
||||
"useNumericLiterals": "error",
|
||||
"noArguments": "error",
|
||||
"noCommaOperator": "error"
|
||||
},
|
||||
"suspicious": {
|
||||
"noDuplicateTestHooks": "error",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getLogger, type Logger } from "@logtape/logtape";
|
||||
import { EntitySorter, type JSONObject } from "@versia/sdk";
|
||||
import { verify } from "@versia/sdk/crypto";
|
||||
import * as VersiaEntities from "@versia/sdk/entities";
|
||||
|
|
@ -13,13 +12,13 @@ import {
|
|||
User,
|
||||
} from "@versia-server/kit/db";
|
||||
import { Likes, Notes } from "@versia-server/kit/tables";
|
||||
import { federationInboxLogger } from "@versia-server/logging";
|
||||
import type { SocketAddress } from "bun";
|
||||
import { Glob } from "bun";
|
||||
import chalk from "chalk";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { matches } from "ip-matching";
|
||||
import { isValidationError } from "zod-validation-error";
|
||||
import { sentry } from "@/sentry";
|
||||
|
||||
/**
|
||||
* Checks if the hostname is defederated using glob matching.
|
||||
|
|
@ -65,7 +64,6 @@ export class InboxProcessor {
|
|||
key: CryptoKey;
|
||||
} | null,
|
||||
private authorizationHeader?: string,
|
||||
private logger: Logger = getLogger(["federation", "inbox"]),
|
||||
private requestIp: SocketAddress | null = null,
|
||||
) {}
|
||||
|
||||
|
|
@ -156,7 +154,7 @@ export class InboxProcessor {
|
|||
*/
|
||||
public async process(): Promise<void> {
|
||||
!this.sender &&
|
||||
this.logger.debug`Processing request from potential bridge`;
|
||||
federationInboxLogger.debug`Processing request from potential bridge`;
|
||||
|
||||
if (this.sender && isDefederated(this.sender.instance.data.baseUrl)) {
|
||||
// Return 201 to avoid
|
||||
|
|
@ -165,15 +163,15 @@ export class InboxProcessor {
|
|||
return;
|
||||
}
|
||||
|
||||
this.logger.debug`Instance ${chalk.gray(
|
||||
federationInboxLogger.debug`Instance ${chalk.gray(
|
||||
this.sender?.instance.data.baseUrl,
|
||||
)} is not defederated`;
|
||||
|
||||
const shouldCheckSignature = this.shouldCheckSignature();
|
||||
|
||||
shouldCheckSignature
|
||||
? this.logger.debug`Checking signature`
|
||||
: this.logger.debug`Skipping signature check`;
|
||||
? federationInboxLogger.debug`Checking signature`
|
||||
: federationInboxLogger.debug`Skipping signature check`;
|
||||
|
||||
if (shouldCheckSignature) {
|
||||
const isValid = await this.isSignatureValid();
|
||||
|
|
@ -183,7 +181,7 @@ export class InboxProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
shouldCheckSignature && this.logger.debug`Signature is valid`;
|
||||
shouldCheckSignature && federationInboxLogger.debug`Signature is valid`;
|
||||
|
||||
try {
|
||||
await new EntitySorter(this.body)
|
||||
|
|
@ -596,8 +594,7 @@ export class InboxProcessor {
|
|||
throw new ApiError(400, "Failed to process request", e.message);
|
||||
}
|
||||
|
||||
this.logger.error`${e}`;
|
||||
sentry?.captureException(e);
|
||||
federationInboxLogger.error`${e}`;
|
||||
|
||||
throw new ApiError(500, "Failed to process request", e.message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { readdir } from "node:fs/promises";
|
||||
import { getLogger, type Logger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { type Manifest, manifestSchema, Plugin } from "@versia-server/kit";
|
||||
import { pluginLogger, serverLogger } from "@versia-server/logging";
|
||||
import { file, sleep } from "bun";
|
||||
import chalk from "chalk";
|
||||
import { parseJSON5, parseJSONC } from "confbox";
|
||||
|
|
@ -14,8 +14,6 @@ import type { HonoEnv } from "~/types/api";
|
|||
* Class to manage plugins.
|
||||
*/
|
||||
export class PluginLoader {
|
||||
private logger = getLogger("plugin");
|
||||
|
||||
/**
|
||||
* Get all directories in a given directory.
|
||||
* @param {string} dir - The directory to search.
|
||||
|
|
@ -74,8 +72,7 @@ export class PluginLoader {
|
|||
|
||||
throw new Error(`Unsupported manifest file type: ${manifestFile}`);
|
||||
} catch (e) {
|
||||
this.logger
|
||||
.fatal`Could not parse plugin manifest ${chalk.blue(manifestPath)} as ${manifestFile.split(".").pop()?.toUpperCase()}.`;
|
||||
pluginLogger.fatal`Could not parse plugin manifest ${chalk.blue(manifestPath)} as ${manifestFile.split(".").pop()?.toUpperCase()}.`;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
|
@ -129,8 +126,7 @@ export class PluginLoader {
|
|||
const result = await manifestSchema.safeParseAsync(manifest);
|
||||
|
||||
if (!result.success) {
|
||||
this.logger
|
||||
.fatal`Plugin manifest ${chalk.blue(manifestPath)} is invalid.`;
|
||||
pluginLogger.fatal`Plugin manifest ${chalk.blue(manifestPath)} is invalid.`;
|
||||
throw fromZodError(result.error);
|
||||
}
|
||||
|
||||
|
|
@ -154,8 +150,7 @@ export class PluginLoader {
|
|||
return plugin;
|
||||
}
|
||||
|
||||
this.logger
|
||||
.fatal`Default export of entrypoint ${chalk.blue(entrypoint)} at ${chalk.blue(dir)} is not a Plugin.`;
|
||||
pluginLogger.fatal`Default export of entrypoint ${chalk.blue(entrypoint)} at ${chalk.blue(dir)} is not a Plugin.`;
|
||||
|
||||
throw new Error("Entrypoint is not a Plugin");
|
||||
}
|
||||
|
|
@ -177,8 +172,7 @@ export class PluginLoader {
|
|||
const disabledOn = (disabled?.length ?? 0) > 0;
|
||||
|
||||
if (enabledOn && disabledOn) {
|
||||
this.logger
|
||||
.fatal`Both enabled and disabled lists are specified. Only one of them can be used.`;
|
||||
pluginLogger.fatal`Both enabled and disabled lists are specified. Only one of them can be used.`;
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
|
||||
|
|
@ -219,10 +213,9 @@ export class PluginLoader {
|
|||
plugin: Plugin<ZodTypeAny>;
|
||||
}[],
|
||||
app: Hono<HonoEnv>,
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
for (const data of plugins) {
|
||||
logger.info`Loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} ${chalk.gray(`[${plugins.indexOf(data) + 1}/${plugins.length}]`)}`;
|
||||
serverLogger.info`Loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} ${chalk.gray(`[${plugins.indexOf(data) + 1}/${plugins.length}]`)}`;
|
||||
|
||||
const time1 = performance.now();
|
||||
|
||||
|
|
@ -232,13 +225,13 @@ export class PluginLoader {
|
|||
config.plugins?.config?.[data.manifest.name],
|
||||
);
|
||||
} catch (e) {
|
||||
logger.fatal`Error encountered while loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} configuration.`;
|
||||
logger.fatal`This is due to invalid, missing or incomplete configuration.`;
|
||||
logger.fatal`Put your configuration at ${chalk.blueBright(
|
||||
serverLogger.fatal`Error encountered while loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} configuration.`;
|
||||
serverLogger.fatal`This is due to invalid, missing or incomplete configuration.`;
|
||||
serverLogger.fatal`Put your configuration at ${chalk.blueBright(
|
||||
"plugins.config.<plugin-name>",
|
||||
)}`;
|
||||
logger.fatal`Here is the error message, please fix the configuration file accordingly:`;
|
||||
logger.fatal`${(e as ValidationError).message}`;
|
||||
serverLogger.fatal`Here is the error message, please fix the configuration file accordingly:`;
|
||||
serverLogger.fatal`${(e as ValidationError).message}`;
|
||||
|
||||
await sleep(Number.POSITIVE_INFINITY);
|
||||
}
|
||||
|
|
@ -250,7 +243,7 @@ export class PluginLoader {
|
|||
|
||||
const time3 = performance.now();
|
||||
|
||||
logger.info`Plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(
|
||||
serverLogger.info`Plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(
|
||||
data.manifest.version,
|
||||
)} loaded in ${chalk.gray(
|
||||
`${(time2 - time1).toFixed(2)}ms`,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import type { JSONObject } from "@versia/sdk";
|
||||
import { config } from "@versia-server/config";
|
||||
import { ApiError } from "@versia-server/kit";
|
||||
|
|
@ -64,7 +63,6 @@ export const getInboxWorker = (): Worker<InboxJobData, void, InboxJobType> =>
|
|||
data,
|
||||
null,
|
||||
headers.authorization,
|
||||
getLogger(["federation", "inbox"]),
|
||||
ip,
|
||||
);
|
||||
|
||||
|
|
@ -160,7 +158,6 @@ export const getInboxWorker = (): Worker<InboxJobData, void, InboxJobType> =>
|
|||
key,
|
||||
},
|
||||
undefined,
|
||||
getLogger(["federation", "inbox"]),
|
||||
ip,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
* @description Sonic search integration for indexing and searching accounts and statuses
|
||||
*/
|
||||
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { db, Note, User } from "@versia-server/kit/db";
|
||||
import { sonicLogger } from "@versia-server/logging";
|
||||
import type { SQL, ValueOrArray } from "drizzle-orm";
|
||||
import {
|
||||
Ingest as SonicChannelIngest,
|
||||
|
|
@ -27,7 +27,6 @@ export class SonicSearchManager {
|
|||
private searchChannel: SonicChannelSearch;
|
||||
private ingestChannel: SonicChannelIngest;
|
||||
private connected = false;
|
||||
private logger = getLogger("sonic");
|
||||
|
||||
/**
|
||||
* @param config Configuration for Sonic
|
||||
|
|
@ -55,7 +54,7 @@ export class SonicSearchManager {
|
|||
*/
|
||||
public async connect(silent = false): Promise<void> {
|
||||
if (!config.search.enabled) {
|
||||
!silent && this.logger.info`Sonic search is disabled`;
|
||||
!silent && sonicLogger.info`Sonic search is disabled`;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -63,28 +62,24 @@ export class SonicSearchManager {
|
|||
return;
|
||||
}
|
||||
|
||||
!silent && this.logger.info`Connecting to Sonic...`;
|
||||
!silent && sonicLogger.info`Connecting to Sonic...`;
|
||||
|
||||
// Connect to Sonic
|
||||
await new Promise<boolean>((resolve, reject) => {
|
||||
this.searchChannel.connect({
|
||||
connected: (): void => {
|
||||
!silent &&
|
||||
this.logger.info`Connected to Sonic Search Channel`;
|
||||
sonicLogger.info`Connected to Sonic Search Channel`;
|
||||
resolve(true);
|
||||
},
|
||||
disconnected: (): void =>
|
||||
this.logger
|
||||
.error`Disconnected from Sonic Search Channel. You might be using an incorrect password.`,
|
||||
sonicLogger.error`Disconnected from Sonic Search Channel. You might be using an incorrect password.`,
|
||||
timeout: (): void =>
|
||||
this.logger
|
||||
.error`Sonic Search Channel connection timed out`,
|
||||
sonicLogger.error`Sonic Search Channel connection timed out`,
|
||||
retrying: (): void =>
|
||||
this.logger
|
||||
.warn`Retrying connection to Sonic Search Channel`,
|
||||
sonicLogger.warn`Retrying connection to Sonic Search Channel`,
|
||||
error: (error): void => {
|
||||
this.logger
|
||||
.error`Failed to connect to Sonic Search Channel: ${error}`;
|
||||
sonicLogger.error`Failed to connect to Sonic Search Channel: ${error}`;
|
||||
reject(error);
|
||||
},
|
||||
});
|
||||
|
|
@ -94,20 +89,17 @@ export class SonicSearchManager {
|
|||
this.ingestChannel.connect({
|
||||
connected: (): void => {
|
||||
!silent &&
|
||||
this.logger.info`Connected to Sonic Ingest Channel`;
|
||||
sonicLogger.info`Connected to Sonic Ingest Channel`;
|
||||
resolve(true);
|
||||
},
|
||||
disconnected: (): void =>
|
||||
this.logger.error`Disconnected from Sonic Ingest Channel`,
|
||||
sonicLogger.error`Disconnected from Sonic Ingest Channel`,
|
||||
timeout: (): void =>
|
||||
this.logger
|
||||
.error`Sonic Ingest Channel connection timed out`,
|
||||
sonicLogger.error`Sonic Ingest Channel connection timed out`,
|
||||
retrying: (): void =>
|
||||
this.logger
|
||||
.warn`Retrying connection to Sonic Ingest Channel`,
|
||||
sonicLogger.warn`Retrying connection to Sonic Ingest Channel`,
|
||||
error: (error): void => {
|
||||
this.logger
|
||||
.error`Failed to connect to Sonic Ingest Channel: ${error}`;
|
||||
sonicLogger.error`Failed to connect to Sonic Ingest Channel: ${error}`;
|
||||
reject(error);
|
||||
},
|
||||
});
|
||||
|
|
@ -119,9 +111,9 @@ export class SonicSearchManager {
|
|||
this.ingestChannel.ping(),
|
||||
]);
|
||||
this.connected = true;
|
||||
!silent && this.logger.info`Connected to Sonic`;
|
||||
!silent && sonicLogger.info`Connected to Sonic`;
|
||||
} catch (error) {
|
||||
this.logger.fatal`Error while connecting to Sonic: ${error}`;
|
||||
sonicLogger.fatal`Error while connecting to Sonic: ${error}`;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
@ -143,7 +135,7 @@ export class SonicSearchManager {
|
|||
`${user.data.username} ${user.data.displayName} ${user.data.note}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error`Failed to add user to Sonic: ${error}`;
|
||||
sonicLogger.error`Failed to add user to Sonic: ${error}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -435,31 +435,29 @@ text = "No spam"
|
|||
|
||||
[logging]
|
||||
|
||||
# Available levels: debug, info, warning, error, fatal
|
||||
log_level = "debug"
|
||||
|
||||
log_file_path = "logs/versia.log"
|
||||
|
||||
[logging.types]
|
||||
# Either pass a boolean
|
||||
# requests = true
|
||||
# Or a table with the following keys:
|
||||
# requests_content = { level = "debug", log_file_path = "logs/requests.log" }
|
||||
# Available types are: requests, responses, requests_content, filters
|
||||
# Available levels: trace, debug, info, warning, error, fatal
|
||||
log_level = "info" # For console output
|
||||
|
||||
# [logging.file]
|
||||
# path = "logs/versia.log"
|
||||
# log_level = "info"
|
||||
#
|
||||
# [logging.file.rotation]
|
||||
# max_size = 10_000_000 # 10 MB
|
||||
# max_files = 10 # Keep 10 rotated files
|
||||
#
|
||||
# https://sentry.io support
|
||||
# Uncomment to enable
|
||||
# [logging.sentry]
|
||||
# Sentry DSN for error logging
|
||||
# dsn = "https://example.com"
|
||||
# debug = false
|
||||
|
||||
# sample_rate = 1.0
|
||||
# traces_sample_rate = 1.0
|
||||
# Can also be regex
|
||||
# trace_propagation_targets = []
|
||||
# max_breadcrumbs = 100
|
||||
# environment = "production"
|
||||
# log_level = "info"
|
||||
|
||||
|
||||
[plugins]
|
||||
# Whether to automatically load all plugins in the plugins directory
|
||||
|
|
|
|||
31
flake.lock
31
flake.lock
|
|
@ -20,16 +20,32 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1749903597,
|
||||
"narHash": "sha256-jp0D4vzBcRKwNZwfY4BcWHemLGUs4JrS3X9w5k/JYDA=",
|
||||
"owner": "nixos",
|
||||
"lastModified": 1750365781,
|
||||
"narHash": "sha256-XE/lFNhz5lsriMm/yjXkvSZz5DfvKJLUjsS6pP8EC50=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "41da1e3ea8e23e094e5e3eeb1e6b830468a7399e",
|
||||
"rev": "08f22084e6085d19bcfb4be30d1ca76ecb96fe54",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-bun": {
|
||||
"locked": {
|
||||
"lastModified": 1749427688,
|
||||
"narHash": "sha256-mMfhQsEYlfOvtjMs6BNPduuRc4YP/+Mj3G+/KYLQLUw=",
|
||||
"owner": "0xdsqr",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "09f139e43b59756fcbd9437b3a8726238fa62880",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "0xdsqr",
|
||||
"ref": "add-bun-support",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
|
@ -37,7 +53,8 @@
|
|||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-bun": "nixpkgs-bun"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
|
|
|
|||
14
flake.nix
14
flake.nix
|
|
@ -2,7 +2,8 @@
|
|||
description = "Versia Server";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
nixpkgs-bun.url = "github:0xdsqr/nixpkgs/add-bun-support";
|
||||
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
|
@ -10,12 +11,21 @@
|
|||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
nixpkgs-bun,
|
||||
flake-utils,
|
||||
...
|
||||
}:
|
||||
{
|
||||
overlays.default = final: prev: rec {
|
||||
versia-server = final.callPackage ./nix/package.nix {};
|
||||
versia-server = final.callPackage ./nix/package.nix {
|
||||
inherit
|
||||
(nixpkgs-bun.legacyPackages.x86_64-linux)
|
||||
fetchBunDeps
|
||||
bunConfigHook
|
||||
bunInstallHook
|
||||
bunBuildHook
|
||||
;
|
||||
};
|
||||
versia-server-worker = final.callPackage ./nix/package-worker.nix {
|
||||
inherit versia-server;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
pnpm,
|
||||
bun,
|
||||
nodejs,
|
||||
vips,
|
||||
makeWrapper,
|
||||
fetchBunDeps,
|
||||
bunConfigHook,
|
||||
bunInstallHook,
|
||||
bunBuildHook,
|
||||
...
|
||||
}: let
|
||||
packageJson = builtins.fromJSON (builtins.readFile ../package.json);
|
||||
|
|
@ -16,34 +19,22 @@ in
|
|||
|
||||
src = ../.;
|
||||
|
||||
# Fixes the build script mv usage
|
||||
pnpmInstallFlags = ["--shamefully-hoist"];
|
||||
|
||||
pnpmDeps = pnpm.fetchDeps {
|
||||
inherit (finalAttrs) pname version src pnpmInstallFlags;
|
||||
hash = "sha256-nC1bYW+It2N0Mp8+Yh1uk3MOj8DABOCNP5E3LbMuCEQ=";
|
||||
bunOfflineCache = fetchBunDeps {
|
||||
bunLock = finalAttrs.src + "/bun.lock";
|
||||
hash = "sha256-8R+LzgqAiqRGCMDBw2R7QO6hbdNrtIwzSjR3A8xhfVw=";
|
||||
};
|
||||
|
||||
bunBuildScript = "packages/api/build.ts";
|
||||
|
||||
nativeBuildInputs = [
|
||||
pnpm
|
||||
pnpm.configHook
|
||||
bun
|
||||
nodejs
|
||||
makeWrapper
|
||||
bunConfigHook
|
||||
bunInstallHook
|
||||
bunBuildHook
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
vips
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
bun run packages/api/build.ts
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
entrypointPath = "packages/api/index.js";
|
||||
|
||||
installPhase = let
|
||||
|
|
|
|||
34
package.json
34
package.json
|
|
@ -25,8 +25,8 @@
|
|||
"packages/*"
|
||||
],
|
||||
"catalog": {
|
||||
"@biomejs/biome": "2.0.0-beta.5",
|
||||
"@types/bun": "^1.2.16",
|
||||
"@biomejs/biome": "^2.0.4",
|
||||
"@types/bun": "^1.2.17",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
"@types/markdown-it-container": "^2.0.10",
|
||||
"@types/mime-types": "^3.0.1",
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
"vitepress": "^1.6.3",
|
||||
"vitepress-plugin-tabs": "^0.7.1",
|
||||
"vitepress-sidebar": "^1.31.1",
|
||||
"vue": "^3.5.16",
|
||||
"vue": "^3.5.17",
|
||||
"zod-to-json-schema": "^3.24.5",
|
||||
"@bull-board/api": "^6.10.1",
|
||||
"@bull-board/hono": "^6.10.1",
|
||||
|
|
@ -53,19 +53,21 @@
|
|||
"@hackmd/markdown-it-task-lists": "^2.1.4",
|
||||
"@hono/zod-validator": "^0.7.0",
|
||||
"@inquirer/confirm": "^5.1.12",
|
||||
"@logtape/file": "^0.12.0",
|
||||
"@logtape/logtape": "^0.12.0",
|
||||
"@scalar/hono-api-reference": "^0.9.4",
|
||||
"@logtape/file": "^1.0.0",
|
||||
"@logtape/logtape": "^1.0.0",
|
||||
"@logtape/sentry": "^1.0.0",
|
||||
"@logtape/otel": "^1.0.0",
|
||||
"@scalar/hono-api-reference": "^0.9.6",
|
||||
"@sentry/bun": "^9.29.0",
|
||||
"altcha-lib": "^1.3.0",
|
||||
"blurhash": "^2.0.5",
|
||||
"bullmq": "^5.53.3",
|
||||
"bullmq": "^5.55.0",
|
||||
"chalk": "^5.4.1",
|
||||
"clerc": "^0.44.0",
|
||||
"confbox": "^0.2.2",
|
||||
"drizzle-orm": "^0.44.2",
|
||||
"feed": "^5.1.0",
|
||||
"hono": "^4.7.11",
|
||||
"hono": "^4.8.2",
|
||||
"hono-openapi": "^0.4.8",
|
||||
"hono-rate-limiter": "^0.4.2",
|
||||
"html-to-text": "^9.0.5",
|
||||
|
|
@ -98,7 +100,7 @@
|
|||
"youch": "^4.1.0-beta.7",
|
||||
"zod": "^3.25.64",
|
||||
"zod-openapi": "^4.2.4",
|
||||
"zod-validation-error": "^3.5.0"
|
||||
"zod-validation-error": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"maintainers": [
|
||||
|
|
@ -130,6 +132,7 @@
|
|||
"es5-ext",
|
||||
"esbuild",
|
||||
"msgpackr-extract",
|
||||
"protobufjs",
|
||||
"sharp"
|
||||
],
|
||||
"devDependencies": {
|
||||
|
|
@ -162,15 +165,14 @@
|
|||
"@hackmd/markdown-it-task-lists": "catalog:",
|
||||
"@hono/zod-validator": "catalog:",
|
||||
"@inquirer/confirm": "catalog:",
|
||||
"@logtape/file": "catalog:",
|
||||
"@logtape/logtape": "catalog:",
|
||||
"@scalar/hono-api-reference": "catalog:",
|
||||
"@sentry/bun": "catalog:",
|
||||
"@versia/client": "workspace:*",
|
||||
"@versia-server/kit": "workspace:*",
|
||||
"@versia/sdk": "workspace:*",
|
||||
"@versia-server/tests": "workspace:*",
|
||||
"@versia-server/config": "workspace:*",
|
||||
"@versia-server/kit": "workspace:*",
|
||||
"@versia-server/tests": "workspace:*",
|
||||
"@versia-server/logging": "workspace:*",
|
||||
"@versia/client": "workspace:*",
|
||||
"@versia/sdk": "workspace:*",
|
||||
"altcha-lib": "catalog:",
|
||||
"blurhash": "catalog:",
|
||||
"bullmq": "catalog:",
|
||||
|
|
@ -185,7 +187,7 @@
|
|||
"html-to-text": "catalog:",
|
||||
"ioredis": "catalog:",
|
||||
"ip-matching": "catalog:",
|
||||
"iso-639-1": "^3.1.5",
|
||||
"iso-639-1": "catalog:",
|
||||
"jose": "catalog:",
|
||||
"linkify-html": "catalog:",
|
||||
"linkify-string": "catalog:",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { resolve } from "node:path";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import { Scalar } from "@scalar/hono-api-reference";
|
||||
import { config } from "@versia-server/config";
|
||||
import { ApiError } from "@versia-server/kit";
|
||||
import { serverLogger } from "@versia-server/logging";
|
||||
import chalk from "chalk";
|
||||
import { Hono } from "hono";
|
||||
import { serveStatic } from "hono/bun";
|
||||
|
|
@ -13,8 +13,6 @@ import { secureHeaders } from "hono/secure-headers";
|
|||
import { openAPISpecs } from "hono-openapi";
|
||||
import { Youch } from "youch";
|
||||
import { applyToHono } from "@/bull-board.ts";
|
||||
import { configureLoggers } from "@/loggers";
|
||||
import { sentry } from "@/sentry";
|
||||
import pkg from "~/package.json" with { type: "application/json" };
|
||||
import { PluginLoader } from "../../classes/plugin/loader.ts";
|
||||
import type { ApiRouteExports, HonoEnv } from "../../types/api.ts";
|
||||
|
|
@ -28,9 +26,6 @@ import { routes } from "./routes.ts";
|
|||
import "zod-openapi/extend";
|
||||
|
||||
export const appFactory = async (): Promise<Hono<HonoEnv>> => {
|
||||
await configureLoggers();
|
||||
const serverLogger = getLogger("server");
|
||||
|
||||
const app = new Hono<HonoEnv>({
|
||||
strict: false,
|
||||
});
|
||||
|
|
@ -124,7 +119,7 @@ export const appFactory = async (): Promise<Hono<HonoEnv>> => {
|
|||
config.plugins?.overrides.disabled,
|
||||
);
|
||||
|
||||
await PluginLoader.addToApp(plugins, app, serverLogger);
|
||||
await PluginLoader.addToApp(plugins, app);
|
||||
|
||||
const time2 = performance.now();
|
||||
|
||||
|
|
@ -193,7 +188,6 @@ export const appFactory = async (): Promise<Hono<HonoEnv>> => {
|
|||
const youch = new Youch();
|
||||
console.error(await youch.toANSI(error));
|
||||
|
||||
sentry?.captureException(error);
|
||||
return c.json(
|
||||
{
|
||||
error: "A server error occured",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import process from "node:process";
|
||||
import { config } from "@versia-server/config";
|
||||
import { Youch } from "youch";
|
||||
import { sentry } from "@/sentry";
|
||||
import { createServer } from "@/server";
|
||||
import { appFactory } from "./app.ts";
|
||||
|
||||
|
|
@ -16,6 +15,5 @@ process.on("uncaughtException", async (error) => {
|
|||
});
|
||||
|
||||
await import("./setup.ts");
|
||||
sentry?.captureMessage("Server started", "info");
|
||||
|
||||
createServer(config, await appFactory());
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { ApiError } from "@versia-server/kit";
|
||||
import { serverLogger } from "@versia-server/logging";
|
||||
import type { SocketAddress } from "bun";
|
||||
import { createMiddleware } from "hono/factory";
|
||||
import { matches } from "ip-matching";
|
||||
import { sentry } from "@/sentry";
|
||||
|
||||
export const ipBans = createMiddleware(async (context, next) => {
|
||||
// Check for banned IPs
|
||||
|
|
@ -22,11 +21,8 @@ export const ipBans = createMiddleware(async (context, next) => {
|
|||
throw new ApiError(403, "Forbidden");
|
||||
}
|
||||
} catch (e) {
|
||||
const logger = getLogger("server");
|
||||
|
||||
logger.error`Error while parsing banned IP "${ip}" `;
|
||||
logger.error`${e}`;
|
||||
sentry?.captureException(e);
|
||||
serverLogger.error`Error while parsing banned IP "${ip}" `;
|
||||
serverLogger.error`${e}`;
|
||||
|
||||
return context.json(
|
||||
{ error: `A server error occured: ${(e as Error).message}` },
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { serverLogger } from "@versia-server/logging";
|
||||
import { SHA256 } from "bun";
|
||||
import chalk from "chalk";
|
||||
import { createMiddleware } from "hono/factory";
|
||||
|
||||
export const logger = createMiddleware(async (context, next) => {
|
||||
if (config.logging.types.requests) {
|
||||
const serverLogger = getLogger("server");
|
||||
const body = await context.req.raw.clone().text();
|
||||
|
||||
const urlAndMethod = `${chalk.green(context.req.method)} ${chalk.blue(context.req.url)}`;
|
||||
|
|
@ -18,20 +15,12 @@ export const logger = createMiddleware(async (context, next) => {
|
|||
const headers = `${chalk.bold("Headers")}:\n${Array.from(
|
||||
context.req.raw.headers.entries(),
|
||||
)
|
||||
.map(
|
||||
([key, value]) =>
|
||||
` - ${chalk.cyan(key)}: ${chalk.white(value)}`,
|
||||
)
|
||||
.map(([key, value]) => ` - ${chalk.cyan(key)}: ${chalk.white(value)}`)
|
||||
.join("\n")}`;
|
||||
|
||||
const bodyLog = `${chalk.bold("Body")}: ${chalk.gray(body)}`;
|
||||
|
||||
if (config.logging.types.requests_content) {
|
||||
serverLogger.debug`${urlAndMethod}\n${hash}\n${headers}\n${bodyLog}`;
|
||||
} else {
|
||||
serverLogger.debug`${urlAndMethod}`;
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@
|
|||
"@versia-server/config": "workspace:*",
|
||||
"@versia-server/tests": "workspace:*",
|
||||
"@versia-server/kit": "workspace:*",
|
||||
"@versia-server/logging": "workspace:*",
|
||||
"@versia/client": "workspace:*",
|
||||
"@versia/sdk": "workspace:*",
|
||||
"@logtape/logtape": "catalog:",
|
||||
"youch": "catalog:",
|
||||
"hono": "catalog:",
|
||||
"hono-openapi": "catalog:",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { apiRoute } from "@versia-server/kit/api";
|
||||
import { federationMessagingLogger } from "@versia-server/logging";
|
||||
import chalk from "chalk";
|
||||
import { describeRoute } from "hono-openapi";
|
||||
|
||||
|
|
@ -19,8 +19,7 @@ export default apiRoute((app) =>
|
|||
async (context) => {
|
||||
const content = await context.req.text();
|
||||
|
||||
getLogger(["federation", "messaging"])
|
||||
.info`Received message via ${chalk.bold("Instance Messaging")}:\n${content}`;
|
||||
federationMessagingLogger.info`Received message via ${chalk.bold("Instance Messaging")}:\n${content}`;
|
||||
|
||||
return context.text("", 200);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { FederationRequester } from "@versia/sdk/http";
|
||||
import { WebFingerSchema } from "@versia/sdk/schemas";
|
||||
import { config } from "@versia-server/config";
|
||||
|
|
@ -8,6 +7,7 @@ import { User } from "@versia-server/kit/db";
|
|||
import { parseUserAddress } from "@versia-server/kit/parsers";
|
||||
import { uuid, webfingerMention } from "@versia-server/kit/regex";
|
||||
import { Users } from "@versia-server/kit/tables";
|
||||
import { federationBridgeLogger } from "@versia-server/logging";
|
||||
import { and, eq, isNull } from "drizzle-orm";
|
||||
import { describeRoute } from "hono-openapi";
|
||||
import { resolver, validator } from "hono-openapi/zod";
|
||||
|
|
@ -90,8 +90,7 @@ export default apiRoute((app) =>
|
|||
} catch (e) {
|
||||
const error = e as ApiError;
|
||||
|
||||
getLogger(["federation", "bridge"])
|
||||
.error`Error from bridge: ${error.message}`;
|
||||
federationBridgeLogger.error`Error from bridge: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { Note, setupDatabase } from "@versia-server/kit/db";
|
||||
import { connection } from "@versia-server/kit/redis";
|
||||
import { configureLoggers } from "@/loggers";
|
||||
import { serverLogger } from "@versia-server/logging";
|
||||
import { searchManager } from "../../classes/search/search-manager.ts";
|
||||
|
||||
const timeAtStart = performance.now();
|
||||
|
||||
await configureLoggers();
|
||||
|
||||
const serverLogger = getLogger("server");
|
||||
|
||||
console.info(`
|
||||
██╗ ██╗███████╗██████╗ ███████╗██╗ █████╗
|
||||
██║ ██║██╔════╝██╔══██╗██╔════╝██║██╔══██╗
|
||||
|
|
|
|||
|
|
@ -716,20 +716,26 @@ export const ConfigSchema = z
|
|||
admin: z.array(z.nativeEnum(RolePermission)).default(ADMIN_ROLES),
|
||||
}),
|
||||
logging: z.strictObject({
|
||||
types: z.record(
|
||||
z.enum([
|
||||
"requests",
|
||||
"responses",
|
||||
"requests_content",
|
||||
"filters",
|
||||
]),
|
||||
z
|
||||
.boolean()
|
||||
.default(false)
|
||||
.or(
|
||||
z.strictObject({
|
||||
level: z
|
||||
file: z
|
||||
.strictObject({
|
||||
path: z.string().default("logs/versia.log"),
|
||||
rotation: z
|
||||
.strictObject({
|
||||
max_size: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.default(10_000_000), // 10 MB
|
||||
max_files: z
|
||||
.number()
|
||||
.int()
|
||||
.nonnegative()
|
||||
.default(10),
|
||||
})
|
||||
.optional(),
|
||||
log_level: z
|
||||
.enum([
|
||||
"trace",
|
||||
"debug",
|
||||
"info",
|
||||
"warning",
|
||||
|
|
@ -737,13 +743,8 @@ export const ConfigSchema = z
|
|||
"fatal",
|
||||
])
|
||||
.default("info"),
|
||||
log_file_path: z.string().optional(),
|
||||
}),
|
||||
),
|
||||
),
|
||||
log_level: z
|
||||
.enum(["debug", "info", "warning", "error", "fatal"])
|
||||
.default("info"),
|
||||
})
|
||||
.optional(),
|
||||
sentry: z
|
||||
.strictObject({
|
||||
dsn: url,
|
||||
|
|
@ -753,9 +754,21 @@ export const ConfigSchema = z
|
|||
trace_propagation_targets: z.array(z.string()).default([]),
|
||||
max_breadcrumbs: z.number().default(100),
|
||||
environment: z.string().optional(),
|
||||
log_level: z
|
||||
.enum([
|
||||
"trace",
|
||||
"debug",
|
||||
"info",
|
||||
"warning",
|
||||
"error",
|
||||
"fatal",
|
||||
])
|
||||
.default("info"),
|
||||
})
|
||||
.optional(),
|
||||
log_file_path: z.string().default("logs/versia.log"),
|
||||
log_level: z
|
||||
.enum(["trace", "debug", "info", "warning", "error", "fatal"])
|
||||
.default("info"),
|
||||
}),
|
||||
debug: z
|
||||
.strictObject({
|
||||
|
|
|
|||
56
packages/logging/formatter.ts
Normal file
56
packages/logging/formatter.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import type { LogLevel, LogRecord } from "@logtape/logtape";
|
||||
import chalk, { type ChalkInstance } from "chalk";
|
||||
|
||||
const levelAbbreviations: Record<LogLevel, string> = {
|
||||
debug: "DBG",
|
||||
info: "INF",
|
||||
warning: "WRN",
|
||||
error: "ERR",
|
||||
fatal: "FTL",
|
||||
trace: "TRC",
|
||||
};
|
||||
|
||||
/**
|
||||
* The styles for the log level in the console.
|
||||
*/
|
||||
const logLevelStyles: Record<LogLevel, ChalkInstance> = {
|
||||
debug: chalk.white.bgGray,
|
||||
info: chalk.black.bgWhite,
|
||||
warning: chalk.black.bgYellow,
|
||||
error: chalk.white.bgRed,
|
||||
fatal: chalk.white.bgRedBright,
|
||||
trace: chalk.white.bgBlue,
|
||||
};
|
||||
|
||||
/**
|
||||
* Pretty colored console formatter.
|
||||
*
|
||||
* @param record The log record to format.
|
||||
* @returns The formatted log record, as an array of arguments for
|
||||
* {@link console.log}.
|
||||
*/
|
||||
export function consoleFormatter(record: LogRecord): string[] {
|
||||
const msg = record.message.join("");
|
||||
const date = new Date(record.timestamp);
|
||||
const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date
|
||||
.getUTCMinutes()
|
||||
.toString()
|
||||
.padStart(
|
||||
2,
|
||||
"0",
|
||||
)}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date
|
||||
.getUTCMilliseconds()
|
||||
.toString()
|
||||
.padStart(3, "0")}`;
|
||||
|
||||
const formattedTime = chalk.gray(time);
|
||||
const formattedLevel = logLevelStyles[record.level](
|
||||
levelAbbreviations[record.level],
|
||||
);
|
||||
const formattedCategory = chalk.gray(record.category.join("\xb7"));
|
||||
const formattedMsg = chalk.reset(msg);
|
||||
|
||||
return [
|
||||
`${formattedTime} ${formattedLevel} ${formattedCategory} ${formattedMsg}`,
|
||||
];
|
||||
}
|
||||
158
packages/logging/index.ts
Normal file
158
packages/logging/index.ts
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import { mkdir } from "node:fs/promises";
|
||||
import { dirname } from "node:path";
|
||||
import { getFileSink, getRotatingFileSink } from "@logtape/file";
|
||||
import {
|
||||
configure,
|
||||
getConsoleSink,
|
||||
getLevelFilter,
|
||||
getLogger,
|
||||
type Sink,
|
||||
withFilter,
|
||||
} from "@logtape/logtape";
|
||||
import { getSentrySink } from "@logtape/sentry";
|
||||
import * as Sentry from "@sentry/bun";
|
||||
import { config } from "@versia-server/config";
|
||||
import { env } from "bun";
|
||||
import pkg from "../../package.json" with { type: "json" };
|
||||
import { consoleFormatter } from "./formatter.ts";
|
||||
|
||||
if (config.logging.file?.path) {
|
||||
// config.logging.file.path is a path to a file, create the directory if it doesn't exist
|
||||
await mkdir(dirname(config.logging.file.path), { recursive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all configured sinks depending on the configuration.
|
||||
*/
|
||||
const getSinks = (): Record<"file" | "console" | "sentry", Sink> => {
|
||||
const sinks: Record<string, Sink> = {};
|
||||
|
||||
if (config.logging.file) {
|
||||
if (config.logging.file.rotation) {
|
||||
sinks.file = getRotatingFileSink(config.logging.file.path, {
|
||||
maxFiles: config.logging.file.rotation.max_files,
|
||||
maxSize: config.logging.file.rotation.max_size,
|
||||
});
|
||||
} else {
|
||||
sinks.file = getFileSink(config.logging.file.path);
|
||||
}
|
||||
|
||||
sinks.file = withFilter(
|
||||
sinks.file,
|
||||
getLevelFilter(config.logging.file.log_level),
|
||||
);
|
||||
}
|
||||
|
||||
if (config.logging.sentry) {
|
||||
sinks.sentry = getSentrySink(
|
||||
Sentry.init({
|
||||
dsn: config.logging.sentry.dsn.origin,
|
||||
debug: config.logging.sentry.debug,
|
||||
sampleRate: config.logging.sentry.sample_rate,
|
||||
maxBreadcrumbs: config.logging.sentry.max_breadcrumbs,
|
||||
tracesSampleRate: config.logging.sentry.traces_sample_rate,
|
||||
environment: config.logging.sentry.environment,
|
||||
tracePropagationTargets:
|
||||
config.logging.sentry.trace_propagation_targets,
|
||||
release: env.GIT_COMMIT
|
||||
? `${pkg.version}-${env.GIT_COMMIT}`
|
||||
: pkg.version,
|
||||
integrations: [Sentry.extraErrorDataIntegration()],
|
||||
}),
|
||||
);
|
||||
|
||||
sinks.sentry = withFilter(
|
||||
sinks.sentry,
|
||||
getLevelFilter(config.logging.sentry.log_level),
|
||||
);
|
||||
}
|
||||
|
||||
sinks.console = getConsoleSink({
|
||||
formatter: consoleFormatter,
|
||||
});
|
||||
|
||||
sinks.console = withFilter(
|
||||
sinks.console,
|
||||
getLevelFilter(config.logging.log_level),
|
||||
);
|
||||
|
||||
return sinks;
|
||||
};
|
||||
|
||||
const getSinkNames = (): ("file" | "console" | "sentry")[] => {
|
||||
const names = [] as ("file" | "console" | "sentry")[];
|
||||
|
||||
if (config.logging.file) {
|
||||
names.push("file");
|
||||
}
|
||||
|
||||
if (config.logging.sentry) {
|
||||
names.push("sentry");
|
||||
}
|
||||
|
||||
names.push("console");
|
||||
|
||||
return names;
|
||||
};
|
||||
|
||||
await configure({
|
||||
reset: true,
|
||||
sinks: getSinks(),
|
||||
loggers: [
|
||||
{
|
||||
category: "server",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "inbox"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "delivery"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "bridge"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "resolvers"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "messaging"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: "database",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: "webfinger",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: "sonic",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["logtape", "meta"],
|
||||
lowestLevel: "error",
|
||||
},
|
||||
{
|
||||
category: "plugin",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const serverLogger = getLogger("server");
|
||||
export const federationInboxLogger = getLogger(["federation", "inbox"]);
|
||||
export const federationDeliveryLogger = getLogger(["federation", "delivery"]);
|
||||
export const federationBridgeLogger = getLogger(["federation", "bridge"]);
|
||||
export const federationResolversLogger = getLogger(["federation", "resolvers"]);
|
||||
export const federationMessagingLogger = getLogger(["federation", "messaging"]);
|
||||
export const databaseLogger = getLogger("database");
|
||||
export const webfingerLogger = getLogger("webfinger");
|
||||
export const sonicLogger = getLogger("sonic");
|
||||
export const pluginLogger = getLogger("plugin");
|
||||
22
packages/logging/package.json
Normal file
22
packages/logging/package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "@versia-server/logging",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.ts",
|
||||
"default": "./index.ts"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@versia-server/config": "workspace:*",
|
||||
"@logtape/logtape": "catalog:",
|
||||
"@logtape/file": "catalog:",
|
||||
"@logtape/sentry": "catalog:",
|
||||
"@logtape/otel": "catalog:",
|
||||
"@sentry/bun": "catalog:",
|
||||
"chalk": "catalog:"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import type { Hook } from "@hono/zod-validator";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import type { RolePermission } from "@versia/client/schemas";
|
||||
import { config } from "@versia-server/config";
|
||||
import { serverLogger } from "@versia-server/logging";
|
||||
import { extractParams, verifySolution } from "altcha-lib";
|
||||
import chalk from "chalk";
|
||||
import { eq, type SQL } from "drizzle-orm";
|
||||
|
|
@ -418,7 +418,6 @@ export const jsonOrForm = (): MiddlewareHandler<HonoEnv> => {
|
|||
|
||||
export const debugResponse = async (res: Response): Promise<void> => {
|
||||
const body = await res.clone().text();
|
||||
const logger = getLogger("server");
|
||||
|
||||
const status = `${chalk.bold("Status")}: ${chalk.green(res.status)}`;
|
||||
|
||||
|
|
@ -430,9 +429,5 @@ export const debugResponse = async (res: Response): Promise<void> => {
|
|||
|
||||
const bodyLog = `${chalk.bold("Body")}: ${chalk.gray(body)}`;
|
||||
|
||||
if (config.logging.types.requests_content) {
|
||||
logger.debug`${status}\n${headers}\n${bodyLog}`;
|
||||
} else {
|
||||
logger.debug`${status}`;
|
||||
}
|
||||
serverLogger.debug`${status}\n${headers}\n${bodyLog}`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import * as VersiaEntities from "@versia/sdk/entities";
|
||||
import { config } from "@versia-server/config";
|
||||
import { ApiError } from "@versia-server/kit";
|
||||
import { db } from "@versia-server/kit/db";
|
||||
import { Instances } from "@versia-server/kit/tables";
|
||||
import {
|
||||
federationMessagingLogger,
|
||||
federationResolversLogger,
|
||||
} from "@versia-server/logging";
|
||||
import { randomUUIDv7 } from "bun";
|
||||
import chalk from "chalk";
|
||||
import {
|
||||
|
|
@ -175,9 +178,6 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
const wellKnownUrl = new URL("/.well-known/nodeinfo", origin);
|
||||
|
||||
// Go to endpoint, then follow the links to the actual metadata
|
||||
|
||||
const logger = getLogger(["federation", "resolvers"]);
|
||||
|
||||
try {
|
||||
const { json, ok, status } = await fetch(wellKnownUrl, {
|
||||
// @ts-expect-error Bun extension
|
||||
|
|
@ -185,7 +185,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
});
|
||||
|
||||
if (!ok) {
|
||||
logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
origin,
|
||||
)} - HTTP ${status}`;
|
||||
return null;
|
||||
|
|
@ -196,7 +196,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
};
|
||||
|
||||
if (!wellKnown.links) {
|
||||
logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
origin,
|
||||
)} - No links found`;
|
||||
return null;
|
||||
|
|
@ -209,7 +209,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
);
|
||||
|
||||
if (!metadataUrl) {
|
||||
logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
origin,
|
||||
)} - No metadata URL found`;
|
||||
return null;
|
||||
|
|
@ -225,7 +225,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
});
|
||||
|
||||
if (!ok2) {
|
||||
logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
origin,
|
||||
)} - HTTP ${status2}`;
|
||||
return null;
|
||||
|
|
@ -264,7 +264,7 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold(
|
||||
origin,
|
||||
)} - Error! ${error}`;
|
||||
return null;
|
||||
|
|
@ -312,14 +312,12 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
}
|
||||
|
||||
public async updateFromRemote(): Promise<Instance> {
|
||||
const logger = getLogger(["federation", "resolvers"]);
|
||||
|
||||
const output = await Instance.fetchMetadata(
|
||||
new URL(`https://${this.data.baseUrl}`),
|
||||
);
|
||||
|
||||
if (!output) {
|
||||
logger.error`Failed to update instance ${chalk.bold(
|
||||
federationResolversLogger.error`Failed to update instance ${chalk.bold(
|
||||
this.data.baseUrl,
|
||||
)}`;
|
||||
throw new Error("Failed to update instance");
|
||||
|
|
@ -341,12 +339,10 @@ export class Instance extends BaseInterface<typeof Instances> {
|
|||
}
|
||||
|
||||
public async sendMessage(content: string): Promise<void> {
|
||||
const logger = getLogger(["federation", "messaging"]);
|
||||
|
||||
if (
|
||||
!this.data.extensions?.["pub.versia:instance_messaging"]?.endpoint
|
||||
) {
|
||||
logger.info`Instance ${chalk.gray(
|
||||
federationMessagingLogger.info`Instance ${chalk.gray(
|
||||
this.data.baseUrl,
|
||||
)} does not support Instance Messaging, skipping message`;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import type {
|
||||
Account,
|
||||
Mention as MentionSchema,
|
||||
|
|
@ -29,6 +28,10 @@ import {
|
|||
Users,
|
||||
UserToPinnedNotes,
|
||||
} from "@versia-server/kit/tables";
|
||||
import {
|
||||
federationDeliveryLogger,
|
||||
federationResolversLogger,
|
||||
} from "@versia-server/logging";
|
||||
import { password as bunPassword, randomUUIDv7 } from "bun";
|
||||
import chalk from "chalk";
|
||||
import {
|
||||
|
|
@ -49,7 +52,6 @@ import { htmlToText } from "html-to-text";
|
|||
import type { z } from "zod";
|
||||
import { getBestContentType } from "@/content_types";
|
||||
import { randomString } from "@/math";
|
||||
import { sentry } from "@/sentry";
|
||||
import { searchManager } from "~/classes/search/search-manager";
|
||||
import type { HttpVerb, KnownEntity } from "~/types/api.ts";
|
||||
import {
|
||||
|
|
@ -1165,8 +1167,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
}
|
||||
|
||||
public static async resolve(uri: URL): Promise<User | null> {
|
||||
getLogger(["federation", "resolvers"])
|
||||
.debug`Resolving user ${chalk.gray(uri)}`;
|
||||
federationResolversLogger.debug`Resolving user ${chalk.gray(uri)}`;
|
||||
// Check if user not already in database
|
||||
const foundUser = await User.fromSql(eq(Users.uri, uri.href));
|
||||
|
||||
|
|
@ -1187,8 +1188,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
return await User.fromId(userUuid[0]);
|
||||
}
|
||||
|
||||
getLogger(["federation", "resolvers"])
|
||||
.debug`User not found in database, fetching from remote`;
|
||||
federationResolversLogger.debug`User not found in database, fetching from remote`;
|
||||
|
||||
return User.fromVersia(uri);
|
||||
}
|
||||
|
|
@ -1419,11 +1419,10 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
|
|||
entity,
|
||||
);
|
||||
} catch (e) {
|
||||
getLogger(["federation", "delivery"]).error`Federating ${chalk.gray(
|
||||
federationDeliveryLogger.error`Federating ${chalk.gray(
|
||||
entity.data.type,
|
||||
)} to ${user.uri} ${chalk.bold.red("failed")}`;
|
||||
getLogger(["federation", "delivery"]).error`${e}`;
|
||||
sentry?.captureException(e);
|
||||
federationDeliveryLogger.error`${e}`;
|
||||
|
||||
return { ok: false };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@
|
|||
"chalk": "catalog:",
|
||||
"@versia/client": "workspace:*",
|
||||
"@versia-server/config": "workspace:*",
|
||||
"@versia-server/logging": "workspace:*",
|
||||
"@versia/sdk": "workspace:*",
|
||||
"html-to-text": "catalog:",
|
||||
"@logtape/logtape": "catalog:",
|
||||
"sharp": "catalog:",
|
||||
"magic-regexp": "catalog:",
|
||||
"altcha-lib": "catalog:",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { databaseLogger } from "@versia-server/logging";
|
||||
import { SQL } from "bun";
|
||||
import chalk from "chalk";
|
||||
import { type BunSQLDatabase, drizzle } from "drizzle-orm/bun-sql";
|
||||
|
|
@ -40,8 +40,6 @@ export const db =
|
|||
: drizzle(primaryDb, { schema });
|
||||
|
||||
export const setupDatabase = async (info = true): Promise<void> => {
|
||||
const logger = getLogger("database");
|
||||
|
||||
for (const dbPool of [primaryDb, ...replicas]) {
|
||||
try {
|
||||
await dbPool.connect();
|
||||
|
|
@ -53,7 +51,7 @@ export const setupDatabase = async (info = true): Promise<void> => {
|
|||
return;
|
||||
}
|
||||
|
||||
logger.fatal`Failed to connect to database ${chalk.bold(
|
||||
databaseLogger.fatal`Failed to connect to database ${chalk.bold(
|
||||
// Index of the database in the array
|
||||
replicas.indexOf(dbPool) === -1
|
||||
? "primary"
|
||||
|
|
@ -65,17 +63,17 @@ export const setupDatabase = async (info = true): Promise<void> => {
|
|||
}
|
||||
|
||||
// Migrate the database
|
||||
info && logger.info`Migrating database...`;
|
||||
info && databaseLogger.info`Migrating database...`;
|
||||
|
||||
try {
|
||||
await migrate(db, {
|
||||
migrationsFolder: "./packages/plugin-kit/tables/migrations",
|
||||
});
|
||||
} catch (e) {
|
||||
logger.fatal`Failed to migrate database. Please check your configuration.`;
|
||||
databaseLogger.fatal`Failed to migrate database. Please check your configuration.`;
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
info && logger.info`Database migrated`;
|
||||
info && databaseLogger.info`Database migrated`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import process from "node:process";
|
||||
import { getLogger } from "@logtape/logtape";
|
||||
import { serverLogger } from "@versia-server/logging";
|
||||
import chalk from "chalk";
|
||||
import { sentry } from "@/sentry";
|
||||
import { workers } from "./workers.ts";
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
|
|
@ -9,9 +8,6 @@ process.on("SIGINT", () => {
|
|||
});
|
||||
|
||||
await import("./setup.ts");
|
||||
sentry?.captureMessage("Server started", "info");
|
||||
|
||||
const serverLogger = getLogger("server");
|
||||
|
||||
for (const [worker, fn] of Object.entries(workers)) {
|
||||
serverLogger.info`Starting ${worker} Worker...`;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
"dependencies": {
|
||||
"@versia-server/config": "workspace:*",
|
||||
"@versia-server/kit": "workspace:*",
|
||||
"chalk": "catalog:",
|
||||
"@logtape/logtape": "catalog:"
|
||||
"@versia-server/logging": "workspace:*",
|
||||
"chalk": "catalog:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
import { getLogger } from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import { Note, setupDatabase } from "@versia-server/kit/db";
|
||||
import { connection } from "@versia-server/kit/redis";
|
||||
import { serverLogger } from "@versia-server/logging";
|
||||
import chalk from "chalk";
|
||||
import { configureLoggers } from "@/loggers";
|
||||
import { searchManager } from "../../classes/search/search-manager.ts";
|
||||
|
||||
const timeAtStart = performance.now();
|
||||
|
||||
await configureLoggers();
|
||||
|
||||
const serverLogger = getLogger("server");
|
||||
|
||||
console.info(`
|
||||
██╗ ██╗███████╗██████╗ ███████╗██╗ █████╗
|
||||
██║ ██║██╔════╝██╔══██╗██╔════╝██║██╔══██╗
|
||||
|
|
|
|||
144
utils/loggers.ts
144
utils/loggers.ts
|
|
@ -1,144 +0,0 @@
|
|||
import { mkdir } from "node:fs/promises";
|
||||
import { dirname } from "node:path";
|
||||
import { getRotatingFileSink } from "@logtape/file";
|
||||
import {
|
||||
configure,
|
||||
getConsoleSink,
|
||||
getLevelFilter,
|
||||
type LogLevel,
|
||||
type LogRecord,
|
||||
} from "@logtape/logtape";
|
||||
import { config } from "@versia-server/config";
|
||||
import chalk from "chalk";
|
||||
|
||||
// config.logging.log_file_path is a path to a file, create the directory if it doesn't exist
|
||||
await mkdir(dirname(config.logging.log_file_path), { recursive: true });
|
||||
|
||||
const levelAbbreviations: Record<LogLevel, string> = {
|
||||
debug: "DBG",
|
||||
info: "INF",
|
||||
warning: "WRN",
|
||||
error: "ERR",
|
||||
fatal: "FTL",
|
||||
trace: "TRC",
|
||||
};
|
||||
|
||||
/**
|
||||
* The styles for the log level in the console.
|
||||
*/
|
||||
const logLevelStyles: Record<LogLevel, (text: string) => string> = {
|
||||
debug: chalk.white.bgGray,
|
||||
info: chalk.black.bgWhite,
|
||||
warning: chalk.black.bgYellow,
|
||||
error: chalk.white.bgRed,
|
||||
fatal: chalk.white.bgRedBright,
|
||||
trace: chalk.white.bgBlue,
|
||||
};
|
||||
|
||||
/**
|
||||
* The default console formatter.
|
||||
*
|
||||
* @param record The log record to format.
|
||||
* @returns The formatted log record, as an array of arguments for
|
||||
* {@link console.log}.
|
||||
*/
|
||||
export function defaultConsoleFormatter(record: LogRecord): string[] {
|
||||
const msg = record.message.join("");
|
||||
const date = new Date(record.timestamp);
|
||||
const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date
|
||||
.getUTCMinutes()
|
||||
.toString()
|
||||
.padStart(
|
||||
2,
|
||||
"0",
|
||||
)}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date
|
||||
.getUTCMilliseconds()
|
||||
.toString()
|
||||
.padStart(3, "0")}`;
|
||||
|
||||
const formattedTime = chalk.gray(time);
|
||||
const formattedLevel = logLevelStyles[record.level](
|
||||
levelAbbreviations[record.level],
|
||||
);
|
||||
const formattedCategory = chalk.gray(record.category.join("\xb7"));
|
||||
const formattedMsg = chalk.reset(msg);
|
||||
|
||||
return [
|
||||
`${formattedTime} ${formattedLevel} ${formattedCategory} ${formattedMsg}`,
|
||||
];
|
||||
}
|
||||
|
||||
export const configureLoggers = (silent = false): Promise<void> =>
|
||||
configure({
|
||||
reset: true,
|
||||
sinks: {
|
||||
console: getConsoleSink({
|
||||
formatter: defaultConsoleFormatter,
|
||||
}),
|
||||
file: getRotatingFileSink(config.logging.log_file_path, {
|
||||
maxFiles: 10,
|
||||
maxSize: 10 * 1024 * 1024,
|
||||
}),
|
||||
},
|
||||
filters: {
|
||||
configFilter: silent
|
||||
? getLevelFilter(null)
|
||||
: getLevelFilter(config.logging.log_level),
|
||||
},
|
||||
loggers: [
|
||||
{
|
||||
category: "server",
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: ["federation", "inbox"],
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: ["federation", "delivery"],
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: ["federation", "bridge"],
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: ["federation", "resolvers"],
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: ["federation", "messaging"],
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: "database",
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: "webfinger",
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: "sonic",
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
{
|
||||
category: ["logtape", "meta"],
|
||||
lowestLevel: "error",
|
||||
},
|
||||
{
|
||||
category: "plugin",
|
||||
sinks: ["console", "file"],
|
||||
filters: ["configFilter"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import * as Sentry from "@sentry/bun";
|
||||
import { config } from "@versia-server/config";
|
||||
import { env } from "bun";
|
||||
import pkg from "~/package.json" with { type: "json" };
|
||||
|
||||
const sentryInstance =
|
||||
config.logging.sentry &&
|
||||
Sentry.init({
|
||||
dsn: config.logging.sentry.dsn.origin,
|
||||
debug: config.logging.sentry.debug,
|
||||
sampleRate: config.logging.sentry.sample_rate,
|
||||
maxBreadcrumbs: config.logging.sentry.max_breadcrumbs,
|
||||
tracesSampleRate: config.logging.sentry.traces_sample_rate,
|
||||
environment: config.logging.sentry.environment,
|
||||
tracePropagationTargets:
|
||||
config.logging.sentry.trace_propagation_targets,
|
||||
release: env.GIT_COMMIT
|
||||
? `${pkg.version}-${env.GIT_COMMIT}`
|
||||
: pkg.version,
|
||||
integrations: [Sentry.extraErrorDataIntegration()],
|
||||
});
|
||||
|
||||
export const sentry = sentryInstance || undefined;
|
||||
|
|
@ -24,9 +24,7 @@ export const createServer = (
|
|||
async fetch(req, server): Promise<Response> {
|
||||
const output = await app.fetch(req, { ip: server.requestIP(req) });
|
||||
|
||||
if (config.logging.types.responses) {
|
||||
await debugResponse(output.clone());
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue