2021-07-12 20:14:39 +00:00
|
|
|
mod admin;
|
|
|
|
mod assets;
|
|
|
|
mod auth;
|
2021-09-29 17:15:19 +00:00
|
|
|
mod community;
|
2021-08-06 02:06:50 +00:00
|
|
|
mod db;
|
2021-07-12 20:14:39 +00:00
|
|
|
mod env;
|
|
|
|
mod errors;
|
|
|
|
mod expiring;
|
|
|
|
mod github;
|
|
|
|
mod home;
|
2021-09-29 17:15:19 +00:00
|
|
|
mod releases;
|
2021-07-12 20:14:39 +00:00
|
|
|
mod rpc;
|
|
|
|
mod team;
|
|
|
|
|
|
|
|
use self::errors::TideResultExt as _;
|
2021-08-20 14:24:33 +00:00
|
|
|
use anyhow::Result;
|
2021-08-19 19:17:52 +00:00
|
|
|
use async_std::net::TcpListener;
|
2021-07-12 20:14:39 +00:00
|
|
|
use async_trait::async_trait;
|
|
|
|
use auth::RequestExt as _;
|
2021-08-20 14:24:33 +00:00
|
|
|
use db::Db;
|
2021-07-12 20:14:39 +00:00
|
|
|
use handlebars::{Handlebars, TemplateRenderError};
|
|
|
|
use parking_lot::RwLock;
|
|
|
|
use rust_embed::RustEmbed;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::sync::Arc;
|
|
|
|
use surf::http::cookies::SameSite;
|
|
|
|
use tide::{log, sessions::SessionMiddleware};
|
|
|
|
use tide_compress::CompressMiddleware;
|
2021-10-04 19:28:00 +00:00
|
|
|
use rpc::Peer;
|
2021-07-12 20:14:39 +00:00
|
|
|
|
|
|
|
type Request = tide::Request<Arc<AppState>>;
|
|
|
|
|
|
|
|
#[derive(RustEmbed)]
|
|
|
|
#[folder = "templates"]
|
|
|
|
struct Templates;
|
|
|
|
|
|
|
|
#[derive(Default, Deserialize)]
|
|
|
|
pub struct Config {
|
|
|
|
pub http_port: u16,
|
|
|
|
pub database_url: String,
|
|
|
|
pub session_secret: String,
|
|
|
|
pub github_app_id: usize,
|
|
|
|
pub github_client_id: String,
|
|
|
|
pub github_client_secret: String,
|
|
|
|
pub github_private_key: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct AppState {
|
2021-08-06 02:06:50 +00:00
|
|
|
db: Db,
|
2021-07-12 20:14:39 +00:00
|
|
|
handlebars: RwLock<Handlebars<'static>>,
|
|
|
|
auth_client: auth::Client,
|
|
|
|
github_client: Arc<github::AppClient>,
|
|
|
|
repo_client: github::RepoClient,
|
|
|
|
config: Config,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AppState {
|
|
|
|
async fn new(config: Config) -> tide::Result<Arc<Self>> {
|
2021-08-20 14:24:33 +00:00
|
|
|
let db = Db::new(&config.database_url, 5).await?;
|
2021-07-12 20:14:39 +00:00
|
|
|
let github_client =
|
|
|
|
github::AppClient::new(config.github_app_id, config.github_private_key.clone());
|
|
|
|
let repo_client = github_client
|
|
|
|
.repo("zed-industries/zed".into())
|
|
|
|
.await
|
|
|
|
.context("failed to initialize github client")?;
|
|
|
|
|
|
|
|
let this = Self {
|
|
|
|
db,
|
|
|
|
handlebars: Default::default(),
|
|
|
|
auth_client: auth::build_client(&config.github_client_id, &config.github_client_secret),
|
|
|
|
github_client,
|
|
|
|
repo_client,
|
|
|
|
config,
|
|
|
|
};
|
|
|
|
this.register_partials();
|
|
|
|
Ok(Arc::new(this))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_partials(&self) {
|
|
|
|
for path in Templates::iter() {
|
|
|
|
if let Some(partial_name) = path
|
|
|
|
.strip_prefix("partials/")
|
|
|
|
.and_then(|path| path.strip_suffix(".hbs"))
|
|
|
|
{
|
|
|
|
let partial = Templates::get(path.as_ref()).unwrap();
|
|
|
|
self.handlebars
|
|
|
|
.write()
|
2021-09-04 15:02:20 +00:00
|
|
|
.register_partial(partial_name, std::str::from_utf8(&partial.data).unwrap())
|
2021-07-12 20:14:39 +00:00
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_template(
|
|
|
|
&self,
|
|
|
|
path: &'static str,
|
|
|
|
data: &impl Serialize,
|
|
|
|
) -> Result<String, TemplateRenderError> {
|
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
self.register_partials();
|
|
|
|
|
|
|
|
self.handlebars.read().render_template(
|
2021-09-04 15:02:20 +00:00
|
|
|
std::str::from_utf8(&Templates::get(path).unwrap().data).unwrap(),
|
2021-07-12 20:14:39 +00:00
|
|
|
data,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
trait RequestExt {
|
|
|
|
async fn layout_data(&mut self) -> tide::Result<Arc<LayoutData>>;
|
2021-08-06 02:06:50 +00:00
|
|
|
fn db(&self) -> &Db;
|
2021-07-12 20:14:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
impl RequestExt for Request {
|
|
|
|
async fn layout_data(&mut self) -> tide::Result<Arc<LayoutData>> {
|
|
|
|
if self.ext::<Arc<LayoutData>>().is_none() {
|
|
|
|
self.set_ext(Arc::new(LayoutData {
|
|
|
|
current_user: self.current_user().await?,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
Ok(self.ext::<Arc<LayoutData>>().unwrap().clone())
|
|
|
|
}
|
|
|
|
|
2021-08-06 02:06:50 +00:00
|
|
|
fn db(&self) -> &Db {
|
2021-07-12 20:14:39 +00:00
|
|
|
&self.state().db
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
struct LayoutData {
|
|
|
|
current_user: Option<auth::User>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_std::main]
|
|
|
|
async fn main() -> tide::Result<()> {
|
|
|
|
log::start();
|
|
|
|
|
|
|
|
if let Err(error) = env::load_dotenv() {
|
|
|
|
log::error!(
|
|
|
|
"error loading .env.toml (this is expected in production): {}",
|
|
|
|
error
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let config = envy::from_env::<Config>().expect("error loading config");
|
|
|
|
let state = AppState::new(config).await?;
|
|
|
|
let rpc = Peer::new();
|
|
|
|
run_server(
|
|
|
|
state.clone(),
|
|
|
|
rpc,
|
|
|
|
TcpListener::bind(&format!("0.0.0.0:{}", state.config.http_port)).await?,
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn run_server(
|
|
|
|
state: Arc<AppState>,
|
|
|
|
rpc: Arc<Peer>,
|
|
|
|
listener: TcpListener,
|
|
|
|
) -> tide::Result<()> {
|
|
|
|
let mut web = tide::with_state(state.clone());
|
|
|
|
web.with(CompressMiddleware::new());
|
|
|
|
web.with(
|
|
|
|
SessionMiddleware::new(
|
2021-08-06 02:06:50 +00:00
|
|
|
db::SessionStore::new_with_table_name(&state.config.database_url, "sessions")
|
2021-07-12 20:14:39 +00:00
|
|
|
.await
|
|
|
|
.unwrap(),
|
|
|
|
state.config.session_secret.as_bytes(),
|
|
|
|
)
|
|
|
|
.with_same_site_policy(SameSite::Lax), // Required obtain our session in /auth_callback
|
|
|
|
);
|
|
|
|
web.with(errors::Middleware);
|
|
|
|
home::add_routes(&mut web);
|
|
|
|
team::add_routes(&mut web);
|
2021-09-15 22:28:38 +00:00
|
|
|
releases::add_routes(&mut web);
|
|
|
|
community::add_routes(&mut web);
|
2021-07-12 20:14:39 +00:00
|
|
|
admin::add_routes(&mut web);
|
|
|
|
auth::add_routes(&mut web);
|
2021-09-29 17:15:19 +00:00
|
|
|
|
|
|
|
let mut assets = tide::new();
|
|
|
|
assets.with(CompressMiddleware::new());
|
|
|
|
assets::add_routes(&mut assets);
|
2021-07-12 20:14:39 +00:00
|
|
|
|
|
|
|
let mut app = tide::with_state(state.clone());
|
|
|
|
rpc::add_routes(&mut app, &rpc);
|
2021-09-29 17:15:19 +00:00
|
|
|
|
2021-07-12 20:14:39 +00:00
|
|
|
app.at("/").nest(web);
|
2021-09-29 17:15:19 +00:00
|
|
|
app.at("/static").nest(assets);
|
2021-07-12 20:14:39 +00:00
|
|
|
|
|
|
|
app.listen(listener).await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|