Merge branch 'develop' into iceshrimp_mastodon

This commit is contained in:
naskya 2024-06-07 22:49:33 +09:00
commit 73461617ef
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
11 changed files with 157 additions and 101 deletions

View file

@ -216,6 +216,7 @@ export function acctToString(acct: Acct): string
export function fetchNodeinfo(host: string): Promise<Nodeinfo>
export function nodeinfo_2_1(): Promise<any>
export function nodeinfo_2_0(): Promise<any>
export function updateNodeinfoCache(): Promise<void>
/** NodeInfo schema version 2.0. <https://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.0> */
export interface Nodeinfo {
/** The schema version, must be 2.0. */
@ -241,16 +242,16 @@ export interface Software20 {
version: string
}
export enum Protocol {
Activitypub = 'activitypub',
Buddycloud = 'buddycloud',
Dfrn = 'dfrn',
Diaspora = 'diaspora',
Libertree = 'libertree',
Ostatus = 'ostatus',
Pumpio = 'pumpio',
Tent = 'tent',
Xmpp = 'xmpp',
Zot = 'zot'
Activitypub = 0,
Buddycloud = 1,
Dfrn = 2,
Diaspora = 3,
Libertree = 4,
Ostatus = 5,
Pumpio = 6,
Tent = 7,
Xmpp = 8,
Zot = 9
}
/** The third party sites this server can connect to via their application API. */
export interface Services {
@ -261,45 +262,45 @@ export interface Services {
}
/** The third party sites this server can retrieve messages from for combined display with regular traffic. */
export enum Inbound {
Atom1 = 'atom1',
Gnusocial = 'gnusocial',
Imap = 'imap',
Pnut = 'pnut',
Pop3 = 'pop3',
Pumpio = 'pumpio',
Rss2 = 'rss2',
Twitter = 'twitter'
Atom1 = 0,
Gnusocial = 1,
Imap = 2,
Pnut = 3,
Pop3 = 4,
Pumpio = 5,
Rss2 = 6,
Twitter = 7
}
/** The third party sites this server can publish messages to on the behalf of a user. */
export enum Outbound {
Atom1 = 'atom1',
Blogger = 'blogger',
Buddycloud = 'buddycloud',
Diaspora = 'diaspora',
Dreamwidth = 'dreamwidth',
Drupal = 'drupal',
Facebook = 'facebook',
Friendica = 'friendica',
Gnusocial = 'gnusocial',
Google = 'google',
Insanejournal = 'insanejournal',
Libertree = 'libertree',
Linkedin = 'linkedin',
Livejournal = 'livejournal',
Mediagoblin = 'mediagoblin',
Myspace = 'myspace',
Pinterest = 'pinterest',
Pnut = 'pnut',
Posterous = 'posterous',
Pumpio = 'pumpio',
Redmatrix = 'redmatrix',
Rss2 = 'rss2',
Smtp = 'smtp',
Tent = 'tent',
Tumblr = 'tumblr',
Twitter = 'twitter',
Wordpress = 'wordpress',
Xmpp = 'xmpp'
Atom1 = 0,
Blogger = 1,
Buddycloud = 2,
Diaspora = 3,
Dreamwidth = 4,
Drupal = 5,
Facebook = 6,
Friendica = 7,
Gnusocial = 8,
Google = 9,
Insanejournal = 10,
Libertree = 11,
Linkedin = 12,
Livejournal = 13,
Mediagoblin = 14,
Myspace = 15,
Pinterest = 16,
Pnut = 17,
Posterous = 18,
Pumpio = 19,
Redmatrix = 20,
Rss2 = 21,
Smtp = 22,
Tent = 23,
Tumblr = 24,
Twitter = 25,
Wordpress = 26,
Xmpp = 27
}
/** Usage statistics for this server. */
export interface Usage {

View file

@ -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, fetchMeta, updateMetaCache, metaToPugArgs, loadConfig, stringToAcct, acctToString, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, sqlRegexEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, PushSubscriptionType, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, updateAntennaCache, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, ChatEvent, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, fetchMeta, updateMetaCache, metaToPugArgs, loadConfig, stringToAcct, acctToString, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, updateNodeinfoCache, Protocol, Inbound, Outbound, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, sqlRegexEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, PushSubscriptionType, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, updateAntennaCache, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, ChatEvent, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
module.exports.SECOND = SECOND
module.exports.MINUTE = MINUTE
@ -328,6 +328,7 @@ module.exports.acctToString = acctToString
module.exports.fetchNodeinfo = fetchNodeinfo
module.exports.nodeinfo_2_1 = nodeinfo_2_1
module.exports.nodeinfo_2_0 = nodeinfo_2_0
module.exports.updateNodeinfoCache = updateNodeinfoCache
module.exports.Protocol = Protocol
module.exports.Inbound = Inbound
module.exports.Outbound = Outbound

View file

@ -2,23 +2,20 @@
use crate::{
config::{local_server_info, CONFIG},
database::{cache, db_conn},
database::db_conn,
federation::nodeinfo::schema::*,
model::entity::{note, user},
};
use sea_orm::prelude::*;
use serde_json::json;
use std::collections::HashMap;
use std::{collections::HashMap, sync::Mutex};
/// Errors that can occur while generating NodeInfo of the local server
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Database error: {0}")]
Db(#[from] DbErr),
#[error("Cache error: {0}")]
Cache(#[from] cache::Error),
#[error("Failed to serialize nodeinfo to JSON: {0}")]
Json(#[from] serde_json::Error),
static CACHE: Mutex<Option<Nodeinfo21>> = Mutex::new(None);
fn set_cache(nodeinfo: &Nodeinfo21) {
let _ = CACHE
.lock()
.map(|mut cache| *cache = Some(nodeinfo.to_owned()));
}
/// Fetches the number of total/active local users and local posts.
@ -61,11 +58,13 @@ async fn statistics() -> Result<(u64, u64, u64, u64), DbErr> {
/// Generates NodeInfo (version 2.1) of the local server.
/// This function doesn't use caches and returns the latest information.
async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, DbErr> {
tracing::info!("generating NodeInfo");
let (local_users, local_active_halfyear, local_active_month, local_posts) =
statistics().await?;
let meta = local_server_info().await?;
let metadata = HashMap::from([
let mut metadata = HashMap::from([
(
"nodeName".to_string(),
json!(meta.name.unwrap_or_else(|| CONFIG.host.clone())),
@ -98,6 +97,7 @@ async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
json!(meta.theme_color.unwrap_or_else(|| "#31748f".to_string())),
),
]);
metadata.shrink_to_fit();
Ok(Nodeinfo21 {
version: "2.1".to_string(),
@ -126,26 +126,40 @@ async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
})
}
/// Returns NodeInfo (version 2.1) of the local server.
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).await?;
if let Some(nodeinfo) = cached {
Ok(nodeinfo)
} else {
let nodeinfo = generate_nodeinfo_2_1().await?;
cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60).await?;
Ok(nodeinfo)
async fn nodeinfo_2_1_impl(use_cache: bool) -> Result<Nodeinfo21, DbErr> {
if use_cache {
if let Some(nodeinfo) = CACHE.lock().ok().and_then(|cache| cache.to_owned()) {
return Ok(nodeinfo);
}
}
let nodeinfo = generate_nodeinfo_2_1().await?;
tracing::info!("updating cache");
set_cache(&nodeinfo);
Ok(nodeinfo)
}
/// Returns NodeInfo (version 2.1) of the local server.
pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, DbErr> {
nodeinfo_2_1_impl(true).await
}
/// Returns NodeInfo (version 2.0) of the local server.
pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, Error> {
pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, DbErr> {
Ok(nodeinfo_2_1().await?.into())
}
#[cfg(feature = "napi")]
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Database error: {0}")]
Db(#[from] DbErr),
#[error("Failed to serialize nodeinfo into JSON: {0}")]
Json(#[from] serde_json::Error),
}
#[crate::ts_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?)?)
@ -155,3 +169,9 @@ pub async fn nodeinfo_2_1_as_json() -> Result<serde_json::Value, Error> {
pub async fn nodeinfo_2_0_as_json() -> Result<serde_json::Value, Error> {
Ok(serde_json::to_value(nodeinfo_2_0().await?)?)
}
#[crate::ts_export(js_name = "updateNodeinfoCache")]
pub async fn update_cache() -> Result<(), DbErr> {
nodeinfo_2_1_impl(false).await?;
Ok(())
}

View file

@ -10,8 +10,8 @@ use std::collections::HashMap;
// * #[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>
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[cfg_attr(test, derive(Debug, PartialEq, Deserialize))]
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Nodeinfo21 {
/// The schema version, must be 2.1.
@ -53,8 +53,8 @@ pub struct Nodeinfo20 {
}
/// Metadata about server software in use (version 2.1).
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[cfg_attr(test, derive(Debug, PartialEq, Deserialize))]
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Software21 {
/// The canonical name of this server software.
@ -82,7 +82,7 @@ pub struct Software20 {
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[crate::export(string_enum = "lowercase")]
#[crate::derive_clone_and_export]
pub enum Protocol {
Activitypub,
Buddycloud,
@ -98,7 +98,7 @@ pub enum Protocol {
/// The third party sites this server can connect to via their application API.
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object)]
pub struct Services {
@ -112,7 +112,7 @@ pub struct Services {
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[crate::export(string_enum = "lowercase")]
#[crate::derive_clone_and_export]
pub enum Inbound {
#[serde(rename = "atom1.0")]
Atom1,
@ -131,7 +131,7 @@ pub enum Inbound {
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[crate::export(string_enum = "lowercase")]
#[crate::derive_clone_and_export]
pub enum Outbound {
#[serde(rename = "atom1.0")]
Atom1,
@ -167,7 +167,7 @@ pub enum Outbound {
/// Usage statistics for this server.
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object)]
pub struct Usage {
@ -178,7 +178,7 @@ pub struct Usage {
/// statistics about the users of this server.
#[cfg_attr(test, derive(Debug, PartialEq))]
#[derive(Deserialize, Serialize)]
#[derive(Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object)]
pub struct Users {

View file

@ -1,4 +1,4 @@
use macro_rs::{export, ts_export};
use macro_rs::{derive_clone_and_export, export, ts_export};
pub mod config;
pub mod database;

View file

@ -81,5 +81,7 @@ async fn add_note_to_antenna(antenna_id: &str, note: &Note) -> Result<(), Error>
.await?;
// for streaming API
Ok(stream::antenna::publish(antenna_id.to_string(), note).await?)
stream::antenna::publish(antenna_id.to_string(), note).await?;
Ok(())
}

View file

@ -8,6 +8,7 @@ import {
removeOldAttestationChallenges,
showServerInfo,
updateMetaCache,
updateNodeinfoCache,
type Config,
} from "backend-rs";
import { config } from "@/config.js";
@ -51,6 +52,8 @@ export async function masterMain() {
import("../daemons/queue-stats.js").then((x) => x.default());
// Update meta cache every 5 minitues
setInterval(() => updateMetaCache(), 1000 * 60 * 5);
// Update nodeinfo cache every hour
setInterval(() => updateNodeinfoCache(), 1000 * 60 * 60);
// Remove old attestation challenges
setInterval(() => removeOldAttestationChallenges(), 1000 * 60 * 30);
}

View file

@ -84,7 +84,7 @@
"typescript": "5.4.5",
"unicode-emoji-json": "0.6.0",
"uuid": "9.0.1",
"vite": "5.2.12",
"vite": "5.2.13",
"vite-plugin-compression": "0.5.1",
"vue": "3.4.27",
"vue-draggable-plus": "0.5.0",

View file

@ -25,6 +25,35 @@ pub fn read_version_from_package_json(_item: proc_macro::TokenStream) -> proc_ma
quote! { #version }.into()
}
/// Export an enum to TypeScript, and derive [Clone].
///
/// You need this macro because [`napi_derive::napi`](https://docs.rs/napi-derive/latest/napi_derive/attr.napi.html)
/// automatically derives the [Clone] trait for enums and causes conflicts.
///
/// This is a wrapper of [`napi_derive::napi`](https://docs.rs/napi-derive/latest/napi_derive/attr.napi.html)
/// that expands to
/// ```no_run
/// #[cfg_attr(not(feature = "napi"), derive(Clone))]
/// #[cfg_attr(feature = "napi", napi_derive::napi(attr))]
/// # enum E {} // to work around doc test compilation error
/// ```
/// where `attr` is given attribute(s).
#[proc_macro_attribute]
pub fn derive_clone_and_export(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let attr: TokenStream = attr.into();
let item: TokenStream = item.into();
quote! {
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(#attr))]
#item
}
.into()
}
/// Export a function, struct, enum, const, etc. to TypeScript.
///
/// This is a wrapper of [macro@napi] that expands to

View file

@ -11,7 +11,7 @@
"devDependencies": {
"firefish-js": "workspace:*",
"idb-keyval": "6.2.1",
"vite": "5.2.12",
"vite": "5.2.13",
"vite-plugin-compression": "0.5.1"
}
}

View file

@ -592,7 +592,7 @@ importers:
version: 9.0.8
'@vitejs/plugin-vue':
specifier: 5.0.5
version: 5.0.5(vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
version: 5.0.5(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))
'@vue/runtime-core':
specifier: 3.4.27
version: 3.4.27
@ -744,11 +744,11 @@ importers:
specifier: 9.0.1
version: 9.0.1
vite:
specifier: 5.2.12
version: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
specifier: 5.2.13
version: 5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
vite-plugin-compression:
specifier: 0.5.1
version: 0.5.1(vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))
version: 0.5.1(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))
vue:
specifier: 3.4.27
version: 3.4.27(typescript@5.4.5)
@ -827,11 +827,11 @@ importers:
specifier: 6.2.1
version: 6.2.1
vite:
specifier: 5.2.12
version: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
specifier: 5.2.13
version: 5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
vite-plugin-compression:
specifier: 0.5.1
version: 0.5.1(vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))
version: 0.5.1(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))
packages:
@ -7450,8 +7450,8 @@ packages:
peerDependencies:
vite: '>=2.0.0'
vite@5.2.12:
resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==}
vite@5.2.13:
resolution: {integrity: sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@ -9672,9 +9672,9 @@ snapshots:
'@typescript-eslint/types': 6.21.0
eslint-visitor-keys: 3.4.3
'@vitejs/plugin-vue@5.0.5(vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
'@vitejs/plugin-vue@5.0.5(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0))(vue@3.4.27(typescript@5.4.5))':
dependencies:
vite: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
vite: 5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
vue: 3.4.27(typescript@5.4.5)
'@volar/language-core@2.2.5':
@ -15144,16 +15144,16 @@ snapshots:
core-util-is: 1.0.2
extsprintf: 1.3.0
vite-plugin-compression@0.5.1(vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)):
vite-plugin-compression@0.5.1(vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)):
dependencies:
chalk: 4.1.2
debug: 4.3.4(supports-color@8.1.1)
fs-extra: 10.1.0
vite: 5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
vite: 5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0)
transitivePeerDependencies:
- supports-color
vite@5.2.12(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0):
vite@5.2.13(@types/node@20.14.2)(sass@1.77.4)(stylus@0.57.0)(terser@5.31.0):
dependencies:
esbuild: 0.20.2
postcss: 8.4.38