mirror of
https://github.com/ivabus/aliurl
synced 2024-11-24 23:45:06 +03:00
0.3.0: Add more api requests
This commit is contained in:
parent
15fa31207e
commit
5042f009cb
4 changed files with 245 additions and 75 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "aliurl"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/ivabus/aliurl"
|
||||
|
|
80
README.md
80
README.md
|
@ -24,14 +24,14 @@ Edit `Rocket.toml` to set port and ip.
|
|||
cargo run -r
|
||||
```
|
||||
|
||||
## Usage
|
||||
## API
|
||||
|
||||
### Create new alias
|
||||
|
||||
#### Request
|
||||
|
||||
```http request
|
||||
POST /post HTTP/1.1
|
||||
POST /api/create_alias HTTP/1.1
|
||||
```
|
||||
|
||||
#### Request body
|
||||
|
@ -45,8 +45,82 @@ POST /post HTTP/1.1
|
|||
}
|
||||
```
|
||||
|
||||
### Use alias
|
||||
### Get all aliases
|
||||
|
||||
#### Request
|
||||
|
||||
```http request
|
||||
POST /api/create_alias HTTP/1.1
|
||||
```
|
||||
|
||||
#### Request body
|
||||
|
||||
```json
|
||||
{
|
||||
"access_key": "<ACCESS_KEY>" // May not be provided, if no ./access_keys file
|
||||
}
|
||||
```
|
||||
|
||||
#### Response body
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"alias": "alias_without_ad",
|
||||
"url": "https://example.com"
|
||||
},
|
||||
{
|
||||
"alias": "alias_with_ad",
|
||||
"redirect_with_ad": true,
|
||||
"url": "https://example.com"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Remove alias
|
||||
|
||||
Removes all alias with provided name.
|
||||
|
||||
#### Request
|
||||
|
||||
```http request
|
||||
POST /api/remove_alias HTTP/1.1
|
||||
```
|
||||
|
||||
#### Request body
|
||||
|
||||
```json
|
||||
{
|
||||
"alias": "<ALIAS>",
|
||||
"access_key": "<ACCESS_KEY>" // May not be provided, if no ./access_keys file
|
||||
}
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
##### Alias(es) removed
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"alias": "alias",
|
||||
"url": "https://example.com"
|
||||
},
|
||||
{
|
||||
"alias": "alias",
|
||||
"redirect_with_ad": true,
|
||||
"url": "https://another.com"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
##### No aliases found
|
||||
|
||||
```json
|
||||
[]
|
||||
```
|
||||
|
||||
### Use alias
|
||||
|
||||
#### Request
|
||||
|
||||
|
|
98
src/main.rs
98
src/main.rs
|
@ -1,25 +1,19 @@
|
|||
mod post;
|
||||
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
|
||||
use rocket::http::RawStr;
|
||||
use rocket::http::Status;
|
||||
use rocket::response::content::RawHtml;
|
||||
use rocket::response::Redirect;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct CreateAliasRequest {
|
||||
url: String,
|
||||
redirect_with_ad: Option<String>,
|
||||
access_key: Option<String>,
|
||||
alias: Option<String>,
|
||||
}
|
||||
|
||||
static mut ACCESS_KEY_REQUIRED: bool = true;
|
||||
const INDEX_REDIRECT: &'static str = "https://ivabus.dev";
|
||||
const INDEX_WITH_AD: bool = true;
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
struct Alias {
|
||||
|
@ -29,7 +23,7 @@ struct Alias {
|
|||
redirect_with_ad: Option<bool>,
|
||||
}
|
||||
|
||||
fn read_alias() -> Vec<Alias> {
|
||||
fn read_aliases() -> Vec<Alias> {
|
||||
if !std::path::Path::new("./alias.json").exists() {
|
||||
let mut file = std::fs::File::create("./alias.json").unwrap();
|
||||
file.write_all(b"[]").unwrap();
|
||||
|
@ -44,63 +38,8 @@ fn read_alias() -> Vec<Alias> {
|
|||
let mut buf_reader = BufReader::new(file);
|
||||
let mut contents = String::new();
|
||||
buf_reader.read_to_string(&mut contents).unwrap();
|
||||
let alias_list: Vec<Alias> = serde_json::from_str(&contents).unwrap();
|
||||
alias_list
|
||||
}
|
||||
|
||||
#[post("/post", data = "<data>")]
|
||||
fn create_alias(data: &RawStr) -> (Status, String) {
|
||||
let data: CreateAliasRequest = match serde_json::from_str(&data.to_string()) {
|
||||
Ok(req) => req,
|
||||
Err(e) => return (Status::BadRequest, format!("Error: {e}")),
|
||||
};
|
||||
let mut file = std::fs::File::open("./access_keys").unwrap();
|
||||
let mut buffer: String = String::new();
|
||||
file.read_to_string(&mut buffer).unwrap();
|
||||
let access_keys: Vec<&str> = buffer.split("\n").collect();
|
||||
if let Some(key) = data.access_key {
|
||||
if !access_keys.contains(&key.as_str()) {
|
||||
return (Status::Forbidden, "Access key is invalid".to_string());
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
if ACCESS_KEY_REQUIRED {
|
||||
return (Status::Forbidden, "Access key needs to be provided".to_string());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut alias_list = read_alias();
|
||||
let mut file = std::fs::File::options().write(true).open("./alias.json").unwrap();
|
||||
let alias = match data.alias {
|
||||
None => uuid::Uuid::new_v4().to_string(),
|
||||
Some(alias) => alias,
|
||||
};
|
||||
if alias.contains("?") {
|
||||
return (Status::BadRequest, format!("Error: alias should not contain '?'"));
|
||||
}
|
||||
if let Some(s) = data.redirect_with_ad {
|
||||
if s.to_lowercase() == "true" {
|
||||
alias_list.push(Alias {
|
||||
url: data.url.clone(),
|
||||
alias: alias.clone(),
|
||||
redirect_with_ad: Some(true),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
alias_list.push(Alias {
|
||||
url: data.url.clone(),
|
||||
alias: alias.clone(),
|
||||
redirect_with_ad: None,
|
||||
});
|
||||
}
|
||||
|
||||
alias_list.dedup_by(|a, b| a.alias == b.alias);
|
||||
|
||||
file.write_all(serde_json::to_string(&alias_list).unwrap().as_bytes()).unwrap();
|
||||
|
||||
file.sync_all().unwrap();
|
||||
return (Status::Ok, format!("Created {} at {}", data.url, alias));
|
||||
let aliases_list: Vec<Alias> = serde_json::from_str(&contents).unwrap();
|
||||
aliases_list
|
||||
}
|
||||
|
||||
#[get("/404")]
|
||||
|
@ -112,7 +51,7 @@ fn not_found() -> Status {
|
|||
async fn get_page(page: String) -> Result<Redirect, RawHtml<String>> {
|
||||
let mut decoded_page = String::new();
|
||||
url_escape::decode_to_string(page, &mut decoded_page);
|
||||
let alias_list = read_alias();
|
||||
let alias_list = read_aliases();
|
||||
for i in alias_list {
|
||||
if i.alias == decoded_page {
|
||||
if let Some(red) = i.redirect_with_ad {
|
||||
|
@ -130,8 +69,15 @@ async fn get_page(page: String) -> Result<Redirect, RawHtml<String>> {
|
|||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn get_index() -> Redirect {
|
||||
Redirect::to(INDEX_REDIRECT)
|
||||
async fn get_index() -> Result<Redirect, RawHtml<String>> {
|
||||
if INDEX_WITH_AD {
|
||||
let mut redirect = String::new();
|
||||
let mut file = std::fs::File::open("./redirect.html").unwrap();
|
||||
file.read_to_string(&mut redirect).unwrap();
|
||||
Err(RawHtml(redirect.replace("#REDIRECT#", INDEX_REDIRECT)))
|
||||
} else {
|
||||
Ok(Redirect::to(INDEX_REDIRECT))
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::main]
|
||||
|
@ -155,7 +101,17 @@ async fn main() -> Result<(), rocket::Error> {
|
|||
}
|
||||
|
||||
let _rocket = rocket::build()
|
||||
.mount("/", routes![not_found, create_alias, get_page, get_index])
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
not_found,
|
||||
post::create_alias,
|
||||
post::get_aliases,
|
||||
post::remove_alias,
|
||||
get_page,
|
||||
get_index
|
||||
],
|
||||
)
|
||||
.launch()
|
||||
.await?;
|
||||
|
||||
|
|
140
src/post.rs
Normal file
140
src/post.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
use crate::*;
|
||||
use rocket::http::{RawStr, Status};
|
||||
use rocket::response::content::RawJson;
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct CreateAliasRequest {
|
||||
url: String,
|
||||
redirect_with_ad: Option<String>,
|
||||
access_key: Option<String>,
|
||||
alias: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GetAliasesRequest {
|
||||
access_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RemoveAliasRequest {
|
||||
alias: String,
|
||||
access_key: Option<String>,
|
||||
}
|
||||
|
||||
fn check_access_key(key: Option<String>) -> Result<String, (Status, RawJson<String>)> {
|
||||
let mut file = std::fs::File::open("./access_keys").unwrap();
|
||||
let mut buffer: String = String::new();
|
||||
file.read_to_string(&mut buffer).unwrap();
|
||||
let access_keys: Vec<&str> = buffer.split("\n").collect();
|
||||
return if let Some(key) = key {
|
||||
if !access_keys.contains(&key.as_str()) {
|
||||
Err((Status::Forbidden, RawJson(json!({"Error": "Invalid access key"}).to_string())))
|
||||
} else {
|
||||
Ok(key)
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
if ACCESS_KEY_REQUIRED {
|
||||
Err((Status::Forbidden, RawJson(json!({"Error": "No access key"}).to_string())))
|
||||
} else {
|
||||
Ok("".to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[post("/api/create_alias", data = "<data>")]
|
||||
pub fn create_alias(data: &RawStr) -> (Status, RawJson<String>) {
|
||||
let data: CreateAliasRequest = match serde_json::from_str(&data.to_string()) {
|
||||
Ok(req) => req,
|
||||
Err(e) => {
|
||||
return (Status::BadRequest, RawJson(json!({"Error": e.to_string()}).to_string()))
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = check_access_key(data.access_key) {
|
||||
return e;
|
||||
}
|
||||
|
||||
let mut aliases_list = read_aliases();
|
||||
let mut file = std::fs::File::options().write(true).open("./alias.json").unwrap();
|
||||
let alias = match data.alias {
|
||||
None => uuid::Uuid::new_v4().to_string(),
|
||||
Some(alias) => alias,
|
||||
};
|
||||
if alias.contains("?") {
|
||||
return (Status::BadRequest, RawJson(json!({"Error": "No access key"}).to_string()));
|
||||
}
|
||||
let alia = if let Some(s) = data.redirect_with_ad {
|
||||
if s.to_lowercase() == "true" {
|
||||
Alias {
|
||||
url: data.url.clone(),
|
||||
alias: alias.clone(),
|
||||
redirect_with_ad: Some(true),
|
||||
}
|
||||
} else {
|
||||
Alias {
|
||||
url: data.url.clone(),
|
||||
alias: alias.clone(),
|
||||
redirect_with_ad: None,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Alias {
|
||||
url: data.url.clone(),
|
||||
alias: alias.clone(),
|
||||
redirect_with_ad: None,
|
||||
}
|
||||
};
|
||||
aliases_list.push(alia.clone());
|
||||
aliases_list.dedup_by(|a, b| a.alias == b.alias);
|
||||
|
||||
file.write_all(serde_json::to_string(&aliases_list).unwrap().as_bytes()).unwrap();
|
||||
|
||||
file.sync_all().unwrap();
|
||||
return (Status::Ok, RawJson(serde_json::to_string(&alia).unwrap()));
|
||||
}
|
||||
|
||||
#[post("/api/get_aliases", data = "<data>")]
|
||||
pub fn get_aliases(data: &RawStr) -> (Status, RawJson<String>) {
|
||||
let data: GetAliasesRequest = match serde_json::from_str(&data.to_string()) {
|
||||
Ok(req) => req,
|
||||
Err(e) => {
|
||||
return (Status::BadRequest, RawJson(json!({"Error": format!("{e}")}).to_string()))
|
||||
}
|
||||
};
|
||||
if let Err(e) = check_access_key(data.access_key) {
|
||||
return e;
|
||||
}
|
||||
|
||||
return (Status::Ok, RawJson(serde_json::to_string(&read_aliases()).unwrap()));
|
||||
}
|
||||
|
||||
#[post("/api/remove_alias", data = "<data>")]
|
||||
pub fn remove_alias(data: &RawStr) -> (Status, RawJson<String>) {
|
||||
let data: RemoveAliasRequest = match serde_json::from_str(&data.to_string()) {
|
||||
Ok(req) => req,
|
||||
Err(e) => {
|
||||
return (Status::BadRequest, RawJson(json!({"Error": format!("{e}")}).to_string()))
|
||||
}
|
||||
};
|
||||
if let Err(e) = check_access_key(data.access_key) {
|
||||
return e;
|
||||
}
|
||||
let mut aliases_list = read_aliases();
|
||||
let mut removed_aliases: Vec<Alias> = vec![];
|
||||
let mut file = std::fs::File::options().write(true).open("./alias.json").unwrap();
|
||||
|
||||
for i in (0..aliases_list.len()).rev() {
|
||||
if aliases_list[i].alias == data.alias {
|
||||
removed_aliases.push(aliases_list.remove(i));
|
||||
}
|
||||
}
|
||||
let aliases_list = serde_json::to_string(&aliases_list).unwrap();
|
||||
file.write_all(&aliases_list.as_bytes()).unwrap();
|
||||
file.set_len(aliases_list.as_bytes().len() as u64).unwrap();
|
||||
file.sync_all().unwrap();
|
||||
|
||||
return (Status::Ok, RawJson(serde_json::to_string(&removed_aliases).unwrap()));
|
||||
}
|
Loading…
Reference in a new issue