Initial commit
This commit is contained in:
commit
7494237d19
12 changed files with 1890 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/static/**.mjs
|
1600
Cargo.lock
generated
Normal file
1600
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "fedirect"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = "0.5.1"
|
||||||
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
|
serde_json = "1.0.135"
|
18
README.md
Normal file
18
README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# FeDirect
|
||||||
|
|
||||||
|
Fedi links that open on your preferred instance!
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To compile TypeScript, the build script assumes Deno is installed.
|
||||||
|
|
||||||
|
When you have Deno and Rust installed, simply use Cargo to build the project
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# For example, to build for release, you can do
|
||||||
|
cargo build --release
|
||||||
|
# Or, to immediately run it
|
||||||
|
cargo run --release
|
||||||
|
# Or, to run during development:
|
||||||
|
cargo run
|
||||||
|
```
|
14
build.rs
Normal file
14
build.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo::rerun-if-changed=tsconfig.json");
|
||||||
|
println!("cargo::rerun-if-changed=static/**.mts");
|
||||||
|
assert!(Command::new("deno")
|
||||||
|
.args(["run", "-A", "npm:typescript/tsc"])
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success());
|
||||||
|
}
|
89
known-software.json
Normal file
89
known-software.json
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
"software": {
|
||||||
|
"misskey": {
|
||||||
|
"aliases": [
|
||||||
|
"misskey",
|
||||||
|
"mk"
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
"misskey-compliant",
|
||||||
|
"misskey-v13"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sharkey": {
|
||||||
|
"aliases": [
|
||||||
|
"sharkey",
|
||||||
|
"sk"
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
"misskey-compliant",
|
||||||
|
"misskey-v13"
|
||||||
|
],
|
||||||
|
"forkOf": "misskey"
|
||||||
|
},
|
||||||
|
"iceshrimp-js": {
|
||||||
|
"aliases": [
|
||||||
|
"iceshrimp-js"
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
"misskey-compliant",
|
||||||
|
"misskey-v12",
|
||||||
|
"iceshrimp"
|
||||||
|
],
|
||||||
|
"forkOf": "misskey"
|
||||||
|
},
|
||||||
|
"iceshrimp-dotnet": {
|
||||||
|
"aliases": [
|
||||||
|
"iceshrimp-dotnet"
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
"misskey-compliant",
|
||||||
|
"misskey-v12",
|
||||||
|
"iceshrimp"
|
||||||
|
],
|
||||||
|
"forkOf": "misskey"
|
||||||
|
},
|
||||||
|
"firefish": {
|
||||||
|
"aliases": [
|
||||||
|
"firefish",
|
||||||
|
"calckey"
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
"misskey-compliant",
|
||||||
|
"misskey-v12"
|
||||||
|
],
|
||||||
|
"forkOf": "misskey"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"misskey-compliant": {
|
||||||
|
"aliases": [
|
||||||
|
"misskey-compliant",
|
||||||
|
"mkc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"misskey-v12": {
|
||||||
|
"aliases": [
|
||||||
|
"misskey-v12",
|
||||||
|
"misskeyv12",
|
||||||
|
"misskey12",
|
||||||
|
"mkv12",
|
||||||
|
"mk12"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"misskey-v13": {
|
||||||
|
"aliases": [
|
||||||
|
"misskey-v13",
|
||||||
|
"misskeyv13",
|
||||||
|
"misskey13",
|
||||||
|
"mkv13",
|
||||||
|
"mk13"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"iceshrimp": {
|
||||||
|
"aliases": [
|
||||||
|
"iceshrimp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/known_software.rs
Normal file
67
src/known_software.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use rocket::request::FromParam;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static KNOWN_SOFTWARE: LazyLock<KnownSoftware> =
|
||||||
|
LazyLock::new(|| serde_json::from_str(include_str!("../known-software.json")).unwrap());
|
||||||
|
pub static KNOWN_SOFTWARE_NAMES: LazyLock<HashMap<String, String>> =
|
||||||
|
LazyLock::new(|| KNOWN_SOFTWARE.get_name_map());
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Software {
|
||||||
|
aliases: HashSet<String>,
|
||||||
|
groups: HashSet<String>,
|
||||||
|
fork_of: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Group {
|
||||||
|
aliases: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct KnownSoftware {
|
||||||
|
software: HashMap<String, Software>,
|
||||||
|
groups: HashMap<String, Group>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KnownSoftware {
|
||||||
|
fn get_name_map(&self) -> HashMap<String, String> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
self.software.iter().for_each(|(name, software)| {
|
||||||
|
software.aliases.iter().for_each(|alias| {
|
||||||
|
assert_eq!(map.insert(alias.to_owned(), name.to_owned()), None);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
self.groups.iter().for_each(|(name, group)| {
|
||||||
|
group.aliases.iter().for_each(|alias: &String| {
|
||||||
|
assert_eq!(map.insert(alias.to_owned(), name.to_owned()), None);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KnownInstanceSoftware<'r> {
|
||||||
|
pub requested: &'r str,
|
||||||
|
pub resolved: &'static String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> FromParam<'r> for KnownInstanceSoftware<'r> {
|
||||||
|
type Error = &'r str;
|
||||||
|
|
||||||
|
fn from_param(param: &'r str) -> Result<Self, Self::Error> {
|
||||||
|
if let Some(resolved) = KNOWN_SOFTWARE_NAMES.get(param) {
|
||||||
|
Ok(KnownInstanceSoftware {
|
||||||
|
requested: param,
|
||||||
|
resolved,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/main.rs
Normal file
51
src/main.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
use known_software::KnownInstanceSoftware;
|
||||||
|
use rocket::{
|
||||||
|
fs::{FileServer, NamedFile},
|
||||||
|
http::ContentType,
|
||||||
|
};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
mod known_software;
|
||||||
|
|
||||||
|
#[get("/known-software.json")]
|
||||||
|
async fn known_software_json() -> Option<NamedFile> {
|
||||||
|
NamedFile::open("known-software.json").await.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/<instance>/<route..>", rank = 1)]
|
||||||
|
async fn route_for_known_instance_software(
|
||||||
|
instance: KnownInstanceSoftware<'_>,
|
||||||
|
route: PathBuf,
|
||||||
|
) -> Option<NamedFile> {
|
||||||
|
NamedFile::open("static/crossroad.html").await.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/<instance>/<route..>", rank = 2)]
|
||||||
|
fn route_for_unknown_instance_software(instance: &str, route: PathBuf) -> (ContentType, String) {
|
||||||
|
(
|
||||||
|
ContentType::HTML,
|
||||||
|
format!(
|
||||||
|
"Hello from <code>{}</code>! The software <code>{}</code> is unknown.",
|
||||||
|
route.display(),
|
||||||
|
instance
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[launch]
|
||||||
|
fn rocket() -> _ {
|
||||||
|
rocket::build()
|
||||||
|
.mount("/static", FileServer::from("static").rank(0))
|
||||||
|
.mount("/api", routes![])
|
||||||
|
.mount(
|
||||||
|
"/",
|
||||||
|
routes![
|
||||||
|
known_software_json,
|
||||||
|
route_for_known_instance_software,
|
||||||
|
route_for_unknown_instance_software
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
11
static/crossroad.html
Normal file
11
static/crossroad.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>FeDirect</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="module" src="/static/crossroad.mjs"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
2
static/crossroad.mts
Normal file
2
static/crossroad.mts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import knownSoftware from "./known_software.mjs";
|
||||||
|
console.log(knownSoftware);
|
16
static/known_software.mts
Normal file
16
static/known_software.mts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
type Software = {
|
||||||
|
aliases: string[],
|
||||||
|
groups: string[],
|
||||||
|
forkOf?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Group = {
|
||||||
|
aliases: string[],
|
||||||
|
}
|
||||||
|
|
||||||
|
type KnownSoftware = {
|
||||||
|
software: Record<string, Software>,
|
||||||
|
groups: Record<string, Group>,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default await fetch("/known-software.json").then(r => r.json()) as KnownSoftware;
|
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2023",
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"static/**.mts",
|
||||||
|
],
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue