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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user