chore (backend-rs): documents, organize exports, typo fixes
This commit is contained in:
parent
0471df369d
commit
890ca846d0
40 changed files with 284 additions and 97 deletions
|
@ -5,6 +5,6 @@ These are the extensions to ActivityPub that Firefish implements. This page uses
|
|||
## speakAsCat
|
||||
|
||||
- Compact IRI: `firefish:speakAsCat`
|
||||
- Canonical IRI: `https://firefish.dev/ns#speakascat`
|
||||
- Canonical IRI: `https://firefish.dev/ns#speakAsCat`
|
||||
|
||||
Used on actors to indicate that they not only identify as a cat, but also want to have their text be transformed to speak like one, expressed as a boolean value. If this property is set to true, displaying the actor’s posts will make them speak with “nya” instead of “na” and other cat-related text mannerisms. Used in combination with [misskey:isCat](https://misskey-hub.net/ns/#iscat).
|
||||
|
|
96
packages/backend-rs/index.d.ts
vendored
96
packages/backend-rs/index.d.ts
vendored
|
@ -11,8 +11,9 @@ export const USER_ONLINE_THRESHOLD: number
|
|||
export const USER_ACTIVE_THRESHOLD: number
|
||||
/**
|
||||
* List of file types allowed to be viewed directly in the browser
|
||||
*
|
||||
* Anything not included here will be responded as application/octet-stream
|
||||
* SVG is not allowed because it generates XSS <- we need to fix this and later allow it to be viewed directly
|
||||
* SVG is not allowed because it generates XSS (TODO: fix this and later allow it to be viewed directly)
|
||||
* * <https://github.com/sindresorhus/file-type/blob/main/supported.js>
|
||||
* * <https://github.com/sindresorhus/file-type/blob/main/core.js>
|
||||
* * <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers>
|
||||
|
@ -207,6 +208,7 @@ export interface Acct {
|
|||
}
|
||||
export function stringToAcct(acct: string): Acct
|
||||
export function acctToString(acct: Acct): string
|
||||
/** Fetches and returns the NodeInfo of a remote server. */
|
||||
export function fetchNodeinfo(host: string): Promise<Nodeinfo>
|
||||
export function nodeinfo_2_1(): Promise<any>
|
||||
export function nodeinfo_2_0(): Promise<any>
|
||||
|
@ -307,31 +309,86 @@ export interface Users {
|
|||
activeHalfyear: number | null
|
||||
activeMonth: number | null
|
||||
}
|
||||
/** Prints the greeting message and the Firefish version to stdout. */
|
||||
export function greet(): void
|
||||
/** Initializes the [tracing] logger. */
|
||||
export function initializeRustLogger(): void
|
||||
/** Prints the server hardware information as the server info log. */
|
||||
export function showServerInfo(): void
|
||||
/**
|
||||
* Checks if a server is blocked.
|
||||
*
|
||||
* ## Argument
|
||||
* # Argument
|
||||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* # use backend_rs::misc::check_server_block::is_blocked_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_blocked_server("blocked.com").await?);
|
||||
* assert_eq!(false, is_blocked_server("not-blocked.com").await?);
|
||||
* assert_eq!(true, is_blocked_server("subdomain.of.blocked.com").await?);
|
||||
* assert_eq!(true, is_blocked_server("xn--l8jegik.blocked.com").await?);
|
||||
* # Ok(())
|
||||
* # }
|
||||
* ```
|
||||
*/
|
||||
export function isBlockedServer(host: string): Promise<boolean>
|
||||
/**
|
||||
* Checks if a server is silenced.
|
||||
*
|
||||
* ## Argument
|
||||
* # Argument
|
||||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* # use backend_rs::misc::check_server_block::is_silenced_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_silenced_server("silenced.com").await?);
|
||||
* assert_eq!(false, is_silenced_server("not-silenced.com").await?);
|
||||
* assert_eq!(true, is_silenced_server("subdomain.of.silenced.com").await?);
|
||||
* assert_eq!(true, is_silenced_server("xn--l8jegik.silenced.com").await?);
|
||||
* # Ok(())
|
||||
* # }
|
||||
* ```
|
||||
*/
|
||||
export function isSilencedServer(host: string): Promise<boolean>
|
||||
/**
|
||||
* Checks if a server is allowlisted.
|
||||
* Returns `Ok(true)` if private mode is disabled.
|
||||
*
|
||||
* ## Argument
|
||||
* # Argument
|
||||
* `host` - punycoded instance host
|
||||
*
|
||||
* # Example
|
||||
* ```no_run
|
||||
* # use backend_rs::misc::check_server_block::is_allowed_server;
|
||||
* # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
* assert_eq!(true, is_allowed_server("allowed.com").await?);
|
||||
* assert_eq!(false, is_allowed_server("not-allowed.com").await?);
|
||||
* assert_eq!(false, is_allowed_server("subdomain.of.allowed.com").await?);
|
||||
* assert_eq!(false, is_allowed_server("xn--l8jegik.allowed.com").await?);
|
||||
* # Ok(())
|
||||
* # }
|
||||
* ```
|
||||
*/
|
||||
export function isAllowedServer(host: string): Promise<boolean>
|
||||
/**
|
||||
* Returns whether `note` should be hard-muted.
|
||||
*
|
||||
* More specifically, this function returns `Ok(true)`
|
||||
* if and only if one or more of these conditions are met:
|
||||
*
|
||||
* * the note (text or CW) contains any of the words/patterns
|
||||
* * the "parent" note(s) (reply, quote) contain any of the words/patterns
|
||||
* * the alt text of the attached files contains any of the words/patterns
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `note` : [NoteLike] object
|
||||
* * `muted_words` : list of muted keyword lists (each array item is a space-separated keyword list that represents an AND condition)
|
||||
* * `muted_patterns` : list of JavaScript-style (e.g., `/foo/i`) regular expressions
|
||||
*/
|
||||
export function checkWordMute(note: NoteLike, mutedWords: Array<string>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function getFullApAccount(username: string, host?: string | undefined | null): string
|
||||
export function isSelfHost(host?: string | undefined | null): boolean
|
||||
|
@ -339,9 +396,11 @@ export function isSameOrigin(uri: string): boolean
|
|||
export function extractHost(uri: string): string
|
||||
export function toPuny(host: string): string
|
||||
export function isUnicodeEmoji(s: string): boolean
|
||||
/** Escapes `%` and `\` in the given string. */
|
||||
export function sqlLikeEscape(src: string): string
|
||||
/** Returns `true` if `src` does not contain suspicious characters like `%`. */
|
||||
export function safeForSql(src: string): boolean
|
||||
/** Convert milliseconds to a human readable string */
|
||||
/** Converts milliseconds to a human readable string. */
|
||||
export function formatMilliseconds(milliseconds: number): string
|
||||
export interface ImageSize {
|
||||
width: number
|
||||
|
@ -365,6 +424,7 @@ export interface NoteLikeForGetNoteSummary {
|
|||
export function getNoteSummary(note: NoteLikeForGetNoteSummary): string
|
||||
export function isQuote(note: Note): boolean
|
||||
export function isSafeUrl(url: string): boolean
|
||||
/** Returns the latest Firefish version. */
|
||||
export function latestVersion(): Promise<string>
|
||||
export function toMastodonId(firefishId: string): string | null
|
||||
export function fromMastodonId(mastodonId: string): string | null
|
||||
|
@ -381,9 +441,31 @@ export interface PugArgs {
|
|||
privateMode: boolean | null
|
||||
}
|
||||
export function metaToPugArgs(meta: Meta): PugArgs
|
||||
/**
|
||||
* Converts the given text into the cat language.
|
||||
*
|
||||
* refs:
|
||||
* * <https://misskey-hub.net/ns/#isCat>
|
||||
* * <https://firefish.dev/ns#speakAsCat>
|
||||
*
|
||||
* # Arguments
|
||||
*
|
||||
* * `text` : original text
|
||||
* * `lang` : language code (e.g., `Some("en")`, `Some("en-US")`, `Some("uk-UA")`, `None`)
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* ```
|
||||
* # use backend_rs::misc::nyaify::nyaify;
|
||||
* assert_eq!(nyaify("I'll take a nap.", Some("en")), "I'll take a nyap.");
|
||||
* ```
|
||||
*/
|
||||
export function nyaify(text: string, lang?: string | undefined | null): string
|
||||
/** Hashes the given password using [Argon2] algorithm. */
|
||||
export function hashPassword(password: string): string
|
||||
/** Checks whether the given password and hash match. */
|
||||
export function verifyPassword(password: string, hash: string): boolean
|
||||
/** Returns whether the [bcrypt] algorithm is used for the password hash. */
|
||||
export function isOldPasswordAlgorithm(hash: string): boolean
|
||||
export interface DecodedReaction {
|
||||
reaction: string
|
||||
|
@ -393,7 +475,7 @@ export interface DecodedReaction {
|
|||
export function decodeReaction(reaction: string): DecodedReaction
|
||||
export function countReactions(reactions: Record<string, number>): Record<string, number>
|
||||
export function toDbReaction(reaction?: string | undefined | null, host?: string | undefined | null): Promise<string>
|
||||
/** Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago */
|
||||
/** Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago */
|
||||
export function removeOldAttestationChallenges(): Promise<void>
|
||||
export interface Cpu {
|
||||
model: string
|
||||
|
@ -1336,6 +1418,6 @@ export function getTimestamp(id: string): number
|
|||
export function genId(): string
|
||||
/** Generate an ID using a specific datetime */
|
||||
export function genIdAt(date: Date): string
|
||||
/** Generate random string based on [thread_rng] and [Alphanumeric]. */
|
||||
/** Generates a random string based on [thread_rng] and [Alphanumeric]. */
|
||||
export function generateSecureRandomString(length: number): string
|
||||
export function generateUserToken(): string
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const SECOND: i32 = 1000;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const MINUTE: i32 = 60 * SECOND;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const HOUR: i32 = 60 * MINUTE;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const DAY: i32 = 24 * HOUR;
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const USER_ONLINE_THRESHOLD: i32 = 10 * MINUTE;
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const USER_ACTIVE_THRESHOLD: i32 = 3 * DAY;
|
||||
|
||||
/// List of file types allowed to be viewed directly in the browser
|
||||
///
|
||||
/// Anything not included here will be responded as application/octet-stream
|
||||
/// SVG is not allowed because it generates XSS <- we need to fix this and later allow it to be viewed directly
|
||||
/// SVG is not allowed because it generates XSS (TODO: fix this and later allow it to be viewed directly)
|
||||
/// * <https://github.com/sindresorhus/file-type/blob/main/supported.js>
|
||||
/// * <https://github.com/sindresorhus/file-type/blob/main/core.js>
|
||||
/// * <https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers>
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub const FILE_TYPE_BROWSERSAFE: [&str; 41] = [
|
||||
// Images
|
||||
"image/png",
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Server configurations and environment variables
|
||||
|
||||
pub use server::CONFIG;
|
||||
|
||||
pub mod constant;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Utilities for using Redis cache
|
||||
|
||||
use crate::database::{redis_conn, redis_key, RedisConnError};
|
||||
use redis::{AsyncCommands, RedisError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
//! Interfaces for accessing PostgreSQL and Redis
|
||||
|
||||
pub use postgresql::db_conn;
|
||||
pub use redis::key as redis_key;
|
||||
pub use redis::redis_conn;
|
||||
pub use redis::RedisConnError;
|
||||
|
||||
/// Utilities for using Redis cache
|
||||
pub mod cache;
|
||||
/// PostgreSQL interface
|
||||
pub mod postgresql;
|
||||
/// Redis interface
|
||||
pub mod redis;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! PostgreSQL interface
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sea_orm::{ConnectOptions, Database, DbConn, DbErr};
|
||||
|
@ -5,7 +7,7 @@ use tracing::log::LevelFilter;
|
|||
|
||||
static DB_CONN: OnceCell<DbConn> = OnceCell::new();
|
||||
|
||||
async fn init_database() -> Result<&'static DbConn, DbErr> {
|
||||
async fn init_conn() -> Result<&'static DbConn, DbErr> {
|
||||
let database_uri = format!(
|
||||
"postgres://{}:{}@{}:{}/{}",
|
||||
CONFIG.db.user,
|
||||
|
@ -24,10 +26,11 @@ async fn init_database() -> Result<&'static DbConn, DbErr> {
|
|||
Ok(DB_CONN.get_or_init(move || conn))
|
||||
}
|
||||
|
||||
/// Returns an async PostgreSQL connection that can be used with [sea_orm] utilities.
|
||||
pub async fn db_conn() -> Result<&'static DbConn, DbErr> {
|
||||
match DB_CONN.get() {
|
||||
Some(conn) => Ok(conn),
|
||||
None => init_database().await,
|
||||
None => init_conn().await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
//! Redis interface
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use async_trait::async_trait;
|
||||
use bb8::{ManageConnection, Pool, PooledConnection, RunError};
|
||||
use redis::{aio::MultiplexedConnection, Client, ErrorKind, IntoConnectionInfo, RedisError};
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
/// A `bb8::ManageConnection` for `redis::Client::get_multiplexed_async_connection`.
|
||||
/// A [bb8::ManageConnection] for [redis::Client::get_multiplexed_async_connection].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RedisConnectionManager {
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl RedisConnectionManager {
|
||||
/// Create a new `RedisConnectionManager`.
|
||||
/// See `redis::Client::open` for a description of the parameter types.
|
||||
/// Creates a new [RedisConnectionManager].
|
||||
/// See [redis::Client::open] for a description of the parameter types.
|
||||
pub fn new<T: IntoConnectionInfo>(info: T) -> Result<Self, RedisError> {
|
||||
Ok(Self {
|
||||
client: Client::open(info.into_connection_info()?)?,
|
||||
|
@ -85,6 +87,7 @@ pub enum RedisConnError {
|
|||
Bb8Pool(RunError<RedisError>),
|
||||
}
|
||||
|
||||
/// Returns an async [redis] connection managed by a [bb8] connection pool.
|
||||
pub async fn redis_conn(
|
||||
) -> Result<PooledConnection<'static, RedisConnectionManager>, RedisConnError> {
|
||||
if !CONN_POOL.initialized() {
|
||||
|
@ -103,7 +106,7 @@ pub async fn redis_conn(
|
|||
.map_err(RedisConnError::Bb8Pool)
|
||||
}
|
||||
|
||||
/// prefix redis key
|
||||
/// prefix Redis key
|
||||
#[inline]
|
||||
pub fn key(key: impl ToString) -> String {
|
||||
format!("{}:{}", CONFIG.redis_key_prefix, key.to_string())
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
//! Services used to federate with other servers
|
||||
|
||||
pub mod acct;
|
||||
pub mod nodeinfo;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! NodeInfo fetcher
|
||||
|
||||
use crate::federation::nodeinfo::schema::*;
|
||||
use crate::util::http_client;
|
||||
use isahc::AsyncReadResponseExt;
|
||||
|
@ -83,6 +85,7 @@ async fn fetch_nodeinfo_impl(nodeinfo_link: &str) -> Result<Nodeinfo20, Error> {
|
|||
// for napi export
|
||||
type Nodeinfo = Nodeinfo20;
|
||||
|
||||
/// Fetches and returns the NodeInfo of a remote server.
|
||||
#[crate::export]
|
||||
pub async fn fetch_nodeinfo(host: &str) -> Result<Nodeinfo, Error> {
|
||||
tracing::info!("fetching from {}", host);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! NodeInfo generator
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use crate::database::{cache, db_conn};
|
||||
use crate::federation::nodeinfo::schema::*;
|
||||
|
@ -112,6 +114,7 @@ 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";
|
||||
|
||||
|
@ -126,6 +129,7 @@ pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, Error> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns NodeInfo (version 2.0) of the local server.
|
||||
pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, Error> {
|
||||
Ok(nodeinfo_2_1().await?.into())
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! NodeInfo handler
|
||||
|
||||
pub mod fetch;
|
||||
pub mod generate;
|
||||
mod schema;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
//! Schema definitions of NodeInfo version 2.0 and 2.1
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
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)
|
||||
// * #[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, PartialEq)]
|
||||
|
|
|
@ -11,6 +11,7 @@ const GREETING_MESSAGE: &str = "\
|
|||
If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish
|
||||
";
|
||||
|
||||
/// Prints the greeting message and the Firefish version to stdout.
|
||||
#[crate::export]
|
||||
pub fn greet() {
|
||||
println!("{}", GREETING_MESSAGE);
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::config::CONFIG;
|
|||
use tracing::Level;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
/// Initializes the [tracing] logger.
|
||||
#[crate::export(js_name = "initializeRustLogger")]
|
||||
pub fn initialize_logger() {
|
||||
let mut builder = FmtSubscriber::builder();
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Initializers
|
||||
|
||||
pub mod greet;
|
||||
pub mod log;
|
||||
pub mod system_info;
|
||||
|
|
|
@ -5,10 +5,21 @@ pub type SysinfoPoisonError = PoisonError<MutexGuard<'static, System>>;
|
|||
|
||||
static SYSTEM_INFO: OnceLock<Mutex<System>> = OnceLock::new();
|
||||
|
||||
pub fn system_info() -> &'static std::sync::Mutex<sysinfo::System> {
|
||||
/// Gives an access to the shared static [System] object.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::init::system_info::{system_info, SysinfoPoisonError};
|
||||
/// let system_info = system_info().lock()?;
|
||||
/// println!("The number of CPU threads is {}.", system_info.cpus().len());
|
||||
/// # Ok::<(), SysinfoPoisonError>(())
|
||||
/// ```
|
||||
pub fn system_info() -> &'static std::sync::Mutex<System> {
|
||||
SYSTEM_INFO.get_or_init(|| Mutex::new(System::new_all()))
|
||||
}
|
||||
|
||||
/// Prints the server hardware information as the server info log.
|
||||
#[crate::export]
|
||||
pub fn show_server_info() -> Result<(), SysinfoPoisonError> {
|
||||
let system_info = system_info().lock()?;
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
use macro_rs::{export, ts_export};
|
||||
|
||||
/// Server configurations and environment variables
|
||||
pub mod config;
|
||||
/// Interfaces for accessing PostgreSQL and Redis
|
||||
pub mod database;
|
||||
/// Services used to federate with other servers
|
||||
pub mod federation;
|
||||
/// Initializers
|
||||
pub mod init;
|
||||
/// Miscellaneous utilities
|
||||
pub mod misc;
|
||||
/// Database structure, auto-generated by [sea_orm]
|
||||
pub mod model;
|
||||
/// Services provided for local users
|
||||
pub mod service;
|
||||
/// Basic utilities such as ID generator and HTTP client
|
||||
pub mod util;
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
use crate::misc::meta::fetch_meta;
|
||||
use sea_orm::DbErr;
|
||||
//! This module is used in the TypeScript backend only.
|
||||
// We may want to (re)implement these functions in the `federation` module
|
||||
// in a Rusty way (e.g., traits of server type) if needed.
|
||||
|
||||
/// Checks if a server is blocked.
|
||||
///
|
||||
/// ## Argument
|
||||
/// # Argument
|
||||
/// `host` - punycoded instance host
|
||||
#[crate::export]
|
||||
pub async fn is_blocked_server(host: &str) -> Result<bool, DbErr> {
|
||||
Ok(fetch_meta(true)
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use backend_rs::misc::check_server_block::is_blocked_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_blocked_server("blocked.com").await?);
|
||||
/// assert_eq!(false, is_blocked_server("not-blocked.com").await?);
|
||||
/// assert_eq!(true, is_blocked_server("subdomain.of.blocked.com").await?);
|
||||
/// assert_eq!(true, is_blocked_server("xn--l8jegik.blocked.com").await?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[crate::ts_export]
|
||||
pub async fn is_blocked_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
||||
Ok(crate::misc::meta::fetch_meta(true)
|
||||
.await?
|
||||
.blocked_hosts
|
||||
.iter()
|
||||
|
@ -18,11 +31,23 @@ pub async fn is_blocked_server(host: &str) -> Result<bool, DbErr> {
|
|||
|
||||
/// Checks if a server is silenced.
|
||||
///
|
||||
/// ## Argument
|
||||
/// # Argument
|
||||
/// `host` - punycoded instance host
|
||||
#[crate::export]
|
||||
pub async fn is_silenced_server(host: &str) -> Result<bool, DbErr> {
|
||||
Ok(fetch_meta(true)
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use backend_rs::misc::check_server_block::is_silenced_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_silenced_server("silenced.com").await?);
|
||||
/// assert_eq!(false, is_silenced_server("not-silenced.com").await?);
|
||||
/// assert_eq!(true, is_silenced_server("subdomain.of.silenced.com").await?);
|
||||
/// assert_eq!(true, is_silenced_server("xn--l8jegik.silenced.com").await?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[crate::ts_export]
|
||||
pub async fn is_silenced_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
||||
Ok(crate::misc::meta::fetch_meta(true)
|
||||
.await?
|
||||
.silenced_hosts
|
||||
.iter()
|
||||
|
@ -34,11 +59,23 @@ pub async fn is_silenced_server(host: &str) -> Result<bool, DbErr> {
|
|||
/// Checks if a server is allowlisted.
|
||||
/// Returns `Ok(true)` if private mode is disabled.
|
||||
///
|
||||
/// ## Argument
|
||||
/// # Argument
|
||||
/// `host` - punycoded instance host
|
||||
#[crate::export]
|
||||
pub async fn is_allowed_server(host: &str) -> Result<bool, DbErr> {
|
||||
let meta = fetch_meta(true).await?;
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use backend_rs::misc::check_server_block::is_allowed_server;
|
||||
/// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// assert_eq!(true, is_allowed_server("allowed.com").await?);
|
||||
/// assert_eq!(false, is_allowed_server("not-allowed.com").await?);
|
||||
/// assert_eq!(false, is_allowed_server("subdomain.of.allowed.com").await?);
|
||||
/// assert_eq!(false, is_allowed_server("xn--l8jegik.allowed.com").await?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[crate::ts_export]
|
||||
pub async fn is_allowed_server(host: &str) -> Result<bool, sea_orm::DbErr> {
|
||||
let meta = crate::misc::meta::fetch_meta(true).await?;
|
||||
|
||||
if !meta.private_mode.unwrap_or(false) {
|
||||
return Ok(true);
|
||||
|
|
|
@ -26,6 +26,20 @@ fn check_word_mute_impl(
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns whether `note` should be hard-muted.
|
||||
///
|
||||
/// More specifically, this function returns `Ok(true)`
|
||||
/// if and only if one or more of these conditions are met:
|
||||
///
|
||||
/// * the note (text or CW) contains any of the words/patterns
|
||||
/// * the "parent" note(s) (reply, quote) contain any of the words/patterns
|
||||
/// * the alt text of the attached files contains any of the words/patterns
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `note` : [NoteLike] object
|
||||
/// * `muted_words` : list of muted keyword lists (each array item is a space-separated keyword list that represents an AND condition)
|
||||
/// * `muted_patterns` : list of JavaScript-style (e.g., `/foo/i`) regular expressions
|
||||
#[crate::export]
|
||||
pub async fn check_word_mute(
|
||||
note: NoteLike,
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::config::CONFIG;
|
||||
//! This module is used in the TypeScript backend only.
|
||||
// We may want to (re)implement these functions in the `federation` module
|
||||
// in a Rusty way (e.g., traits of actor type) if needed.
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -10,28 +12,28 @@ pub enum Error {
|
|||
NoHostname,
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn get_full_ap_account(username: &str, host: Option<&str>) -> Result<String, Error> {
|
||||
Ok(match host {
|
||||
Some(host) => format!("{}@{}", username, to_puny(host)?),
|
||||
None => format!("{}@{}", username, extract_host(&CONFIG.url)?),
|
||||
None => format!("{}@{}", username, extract_host(&crate::config::CONFIG.url)?),
|
||||
})
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn is_self_host(host: Option<&str>) -> Result<bool, Error> {
|
||||
Ok(match host {
|
||||
Some(host) => extract_host(&CONFIG.url)? == to_puny(host)?,
|
||||
Some(host) => extract_host(&crate::config::CONFIG.url)? == to_puny(host)?,
|
||||
None => true,
|
||||
})
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn is_same_origin(uri: &str) -> Result<bool, Error> {
|
||||
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == CONFIG.url)
|
||||
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == crate::config::CONFIG.url)
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn extract_host(uri: &str) -> Result<String, Error> {
|
||||
url::Url::parse(uri)?
|
||||
.host_str()
|
||||
|
@ -39,29 +41,7 @@ pub fn extract_host(uri: &str) -> Result<String, Error> {
|
|||
.and_then(|v| Ok(to_puny(v)?))
|
||||
}
|
||||
|
||||
#[crate::export]
|
||||
#[crate::ts_export]
|
||||
pub fn to_puny(host: &str) -> Result<String, idna::Errors> {
|
||||
idna::domain_to_ascii(host)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::{extract_host, to_puny};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn extract_host_test() {
|
||||
assert_eq!(
|
||||
extract_host("https://firefish.dev/firefish/firefish.git").unwrap(),
|
||||
"firefish.dev"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_puny_test() {
|
||||
assert_eq!(
|
||||
to_puny("何もかも.owari.shop").unwrap(),
|
||||
"xn--u8jyfb5762a.owari.shop"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! This module is used in the TypeScript backend only.
|
||||
|
||||
#[crate::ts_export]
|
||||
pub fn is_unicode_emoji(s: &str) -> bool {
|
||||
emojis::get(s).is_some()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
/// Escapes `%` and `\` in the given string.
|
||||
#[crate::export]
|
||||
pub fn sql_like_escape(src: &str) -> String {
|
||||
src.replace('%', r"\%").replace('_', r"\_")
|
||||
}
|
||||
|
||||
/// Returns `true` if `src` does not contain suspicious characters like `%`.
|
||||
#[crate::export]
|
||||
pub fn safe_for_sql(src: &str) -> bool {
|
||||
!src.contains([
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// Convert milliseconds to a human readable string
|
||||
/// Converts milliseconds to a human readable string.
|
||||
#[crate::export]
|
||||
pub fn format_milliseconds(milliseconds: u32) -> String {
|
||||
let mut seconds = milliseconds / 1000;
|
||||
|
|
|
@ -18,8 +18,8 @@ pub struct NoteLike {
|
|||
///
|
||||
/// ## Arguments
|
||||
///
|
||||
/// * `note` - [NoteLike] object
|
||||
/// * `include_parent` - whether to take the reply-to post and quoted post into account
|
||||
/// * `note` : [NoteLike] object
|
||||
/// * `include_parent` : whether to take the reply-to post and quoted post into account
|
||||
pub async fn all_texts(note: NoteLike, include_parent: bool) -> Result<Vec<String>, DbErr> {
|
||||
let db = db_conn().await?;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Fetch latest Firefish version from the Firefish repository
|
||||
|
||||
use crate::database::cache;
|
||||
use crate::util::http_client;
|
||||
use isahc::ReadResponseExt;
|
||||
|
@ -43,6 +45,7 @@ async fn get_latest_version() -> Result<String, Error> {
|
|||
Ok(res_parsed.version)
|
||||
}
|
||||
|
||||
/// Returns the latest Firefish version.
|
||||
#[crate::export]
|
||||
pub async fn latest_version() -> Result<String, Error> {
|
||||
let version: Option<String> =
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Miscellaneous utilities
|
||||
|
||||
pub mod check_server_block;
|
||||
pub mod check_word_mute;
|
||||
pub mod convert_host;
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
//! Cat language converter
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::{Captures, Regex};
|
||||
|
||||
/// Converts the given text into the cat language.
|
||||
///
|
||||
/// refs:
|
||||
/// * <https://misskey-hub.net/ns/#isCat>
|
||||
/// * <https://firefish.dev/ns#speakAsCat>
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `text` : original text
|
||||
/// * `lang` : language code (e.g., `Some("en")`, `Some("en-US")`, `Some("uk-UA")`, `None`)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use backend_rs::misc::nyaify::nyaify;
|
||||
/// assert_eq!(nyaify("I'll take a nap.", Some("en")), "I'll take a nyap.");
|
||||
/// ```
|
||||
#[crate::export]
|
||||
pub fn nyaify(text: &str, lang: Option<&str>) -> String {
|
||||
let mut to_return = text.to_owned();
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
//! Utilities for password hash generation and verification
|
||||
|
||||
use argon2::{
|
||||
password_hash,
|
||||
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||
Argon2,
|
||||
};
|
||||
|
||||
/// Hashes the given password using [Argon2] algorithm.
|
||||
#[crate::export]
|
||||
pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Error> {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
|
@ -13,7 +16,7 @@ pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Er
|
|||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum VerifyError {
|
||||
pub enum Error {
|
||||
#[error("An error occured while bcrypt verification: {0}")]
|
||||
Bcrypt(#[from] bcrypt::BcryptError),
|
||||
#[error("Invalid argon2 password hash: {0}")]
|
||||
|
@ -22,8 +25,9 @@ pub enum VerifyError {
|
|||
Argon2(#[from] argon2::Error),
|
||||
}
|
||||
|
||||
/// Checks whether the given password and hash match.
|
||||
#[crate::export]
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, VerifyError> {
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, Error> {
|
||||
if is_old_password_algorithm(hash) {
|
||||
Ok(bcrypt::verify(password, hash)?)
|
||||
} else {
|
||||
|
@ -34,6 +38,7 @@ pub fn verify_password(password: &str, hash: &str) -> Result<bool, VerifyError>
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether the [bcrypt] algorithm is used for the password hash.
|
||||
#[inline]
|
||||
#[crate::export]
|
||||
pub fn is_old_password_algorithm(hash: &str) -> bool {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::database::db_conn;
|
||||
use crate::misc::convert_host::to_puny;
|
||||
use crate::misc::meta::fetch_meta;
|
||||
use crate::model::entity::emoji;
|
||||
use once_cell::sync::Lazy;
|
||||
|
@ -87,7 +86,7 @@ pub async fn to_db_reaction(reaction: Option<&str>, host: Option<&str>) -> Resul
|
|||
|
||||
if let Some(host) = host {
|
||||
// remote emoji
|
||||
let ascii_host = to_puny(host)?;
|
||||
let ascii_host = idna::domain_to_ascii(host)?;
|
||||
|
||||
// TODO: Does SeaORM have the `exists` method?
|
||||
if emoji::Entity::find()
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::model::entity::attestation_challenge;
|
|||
use chrono::{Duration, Utc};
|
||||
use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter};
|
||||
|
||||
/// Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago
|
||||
/// Delete all entries in the [attestation_challenge] table created at more than 5 minutes ago
|
||||
#[crate::export]
|
||||
pub async fn remove_old_attestation_challenges() -> Result<(), DbErr> {
|
||||
let res = attestation_challenge::Entity::delete_many()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Utilities to check hardware information such as cpu, memory, storage usage
|
||||
|
||||
use crate::init::system_info::{system_info, SysinfoPoisonError};
|
||||
use sysinfo::{Disks, MemoryRefreshKind};
|
||||
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
//! Database structure, auto-generated by [sea_orm]
|
||||
|
||||
pub mod entity;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Services provided for local users
|
||||
|
||||
pub mod antenna;
|
||||
pub mod note;
|
||||
pub mod push_notification;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Shared [isahc] HTTP client
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use isahc::{config::*, HttpClient};
|
||||
use once_cell::sync::OnceCell;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! ID generation utility based on [cuid2]
|
||||
|
||||
use crate::config::CONFIG;
|
||||
use basen::BASE36;
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/// Shared [isahc] HTTP client
|
||||
//! Basic utilities such as ID generator and HTTP client
|
||||
|
||||
pub mod http_client;
|
||||
/// ID generation utility based on [cuid2]
|
||||
pub mod id;
|
||||
/// Secure random string generator
|
||||
pub mod random;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Secure random string generator
|
||||
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
|
||||
/// Generate random string based on [thread_rng] and [Alphanumeric].
|
||||
/// Generates a random string based on [thread_rng] and [Alphanumeric].
|
||||
#[crate::export]
|
||||
pub fn generate_secure_random_string(length: u16) -> String {
|
||||
thread_rng()
|
||||
|
|
|
@ -94,6 +94,7 @@ export default async (ctx: Koa.Context) => {
|
|||
// Compare passwords
|
||||
const same = verifyPassword(password, profile.password!);
|
||||
|
||||
// Update the password hashing algorithm
|
||||
if (same && isOldPasswordAlgorithm(profile.password!)) {
|
||||
profile.password = hashPassword(password);
|
||||
await UserProfiles.save(profile);
|
||||
|
|
|
@ -97,16 +97,16 @@ pub fn ts_export(
|
|||
/// ```
|
||||
/// #[macro_rs::napi(object)]
|
||||
/// struct Person {
|
||||
/// id: i32,
|
||||
/// name: String,
|
||||
/// id: i32,
|
||||
/// name: String,
|
||||
/// }
|
||||
/// ```
|
||||
/// simply becomes
|
||||
/// ```
|
||||
/// #[napi_derive::napi(use_nullable = true, object)]
|
||||
/// struct Person {
|
||||
/// id: i32,
|
||||
/// name: String,
|
||||
/// id: i32,
|
||||
/// name: String,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
|
Loading…
Reference in a new issue