You've already forked AstralRinth
forked from didirus/AstralRinth
Basic Database
This commit is contained in:
11
src/database/database.rs
Normal file
11
src/database/database.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use diesel::prelude::*;
|
||||
use diesel::pg::PgConnection;
|
||||
use dotenv::dotenv;
|
||||
use std::env;
|
||||
|
||||
pub fn connect() -> PgConnection{
|
||||
dotenv.ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set!");
|
||||
PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
|
||||
}
|
||||
6
src/database/mod.rs
Normal file
6
src/database/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
mod database;
|
||||
mod models;
|
||||
|
||||
pub use database::connect;
|
||||
pub use models::Mod;
|
||||
pub use models::Version;
|
||||
31
src/database/models.rs
Normal file
31
src/database/models.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::schema::mods;
|
||||
|
||||
#[derive(Queryable)]
|
||||
pub struct Mod {
|
||||
pub id: i32,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub published: String,
|
||||
pub author: String,
|
||||
pub downloads: i32,
|
||||
pub categories: Vec<String>,
|
||||
pub body_path: String,
|
||||
pub icon_path: String
|
||||
}
|
||||
|
||||
#[derive(Queryable)]
|
||||
pub struct Version {
|
||||
pub id: i32,
|
||||
pub mod_id: i32,
|
||||
pub title: String,
|
||||
pub changelog_path: String,
|
||||
pub files_path: Vec<String>,
|
||||
pub date_published: String,
|
||||
pub author: String,
|
||||
pub downloads: i32,
|
||||
pub dependencies: Vec<String>,
|
||||
pub game_versions: Vec<String>
|
||||
}
|
||||
|
||||
18
src/helpers/contains.rs
Normal file
18
src/helpers/contains.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use handlebars::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ContainsHelper;
|
||||
|
||||
impl HelperDef for ContainsHelper {
|
||||
fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars<'_>, ctx: &'rc Context, rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output) -> HelperResult {
|
||||
let array = h.param(0).map(|v| serde_json::from_value::<Vec<String>>(v.value().clone()).unwrap()).ok_or(RenderError::new("Parameter not found!"))?;
|
||||
let value = h.param(1).map(|v| v.value().as_str().unwrap()).ok_or(RenderError::new("Parameter not found!"))?;
|
||||
|
||||
let tmpl = if array.contains(&String::from(value)) { h.template() } else { h.inverse() };
|
||||
|
||||
match tmpl {
|
||||
Some(ref t) => t.render(r, ctx, rc, out),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/helpers/mod.rs
Normal file
3
src/helpers/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod contains;
|
||||
|
||||
pub use contains::ContainsHelper;
|
||||
162
src/main.rs
162
src/main.rs
@@ -1,179 +1,43 @@
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
||||
use actix_web::{web, web::Data, App, HttpRequest, HttpResponse, HttpServer, Responder, get, post};
|
||||
use handlebars::*;
|
||||
use meilisearch_sdk::{document::*, indexes::*, client::*, search::*};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use actix_files as fs;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ContainsHelper;
|
||||
|
||||
impl HelperDef for ContainsHelper {
|
||||
fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars<'_>, ctx: &'rc Context, rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output) -> HelperResult {
|
||||
let array = h.param(0).map(|v| serde_json::from_value::<Vec<String>>(v.value().clone()).unwrap()).ok_or(RenderError::new("Parameter not found!"))?;
|
||||
let value = h.param(1).map(|v| v.value().as_str().unwrap()).ok_or(RenderError::new("Parameter not found!"))?;
|
||||
|
||||
let tmpl = if array.contains(&String::from(value)) { h.template() } else { h.inverse() };
|
||||
|
||||
match tmpl {
|
||||
Some(ref t) => t.render(r, ctx, rc, out),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Mod {
|
||||
mod_id: usize,
|
||||
title: String,
|
||||
description: String,
|
||||
keywords: Vec<String>,
|
||||
versions: Vec<String>,
|
||||
}
|
||||
|
||||
impl Document for Mod {
|
||||
type UIDType = usize;
|
||||
|
||||
fn get_uid(&self) -> &Self::UIDType {
|
||||
&self.mod_id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SearchRequest {
|
||||
q: Option<String>,
|
||||
f: Option<String>,
|
||||
v: Option<String>,
|
||||
}
|
||||
|
||||
#[post("search")]
|
||||
async fn search_post(web::Query(info): web::Query<SearchRequest>, hb: Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let results = search(web::Query(info));
|
||||
|
||||
let data = json!({
|
||||
"results": results,
|
||||
});
|
||||
|
||||
let body = hb.render("search_results", &data).unwrap();
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
#[get("search")]
|
||||
async fn search_get(web::Query(info): web::Query<SearchRequest>, hb: Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let results = search(web::Query(info));
|
||||
|
||||
let data = json!({
|
||||
"results": results,
|
||||
});
|
||||
|
||||
let body = hb.render("search", &data).unwrap();
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
fn search(web::Query(info): web::Query<SearchRequest>) -> Vec<Mod> {
|
||||
let client = Client::new("http://localhost:7700", "");
|
||||
|
||||
let mut search_query = "".to_string();
|
||||
let mut filters = "".to_string();
|
||||
|
||||
|
||||
if let Some(q) = info.q {
|
||||
search_query = q;
|
||||
}
|
||||
|
||||
if let Some(f) = info.f {
|
||||
filters = f;
|
||||
}
|
||||
|
||||
if let Some(v) = info.v {
|
||||
if filters.is_empty() {
|
||||
filters = v;
|
||||
}
|
||||
else {
|
||||
filters = format!("({}) AND {}", filters, v);
|
||||
}
|
||||
}
|
||||
|
||||
let mut query = Query::new(&search_query).with_limit(10);
|
||||
|
||||
if !filters.is_empty() {
|
||||
query = Query::new(&search_query).with_limit(10).with_filters(&filters);
|
||||
}
|
||||
|
||||
client.get_index("mods").unwrap().search::<Mod>(&query).unwrap().hits
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(hb: web::Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let data = json!({
|
||||
"name": "Handlebars"
|
||||
});
|
||||
let body = hb.render("index", &data).unwrap();
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
mod schema;
|
||||
mod routes;
|
||||
mod helpers;
|
||||
mod database;
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
//Handlebars
|
||||
let mut handlebars = Handlebars::new();
|
||||
|
||||
handlebars.register_helper("contains", Box::new(ContainsHelper));
|
||||
handlebars.register_helper("contains", Box::new(helpers::ContainsHelper));
|
||||
handlebars
|
||||
.register_templates_directory(".hbs", "./templates")
|
||||
.unwrap();
|
||||
|
||||
let handlebars_ref = web::Data::new(handlebars);
|
||||
|
||||
//Search
|
||||
|
||||
let client = Client::new("http://localhost:7700", "");
|
||||
let mut mods = client.get_or_create("mods").unwrap();
|
||||
|
||||
mods.add_documents(vec![
|
||||
Mod {
|
||||
mod_id: 0,
|
||||
title: String::from("Magic Mod"),
|
||||
description: String::from("An illustrious magic mod for magical wizards"),
|
||||
keywords: vec![String::from("fabric"), String::from("magic"), String::from("library")],
|
||||
versions: vec![String::from("1.15.2"), String::from("1.15.1"), String::from("1.15")],
|
||||
},
|
||||
Mod {
|
||||
mod_id: 1,
|
||||
title: String::from("Tech Mod"),
|
||||
description: String::from("An technological mod for complete NERDS"),
|
||||
keywords: vec![String::from("fabric"), String::from("utility"), String::from("technology")],
|
||||
versions: vec![String::from("1.14.1"), String::from("1.15.1"), String::from("1.15")],
|
||||
},
|
||||
Mod {
|
||||
mod_id: 2,
|
||||
title: String::from("Gamer Mod"),
|
||||
description: String::from("A gamer mod to roleplay as if you were an epic gamer person."),
|
||||
keywords: vec![String::from("cursed"), String::from("adventure"), String::from("forge")],
|
||||
versions: vec![String::from("20w20a"), String::from("1.15.1"), String::from("1.15")],
|
||||
},
|
||||
Mod {
|
||||
mod_id: 3,
|
||||
title: String::from("Adventure Mod"),
|
||||
description: String::from("An epic gamer adventure mod for epic adventure gamers"),
|
||||
keywords: vec![String::from("decoration"), String::from("utility"), String::from("worldgen")],
|
||||
versions: vec![String::from("1.12.2"), String::from("1.15.1"), String::from("1.15")]
|
||||
},
|
||||
], Some("mod_id")).unwrap();
|
||||
let database = database::connect();
|
||||
routes::index_mods(database);
|
||||
|
||||
//Init App
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(handlebars_ref.clone())
|
||||
.service(fs::Files::new("/static", "./static").show_files_listing())
|
||||
.service(index)
|
||||
.service(search_get)
|
||||
.service(search_post)
|
||||
.service(routes::index_get)
|
||||
.service(routes::search_post)
|
||||
.service(routes::search_get)
|
||||
})
|
||||
.bind("127.0.0.1:8000")?
|
||||
.run()
|
||||
|
||||
12
src/routes/index.rs
Normal file
12
src/routes/index.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use actix_web::{web, web::Data, App, HttpRequest, HttpResponse, HttpServer, Responder, get, post};
|
||||
use handlebars::*;
|
||||
|
||||
#[get("/")]
|
||||
pub async fn index_get(hb: web::Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let data = json!({
|
||||
"name": "Handlebars"
|
||||
});
|
||||
let body = hb.render("index", &data).unwrap();
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
8
src/routes/mod.rs
Normal file
8
src/routes/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
mod search;
|
||||
mod index;
|
||||
|
||||
pub use self::search::search_get;
|
||||
pub use self::search::search_post;
|
||||
pub use self::search::index_mods;
|
||||
|
||||
pub use self::index::index_get;
|
||||
125
src/routes/search.rs
Normal file
125
src/routes/search.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
extern crate diesel;
|
||||
|
||||
use actix_web::{web, web::Data, App, HttpRequest, HttpResponse, HttpServer, Responder, get, post};
|
||||
use handlebars::*;
|
||||
use meilisearch_sdk::{document::*, indexes::*, client::*, search::*};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::database::*;
|
||||
use diesel::prelude::*;
|
||||
use std::ptr::eq;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct SearchMod {
|
||||
mod_id: i32,
|
||||
title: String,
|
||||
description: String,
|
||||
keywords: Vec<String>,
|
||||
versions: Vec<String>,
|
||||
}
|
||||
|
||||
impl Document for SearchMod {
|
||||
type UIDType = i32;
|
||||
|
||||
fn get_uid(&self) -> &Self::UIDType {
|
||||
&self.mod_id
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SearchRequest {
|
||||
q: Option<String>,
|
||||
f: Option<String>,
|
||||
v: Option<String>,
|
||||
}
|
||||
|
||||
#[post("search")]
|
||||
pub async fn search_post(web::Query(info): web::Query<SearchRequest>, hb: Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let results = search(web::Query(info));
|
||||
|
||||
let data = json!({
|
||||
"results": results,
|
||||
});
|
||||
|
||||
let body = hb.render("search_results", &data).unwrap();
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
#[get("search")]
|
||||
pub async fn search_get(web::Query(info): web::Query<SearchRequest>, hb: Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let results = search(web::Query(info));
|
||||
|
||||
let data = json!({
|
||||
"results": results,
|
||||
});
|
||||
|
||||
let body = hb.render("search", &data).unwrap();
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
fn search(web::Query(info): web::Query<SearchRequest>) -> Vec<SearchMod> {
|
||||
let client = Client::new("http://localhost:7700", "");
|
||||
|
||||
let mut search_query = "".to_string();
|
||||
let mut filters = "".to_string();
|
||||
|
||||
|
||||
if let Some(q) = info.q {
|
||||
search_query = q;
|
||||
}
|
||||
|
||||
if let Some(f) = info.f {
|
||||
filters = f;
|
||||
}
|
||||
|
||||
if let Some(v) = info.v {
|
||||
if filters.is_empty() {
|
||||
filters = v;
|
||||
}
|
||||
else {
|
||||
filters = format!("({}) AND {}", filters, v);
|
||||
}
|
||||
}
|
||||
|
||||
let mut query = Query::new(&search_query).with_limit(10);
|
||||
|
||||
if !filters.is_empty() {
|
||||
query = Query::new(&search_query).with_limit(10).with_filters(&filters);
|
||||
}
|
||||
|
||||
client.get_index("mods").unwrap().search::<SearchMod>(&query).unwrap().hits
|
||||
}
|
||||
|
||||
pub fn index_mods(conn : PgConnection) {
|
||||
use crate::schema::mods::dsl::*;
|
||||
use crate::schema::versions::dsl::*;
|
||||
|
||||
let client = Client::new("http://localhost:7700", "");
|
||||
let mut mods_index = client.get_or_create("mods").unwrap();
|
||||
|
||||
let results = mods.load::<Mod>(&conn).expect("Error loading mods!");
|
||||
let mut docs_to_add = vec![];
|
||||
|
||||
for result in results {
|
||||
let versions = versions.filter(mod_id.eq(result.id)).load::<Version>(&conn).expect("Error loading versions!");
|
||||
|
||||
let mut mod_versions = vec![];
|
||||
|
||||
for version in versions {
|
||||
mod_versions.append(version.game_versions())
|
||||
}
|
||||
|
||||
docs_to_add.push(SearchMod {
|
||||
mod_id: result.id,
|
||||
title: result.title,
|
||||
description: result.description,
|
||||
keywords: result.categories,
|
||||
versions: vec![]
|
||||
});
|
||||
}
|
||||
|
||||
mods_index.add_documents(docs_to_add, Some("mod_id"));
|
||||
|
||||
}
|
||||
33
src/schema.rs
Normal file
33
src/schema.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
table! {
|
||||
mods (id) {
|
||||
id -> Int4,
|
||||
title -> Varchar,
|
||||
description -> Varchar,
|
||||
datepublished -> Date,
|
||||
author -> Varchar,
|
||||
downloads -> Int4,
|
||||
categories -> Array<Text>,
|
||||
body_path -> Varchar,
|
||||
icon_path -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
versions (id) {
|
||||
id -> Int4,
|
||||
mod_id -> Int4,
|
||||
title -> Varchar,
|
||||
changelog_path -> Varchar,
|
||||
files_path -> Array<Text>,
|
||||
date_published -> Date,
|
||||
author -> Varchar,
|
||||
downloads -> Int4,
|
||||
dependencies -> Array<Text>,
|
||||
game_versions -> Array<Text>,
|
||||
}
|
||||
}
|
||||
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
mods,
|
||||
versions,
|
||||
);
|
||||
Reference in New Issue
Block a user