Merge branch 'redis' into 'develop'

Make Redis accessible from backend-rs


See merge request firefish/firefish!10753
This commit is contained in:
naskya 2024-04-21 22:23:29 +00:00
commit cbd15fb2ca
194 changed files with 989 additions and 479 deletions

63
Cargo.lock generated
View file

@ -220,12 +220,14 @@ dependencies = [
"parse-display",
"pretty_assertions",
"rand",
"redis",
"regex",
"schemars",
"sea-orm",
"serde",
"serde_json",
"serde_yaml",
"strum 0.26.2",
"thiserror",
"tokio",
"url",
@ -524,6 +526,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "const-oid"
version = "0.9.6"
@ -1858,6 +1870,21 @@ dependencies = [
"getrandom",
]
[[package]]
name = "redis"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd"
dependencies = [
"combine",
"itoa",
"percent-encoding",
"ryu",
"sha1_smol",
"socket2",
"url",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
@ -2070,6 +2097,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
[[package]]
name = "ryu"
version = "1.0.17"
@ -2150,7 +2183,7 @@ dependencies = [
"serde",
"serde_json",
"sqlx",
"strum",
"strum 0.25.0",
"thiserror",
"time",
"tracing",
@ -2295,6 +2328,12 @@ dependencies = [
"digest",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sha2"
version = "0.10.8"
@ -2676,6 +2715,28 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
[[package]]
name = "strum"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.58",
]
[[package]]
name = "subtle"
version = "2.5.0"

View file

@ -26,12 +26,14 @@ pretty_assertions = "1.4.0"
proc-macro2 = "1.0.79"
quote = "1.0.36"
rand = "0.8.5"
redis = "0.25.3"
regex = "1.10.4"
schemars = "0.8.16"
sea-orm = "0.12.15"
serde = "1.0.197"
serde_json = "1.0.115"
serde_yaml = "0.9.34"
strum = "0.26.2"
syn = "2.0.58"
thiserror = "1.0.58"
tokio = "1.37.0"

View file

@ -30,12 +30,14 @@ jsonschema = { workspace = true }
once_cell = { workspace = true }
parse-display = { workspace = true }
rand = { workspace = true }
redis = { workspace = true }
regex = { workspace = true }
schemars = { workspace = true, features = ["chrono"] }
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
strum = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["full"] }
url = { workspace = true }

View file

@ -6,6 +6,7 @@ SRC += $(call recursive_wildcard, src, *)
.PHONY: regenerate-entities
regenerate-entities:
rm --recursive --force src/model/entity
sea-orm-cli generate entity \
--output-dir='src/model/entity' \
--database-url='postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:25432/$(POSTGRES_DB)' \
@ -16,8 +17,9 @@ regenerate-entities:
jsname=$$(printf '%s\n' "$${base%.*}" | perl -pe 's/(^|_)./uc($$&)/ge;s/_//g'); \
attribute=$$(printf 'cfg_attr(feature = "napi", napi_derive::napi(object, js_name = "%s", use_nullable = true))' "$${jsname}"); \
sed -i "s/NAPI_EXTRA_ATTR_PLACEHOLDER/$${attribute}/" "$${file}"; \
sed -i 's/#\[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)\]/#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = "camelCase")]/' "$${file}"; \
done
sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]/' \
sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = "camelCase")]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]/' \
src/model/entity/sea_orm_active_enums.rs
cargo fmt --all --

View file

@ -12,7 +12,7 @@ export interface EnvConfig {
withLogTime: boolean
slow: boolean
}
export function readEnvironmentConfig(): EnvConfig
export function loadEnv(): EnvConfig
export interface ServerConfig {
url: string
port: number
@ -29,7 +29,7 @@ export interface ServerConfig {
/** `NapiValue` is not implemented for `u64` */
maxFileSize?: number
accessLog?: string
clusterLimits?: WorkerConfig
clusterLimits?: WorkerConfigInternal
cuid?: IdConfig
outgoingAddress?: string
deliverJobConcurrency?: number
@ -70,13 +70,17 @@ export interface RedisConfig {
pass?: string
tls?: TlsConfig
db: number
prefix: string
prefix?: string
}
export interface TlsConfig {
host: string
rejectUnauthorized: boolean
}
export interface WorkerConfig {
web: number
queue: number
}
export interface WorkerConfigInternal {
web?: number
queue?: number
}
@ -121,7 +125,68 @@ export interface ObjectStorageConfig {
setPublicReadOnUpload?: boolean
s3ForcePathStyle?: boolean
}
export function readServerConfig(): ServerConfig
export interface Config {
url: string
port: number
bind?: string
disableHsts?: boolean
db: DbConfig
redis: RedisConfig
cacheServer?: RedisConfig
proxy?: string
proxySmtp?: string
proxyBypassHosts?: Array<string>
allowedPrivateNetworks?: Array<string>
maxFileSize?: number
accessLog?: string
clusterLimits: WorkerConfig
cuid?: IdConfig
outgoingAddress?: string
deliverJobConcurrency?: number
inboxJobConcurrency?: number
deliverJobPerSec?: number
inboxJobPerSec?: number
deliverJobMaxAttempts?: number
inboxJobMaxAttempts?: number
logLevel?: Array<string>
syslog?: SysLogConfig
proxyRemoteFiles?: boolean
mediaProxy?: string
summalyProxyUrl?: string
reservedUsernames?: Array<string>
maxUserSignups?: number
isManagedHosting?: boolean
maxNoteLength?: number
maxCaptionLength?: number
deepl?: DeepLConfig
libreTranslate?: LibreTranslateConfig
email?: EmailConfig
objectStorage?: ObjectStorageConfig
version: string
host: string
hostname: string
redisKeyPrefix: string
scheme: string
wsScheme: string
apiUrl: string
wsUrl: string
authUrl: string
driveUrl: string
userAgent: string
clientEntry: Manifest
}
export interface Manifest {
file: string
name: string
src: string
isEntry: boolean
isDynamicEntry: boolean
imports: Array<string>
dynamicImports: Array<string>
css: Array<string>
assets: Array<string>
}
export function loadConfig(): Config
export interface Acct {
username: string
host: string | null
@ -1039,6 +1104,7 @@ export interface Webhook {
latestSentAt: Date | null
latestStatus: number | null
}
export function addNoteToAntenna(antennaId: string, note: Note): void
/** Initializes Cuid2 generator. Must be called before any [create_id]. */
export function initIdGenerator(length: number, fingerprint: string): void
export function getTimestamp(id: string): number

View file

@ -310,10 +310,10 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
const { readEnvironmentConfig, readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
const { loadEnv, loadConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, addNoteToAntenna, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
module.exports.readEnvironmentConfig = readEnvironmentConfig
module.exports.readServerConfig = readServerConfig
module.exports.loadEnv = loadEnv
module.exports.loadConfig = loadConfig
module.exports.stringToAcct = stringToAcct
module.exports.acctToString = acctToString
module.exports.checkWordMute = checkWordMute
@ -349,6 +349,7 @@ module.exports.RelayStatusEnum = RelayStatusEnum
module.exports.UserEmojimodpermEnum = UserEmojimodpermEnum
module.exports.UserProfileFfvisibilityEnum = UserProfileFfvisibilityEnum
module.exports.UserProfileMutingnotificationtypesEnum = UserProfileMutingnotificationtypesEnum
module.exports.addNoteToAntenna = addNoteToAntenna
module.exports.initIdGenerator = initIdGenerator
module.exports.getTimestamp = getTimestamp
module.exports.genId = genId

View file

@ -11,7 +11,7 @@ pub struct EnvConfig {
}
#[crate::export]
pub fn read_environment_config() -> EnvConfig {
pub fn load_env() -> EnvConfig {
let node_env = std::env::var("NODE_ENV").unwrap_or_default().to_lowercase();
let is_testing = node_env == "test";

View file

@ -1,2 +1,4 @@
pub use server::CONFIG;
pub mod environment;
pub mod server;

View file

@ -6,7 +6,7 @@ use std::fs;
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object, use_nullable = false)]
pub struct ServerConfig {
struct ServerConfig {
pub url: String,
pub port: u16,
/// host to listen on
@ -25,7 +25,7 @@ pub struct ServerConfig {
/// `NapiValue` is not implemented for `u64`
pub max_file_size: Option<i64>,
pub access_log: Option<String>,
pub cluster_limits: Option<WorkerConfig>,
pub cluster_limits: Option<WorkerConfigInternal>,
pub cuid: Option<IdConfig>,
pub outgoing_address: Option<String>,
@ -82,8 +82,7 @@ pub struct RedisConfig {
pub tls: Option<TlsConfig>,
#[serde(default)]
pub db: u32,
#[serde(default)]
pub prefix: String,
pub prefix: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
@ -94,10 +93,16 @@ pub struct TlsConfig {
pub reject_unauthorized: bool,
}
#[crate::export(object, use_nullable = false)]
pub struct WorkerConfig {
pub web: u32,
pub queue: u32,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object, use_nullable = false)]
pub struct WorkerConfig {
pub struct WorkerConfigInternal {
pub web: Option<u32>,
pub queue: Option<u32>,
}
@ -167,17 +172,207 @@ pub struct ObjectStorageConfig {
pub s3_force_path_style: Option<bool>,
}
#[crate::export]
pub fn read_server_config() -> ServerConfig {
#[crate::export(object, use_nullable = false)]
pub struct Config {
// ServerConfig (from default.yml)
pub url: String,
pub port: u16,
pub bind: Option<String>,
pub disable_hsts: Option<bool>,
pub db: DbConfig,
pub redis: RedisConfig,
pub cache_server: Option<RedisConfig>,
pub proxy: Option<String>,
pub proxy_smtp: Option<String>,
pub proxy_bypass_hosts: Option<Vec<String>>,
pub allowed_private_networks: Option<Vec<String>>,
pub max_file_size: Option<i64>,
pub access_log: Option<String>,
pub cluster_limits: WorkerConfig,
pub cuid: Option<IdConfig>,
pub outgoing_address: Option<String>,
pub deliver_job_concurrency: Option<u32>,
pub inbox_job_concurrency: Option<u32>,
pub deliver_job_per_sec: Option<u32>,
pub inbox_job_per_sec: Option<u32>,
pub deliver_job_max_attempts: Option<u32>,
pub inbox_job_max_attempts: Option<u32>,
pub log_level: Option<Vec<String>>,
pub syslog: Option<SysLogConfig>,
pub proxy_remote_files: Option<bool>,
pub media_proxy: Option<String>,
pub summaly_proxy_url: Option<String>,
pub reserved_usernames: Option<Vec<String>>,
pub max_user_signups: Option<u32>,
pub is_managed_hosting: Option<bool>,
pub max_note_length: Option<u32>,
pub max_caption_length: Option<u32>,
pub deepl: Option<DeepLConfig>,
pub libre_translate: Option<LibreTranslateConfig>,
pub email: Option<EmailConfig>,
pub object_storage: Option<ObjectStorageConfig>,
// Mixin
pub version: String,
pub host: String,
pub hostname: String,
pub redis_key_prefix: String,
pub scheme: String,
pub ws_scheme: String,
pub api_url: String,
pub ws_url: String,
pub auth_url: String,
pub drive_url: String,
pub user_agent: String,
pub client_entry: Manifest,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Meta {
pub version: String,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct ManifestJson {
#[serde(rename = "src/init.ts")]
pub init_ts: Manifest,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object, use_nullable = false)]
pub struct Manifest {
pub file: String,
pub name: String,
pub src: String,
pub is_entry: bool,
pub is_dynamic_entry: bool,
pub imports: Vec<String>,
pub dynamic_imports: Vec<String>,
pub css: Vec<String>,
pub assets: Vec<String>,
}
fn read_config_file() -> ServerConfig {
let cwd = env::current_dir().unwrap();
let yml = fs::File::open(cwd.join("../../.config/default.yml"))
.expect("Failed to open '.config/default.yml'");
let mut data: ServerConfig = serde_yaml::from_reader(yml).expect("Failed to parse yaml");
let mut data: ServerConfig =
serde_yaml::from_reader(yml).expect("Failed to parse .config/default.yml");
data.url = url::Url::parse(&data.url)
.expect("Config url is invalid")
.origin()
.ascii_serialization();
if data.bind.is_none() {
data.bind = std::env::var("BIND").ok()
}
data
}
pub static SERVER_CONFIG: Lazy<ServerConfig> = Lazy::new(read_server_config);
fn read_meta() -> Meta {
let cwd = env::current_dir().unwrap();
let meta_json = fs::File::open(cwd.join("../../built/meta.json"))
.expect("Failed to open 'built/meta.json'");
serde_json::from_reader(meta_json).expect("Failed to parse built/meta.json")
}
fn read_manifest() -> Manifest {
let cwd = env::current_dir().unwrap();
let manifest_json = fs::File::open(cwd.join("../../built/_client_dist_/manifest.json"))
.expect("Failed to open 'built/_client_dist_/manifest.json'");
let manifest: ManifestJson = serde_json::from_reader(manifest_json)
.expect("Failed to parse built/_client_dist_/manifest.json");
manifest.init_ts
}
#[crate::export]
fn load_config() -> Config {
let server_config = read_config_file();
let version = read_meta().version;
let manifest = read_manifest();
let url = url::Url::parse(&server_config.url).expect("Config url is invalid");
let hostname = url
.host_str()
.expect("Hostname is missing in the config url")
.to_owned();
let host = match url.port() {
Some(port) => format!("{}:{}", hostname, port),
None => hostname.clone(),
};
let scheme = url.scheme().to_owned();
let ws_scheme = scheme.replace("http", "ws");
let cluster_limits = match server_config.cluster_limits {
Some(cl) => WorkerConfig {
web: cl.web.unwrap_or(1),
queue: cl.queue.unwrap_or(1),
},
None => WorkerConfig { web: 1, queue: 1 },
};
let redis_key_prefix = if let Some(cache_server) = &server_config.cache_server {
cache_server.prefix.clone()
} else {
server_config.redis.prefix.clone()
}
.unwrap_or(hostname.clone());
Config {
url: server_config.url,
port: server_config.port,
bind: server_config.bind,
disable_hsts: server_config.disable_hsts,
db: server_config.db,
redis: server_config.redis,
cache_server: server_config.cache_server,
proxy: server_config.proxy,
proxy_smtp: server_config.proxy_smtp,
proxy_bypass_hosts: server_config.proxy_bypass_hosts,
allowed_private_networks: server_config.allowed_private_networks,
max_file_size: server_config.max_file_size,
access_log: server_config.access_log,
cluster_limits,
cuid: server_config.cuid,
outgoing_address: server_config.outgoing_address,
deliver_job_concurrency: server_config.deliver_job_concurrency,
inbox_job_concurrency: server_config.inbox_job_concurrency,
deliver_job_per_sec: server_config.deliver_job_per_sec,
inbox_job_per_sec: server_config.inbox_job_per_sec,
deliver_job_max_attempts: server_config.deliver_job_max_attempts,
inbox_job_max_attempts: server_config.inbox_job_max_attempts,
log_level: server_config.log_level,
syslog: server_config.syslog,
proxy_remote_files: server_config.proxy_remote_files,
media_proxy: server_config.media_proxy,
summaly_proxy_url: server_config.summaly_proxy_url,
reserved_usernames: server_config.reserved_usernames,
max_user_signups: server_config.max_user_signups,
is_managed_hosting: server_config.is_managed_hosting,
max_note_length: server_config.max_note_length,
max_caption_length: server_config.max_caption_length,
deepl: server_config.deepl,
libre_translate: server_config.libre_translate,
email: server_config.email,
object_storage: server_config.object_storage,
ws_url: format!("{}://{}", ws_scheme, host),
api_url: format!("{}://{}/api", scheme, host),
auth_url: format!("{}://{}/auth", scheme, host),
drive_url: format!("{}://{}/files", scheme, host),
user_agent: format!("Firefish/{} ({})", version, url),
version,
host,
hostname,
redis_key_prefix,
scheme,
ws_scheme,
client_entry: manifest,
}
}
pub static CONFIG: Lazy<Config> = Lazy::new(load_config);

View file

@ -1,34 +1,6 @@
use crate::config::server::SERVER_CONFIG;
use sea_orm::{Database, DbConn, DbErr};
pub use postgresql::db_conn;
pub use redis::key as redis_key;
pub use redis::redis_conn;
static DB_CONN: once_cell::sync::OnceCell<DbConn> = once_cell::sync::OnceCell::new();
async fn init_database() -> Result<&'static DbConn, DbErr> {
let database_uri = format!(
"postgres://{}:{}@{}:{}/{}",
SERVER_CONFIG.db.user,
urlencoding::encode(&SERVER_CONFIG.db.pass),
SERVER_CONFIG.db.host,
SERVER_CONFIG.db.port,
SERVER_CONFIG.db.db,
);
let conn = Database::connect(database_uri).await?;
Ok(DB_CONN.get_or_init(move || conn))
}
pub async fn db_conn() -> Result<&'static DbConn, DbErr> {
match DB_CONN.get() {
Some(conn) => Ok(conn),
None => init_database().await,
}
}
#[cfg(test)]
mod unit_test {
use super::db_conn;
#[tokio::test]
async fn connect_test() {
assert!(db_conn().await.is_ok());
}
}
pub mod postgresql;
pub mod redis;

View file

@ -0,0 +1,35 @@
use crate::config::CONFIG;
use sea_orm::{Database, DbConn, DbErr};
static DB_CONN: once_cell::sync::OnceCell<DbConn> = once_cell::sync::OnceCell::new();
async fn init_database() -> Result<&'static DbConn, DbErr> {
let database_uri = format!(
"postgres://{}:{}@{}:{}/{}",
CONFIG.db.user,
urlencoding::encode(&CONFIG.db.pass),
CONFIG.db.host,
CONFIG.db.port,
CONFIG.db.db,
);
let conn = Database::connect(database_uri).await?;
Ok(DB_CONN.get_or_init(move || conn))
}
pub async fn db_conn() -> Result<&'static DbConn, DbErr> {
match DB_CONN.get() {
Some(conn) => Ok(conn),
None => init_database().await,
}
}
#[cfg(test)]
mod unit_test {
use super::db_conn;
#[tokio::test]
async fn connect() {
assert!(db_conn().await.is_ok());
assert!(db_conn().await.is_ok());
}
}

View file

@ -0,0 +1,68 @@
use crate::config::CONFIG;
use redis::{Client, Connection, RedisError};
static REDIS_CLIENT: once_cell::sync::OnceCell<Client> = once_cell::sync::OnceCell::new();
fn init_redis() -> Result<Client, RedisError> {
let redis_url = {
let mut params = vec!["redis://".to_owned()];
let redis = if let Some(cache_server) = &CONFIG.cache_server {
cache_server
} else {
&CONFIG.redis
};
if let Some(user) = &redis.user {
params.push(user.to_string())
}
if let Some(pass) = &redis.pass {
params.push(format!(":{}@", pass))
}
params.push(redis.host.to_string());
params.push(format!(":{}", redis.port));
params.push(format!("/{}", redis.db));
params.concat()
};
Client::open(redis_url)
}
pub fn redis_conn() -> Result<Connection, RedisError> {
match REDIS_CLIENT.get() {
Some(client) => Ok(client.get_connection()?),
None => init_redis()?.get_connection(),
}
}
#[inline]
/// prefix redis key
pub fn key(key: impl ToString) -> String {
format!("{}:{}", CONFIG.redis_key_prefix, key.to_string())
}
#[cfg(test)]
mod unit_test {
use super::redis_conn;
use pretty_assertions::assert_eq;
use redis::Commands;
#[test]
fn connect() {
assert!(redis_conn().is_ok());
assert!(redis_conn().is_ok());
}
#[test]
fn access() {
let mut redis = redis_conn().unwrap();
let key = "CARGO_UNIT_TEST_KEY";
let value = "CARGO_UNIT_TEST_VALUE";
assert_eq!(redis.set::<&str, &str, String>(key, value).unwrap(), "OK");
assert_eq!(redis.get::<&str, String>(key).unwrap(), value);
assert_eq!(redis.del::<&str, u32>(key).unwrap(), 1);
}
}

View file

@ -4,4 +4,5 @@ pub mod config;
pub mod database;
pub mod misc;
pub mod model;
pub mod service;
pub mod util;

View file

@ -1,4 +1,4 @@
use crate::config::server::SERVER_CONFIG;
use crate::config::CONFIG;
#[derive(thiserror::Error, Debug)]
pub enum Error {
@ -14,21 +14,21 @@ pub enum Error {
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(&SERVER_CONFIG.url)?),
None => format!("{}@{}", username, extract_host(&CONFIG.url)?),
})
}
#[crate::export]
pub fn is_self_host(host: Option<&str>) -> Result<bool, Error> {
Ok(match host {
Some(host) => extract_host(&SERVER_CONFIG.url)? == to_puny(host)?,
Some(host) => extract_host(&CONFIG.url)? == to_puny(host)?,
None => true,
})
}
#[crate::export]
pub fn is_same_origin(uri: &str) -> Result<bool, Error> {
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == SERVER_CONFIG.url)
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == CONFIG.url)
}
#[crate::export]

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "abuse_user_report")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "access_token")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "ad")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "announcement")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "announcement_read")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::AntennaSrcEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "antenna")]
#[cfg_attr(
feature = "napi",

View file

@ -1,49 +0,0 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)]
#[sea_orm(table_name = "antenna_note")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "noteId")]
pub note_id: String,
#[sea_orm(column_name = "antennaId")]
pub antenna_id: String,
pub read: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::antenna::Entity",
from = "Column::AntennaId",
to = "super::antenna::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Antenna,
#[sea_orm(
belongs_to = "super::note::Entity",
from = "Column::NoteId",
to = "super::note::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Note,
}
impl Related<super::antenna::Entity> for Entity {
fn to() -> RelationDef {
Relation::Antenna.def()
}
}
impl Related<super::note::Entity> for Entity {
fn to() -> RelationDef {
Relation::Note.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "app")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "attestation_challenge")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "auth_session")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "blocking")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "channel")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "channel_following")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "channel_note_pining")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "clip")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "clip_note")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::DriveFileUsageHintEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "drive_file")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "drive_folder")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "emoji")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "follow_request")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "following")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "gallery_like")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "gallery_post")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "hashtag")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "instance")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "messaging_message")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "meta")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "migrations")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "moderation_log")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::MutedNoteReasonEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "muted_note")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "muting")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::NoteVisibilityEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note_edit")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note_favorite")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note_file")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note_reaction")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note_thread_muting")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note_unread")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "note_watching")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::NotificationTypeEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "notification")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::PageVisibilityEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "page")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "page_like")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "password_reset_request")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::PollNotevisibilityEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "poll")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "poll_vote")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "promo_note")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "promo_read")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "registration_ticket")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "registry_item")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::RelayStatusEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "relay")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "renote_muting")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "reply_muting")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,10 @@
use sea_orm::entity::prelude::*;
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "antenna_src_enum")]
@ -20,7 +23,10 @@ pub enum AntennaSrcEnum {
#[sea_orm(string_value = "users")]
Users,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -34,7 +40,10 @@ pub enum DriveFileUsageHintEnum {
#[sea_orm(string_value = "userBanner")]
UserBanner,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -52,7 +61,10 @@ pub enum MutedNoteReasonEnum {
#[sea_orm(string_value = "word")]
Word,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -72,7 +84,10 @@ pub enum NoteVisibilityEnum {
#[sea_orm(string_value = "specified")]
Specified,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -106,7 +121,10 @@ pub enum NotificationTypeEnum {
#[sea_orm(string_value = "reply")]
Reply,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -122,7 +140,10 @@ pub enum PageVisibilityEnum {
#[sea_orm(string_value = "specified")]
Specified,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -140,7 +161,10 @@ pub enum PollNotevisibilityEnum {
#[sea_orm(string_value = "specified")]
Specified,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "relay_status_enum")]
@ -152,7 +176,10 @@ pub enum RelayStatusEnum {
#[sea_orm(string_value = "requesting")]
Requesting,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -170,7 +197,10 @@ pub enum UserEmojimodpermEnum {
#[sea_orm(string_value = "unauthorized")]
Unauthorized,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
@ -186,7 +216,10 @@ pub enum UserProfileFfvisibilityEnum {
#[sea_orm(string_value = "public")]
Public,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[derive(
Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "signin")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "sw_subscription")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "used_username")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::UserEmojimodpermEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_group")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_group_invitation")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_group_invite")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_group_joining")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_ip")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_keypair")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_list")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_list_joining")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_note_pining")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_pending")]
#[cfg_attr(
feature = "napi",

View file

@ -4,7 +4,8 @@ use super::sea_orm_active_enums::UserProfileFfvisibilityEnum;
use super::sea_orm_active_enums::UserProfileMutingnotificationtypesEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_profile")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_publickey")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "user_security_key")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "webhook")]
#[cfg_attr(
feature = "napi",

View file

@ -0,0 +1,23 @@
use crate::database::{redis_conn, redis_key};
use crate::model::entity::note;
use crate::service::stream::{publish_to_stream, Error, Stream};
use crate::util::id::get_timestamp;
use redis::{streams::StreamMaxlen, Commands};
type Note = note::Model;
#[crate::export]
pub fn add_note_to_antenna(antenna_id: String, note: &Note) -> Result<(), Error> {
redis_conn()?.xadd_maxlen(
redis_key(format!("antennaTimeline:{}", antenna_id)),
StreamMaxlen::Approx(200),
format!("{}-*", get_timestamp(&note.id)),
&[("note", &note.id)],
)?;
publish_to_stream(
&Stream::Antenna { antenna_id },
Some("note"),
Some(serde_json::to_string(note)?),
)
}

View file

@ -0,0 +1,2 @@
pub mod add_note_to_antenna;
pub mod stream;

View file

@ -0,0 +1,93 @@
use crate::config::CONFIG;
use crate::database::redis_conn;
use redis::{Commands, RedisError};
#[derive(strum::Display)]
pub enum Stream {
#[strum(serialize = "internal")]
Internal,
#[strum(serialize = "broadcast")]
Broadcast,
#[strum(to_string = "adminStream:{user_id}")]
Admin { user_id: String },
#[strum(to_string = "user:{user_id}")]
User { user_id: String },
#[strum(to_string = "channelStream:{channel_id}")]
Channel { channel_id: String },
#[strum(to_string = "noteStream:{note_id}")]
Note { note_id: String },
#[strum(serialize = "notesStream")]
Notes,
#[strum(to_string = "userListStream:{list_id}")]
UserList { list_id: String },
#[strum(to_string = "mainStream:{user_id}")]
Main { user_id: String },
#[strum(to_string = "driveStream:{user_id}")]
Drive { user_id: String },
#[strum(to_string = "antennaStream:{antenna_id}")]
Antenna { antenna_id: String },
#[strum(to_string = "messagingStream:{sender_user_id}-{receiver_user_id}")]
Chat {
sender_user_id: String,
receiver_user_id: String,
},
#[strum(to_string = "messagingStream:{group_id}")]
GroupChat { group_id: String },
#[strum(to_string = "messagingIndexStream:{user_id}")]
MessagingIndex { user_id: String },
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Redis error: {0}")]
RedisError(#[from] RedisError),
#[error("Json (de)serialization error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Value error: {0}")]
ValueError(String),
}
pub fn publish_to_stream(
stream: &Stream,
kind: Option<&str>,
value: Option<String>,
) -> Result<(), Error> {
let message = if let Some(kind) = kind {
format!(
"{{ \"type\": \"{}\", \"body\": {} }}",
kind,
value.unwrap_or("null".to_string()),
)
} else {
value.ok_or(Error::ValueError("Invalid streaming message".to_string()))?
};
redis_conn()?.publish(
&CONFIG.host,
format!(
"{{ \"channel\": \"{}\", \"message\": {} }}",
stream, message,
),
)?;
Ok(())
}
#[cfg(test)]
mod unit_test {
use super::Stream;
use pretty_assertions::assert_eq;
#[test]
fn channel_to_string() {
assert_eq!(Stream::Internal.to_string(), "internal");
assert_eq!(Stream::Broadcast.to_string(), "broadcast");
assert_eq!(
Stream::Admin {
user_id: "9tb42br63g5apjcq".to_string()
}
.to_string(),
"adminStream:9tb42br63g5apjcq"
);
}
}

View file

@ -178,6 +178,7 @@
"ts-loader": "9.5.1",
"ts-node": "10.9.2",
"tsconfig-paths": "4.2.0",
"type-fest": "4.15.0",
"typescript": "5.4.5",
"webpack": "^5.91.0",
"ws": "8.16.0"

View file

@ -3,7 +3,7 @@ import chalk from "chalk";
import Xev from "xev";
import Logger from "@/services/logger.js";
import { envOption } from "@/config/index.js";
import { envOption } from "@/config.js";
import { inspect } from "node:util";
// for typeorm

View file

@ -8,9 +8,8 @@ import chalkTemplate from "chalk-template";
import semver from "semver";
import Logger from "@/services/logger.js";
import loadConfig from "@/config/load.js";
import type { Config } from "@/config/types.js";
import { envOption } from "@/config/index.js";
import type { Config } from "backend-rs";
import { config, envOption } from "@/config.js";
import { showMachineInfo } from "@/misc/show-machine-info.js";
import { db, initDb } from "@/db/postgre.js";
import { inspect } from "node:util";
@ -87,15 +86,12 @@ function greet() {
* Init master process
*/
export async function masterMain() {
let config!: Config;
// initialize app
try {
greet();
showEnvironment();
await showMachineInfo(bootLogger);
showNodejsVersion();
config = loadConfigBoot();
await connectDb();
} catch (e) {
bootLogger.error(
@ -154,28 +150,6 @@ function showNodejsVersion(): void {
}
}
function loadConfigBoot(): Config {
const configLogger = bootLogger.createSubLogger("config");
let config;
try {
config = loadConfig();
} catch (exception) {
if (exception.code === "ENOENT") {
configLogger.error("Configuration file not found", null, true);
process.exit(1);
} else if (e instanceof Error) {
configLogger.error(e.message);
process.exit(1);
}
throw exception;
}
configLogger.succ("Loaded");
return config;
}
async function connectDb(): Promise<void> {
const dbLogger = bootLogger.createSubLogger("db");
@ -195,23 +169,31 @@ async function connectDb(): Promise<void> {
}
async function spawnWorkers(
clusterLimits: Required<Config["clusterLimits"]>,
clusterLimits: Config["clusterLimits"],
): Promise<void> {
const modes = ["web", "queue"];
const cpus = os.cpus().length;
for (const mode of modes.filter((mode) => clusterLimits[mode] > cpus)) {
if (clusterLimits.queue > cpus) {
bootLogger.warn(
`configuration warning: cluster limit for ${mode} exceeds number of cores (${cpus})`,
"config: queue cluster limit exceeds the number of cpu cores",
);
}
const total = modes.reduce((acc, mode) => acc + clusterLimits[mode], 0);
if (clusterLimits.web > cpus) {
bootLogger.warn(
"config: web cluster limit exceeds the number of cpu cores",
);
}
const total = clusterLimits.queue + clusterLimits.web;
// workers = ["web", "web", ..., "web", "queue", "queue", ..., "queue"]
const workers = new Array(total);
workers.fill("web", 0, clusterLimits?.web);
workers.fill("queue", clusterLimits?.web);
workers.fill("web", 0, clusterLimits.web);
workers.fill("queue", clusterLimits.web);
bootLogger.info(
`Starting ${clusterLimits?.web} web workers and ${clusterLimits?.queue} queue workers (total ${total})...`,
`Starting ${clusterLimits.web} web workers and ${clusterLimits.queue} queue workers (total ${total})...`,
);
await Promise.all(workers.map((mode) => spawnWorker(mode)));
bootLogger.succ("All workers started");

View file

@ -1,5 +1,5 @@
import cluster from "node:cluster";
import config from "@/config/index.js";
import { config } from "@/config.js";
import { initDb } from "@/db/postgre.js";
import { initIdGenerator } from "backend-rs";
import os from "node:os";

View file

@ -0,0 +1,4 @@
import { loadConfig, loadEnv } from "backend-rs";
export const config = loadConfig();
export const envOption = loadEnv();

View file

@ -1,5 +0,0 @@
import load from "./load.js";
import { readEnvironmentConfig } from "backend-rs";
export default load();
export const envOption = readEnvironmentConfig();

View file

@ -1,89 +0,0 @@
/**
* Config loader
*/
import * as fs from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
import type { Mixin } from "./types.js";
import { readServerConfig } from "backend-rs";
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
/**
* Path of configuration directory
*/
const dir = `${_dirname}/../../../../.config`;
/**
* Path of configuration file
*/
const path =
process.env.NODE_ENV === "test" ? `${dir}/test.yml` : `${dir}/default.yml`;
export default function load() {
const meta = JSON.parse(
fs.readFileSync(`${_dirname}/../../../../built/meta.json`, "utf-8"),
);
const clientManifest = JSON.parse(
fs.readFileSync(
`${_dirname}/../../../../built/_client_dist_/manifest.json`,
"utf-8",
),
);
const config = readServerConfig();
const mixin = {} as Mixin;
const url = tryCreateUrl(config.url);
config.url = url.origin;
config.port = config.port || parseInt(process.env.PORT || "", 10);
config.bind = config.bind || process.env.BIND;
mixin.version = meta.version;
mixin.host = url.host;
mixin.hostname = url.hostname;
mixin.scheme = url.protocol.replace(/:$/, "");
mixin.wsScheme = mixin.scheme.replace("http", "ws");
mixin.wsUrl = `${mixin.wsScheme}://${mixin.host}`;
mixin.apiUrl = `${mixin.scheme}://${mixin.host}/api`;
mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`;
mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`;
mixin.userAgent = `Firefish/${meta.version} (${config.url})`;
mixin.clientEntry = clientManifest["src/init.ts"];
if (config.proxyRemoteFiles == null) config.proxyRemoteFiles = true;
if (!config.redis.prefix) config.redis.prefix = mixin.hostname;
if (config.cacheServer && !config.cacheServer.prefix)
config.cacheServer.prefix = mixin.hostname;
if (!config.clusterLimits) {
config.clusterLimits = {
web: 1,
queue: 1,
};
} else {
config.clusterLimits = {
web: 1,
queue: 1,
...config.clusterLimits,
};
if (config.clusterLimits.web! < 1 || config.clusterLimits.queue! < 1) {
throw new Error("Invalid cluster limits");
}
}
return Object.assign(config, mixin);
}
function tryCreateUrl(url: string) {
try {
return new URL(url);
} catch (e) {
throw new Error(`url="${url}" is not a valid URL.`);
}
}

View file

@ -1,20 +0,0 @@
import type { ServerConfig } from "backend-rs";
/**
* Firefish ()
*/
export type Mixin = {
version: string;
host: string;
hostname: string;
scheme: string;
wsScheme: string;
apiUrl: string;
wsUrl: string;
authUrl: string;
driveUrl: string;
userAgent: string;
clientEntry: string;
};
export type Config = ServerConfig & Mixin;

View file

@ -1,4 +1,4 @@
import config from "@/config/index.js";
import { config } from "@/config.js";
import {
DB_MAX_IMAGE_COMMENT_LENGTH,
DB_MAX_NOTE_TEXT_LENGTH,

View file

@ -5,7 +5,7 @@ pg.types.setTypeParser(20, Number);
import type { Logger } from "typeorm";
import { DataSource } from "typeorm";
import * as highlight from "cli-highlight";
import config from "@/config/index.js";
import { config } from "@/config.js";
import { User } from "@/models/entities/user.js";
import { DriveFile } from "@/models/entities/drive-file.js";
@ -77,7 +77,6 @@ import { NoteFile } from "@/models/entities/note-file.js";
import { entities as charts } from "@/services/chart/entities.js";
import { dbLogger } from "./logger.js";
import { redisClient } from "./redis.js";
const sqlLogger = dbLogger.createSubLogger("sql", "gray", false);

View file

@ -1,5 +1,5 @@
import Redis from "ioredis";
import config from "@/config/index.js";
import { config } from "@/config.js";
export function createConnection() {
let source = config.redis;
@ -12,7 +12,7 @@ export function createConnection() {
family: source.family ?? 0,
password: source.pass,
username: source.user ?? "default",
keyPrefix: `${source.prefix}:`,
keyPrefix: `${config.redisKeyPrefix}:`,
db: source.db || 0,
tls: source.tls,
});

View file

@ -1,7 +1,7 @@
import { type HTMLElement, Window } from "happy-dom";
import type * as mfm from "mfm-js";
import katex from "katex";
import config from "@/config/index.js";
import { config } from "@/config.js";
import { intersperse } from "@/prelude/array.js";
import type { IMentionedRemoteUsers } from "@/models/entities/note.js";

Some files were not shown because too many files have changed in this diff Show more