chore: delete backend
Initially, the backend was meant to cache calls to the GitHub API. However, I decided to just cache them in the sessionStorage of the visitor’s browser instead.
This commit is contained in:
parent
e438e120cb
commit
668e1e2a96
@ -1,3 +0,0 @@
|
|||||||
GH_TOKEN=
|
|
||||||
ACTIX_ADDRESS=127.0.0.1
|
|
||||||
ACTIX_PORT=8080
|
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,5 +2,3 @@ node_modules
|
|||||||
.temp
|
.temp
|
||||||
.cache
|
.cache
|
||||||
/content/.vuepress/dist/*
|
/content/.vuepress/dist/*
|
||||||
/target
|
|
||||||
.env
|
|
||||||
|
1838
Cargo.lock
generated
1838
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
21
Cargo.toml
@ -1,21 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "phuncache"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
authors = ["Lucien Cartier-Tilet <lucien@phundrak.com>"]
|
|
||||||
homepage = "https://labs.phundrak.com/phundrak/phundrak.com"
|
|
||||||
license = "AGPL-3.0"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
actix-web = "4"
|
|
||||||
env_logger = "0.10.0"
|
|
||||||
derive_more = "0.99.17"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
dotenvy = "0.15"
|
|
||||||
gql_client = "1.0.7"
|
|
||||||
human-panic = "1.1.0"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "phuncache"
|
|
||||||
path = "src/main.rs"
|
|
21
Dockerfile
21
Dockerfile
@ -1,21 +0,0 @@
|
|||||||
FROM rust:alpine
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
RUN apk update && apk add pkgconfig openssl-dev musl-dev
|
|
||||||
|
|
||||||
COPY Cargo.toml .
|
|
||||||
COPY dummy.rs .
|
|
||||||
|
|
||||||
# Cache building
|
|
||||||
RUN sed -i 's|src/main.rs|dummy.rs|' Cargo.toml
|
|
||||||
RUN cargo build --release
|
|
||||||
RUN cargo build
|
|
||||||
RUN sed -i 's|dummy.rs|src/main.rs|' Cargo.toml
|
|
||||||
|
|
||||||
# Building normally
|
|
||||||
COPY src src
|
|
||||||
RUN cargo build
|
|
||||||
RUN cargo install --path .
|
|
||||||
|
|
||||||
ENTRYPOINT [ "phuncache" ]
|
|
25
README.org
25
README.org
@ -1,5 +1,10 @@
|
|||||||
#+title: phundrak.com
|
#+title: phundrak.com
|
||||||
|
|
||||||
|
#+html: <a href="https://www.gnu.org/software/emacs/"><img src="https://img.shields.io/badge/Emacs-30.0.50-blueviolet.svg?style=flat-square&logo=GNU%20Emacs&logoColor=white" /></a>
|
||||||
|
#+html: <a href="https://orgmode.org/"><img src="https://img.shields.io/badge/Written%20with-Org%20mode-success?logo=Org&logoColor=white&style=flat-square"/></a>
|
||||||
|
#+html: <a href="https://v2.vuepress.vuejs.org/"><img src="https://img.shields.io/badge/Framework-Vuepress-42D392?logo=Vue.js&logoColor=white&style=flat-square"/></a>
|
||||||
|
#+html: <a href="https://conlang.phundrak.com"><img src="https://img.shields.io/badge/dynamic/json?label=Website&query=%24%5B%3A1%5D.status&url=https%3A%2F%2Fdrone.phundrak.com%2Fapi%2Frepos%2Fphundrak%2Fconlang.phundrak.com%2Fbuilds&style=flat-square&logo=buffer" /></a>
|
||||||
|
|
||||||
* Introduction
|
* Introduction
|
||||||
This is the repository for my website [[https://phundrak.com][phundrak.com]]. While it is not
|
This is the repository for my website [[https://phundrak.com][phundrak.com]]. While it is not
|
||||||
yet live on this address, development versions can be found at
|
yet live on this address, development versions can be found at
|
||||||
@ -7,13 +12,12 @@ yet live on this address, development versions can be found at
|
|||||||
=develop= branch while the latter follows the =master= branch).
|
=develop= branch while the latter follows the =master= branch).
|
||||||
|
|
||||||
* Structure of the project
|
* Structure of the project
|
||||||
** Frontend
|
This website is made with [[https://v2.vuepress.vuejs.org/][VuePress]], a Vue-powered static site
|
||||||
The frontend is made with [[https://v2.vuepress.vuejs.org/][VuePress]], a Vue-powered static site
|
|
||||||
generator. You can find its Node.JS configuration in the [[file:package.json][package.json]]
|
generator. You can find its Node.JS configuration in the [[file:package.json][package.json]]
|
||||||
file as well as its content and general configuration in the directory
|
file as well as its content and general configuration in the directory
|
||||||
[[file:content/][content]].
|
[[file:content/][content]].
|
||||||
|
|
||||||
*** Installing and running
|
** Installing and running
|
||||||
To install the NPM dependencies for the project, run one of the
|
To install the NPM dependencies for the project, run one of the
|
||||||
following commands:
|
following commands:
|
||||||
#+begin_src shell
|
#+begin_src shell
|
||||||
@ -38,18 +42,3 @@ npm run build
|
|||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
The compiled version of the website can then be found in =content/.vuepress/dist=.
|
The compiled version of the website can then be found in =content/.vuepress/dist=.
|
||||||
|
|
||||||
** Backend
|
|
||||||
This is a simple backend server written in Rust, offering a REST API
|
|
||||||
at the address =https://phundrak.com/api/v1=. It communicates with a
|
|
||||||
Redis instance in order to cache some queries the frontend makes.
|
|
||||||
|
|
||||||
*** Installing and running
|
|
||||||
The currently preferred way of running the project is through
|
|
||||||
=docker-compose= using these commands:
|
|
||||||
#+begin_src shell
|
|
||||||
docker-compose pull # retrieve the necessary images (optional)
|
|
||||||
docker-compose build
|
|
||||||
docker-compose up # add option -d to detach immediately
|
|
||||||
docker-compose down # to stop the container if you detached it previously
|
|
||||||
#+end_src
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
|
||||||
redis:
|
|
||||||
image: redis:alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
phuncache:
|
|
||||||
build: .
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- 8080:8080
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
51
src/data.rs
51
src/data.rs
@ -1,51 +0,0 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Data {
|
|
||||||
pub user: User,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// GitHub user
|
|
||||||
///
|
|
||||||
/// Contains their newest, most starred, and pinned repositories,
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct User {
|
|
||||||
pub newest: Newest,
|
|
||||||
pub most_starred: MostStarred,
|
|
||||||
pub pinned: Pinned,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Newest repositories
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Newest {
|
|
||||||
pub nodes: Vec<Repository>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Most starred repositories
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct MostStarred {
|
|
||||||
pub nodes: Vec<Repository>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pinned repositories
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Pinned {
|
|
||||||
pub nodes: Vec<Repository>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Repository
|
|
||||||
///
|
|
||||||
/// Contains the name of the repository, the amount of stars, and the
|
|
||||||
/// amount of forks of the repository
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase", rename = "node")]
|
|
||||||
pub struct Repository {
|
|
||||||
pub name: String,
|
|
||||||
pub stargazer_count: i64,
|
|
||||||
pub fork_count: i64,
|
|
||||||
}
|
|
152
src/main.rs
152
src/main.rs
@ -1,152 +0,0 @@
|
|||||||
use actix_web::{error, get, web, Responder, Result};
|
|
||||||
use data::Data;
|
|
||||||
use derive_more::{Display, Error};
|
|
||||||
use dotenvy::dotenv;
|
|
||||||
use gql_client::{Client, GraphQLError};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
mod data;
|
|
||||||
|
|
||||||
/// Squeleton of the GitHub GraphQL query
|
|
||||||
///
|
|
||||||
/// This is a macro and not a constant because `format!()` does not
|
|
||||||
/// accept anything besides string litterals as its first argument.
|
|
||||||
/// Fortunately, macros "return" string litterals, so this should do.
|
|
||||||
macro_rules! GITHUB_GRAPHQL_QUERY {
|
|
||||||
() => {
|
|
||||||
r#"query {{
|
|
||||||
user(login: "{}") {{
|
|
||||||
newest: repositories(first: 10, orderBy: {{field: UPDATED_AT, direction: DESC}}) {{
|
|
||||||
nodes {{
|
|
||||||
name
|
|
||||||
stargazerCount
|
|
||||||
forkCount
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
mostStarred: repositories(first: 10, orderBy: {{field: STARGAZERS, direction: DESC}}) {{
|
|
||||||
nodes {{
|
|
||||||
name
|
|
||||||
stargazerCount
|
|
||||||
forkCount
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
pinned: pinnedItems(first: 10) {{
|
|
||||||
nodes {{
|
|
||||||
... on Repository {{
|
|
||||||
name
|
|
||||||
stargazerCount
|
|
||||||
forkCount
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
}}"#
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Display, Error)]
|
|
||||||
#[display(fmt = "my error: {}", name)]
|
|
||||||
struct MyError {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AppState {
|
|
||||||
github_token: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AppState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
github_token: github_token(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::ResponseError for MyError {}
|
|
||||||
|
|
||||||
/// Retrieve the GitHub token from the environment variables.
|
|
||||||
fn github_token() -> String {
|
|
||||||
std::env::var("GH_TOKEN")
|
|
||||||
.expect("Environment variable GH_TOKEN **MUST** be set!")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepare headers for querying GitHub’s API
|
|
||||||
fn prepare_github_query_headers(gh_token: &String) -> HashMap<String, String> {
|
|
||||||
let mut headers: HashMap<String, String> = HashMap::new();
|
|
||||||
headers.insert("Authorization".to_string(), format!("Bearer {}", gh_token));
|
|
||||||
headers.insert("User-Agent".to_string(), "PhunCache-App".to_string());
|
|
||||||
headers
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a GraphQL client for GitHub’s API
|
|
||||||
fn make_gh_graphql_client(gh_token: &String) -> Client {
|
|
||||||
let headers = prepare_github_query_headers(gh_token);
|
|
||||||
Client::new_with_headers("https://api.github.com/graphql", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make GraphQL call to GitHub
|
|
||||||
///
|
|
||||||
/// This function should be called only when the Redis database holds
|
|
||||||
/// either no records or outdated records.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// Returns the Json response from GitHub. This should be parsable by
|
|
||||||
/// serde into the `Data` type.
|
|
||||||
async fn make_gh_graphql_call(
|
|
||||||
user: String,
|
|
||||||
gh_token: &String,
|
|
||||||
) -> Result<Option<Data>, GraphQLError> {
|
|
||||||
let body = format!(GITHUB_GRAPHQL_QUERY!(), user);
|
|
||||||
let client = make_gh_graphql_client(gh_token);
|
|
||||||
let data = client.query::<Data>(&body).await;
|
|
||||||
println!("{:?}", data);
|
|
||||||
data
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/phundrak-com/{user}")]
|
|
||||||
async fn user_info_github(
|
|
||||||
user: web::Path<String>,
|
|
||||||
state: web::Data<AppState>,
|
|
||||||
) -> Result<impl Responder> {
|
|
||||||
let gh_token = &state.github_token;
|
|
||||||
match make_gh_graphql_call(user.to_string(), gh_token).await {
|
|
||||||
Ok(val) => Ok(web::Json(val)),
|
|
||||||
Err(e) => Err(MyError {
|
|
||||||
name: format!("Failed to retrieve data from GitHub: {e:?}"),
|
|
||||||
}
|
|
||||||
.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Setup various helpers for application
|
|
||||||
fn setup_application() {
|
|
||||||
std::env::set_var("RUST_LOG", "debug");
|
|
||||||
env_logger::init();
|
|
||||||
dotenv().ok();
|
|
||||||
human_panic::setup_panic!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::main]
|
|
||||||
async fn main() -> std::io::Result<()> {
|
|
||||||
use actix_web::{App, HttpServer};
|
|
||||||
use std::env::var;
|
|
||||||
|
|
||||||
setup_application();
|
|
||||||
|
|
||||||
HttpServer::new(|| {
|
|
||||||
App::new()
|
|
||||||
.app_data(web::Data::new(AppState::default()))
|
|
||||||
.service(user_info_github)
|
|
||||||
})
|
|
||||||
.bind((
|
|
||||||
var("ACTIX_ADDRESS")
|
|
||||||
.expect("Environment variable ACTIX_ADDRESS must be set!"),
|
|
||||||
var("ACTIX_PORT")
|
|
||||||
.expect("Environment variable ACTIX_PORT must be set!")
|
|
||||||
.parse()
|
|
||||||
.expect("Failed to parse value of ACTIX_PORT into a u16"),
|
|
||||||
))?
|
|
||||||
.run()
|
|
||||||
.await
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user