refactor (backend): port nodeinfo generator to backend-rs
This commit is contained in:
parent
359fef0a42
commit
49825853c1
7 changed files with 354 additions and 101 deletions
2
packages/backend-rs/index.d.ts
vendored
2
packages/backend-rs/index.d.ts
vendored
|
@ -1155,6 +1155,8 @@ export interface Webhook {
|
|||
latestStatus: number | null
|
||||
}
|
||||
export function initializeRustLogger(): void
|
||||
export function nodeinfo_2_1(): Promise<any>
|
||||
export function nodeinfo_2_0(): Promise<any>
|
||||
export function watchNote(watcherId: string, noteAuthorId: string, noteId: string): Promise<void>
|
||||
export function unwatchNote(watcherId: string, noteId: string): Promise<void>
|
||||
export function publishToChannelStream(channelId: string, userId: string): void
|
||||
|
|
|
@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding
|
||||
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, addNoteToAntenna, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initializeRustLogger, nodeinfo_2_1, nodeinfo_2_0, watchNote, unwatchNote, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, secureRndstr } = nativeBinding
|
||||
|
||||
module.exports.SECOND = SECOND
|
||||
module.exports.MINUTE = MINUTE
|
||||
|
@ -364,6 +364,8 @@ module.exports.UserEmojimodpermEnum = UserEmojimodpermEnum
|
|||
module.exports.UserProfileFfvisibilityEnum = UserProfileFfvisibilityEnum
|
||||
module.exports.UserProfileMutingnotificationtypesEnum = UserProfileMutingnotificationtypesEnum
|
||||
module.exports.initializeRustLogger = initializeRustLogger
|
||||
module.exports.nodeinfo_2_1 = nodeinfo_2_1
|
||||
module.exports.nodeinfo_2_0 = nodeinfo_2_0
|
||||
module.exports.watchNote = watchNote
|
||||
module.exports.unwatchNote = unwatchNote
|
||||
module.exports.publishToChannelStream = publishToChannelStream
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod log;
|
||||
pub mod nodeinfo;
|
||||
pub mod note;
|
||||
pub mod stream;
|
||||
|
|
327
packages/backend-rs/src/service/nodeinfo.rs
Normal file
327
packages/backend-rs/src/service/nodeinfo.rs
Normal file
|
@ -0,0 +1,327 @@
|
|||
use crate::config::CONFIG;
|
||||
use crate::database::cache;
|
||||
use crate::database::db_conn;
|
||||
use crate::misc::meta::fetch_meta;
|
||||
use crate::model::entity::{note, user};
|
||||
use sea_orm::{ColumnTrait, DbErr, EntityTrait, PaginatorTrait, QueryFilter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO: I want to use these macros but they don't work with rmp_serde
|
||||
// - #[serde(skip_serializing_if = "Option::is_none")] (https://github.com/3Hren/msgpack-rust/issues/86)
|
||||
// - #[serde(tag = "version", rename = "2.1")] (https://github.com/3Hren/msgpack-rust/issues/318)
|
||||
|
||||
/// NodeInfo schema version 2.1. https://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.1
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Nodeinfo21 {
|
||||
/// The schema version, must be 2.1.
|
||||
pub version: String,
|
||||
/// Metadata about server software in use.
|
||||
pub software: Software21,
|
||||
/// The protocols supported on this server.
|
||||
pub protocols: Vec<Protocol>,
|
||||
/// The third party sites this server can connect to via their application API.
|
||||
pub services: Services,
|
||||
/// Whether this server allows open self-registration.
|
||||
pub open_registrations: bool,
|
||||
/// Usage statistics for this server.
|
||||
pub usage: Usage,
|
||||
/// Free form key value pairs for software specific values. Clients should not rely on any specific key present.
|
||||
pub metadata: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// NodeInfo schema version 2.0. https://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.0
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Nodeinfo20 {
|
||||
/// The schema version, must be 2.0.
|
||||
pub version: String,
|
||||
/// Metadata about server software in use.
|
||||
pub software: Software20,
|
||||
/// The protocols supported on this server.
|
||||
pub protocols: Vec<Protocol>,
|
||||
/// The third party sites this server can connect to via their application API.
|
||||
pub services: Services,
|
||||
/// Whether this server allows open self-registration.
|
||||
pub open_registrations: bool,
|
||||
/// Usage statistics for this server.
|
||||
pub usage: Usage,
|
||||
/// Free form key value pairs for software specific values. Clients should not rely on any specific key present.
|
||||
pub metadata: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// Metadata about server software in use (version 2.1).
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Software21 {
|
||||
/// The canonical name of this server software.
|
||||
pub name: String,
|
||||
/// The version of this server software.
|
||||
pub version: String,
|
||||
/// The url of the source code repository of this server software.
|
||||
pub repository: Option<String>,
|
||||
/// The url of the homepage of this server software.
|
||||
pub homepage: Option<String>,
|
||||
}
|
||||
|
||||
/// Metadata about server software in use (version 2.0).
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Software20 {
|
||||
/// The canonical name of this server software.
|
||||
pub name: String,
|
||||
/// The version of this server software.
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Protocol {
|
||||
Activitypub,
|
||||
Buddycloud,
|
||||
Dfrn,
|
||||
Diaspora,
|
||||
Libertree,
|
||||
Ostatus,
|
||||
Pumpio,
|
||||
Tent,
|
||||
Xmpp,
|
||||
Zot,
|
||||
}
|
||||
|
||||
/// The third party sites this server can connect to via their application API.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Services {
|
||||
/// The third party sites this server can retrieve messages from for combined display with regular traffic.
|
||||
pub inbound: Vec<Inbound>,
|
||||
/// The third party sites this server can publish messages to on the behalf of a user.
|
||||
pub outbound: Vec<Outbound>,
|
||||
}
|
||||
|
||||
/// The third party sites this server can retrieve messages from for combined display with regular traffic.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Inbound {
|
||||
#[serde(rename = "atom1.0")]
|
||||
Atom1,
|
||||
Gnusocial,
|
||||
Imap,
|
||||
Pnut,
|
||||
#[serde(rename = "pop3")]
|
||||
Pop3,
|
||||
Pumpio,
|
||||
#[serde(rename = "rss2.0")]
|
||||
Rss2,
|
||||
Twitter,
|
||||
}
|
||||
|
||||
/// The third party sites this server can publish messages to on the behalf of a user.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Outbound {
|
||||
#[serde(rename = "atom1.0")]
|
||||
Atom1,
|
||||
Blogger,
|
||||
Buddycloud,
|
||||
Diaspora,
|
||||
Dreamwidth,
|
||||
Drupal,
|
||||
Facebook,
|
||||
Friendica,
|
||||
Gnusocial,
|
||||
Google,
|
||||
Insanejournal,
|
||||
Libertree,
|
||||
Linkedin,
|
||||
Livejournal,
|
||||
Mediagoblin,
|
||||
Myspace,
|
||||
Pinterest,
|
||||
Pnut,
|
||||
Posterous,
|
||||
Pumpio,
|
||||
Redmatrix,
|
||||
#[serde(rename = "rss2.0")]
|
||||
Rss2,
|
||||
Smtp,
|
||||
Tent,
|
||||
Tumblr,
|
||||
Twitter,
|
||||
Wordpress,
|
||||
Xmpp,
|
||||
}
|
||||
|
||||
/// Usage statistics for this server.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Usage {
|
||||
pub users: Users,
|
||||
pub local_posts: Option<u64>,
|
||||
pub local_comments: Option<u64>,
|
||||
}
|
||||
|
||||
/// statistics about the users of this server.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Users {
|
||||
pub total: Option<u64>,
|
||||
pub active_halfyear: Option<u64>,
|
||||
pub active_month: Option<u64>,
|
||||
}
|
||||
|
||||
impl From<Software21> for Software20 {
|
||||
fn from(software: Software21) -> Self {
|
||||
Self {
|
||||
name: software.name,
|
||||
version: software.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Nodeinfo21> for Nodeinfo20 {
|
||||
fn from(nodeinfo: Nodeinfo21) -> Self {
|
||||
Self {
|
||||
version: "2.0".to_string(),
|
||||
software: nodeinfo.software.into(),
|
||||
protocols: nodeinfo.protocols,
|
||||
services: nodeinfo.services,
|
||||
open_registrations: nodeinfo.open_registrations,
|
||||
usage: nodeinfo.usage,
|
||||
metadata: nodeinfo.metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Database error: {0}")]
|
||||
DbErr(#[from] DbErr),
|
||||
#[error("Cache error: {0}")]
|
||||
CacheErr(#[from] cache::Error),
|
||||
#[error("Failed to serialize nodeinfo to JSON: {0}")]
|
||||
JsonErr(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
async fn statistics() -> Result<(u64, u64, u64, u64), DbErr> {
|
||||
let db = db_conn().await?;
|
||||
|
||||
let now = chrono::Local::now().naive_local();
|
||||
const MONTH: chrono::TimeDelta = chrono::Duration::seconds(2592000000);
|
||||
const HALF_YEAR: chrono::TimeDelta = chrono::Duration::seconds(15552000000);
|
||||
|
||||
let local_users = user::Entity::find()
|
||||
.filter(user::Column::Host.is_null())
|
||||
.count(db);
|
||||
let local_active_halfyear = user::Entity::find()
|
||||
.filter(user::Column::Host.is_null())
|
||||
.filter(user::Column::LastActiveDate.gt(now - HALF_YEAR))
|
||||
.count(db);
|
||||
let local_active_month = user::Entity::find()
|
||||
.filter(user::Column::Host.is_null())
|
||||
.filter(user::Column::LastActiveDate.gt(now - MONTH))
|
||||
.count(db);
|
||||
let local_posts = note::Entity::find()
|
||||
.filter(note::Column::UserHost.is_null())
|
||||
.count(db);
|
||||
|
||||
tokio::try_join!(
|
||||
local_users,
|
||||
local_active_halfyear,
|
||||
local_active_month,
|
||||
local_posts
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_new_nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
||||
let (local_users, local_active_halfyear, local_active_month, local_posts) =
|
||||
statistics().await?;
|
||||
let meta = fetch_meta(true).await?;
|
||||
let metadata = HashMap::from([
|
||||
(
|
||||
"nodeName".to_string(),
|
||||
json!(meta.name.unwrap_or(CONFIG.host.clone())),
|
||||
),
|
||||
("nodeDescription".to_string(), json!(meta.description)),
|
||||
("repositoryUrl".to_string(), json!(meta.repository_url)),
|
||||
(
|
||||
"enableLocalTimeline".to_string(),
|
||||
json!(!meta.disable_local_timeline),
|
||||
),
|
||||
(
|
||||
"enableRecommendedTimeline".to_string(),
|
||||
json!(!meta.disable_recommended_timeline),
|
||||
),
|
||||
(
|
||||
"enableGlobalTimeline".to_string(),
|
||||
json!(!meta.disable_global_timeline),
|
||||
),
|
||||
(
|
||||
"enableGuestTimeline".to_string(),
|
||||
json!(meta.enable_guest_timeline),
|
||||
),
|
||||
("maintainerName".to_string(), json!(meta.maintainer_name)),
|
||||
("maintainerEmail".to_string(), json!(meta.maintainer_email)),
|
||||
("proxyAccountName".to_string(), json!(meta.proxy_account_id)),
|
||||
(
|
||||
"themeColor".to_string(),
|
||||
json!(meta.theme_color.unwrap_or("#31748f".to_string())),
|
||||
),
|
||||
]);
|
||||
|
||||
Ok(Nodeinfo21 {
|
||||
version: "2.1".to_string(),
|
||||
software: Software21 {
|
||||
name: "firefish".to_string(),
|
||||
version: CONFIG.version.clone(),
|
||||
repository: Some(meta.repository_url),
|
||||
homepage: Some("https://firefish.dev/firefish/firefish".to_string()),
|
||||
},
|
||||
protocols: vec![Protocol::Activitypub],
|
||||
services: Services {
|
||||
inbound: vec![],
|
||||
outbound: vec![Outbound::Atom1, Outbound::Rss2],
|
||||
},
|
||||
open_registrations: !meta.disable_registration,
|
||||
usage: Usage {
|
||||
users: Users {
|
||||
total: Some(local_users),
|
||||
active_halfyear: Some(local_active_halfyear),
|
||||
active_month: Some(local_active_month),
|
||||
},
|
||||
local_posts: Some(local_posts),
|
||||
local_comments: None,
|
||||
},
|
||||
metadata,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
||||
const NODEINFO_2_1_CACHE_KEY: &str = "nodeinfo_2_1";
|
||||
|
||||
let cached = cache::get::<Nodeinfo21>(NODEINFO_2_1_CACHE_KEY)?;
|
||||
|
||||
if let Some(nodeinfo) = cached {
|
||||
Ok(nodeinfo)
|
||||
} else {
|
||||
let nodeinfo = get_new_nodeinfo_2_1().await?;
|
||||
cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60)?;
|
||||
Ok(nodeinfo)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, Error> {
|
||||
Ok(nodeinfo_2_1().await?.into())
|
||||
}
|
||||
|
||||
#[crate::export(js_name = "nodeinfo_2_1")]
|
||||
pub async fn nodeinfo_2_1_as_json() -> Result<serde_json::Value, Error> {
|
||||
Ok(serde_json::to_value(nodeinfo_2_1().await?)?)
|
||||
}
|
||||
|
||||
#[crate::export(js_name = "nodeinfo_2_0")]
|
||||
pub async fn nodeinfo_2_0_as_json() -> Result<serde_json::Value, Error> {
|
||||
Ok(serde_json::to_value(nodeinfo_2_0().await?)?)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
// https://gist.github.com/tkrotoff/a6baf96eb6b61b445a9142e5555511a0
|
||||
import type { Primitive } from "type-fest";
|
||||
|
||||
type NullToUndefined<T> = T extends null
|
||||
export type NullToUndefined<T> = T extends null
|
||||
? undefined
|
||||
: T extends Primitive | Function | Date | RegExp
|
||||
? T
|
||||
|
@ -15,7 +15,7 @@ type NullToUndefined<T> = T extends null
|
|||
? { [K in keyof T]: NullToUndefined<T[K]> }
|
||||
: unknown;
|
||||
|
||||
type UndefinedToNull<T> = T extends undefined
|
||||
export type UndefinedToNull<T> = T extends undefined
|
||||
? null
|
||||
: T extends Primitive | Function | Date | RegExp
|
||||
? T
|
||||
|
@ -47,6 +47,16 @@ function _nullToUndefined<T>(obj: T): NullToUndefined<T> {
|
|||
return obj as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively converts all null values to undefined.
|
||||
*
|
||||
* @param obj object to convert
|
||||
* @returns a copy of the object with all its null values converted to undefined
|
||||
*/
|
||||
export function fromRustObject<T>(obj: T) {
|
||||
return _nullToUndefined(structuredClone(obj));
|
||||
}
|
||||
|
||||
function _undefinedToNull<T>(obj: T): UndefinedToNull<T> {
|
||||
if (obj === undefined) {
|
||||
return null as any;
|
||||
|
@ -71,6 +81,6 @@ function _undefinedToNull<T>(obj: T): UndefinedToNull<T> {
|
|||
* @param obj object to convert
|
||||
* @returns a copy of the object with all its undefined values converted to null
|
||||
*/
|
||||
export function undefinedToNull<T>(obj: T) {
|
||||
export function toRustObject<T>(obj: T) {
|
||||
return _undefinedToNull(structuredClone(obj));
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import Router from "@koa/router";
|
||||
import { config } from "@/config.js";
|
||||
import { fetchMeta } from "backend-rs";
|
||||
import { Users, Notes } from "@/models/index.js";
|
||||
import { IsNull, MoreThan } from "typeorm";
|
||||
import { Cache } from "@/misc/cache.js";
|
||||
import { nodeinfo_2_0, nodeinfo_2_1 } from "backend-rs";
|
||||
import { fromRustObject } from "@/prelude/undefined-to-null.js";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
|
@ -22,101 +20,14 @@ export const links = [
|
|||
},
|
||||
];
|
||||
|
||||
const nodeinfo2 = async () => {
|
||||
const now = Date.now();
|
||||
const [meta, total, activeHalfyear, activeMonth, localPosts] =
|
||||
await Promise.all([
|
||||
fetchMeta(false),
|
||||
Users.count({ where: { host: IsNull() } }),
|
||||
Users.count({
|
||||
where: {
|
||||
host: IsNull(),
|
||||
lastActiveDate: MoreThan(new Date(now - 15552000000)),
|
||||
},
|
||||
}),
|
||||
Users.count({
|
||||
where: {
|
||||
host: IsNull(),
|
||||
lastActiveDate: MoreThan(new Date(now - 2592000000)),
|
||||
},
|
||||
}),
|
||||
Notes.count({ where: { userHost: IsNull() } }),
|
||||
]);
|
||||
|
||||
const proxyAccount = meta.proxyAccountId
|
||||
? await Users.pack(meta.proxyAccountId).catch(() => null)
|
||||
: null;
|
||||
|
||||
return {
|
||||
software: {
|
||||
name: "firefish",
|
||||
version: config.version,
|
||||
repository: meta.repositoryUrl,
|
||||
homepage: "https://firefish.dev/firefish/firefish",
|
||||
},
|
||||
protocols: ["activitypub"],
|
||||
services: {
|
||||
inbound: [] as string[],
|
||||
outbound: ["atom1.0", "rss2.0"],
|
||||
},
|
||||
openRegistrations: !meta.disableRegistration,
|
||||
usage: {
|
||||
users: { total, activeHalfyear, activeMonth },
|
||||
localPosts,
|
||||
localComments: 0,
|
||||
},
|
||||
metadata: {
|
||||
nodeName: meta.name,
|
||||
nodeDescription: meta.description,
|
||||
maintainer: {
|
||||
name: meta.maintainerName,
|
||||
email: meta.maintainerEmail,
|
||||
},
|
||||
langs: meta.langs,
|
||||
tosUrl: meta.tosUrl,
|
||||
repositoryUrl: meta.repositoryUrl,
|
||||
feedbackUrl: meta.feedbackUrl,
|
||||
disableRegistration: meta.disableRegistration,
|
||||
disableLocalTimeline: meta.disableLocalTimeline,
|
||||
disableRecommendedTimeline: meta.disableRecommendedTimeline,
|
||||
disableGlobalTimeline: meta.disableGlobalTimeline,
|
||||
emailRequiredForSignup: meta.emailRequiredForSignup,
|
||||
postEditing: true,
|
||||
postImports: meta.experimentalFeatures?.postImports || false,
|
||||
enableHcaptcha: meta.enableHcaptcha,
|
||||
enableRecaptcha: meta.enableRecaptcha,
|
||||
maxNoteTextLength: config.maxNoteLength,
|
||||
maxCaptionTextLength: config.maxCaptionLength,
|
||||
enableEmail: meta.enableEmail,
|
||||
enableServiceWorker: meta.enableServiceWorker,
|
||||
proxyAccountName: proxyAccount ? proxyAccount.username : null,
|
||||
themeColor: meta.themeColor || "#31748f",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const cache = new Cache<Awaited<ReturnType<typeof nodeinfo2>>>(
|
||||
"nodeinfo",
|
||||
60 * 10,
|
||||
);
|
||||
|
||||
router.get(nodeinfo2_1path, async (ctx) => {
|
||||
const base = await cache.fetch(null, () => nodeinfo2());
|
||||
|
||||
ctx.body = { version: "2.1", ...base };
|
||||
ctx.set("Cache-Control", "public, max-age=600");
|
||||
ctx.body = fromRustObject(await nodeinfo_2_1());
|
||||
ctx.set("Cache-Control", "public, max-age=3600");
|
||||
});
|
||||
|
||||
router.get(nodeinfo2_0path, async (ctx) => {
|
||||
const base = await cache.fetch(null, () => nodeinfo2());
|
||||
|
||||
// @ts-ignore
|
||||
base.software.repository = undefined;
|
||||
// @ts-ignore
|
||||
base.software.homepage = undefined;
|
||||
|
||||
ctx.body = { version: "2.0", ...base };
|
||||
ctx.set("Cache-Control", "public, max-age=600");
|
||||
ctx.body = fromRustObject(await nodeinfo_2_0());
|
||||
ctx.set("Cache-Control", "public, max-age=3600");
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -66,7 +66,7 @@ import { Mutex } from "redis-semaphore";
|
|||
import { langmap } from "@/misc/langmap.js";
|
||||
import Logger from "@/services/logger.js";
|
||||
import { inspect } from "node:util";
|
||||
import { undefinedToNull } from "@/prelude/undefined-to-null.js";
|
||||
import { toRustObject } from "@/prelude/undefined-to-null.js";
|
||||
|
||||
const logger = new Logger("create-note");
|
||||
|
||||
|
@ -404,7 +404,7 @@ export default async (
|
|||
checkHitAntenna(antenna, note, user).then((hit) => {
|
||||
if (hit) {
|
||||
// TODO: do this more sanely
|
||||
addNoteToAntenna(antenna.id, undefinedToNull(note) as Note);
|
||||
addNoteToAntenna(antenna.id, toRustObject(note));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue