Linstrom is moving to Go!

Reason: Fuck AGPL
This commit is contained in:
mStar aka a person 2024-01-16 21:43:57 +01:00
parent f9509e7791
commit 33bdd96fb1
32 changed files with 41 additions and 4053 deletions

View file

@ -1,36 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/rust
{
"name": "Rust",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye",
"features": {
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {},
"ghcr.io/itsmechlark/features/postgresql:1": {},
"ghcr.io/robbert229/devcontainer-features/postgresql-client:1": {}
},
// Use 'mounts' to make the cargo cache persistent in a Docker Volume.
// "mounts": [
// {
// "source": "devcontainer-cargo-cache-${devcontainerId}",
// "target": "/usr/local/cargo",
// "type": "volume"
// }
// ]
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "cargo install diesel_cli --no-default-features --features postgres,sqlite"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

3047
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,33 +0,0 @@
[package]
name = "linstrom"
version = "0.1.0"
edition = "2021"
authors = ["mStar"]
license = "EUPL-1.2"
description = "An ActivityPub enabled social media server focused on helping with user safety"
readme = "README.md"
repository = "https://gitlab.com/mstarongitlab/linstrom"
exclude = ["/.vscode", "/FeatureTargets.md"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
activitypub_federation = "0.5.0"
axum = "0.7.1"
diesel = { version = "2.1.4", features = ["sqlite", "postgres", "r2d2", "chrono"] }
tokio = { version = "1.34.0", features = ["full"]}
build_html = "2.4.0"
serde = "1.0.193"
url = "2.5.0"
chrono = "0.4.31"
tracing = "0.1.40"
rand = "0.8.5"
anyhow = "1.0.77"
async-trait = "0.1.75"
dotenvy = "0.15.7"
clap = { version = "4.4.12", features = ["derive"] }
toml = "0.8.8"
serde_derive = "1.0.193"
sysinfo = "0.30.3"
diesel_migrations = "2.1.0"
serde_json = "1.0.111"

View file

@ -1,20 +1,25 @@
# UI
# Feature targets
## UI
- Firefish like
- htmx
# Features
## Features
### QoL
## QoL
- [ ] Reactions
- [ ] Qoute tweets
- [ ] misskey/firefish markdown
## Moderation
### Moderation
- [ ] Authorized Fetch
- [ ] Lockdown (user/server) (block/ignore any incoming messages)
- [ ] Filter-based (maybe regex) auto-block/refuse follow requests
## General
### General
- [ ] Masto, MK/FF, Akoma/Plemora API
- [ ] is_cat

View file

@ -1,4 +0,0 @@
[config]
database_path = "src/storage/db.sqlite"
max_workers = 100
min_workers = 200

View file

@ -1,9 +0,0 @@
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/storage/pg_schemas.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]
[migrations_directory]
dir = "migrations"

14
go.mod Normal file
View file

@ -0,0 +1,14 @@
module gitlab.com/beckersam/linstrom
go 1.21.5
require (
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 // indirect
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/valyala/fastjson v1.6.4 // indirect
gorm.io/gorm v1.25.5 // indirect
)

16
go.sum Normal file
View file

@ -0,0 +1,16 @@
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 h1:j2TrkUG/NATGi/EQS+MvEoF79CxiRUmT16ErFroNcKI=
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9/go.mod h1:cJ9Ye0ZNSMN7RzZDBRY3E+8M3Bpf/R1JX22Ir9yX6WI=
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 h1:I2nuhyVI/48VXoRCCZR2hYBgnSXa+EuDJf/VyX06TC0=
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7/go.mod h1:5x8a6P/dhmMGFxWLcyYlyOuJ2lRNaHGhRv+yu8BaTSI=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

View file

View file

@ -1,6 +0,0 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at();

View file

@ -1,36 +0,0 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
-- Sets up a trigger for the given table to automatically set a column called
-- `updated_at` whenever the row is modified (unless `updated_at` was included
-- in the modified columns)
--
-- # Example
--
-- ```sql
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
--
-- SELECT diesel_manage_updated_at('users');
-- ```
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
BEGIN
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
BEGIN
IF (
NEW IS DISTINCT FROM OLD AND
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
) THEN
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

View file

@ -1,2 +0,0 @@
-- This file should undo anything in `up.sql`
DROP TABLE people;

View file

@ -1,13 +0,0 @@
create table people (
ap_id text primary key,
name text not null,
instance text,
mail text,
pw_hash text,
inbox text not null,
public_key text not null,
private_key text,
last_refreshed_at timestamp not null,
local boolean not null,
followers text[] not null
)

View file

@ -1 +0,0 @@
-- This file should undo anything in `up.sql`

View file

@ -1 +0,0 @@
-- Your SQL goes here

View file

@ -1,14 +0,0 @@
// Routes
// User info: /:user
// Inbox: /:user/inbox
// Webfinger: /.well-known/webfinger
use axum::Router;
pub fn register(router: Router) -> Router {
router
}
fn get_user() -> String {
"Foo".into()
}

View file

@ -1,370 +0,0 @@
// admin/meta
// admin/abuse-user-reports
// admin/accounts/create
// admin/accounts/delete
// admin/accounts/find-by-email
// admin/ad/create
// admin/ad/delete
// admin/ad/list
// admin/ad/update
// admin/announcements/create
// admin/announcements/delete
// admin/announcements/list
// admin/announcements/update
// admin/avatar-decorations/create
// admin/avatar-decorations/delete
// admin/avatar-decorations/list
// admin/avatar-decorations/update
// admin/delete-all-files-of-a-user
// admin/unset-user-avatar
// admin/unset-user-banner
// admin/drive/clean-remote-files
// admin/drive/cleanup
// admin/drive/files
// admin/drive/show-file
// admin/emoji/add-aliases-bulk
// admin/emoji/add
// admin/emoji/copy
// admin/emoji/delete-bulk
// admin/emoji/delete
// admin/emoji/import-zip
// admin/emoji/list-remote
// admin/emoji/list
// admin/emoji/remove-aliases-bulk
// admin/emoji/set-aliases-bulk
// admin/emoji/set-category-bulk
// admin/emoji/set-license-bulk
// admin/emoji/update
// admin/federation/delete-all-files
// admin/federation/refresh-remote-instance-metadata
// admin/federation/remove-all-following
// admin/federation/update-instance
// admin/get-index-stats
// admin/get-table-stats
// admin/get-user-ips
// admin/invite/create
// admin/invite/list
// admin/promo/create
// admin/queue/clear
// admin/queue/deliver-delayed
// admin/queue/inbox-delayed
// admin/queue/promote
// admin/queue/stats
// admin/relays/add
// admin/relays/list
// admin/relays/remove
// admin/reset-password
// admin/resolve-abuse-user-report
// admin/send-email
// admin/server-info
// admin/show-moderation-logs
// admin/show-user
// admin/show-users
// admin/nsfw-user
// admin/unnsfw-user
// admin/silence-user
// admin/unsilence-user
// admin/suspend-user
// admin/approve-user
// admin/unsuspend-user
// admin/update-meta
// admin/delete-account
// admin/update-user-note
// admin/roles/create
// admin/roles/delete
// admin/roles/list
// admin/roles/show
// admin/roles/update
// admin/roles/assign
// admin/roles/unassign
// admin/roles/update-default-policies
// admin/roles/users
// announcements
// antennas/create
// antennas/delete
// antennas/list
// antennas/notes
// antennas/show
// antennas/update
// ap/get
// ap/show
// app/create
// app/show
// auth/accept
// auth/session/generate
// auth/session/show
// auth/session/userkey
// blocking/create
// blocking/delete
// blocking/list
// channels/create
// channels/featured
// channels/follow
// channels/followed
// channels/owned
// channels/show
// channels/timeline
// channels/unfollow
// channels/update
// channels/favorite
// channels/unfavorite
// channels/my-favorites
// channels/search
// charts/active-users
// charts/ap-request
// charts/drive
// charts/federation
// charts/instance
// charts/notes
// charts/user/drive
// charts/user/following
// charts/user/notes
// charts/user/pv
// charts/user/reactions
// charts/users
// clips/add-note
// clips/remove-note
// clips/create
// clips/delete
// clips/list
// clips/notes
// clips/show
// clips/update
// clips/favorite
// clips/unfavorite
// clips/my-favorites
// drive
// drive/files
// drive/files/attached-notes
// drive/files/check-existence
// drive/files/create
// drive/files/delete
// drive/files/find-by-hash
// drive/files/find
// drive/files/show
// drive/files/update
// drive/files/upload-from-url
// drive/folders
// drive/folders/create
// drive/folders/delete
// drive/folders/find
// drive/folders/show
// drive/folders/update
// drive/stream
// email-address/available
// endpoint
// endpoints
// export-custom-emojis
// federation/followers
// federation/following
// federation/instances
// federation/show-instance
// federation/update-remote-user
// federation/users
// federation/stats
// following/create
// following/delete
// following/update
// following/update-all
// following/invalidate
// following/requests/accept
// following/requests/cancel
// following/requests/list
// following/requests/reject
// gallery/featured
// gallery/popular
// gallery/posts
// gallery/posts/create
// gallery/posts/delete
// gallery/posts/like
// gallery/posts/show
// gallery/posts/unlike
// gallery/posts/update
// get-online-users-count
// get-avatar-decorations
// hashtags/list
// hashtags/search
// hashtags/show
// hashtags/trend
// hashtags/users
// i
// i/2fa/done
// i/2fa/key-done
// i/2fa/password-less
// i/2fa/register-key
// i/2fa/register
// i/2fa/update-key
// i/2fa/remove-key
// i/2fa/unregister
// i/apps
// i/authorized-apps
// i/claim-achievement
// i/change-password
// i/delete-account
// i/export-data
// i/export-blocking
// i/export-following
// i/export-mute
// i/export-notes
// i/export-favorites
// i/export-user-lists
// i/export-antennas
// i/favorites
// i/gallery/likes
// i/gallery/posts
// i/import-blocking
// i/import-following
// i/import-notes
// i/import-muting
// i/import-user-lists
// i/import-antennas
// i/notifications
// i/notifications-grouped
// i/page-likes
// i/pages
// i/pin
// i/read-all-unread-notes
// i/read-announcement
// i/regenerate-token
// i/registry/get-all
// i/registry/get-unsecure
// i/registry/get-detail
// i/registry/get
// i/registry/keys-with-type
// i/registry/keys
// i/registry/remove
// i/registry/scopes-with-domain
// i/registry/set
// i/revoke-token
// i/signin-history
// i/unpin
// i/update-email
// i/update
// i/move
// i/webhooks/create
// i/webhooks/list
// i/webhooks/show
// i/webhooks/update
// i/webhooks/delete
// invite/create
// invite/delete
// invite/list
// invite/limit
// meta
// emojis
// emoji
// miauth/gen-token
// mute/create
// mute/delete
// mute/list
// renote-mute/create
// renote-mute/delete
// renote-mute/list
// my/apps
// notes
// notes/children
// notes/clips
// notes/conversation
// notes/create
// notes/delete
// notes/favorites/create
// notes/favorites/delete
// notes/featured
// notes/global-timeline
// notes/bubble-timeline
// notes/hybrid-timeline
// notes/local-timeline
// notes/mentions
// notes/polls/recommendation
// notes/polls/vote
// notes/reactions
// notes/reactions/create
// notes/reactions/delete
// notes/like
// notes/renotes
// notes/replies
// notes/search-by-tag
// notes/search
// notes/show
// notes/state
// notes/thread-muting/create
// notes/thread-muting/delete
// notes/timeline
// notes/translate
// notes/unrenote
// notes/user-list-timeline
// notes/edit
// notes/versions
// notifications/create
// notifications/mark-all-as-read
// notifications/test-notification
// page-push
// pages/create
// pages/delete
// pages/featured
// pages/like
// pages/show
// pages/unlike
// pages/update
// flash/create
// flash/delete
// flash/featured
// flash/like
// flash/show
// flash/unlike
// flash/update
// flash/my
// flash/my-likes
// ping
// pinned-users
// promo/read
// roles/list
// roles/show
// roles/users
// roles/notes
// request-reset-password
// reset-db
// reset-password
// server-info
// stats
// sw/show-registration
// sw/update-registration
// sw/register
// sw/unregister
// test
// username/available
// users
// users/clips
// users/followers
// users/following
// users/gallery/posts
// users/get-frequently-replied-users
// users/featured-notes
// users/lists/create
// users/lists/delete
// users/lists/list
// users/lists/pull
// users/lists/push
// users/lists/show
// users/lists/favorite
// users/lists/unfavorite
// users/lists/update
// users/lists/create-from-public
// users/lists/update-membership
// users/lists/get-memberships
// users/notes
// users/pages
// users/flashs
// users/reactions
// users/recommendation
// users/relation
// users/report-abuse
// users/search-by-username-and-host
// users/search
// users/show
// users/achievements
// users/update-memo
// fetch-rss
// fetch-external-resources
// retention
// sponsors

View file

View file

@ -1,4 +0,0 @@
pub mod ap;
pub mod key;
pub mod oma;
pub mod masto;

View file

View file

@ -1,174 +0,0 @@
// Copyright (c) 2023 mStar
//
// Licensed under the EUPL, Version 1.2
//
// You may not use this work except in compliance with the Licence.
// You should have received a copy of the Licence along with this work. If not, see:
// <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
// See the Licence for the specific language governing permissions and limitations under the Licence.
//
use anyhow::{anyhow, bail, Error};
use clap::Parser;
use serde_derive::Deserialize;
use std::{fs, path::PathBuf};
use sysinfo::System;
const MEGABYTE: u64 = 1049000;
/// Assumed memory consumption per worker
/// Rather overshoot this than undershoot
const ASSUMED_MEM_PER_WORKER: u64 = 2 * MEGABYTE;
const CPUS_RESERVED_FOR_SYSTEM: u32 = 2;
#[derive(Debug)]
pub struct Config {
/// Path to the db for diesel to use
pub database_path: String,
/// Maximum number of workers to use
/// A value of 0 makes stuff single-threaded
pub max_workers: u32,
/// Minimum number of workers to use
pub min_workers: u32,
}
#[derive(Debug, Deserialize)]
struct ConfigRootToml {
config: Option<ConfigToml>,
}
#[derive(Debug, Deserialize)]
struct ConfigToml {
/// Path to the db for diesel to use
database_path: Option<String>,
/// Maximum number of workers to use
/// A value of 0 makes stuff single-threaded
max_workers: Option<u32>,
/// Minimum number of workers to use
min_workers: Option<u32>,
}
/// An ActivityPub enabled social media server
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
pub struct CLIArguments {
/// Path to the config file
#[arg(default_value = "config.toml")]
pub config: PathBuf,
/// Maximum amount of workers to use.
/// If not set, the max is calculated depending on system resources.
/// A value of 0 will cause the server to be singlethreaded, potentially reducing the performance and responsiveness by a lot
#[arg(long)]
pub max_workers: Option<u32>,
/// Minimum amount of workers to keep active at all times
/// Can potentially increase reaction speed at the cost of resource usage
#[arg(long)]
pub min_workers: Option<u32>,
#[arg(long)]
pub db_path: Option<String>,
}
pub fn read_from_env() -> Result<Config, Error> {
let args = CLIArguments::parse();
if !args.config.is_file() {
bail!("Config path is not a file");
};
let ext = args.config.extension().ok_or(anyhow!(
"Type of config file couldn't be determined. No extension"
))?;
if ext != "toml" {
bail!("Config file is not a toml file");
}
let contents = match fs::read_to_string(&args.config) {
Ok(c) => c,
Err(err) => bail!("Couldn't read config file. Error: {err}"),
};
let unsanitized: ConfigRootToml = match toml::from_str(&contents) {
Ok(d) => d,
Err(err) => bail!("Failed to parse config file. Error: {err}"),
};
let conf = unsanitized.into_config(&args);
conf.sanity_check()?;
Ok(conf)
}
fn calc_max_workers_from_system() -> u32 {
let mut sys = System::new_all();
sys.refresh_all();
let available_ram = sys.available_memory() - sys.used_memory();
let available_cpus = (sys.cpus().len() as u32) - CPUS_RESERVED_FOR_SYSTEM;
let max_workers_ram = (available_ram / ASSUMED_MEM_PER_WORKER) as u32;
// Assume one worker per available CPU
// The lower number of potential workers is what we'll use
if available_cpus > max_workers_ram {
max_workers_ram
} else {
available_cpus
}
}
impl ConfigRootToml {
fn into_config(&self, cli: &CLIArguments) -> Config {
match &self.config {
Some(config) => {
let db_path = match &cli.db_path {
Some(path) => path.clone(),
None => match &config.database_path {
Some(path) => path.clone(),
None => "config.toml".into(),
},
};
let min_workers = match cli.min_workers {
Some(num) => num,
None => match config.min_workers {
Some(num) => num,
None => 1,
},
};
let max_workers = match cli.max_workers {
Some(num) => num,
None => match config.max_workers {
Some(num) => num,
None => calc_max_workers_from_system(),
},
};
Config {
database_path: db_path,
min_workers,
max_workers,
}
}
None => {
let db_path = match &cli.db_path {
Some(path) => path.clone(),
None => "config.toml".to_string(),
};
let min_workers = match cli.min_workers {
Some(num) => num,
None => 1,
};
let max_workers = match cli.max_workers {
Some(num) => num,
None => calc_max_workers_from_system(),
};
Config {
database_path: db_path.clone(),
min_workers,
max_workers,
}
}
}
}
}
impl Config {
fn sanity_check(&self) -> Result<(), Error> {
if self.min_workers > self.max_workers {
bail!("Minimum amount of workers ({}) can't be greater than maximum amount of workers ({})", self.min_workers, self.max_workers)
};
Ok(())
}
}

View file

@ -1,20 +0,0 @@
use std::fmt::{Display, Formatter};
/// Necessary because of this issue: https://github.com/actix/actix-web/issues/1711
#[derive(Debug)]
pub struct Error(pub(crate) anyhow::Error);
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl<T> From<T> for Error
where
T: Into<anyhow::Error>,
{
fn from(t: T) -> Self {
Error(t.into())
}
}

View file

View file

@ -1,27 +0,0 @@
// Copyright (c) 2023 mStar
//
// Licensed under the EUPL, Version 1.2
//
// You may not use this work except in compliance with the Licence.
// You should have received a copy of the Licence along with this work. If not, see:
// <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
// See the Licence for the specific language governing permissions and limitations under the Licence.
//
use anyhow::Error;
mod objects;
mod apis;
mod storage;
mod guardian;
mod config;
mod webui;
mod error;
#[tokio::main]
async fn main() -> Result<(), Error> {
let conf = config::read_from_env()?;
let _db = storage::establish_connection(&conf.database_path)?;
Ok(())
}

View file

@ -1,12 +0,0 @@
// Copyright (c) 2023 mStar
//
// Licensed under the EUPL, Version 1.2
//
// You may not use this work except in compliance with the Licence.
// You should have received a copy of the Licence along with this work. If not, see:
// <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
// See the Licence for the specific language governing permissions and limitations under the Licence.
//
pub mod person;
pub mod post;

View file

@ -1,30 +0,0 @@
// Copyright (c) 2023 mStar
//
// Licensed under the EUPL, Version 1.2
//
// You may not use this work except in compliance with the Licence.
// You should have received a copy of the Licence along with this work. If not, see:
// <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
// See the Licence for the specific language governing permissions and limitations under the Licence.
//
use activitypub_federation::{kinds::actor::PersonType, fetch::object_id::ObjectId, protocol::public_key::PublicKey};
use serde_derive::{Deserialize, Serialize};
use url::Url;
use crate::storage::pg_models::PersonPG;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Person {
pub kind: PersonType,
pub preferred_username: String,
pub id: ObjectId<PersonPG>,
pub inbox: Url,
pub public_key: PublicKey,
}
pub trait IntoPerson {
fn into_person(&self) -> Person;
}

View file

@ -1,25 +0,0 @@
use activitypub_federation::{kinds::object::NoteType, fetch::object_id::ObjectId};
use serde_derive::{Deserialize, Serialize};
use url::Url;
use crate::storage::pg_person::PersonPG;
#[derive(Deserialize, Serialize, Debug)]
pub struct Post {
#[serde(rename = "type")]
pub kind: NoteType,
pub id: ObjectId<>,
pub(crate) attributed_to: ObjectId<PersonPG>,
#[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub content: String,
pub in_reply_to: Option<ObjectId<>>,
tag: Vec<Mention>
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Mention {
pub href: Url,
#[serde(rename = "type")]
pub kind: MentionType,
}

View file

@ -1,51 +0,0 @@
// Copyright (c) 2023 mStar
//
// Licensed under the EUPL, Version 1.2
//
// You may not use this work except in compliance with the Licence.
// You should have received a copy of the Licence along with this work. If not, see:
// <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
// See the Licence for the specific language governing permissions and limitations under the Licence.
//
// TODO: Once diesel-async supports sqlite (see https://github.com/weiznich/diesel_async/discussions/95), convert to async
pub mod pg_schemas;
pub mod pg_person;
pub mod pg_post;
use anyhow::{Error, bail};
use diesel::{pg::Pg, r2d2::{Pool, ConnectionManager, self}};
use diesel::PgConnection;
use diesel_migrations::{EmbeddedMigrations, embed_migrations, MigrationHarness};
pub const POSTGRES_MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
#[derive(Clone)]
pub struct Storage {
pub pool: Pool<ConnectionManager<PgConnection>>,
}
/// Try and connect to a database at the given url
/// The url can be a path to an sqlite db file or an url to a postgres db
pub fn establish_connection(db_url: &str) -> Result<Storage, Error> {
let manager = ConnectionManager::<PgConnection>::new(db_url);
let pool = match r2d2::Pool::builder().build(manager) {
Ok(a) => a,
Err(err) => bail!("Error while creating the connection pool. Error {}", err)
};
match pool.get() {
Ok(mut conn) => {run_migrations_postgres(&mut conn)?;}
Err(e) => bail!(e)
}
Ok(Storage{
pool
})
}
fn run_migrations_postgres(conn: &mut impl MigrationHarness<Pg>) -> Result<(), Error> {
match conn.run_pending_migrations(POSTGRES_MIGRATIONS) {
Ok(_) => Ok(()),
Err(e) => bail!("Migrations failed. Error {}", e)
}
}

View file

@ -1,98 +0,0 @@
use activitypub_federation::{traits::Object, config::Data, fetch::object_id::ObjectId, protocol::verification::verify_domains_match};
use anyhow::{anyhow, Error};
use chrono::{Utc, NaiveDateTime, DateTime};
use diesel::prelude::*;
use url::Url;
use crate::objects::person::Person;
use super::{Storage, pg_schemas};
#[derive(Queryable, Selectable, Debug, Clone)]
#[diesel(table_name = crate::storage::pg_schemas::people)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct PersonPG {
pub ap_id: String,
pub name: String,
pub instance: Option<String>, // Also url
pub mail: Option<String>,
pub pw_hash: Option<String>,
pub inbox: String, // Is an url but diesel no likey
pub public_key: String,
pub private_key: Option<String>,
pub last_refreshed_at: NaiveDateTime,
pub local: bool,
pub followers: Vec<Option<String>> // Strings are URLs. This is stupid. I wish I could simply use Vec<Url> instead
}
#[async_trait::async_trait]
impl Object for PersonPG {
type DataType = Storage;
type Kind = Person;
type Error = Error;
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
Some(self.last_refreshed_at.and_utc())
}
async fn read_from_id(
object_id: Url,
data: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
use pg_schemas::people::dsl::*;
let pool = data.pool.clone();
let mut conn = pool.get()?;
let results: Vec<PersonPG> = people
.filter(ap_id.eq(object_id.as_str()))
.limit(1)
.select(PersonPG::as_select())
.load(&mut conn)
.expect(format!("Error loading person from id {}", object_id).as_str());
if results.len() == 1 {
Ok(Some(results[0].clone()))
} else {
Ok(None)
}
}
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
Ok(Person {
kind: Default::default(),
preferred_username: self.name.clone(),
id: ObjectId::parse(&self.ap_id)?,
inbox: Url::parse(&self.inbox)?,
public_key: serde_json::from_str(&self.public_key).unwrap(),
})
}
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
_data: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
Ok(verify_domains_match(&json.id.inner(), &expected_domain)?)
}
async fn from_json(
json: Self::Kind,
_data: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
Ok(
PersonPG {
ap_id: json.id.inner().to_string(),
name: json.preferred_username,
instance: Some(json.inbox.authority().to_string()),
mail: None,
pw_hash: None,
inbox: json.inbox.to_string(),
public_key: serde_json::to_string(&json.public_key).unwrap(),
private_key: None,
last_refreshed_at: Utc::now().naive_utc(),
local: false,
followers: vec![]
}
)
}
}

View file

@ -1,7 +0,0 @@
pub struct PostPG {
pub ap_id: String,
pub text: String,
pub creator: String,
pub local: bool,
pub instance: Option<String>
}

View file

@ -1,17 +0,0 @@
// @generated automatically by Diesel CLI.
diesel::table! {
people (ap_id) {
ap_id -> Text,
name -> Text,
instance -> Nullable<Text>,
mail -> Nullable<Text>,
pw_hash -> Nullable<Text>,
inbox -> Text,
public_key -> Text,
private_key -> Nullable<Text>,
last_refreshed_at -> Timestamp,
local -> Bool,
followers -> Array<Nullable<Text>>,
}
}

View file

@ -1,10 +0,0 @@
// Copyright (c) 2023 mStar
//
// Licensed under the EUPL, Version 1.2
//
// You may not use this work except in compliance with the Licence.
// You should have received a copy of the Licence along with this work. If not, see:
// <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
// See the Licence for the specific language governing permissions and limitations under the Licence.
//