aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-03-08 02:12:28 +0000
committerFuwn <[email protected]>2022-03-08 02:12:28 +0000
commit9430e767aa3f3ddd98c2de71a4770722ae7cf072 (patch)
tree303a551c93eee32f900c9022bbbec5cb6d2ed91b /src
parentchore: licensing (diff)
downloadapi-worker-9430e767aa3f3ddd98c2de71a4770722ae7cf072.tar.xz
api-worker-9430e767aa3f3ddd98c2de71a4770722ae7cf072.zip
feat: worker done :star:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs70
-rw-r--r--src/main.rs70
-rw-r--r--src/routes.rs71
-rw-r--r--src/structures.rs18
-rw-r--r--src/utils.rs42
5 files changed, 131 insertions, 140 deletions
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..5f6f77d
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,70 @@
+// This file is part of api-worker <https://github.com/senpy-club/api-worker>.
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// Copyright (C) 2022-2022 Fuwn <[email protected]>
+// SPDX-License-Identifier: GPL-3.0-only
+
+#![deny(
+ warnings,
+ nonstandard_style,
+ unused,
+ future_incompatible,
+ rust_2018_idioms,
+ unsafe_code
+)]
+#![deny(clippy::all, clippy::pedantic)] // clippy::nursery
+#![recursion_limit = "128"]
+
+mod constants;
+mod routes;
+mod structures;
+mod utils;
+
+use worker::Response;
+
+/// # Errors
+/// if `worker::Router` errors
+#[worker::event(fetch)]
+pub async fn main(
+ request: worker::Request,
+ environment: worker::Env,
+ _: worker::Context,
+) -> worker::Result<Response> {
+ dotenv::dotenv().ok();
+
+ worker::Router::new()
+ .get("/", |_, _| routes::index())
+ .get("/v2", |_, _| routes::index())
+ .get_async("/v2/github", |_, _| async move { routes::github().await })
+ .get_async(
+ "/v2/languages",
+ |_, _| async move { routes::languages().await },
+ )
+ .get_async("/v2/language/:language", |_, ctx| {
+ async move {
+ routes::language(ctx.param("language").unwrap_or(&"null".to_string()))
+ .await
+ }
+ })
+ .get_async("/v2/random", |_, _| async move { routes::random().await })
+ .get("/v2/version", |_, _| {
+ Response::from_json(&serde_json::json!({
+ "crate_version": env!("CARGO_PKG_VERSION"),
+ "git_commit_hash": env!("GIT_COMMIT_HASH"),
+ }))
+ })
+ .run(request, environment)
+ .await
+}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 53481e0..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-// This file is part of api-worker <https://github.com/senpy-club/api-worker>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-#![feature(type_ascription)]
-#![deny(
- warnings,
- nonstandard_style,
- unused,
- future_incompatible,
- rust_2018_idioms,
- unsafe_code
-)]
-#![deny(clippy::all, clippy::pedantic)] // clippy::nursery
-#![recursion_limit = "128"]
-
-#[macro_use]
-extern crate actix_web;
-
-pub mod constants;
-pub mod routes;
-pub mod structures;
-pub mod utils;
-
-#[actix_web::main]
-async fn main() -> std::io::Result<()> {
- dotenv::dotenv().ok();
-
- let store = actix_ratelimit::MemoryStore::new();
-
- actix_web::HttpServer::new(move || {
- actix_web::App::new()
- .wrap(actix_cors::Cors::default().allow_any_origin())
- .wrap(
- actix_ratelimit::RateLimiter::new(
- actix_ratelimit::MemoryStoreActor::from(store.clone()).start(),
- )
- .with_interval(std::time::Duration::from_secs(60))
- .with_max_requests(100),
- )
- .service(routes::index)
- .service(
- actix_web::web::scope("/api/v1")
- .service(routes::github)
- .service(routes::languages)
- .service(routes::language)
- .service(routes::random),
- )
- })
- .bind(format!(
- "0.0.0.0:{}",
- std::env::var("PORT").expect("no port was provided... ~why~")
- ))?
- .run()
- .await
-}
diff --git a/src/routes.rs b/src/routes.rs
index d34489d..e2c4e88 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -16,74 +16,67 @@
// Copyright (C) 2022-2022 Fuwn <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only
-use actix_web::{HttpRequest, HttpResponse};
use rand::{thread_rng, Rng};
+use worker::{Response, Result};
use crate::{
structures::SenpyRandom,
utils::{filter_images_by_language, filter_languages, github_api},
};
-#[get("/")]
-pub fn index() -> HttpResponse {
- HttpResponse::Ok().body(
- r#"# senpy-api
+pub fn index() -> Result<Response> {
+ Response::ok(
+ r#"# senpy-club/api-worker
+
## routes
-if a language requires a parameter, it will be notated like <this>.
-for example; if a route is notated as "/api/v1/route?<parameter>", you can
+if a language requires a parameter, it will be notated like ":this".
+for example; if a route is notated as "/v1/route/:parameter", you can
access that route via the url
-"http://this.domain/api/v1/route?parameter=something"
+"http://this.domain/v1/route/something"
- /
- - /: index page (you are here)
+ - /: index page (you are here)
-- /api/v1
- - /github: github api mirror
- - /languages: a list of all languages that appear in _the_ repository
- - /language?<lang>: get a list of all images that pertain to the language "<lang>"
+- /v1
+ - /github: github api mirror
+ - /languages: a list of all languages that appear in _the_ repository
+ - /language/:language: get a list of all images that pertain to the language ":language"
## notes
-### rate-limit (s)
-there aren't any rate-limits or whatnot on api usage but don't abuse it, it only takes one bad
-apple to spoil the lot.
### contributing
-if you'd like to support the project in anyway, check out the repository!
-https://github.com/senpy-club/api
+
+if you'd like to support the project in any way, check out the repository!
+<https://github.com/senpy-club/api>
### supporting
+
if you would like to support my development ventures, visit my github profile here :3
-https://github.com/fuwn
+<https://github.com/fuwn>
### license
+
gnu general public license v3.0 (gpl-3.0-only)
-https://github.com/senpy-club/api/blob/main/license"#,
+<https://github.com/senpy-club/api-worker/blob/main/LICENSE>"#,
)
}
-#[get("/github")]
-pub async fn github() -> HttpResponse { HttpResponse::Ok().json(github_api().await.unwrap()) }
-
-#[get("/languages")]
-pub async fn languages() -> HttpResponse { HttpResponse::Ok().json(filter_languages().await) }
+pub async fn github() -> Result<Response> {
+ Response::from_json(&github_api().await.unwrap())
+}
-#[get("/language")]
-pub async fn language(req: HttpRequest) -> HttpResponse {
- let queries = qstring::QString::from(req.query_string());
- let lang = queries.get("lang").unwrap_or("null");
+pub async fn languages() -> Result<Response> {
+ Response::from_json(&filter_languages().await)
+}
- return if lang == "null" {
- HttpResponse::Ok().json(vec!["invalid language or no language specified".to_string()])
- } else {
- HttpResponse::Ok().json(filter_images_by_language(lang).await)
- };
+pub async fn language(language: &str) -> Result<Response> {
+ Response::from_json(&filter_images_by_language(language).await)
}
-#[get("/random")]
-pub async fn random() -> HttpResponse {
+pub async fn random() -> Result<Response> {
let filtered_languages = filter_languages().await;
- let random_language =
- &filtered_languages[thread_rng().gen_range(0..filtered_languages.len() - 1)];
+ let random_language = &filtered_languages
+ [thread_rng().gen_range(0..filtered_languages.len() - 1)];
let filtered_images = filter_images_by_language(random_language).await;
let random_image = if filtered_images.len() == 1 {
&filtered_images[0]
@@ -91,7 +84,7 @@ pub async fn random() -> HttpResponse {
&filtered_images[thread_rng().gen_range(0..filtered_images.len() - 1)]
};
- HttpResponse::Ok().json(SenpyRandom {
+ Response::from_json(&SenpyRandom {
language: random_language.clone(),
image: random_image.clone(),
})
diff --git a/src/structures.rs b/src/structures.rs
index 4ed5f47..3e9b7bc 100644
--- a/src/structures.rs
+++ b/src/structures.rs
@@ -20,7 +20,7 @@
use serde_derive::{Deserialize, Serialize};
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize)]
pub struct GitHubAPIResponse {
pub sha: String,
pub url: String,
@@ -29,7 +29,7 @@ pub struct GitHubAPIResponse {
}
impl Default for GitHubAPIResponse {
fn default() -> Self {
- GitHubAPIResponse {
+ Self {
sha: "rate limited".to_string(),
url: "rate limited".to_string(),
tree: vec![],
@@ -38,17 +38,17 @@ impl Default for GitHubAPIResponse {
}
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Default)]
pub struct GitHubAPIResponseTree {
- pub path: String,
- pub mode: String,
+ pub path: String,
+ pub mode: String,
#[serde(rename = "type")]
- pub _type: String,
- pub sha: String,
- pub url: String,
+ pub r#type: String,
+ pub sha: String,
+ pub url: String,
}
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Default)]
pub struct SenpyRandom {
pub language: String,
pub image: String,
diff --git a/src/utils.rs b/src/utils.rs
index 85ce4ca..fd2ba5b 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -16,22 +16,17 @@
// Copyright (C) 2022-2022 Fuwn <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only
-use crate::{
- constants::{GITHUB_API_ENDPOINT, GITHUB_USER_CONTENT},
- structures::GitHubAPIResponse,
-};
+use crate::{constants, structures::GitHubAPIResponse};
/// # Errors
/// if GitHub API is unresponsive
-pub async fn github_api() -> Result<GitHubAPIResponse, Box<dyn std::error::Error>> {
- let mut client = actix_web::client::Client::new()
- .get(GITHUB_API_ENDPOINT)
+pub async fn github_api(
+) -> Result<GitHubAPIResponse, Box<dyn std::error::Error>> {
+ let mut client = reqwest::Client::new()
+ .get(constants::GITHUB_API_ENDPOINT)
.header(
"User-Agent",
- format!(
- "senpy-api - {}",
- (0..10).map(|_| rand::random::<char>()).collect::<String>()
- ),
+ format!("senpy-club/api-worker - {}", env!("GIT_COMMIT_HASH")),
);
if std::env::var("GITHUB_TOKEN").is_ok() {
@@ -46,11 +41,9 @@ pub async fn github_api() -> Result<GitHubAPIResponse, Box<dyn std::error::Error
Ok(
client
- .timeout(std::time::Duration::from_secs(60))
.send()
.await?
.json::<GitHubAPIResponse>()
- .limit(20_000_000)
.await
.unwrap_or_default(),
)
@@ -62,8 +55,7 @@ pub async fn filter_languages() -> Vec<String> {
let mut languages = vec![];
for i in github_api().await.unwrap().tree {
- #[allow(clippy::used_underscore_binding)]
- if i._type == "tree" {
+ if i.r#type == "tree" {
languages.push(i.path);
}
}
@@ -76,14 +68,20 @@ pub async fn filter_languages() -> Vec<String> {
pub async fn filter_images_by_language(language: &str) -> Vec<String> {
let mut images = vec![];
- for i in github_api().await.unwrap().tree {
- // Example:
- // "Language/Image.png" would become ["Language", "Image.png"]
+ // URL (percent) encoding of pound symbol to pound symbol
+ let language = language.replace("%23", "#");
- // TODO: Fix this with type_ascription
- let x: Vec<&str> = i.path.split('/').collect();
- if x[0] == language && i.path.contains('/') {
- images.push(format!("{}{}", GITHUB_USER_CONTENT, i.path));
+ for item in github_api().await.unwrap().tree {
+ if item.path.split('/').collect::<Vec<&str>>()[0] == language
+ && item.path.contains('/')
+ {
+ images.push(format!(
+ "{}{}",
+ constants::GITHUB_USER_CONTENT,
+ // Pound symbols to URL (percent) encoding of pound symbol because we
+ // are pushing a URL, not a string
+ item.path.replace("#", "%23")
+ ));
}
}