Skip to content

Commit a373b77

Browse files
authored
Merge pull request #136 from metakgp/import-script
Improvements to import script and other changes
2 parents 750e711 + d181737 commit a373b77

File tree

7 files changed

+109
-36
lines changed

7 files changed

+109
-36
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ go.work.sum
1313

1414
frontend/public/pdf.worker.min.mjs
1515
*.log
16-
*.patch
16+
*.patch
17+
log

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ A user is considered as an admin if they are a part of the team `GH_ORG_TEAM_SLU
126126

127127
### Crawler
128128

129+
The crawler is a go script which crawls and downloads papers from [peqp](http://10.18.24.75/peqp/) (only accessible over campus network) and spits an archive which can be imported into the database.
130+
129131
1. Change directory to `crawler/` and run `go mod tidy`.
130-
2. Run the crawler by running `go run crawler.go`. (Make sure you are connected to the campus network)
132+
2. Run the crawler by running `go run crawler.go [flags]`. (Make sure you are connected to the campus network) (Run `go run crawler.go -h` to see flags that can be set.)
131133
3. This will generate a `qp.tar.gz` file. Transfer this file to the server's `backend/` folder.
132-
4. (Development): In the backend, run `cargo run --bin import-papers` to import the data into the database. (Make sure the database is set up and running)\
134+
4. (Development): In the backend, run `cargo run --bin import-papers qp.tar.gz` to import the data into the database. (Make sure the database is set up and running)\
133135
(Production): In the backend, run `./import_papers.sh ./qp.tar.gz` to run the import script in the docker container running the application.
134136

135137
## Deployment

backend/import_papers.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@ ARCHIVE_PATH="$1"
1010
SERVICE="iqps-backend"
1111
DEST_PATH="/app/qp.tar.gz"
1212

13+
if [[ ! -f "$ARCHIVE_PATH" ]]; then
14+
echo "Error: File '$ARCHIVE_PATH' not found."
15+
exit 1
16+
fi
17+
1318
echo "Copying '$ARCHIVE_PATH' to '$SERVICE'..."
1419
docker compose cp "$ARCHIVE_PATH" "$SERVICE":"$DEST_PATH"
1520

1621
echo "Running import-papers..."
1722
docker compose exec "$SERVICE" ./import-papers
1823

24+
echo "Deleting copied file from container..."
25+
docker compose exec "$SERVICE" rm -f "$DEST_PATH"
26+
1927
echo "Done!"

backend/src/bin/import-papers.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,41 @@ use tempfile::tempdir;
1313
use tracing::{info, warn};
1414
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};
1515

16+
#[derive(Parser, Debug)]
17+
#[command(
18+
name = "import-papers",
19+
about = "Imports papers into the database from an archive.",
20+
version,
21+
author
22+
)]
23+
struct Args {
24+
/// Path to the .tar.gz file containing papers (e.g., qp.tar.gz)
25+
file: String,
26+
}
27+
1628
#[tokio::main]
1729
async fn main() -> Result<(), Box<dyn std::error::Error>> {
1830
if dotenvy::dotenv().is_ok() {
1931
println!("Loaded an existing .env file.");
2032
}
21-
let env_vars = env::EnvVars::parse()
33+
34+
let env_vars = env::EnvVars::parse()?
2235
.process()
2336
.expect("Failed to parse environment variables");
2437

38+
let args = Args::parse();
39+
if !Path::new(&args.file).exists() {
40+
eprintln!("Error: file '{}' not found.", args.file);
41+
std::process::exit(1);
42+
}
43+
2544
let database = db::Database::new(&env_vars)
2645
.await
2746
.expect("Failed to connect to database");
2847

2948
let dir = tempdir()?;
3049
let dir_path = dir.path();
31-
extract_tar_gz("qp.tar.gz", dir_path)?;
50+
extract_tar_gz(&args.file, dir_path)?;
3251

3352
let file = fs::File::open(dir_path.join("qp.json")).expect("Failed to open JSON file");
3453
let reader = BufReader::new(file);
@@ -142,9 +161,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
142161
println!("Finished uploading papers to database.");
143162
dir.close()?;
144163

164+
let total_count = database.get_unapproved_papers_count().await?;
165+
145166
let message = format!(
146-
"{} papers have been imported into IQPS!",
147-
count,
167+
"💥 {count} papers have been imported into IQPS!\n\n<https://qp.metakgp.org/admin|Review> | Total Unapproved papers: *{total_count}*",
148168
);
149169

150170
let _ = slack::send_slack_message(

backend/src/env.rs

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,95 +4,135 @@
44
55
use std::path::PathBuf;
66

7-
use clap::Parser;
87
use hmac::{digest::InvalidLength, Hmac, Mac};
98
use sha2::Sha256;
109

1110
use crate::pathutils::Paths;
1211

13-
#[derive(Parser, Clone)]
12+
#[derive(Clone)]
1413
pub struct EnvVars {
1514
// Database
16-
#[arg(env)]
1715
/// Database name
1816
pub db_name: String,
19-
#[arg(env)]
2017
/// Database hostname
2118
pub db_host: String,
22-
#[arg(env)]
2319
/// Database port
2420
pub db_port: String,
25-
#[arg(env)]
2621
/// Database username
2722
pub db_user: String,
28-
#[arg(env)]
2923
/// Database password
3024
pub db_password: String,
3125

3226
// Auth
33-
#[arg(env)]
3427
/// OAuth app client id (public token)
3528
pub gh_client_id: String,
36-
#[arg(env)]
3729
/// An org admin's Github token (with the `read:org` permission)
3830
pub gh_org_admin_token: String,
39-
#[arg(env)]
4031
/// JWT encryption secret (make it a long, randomized string)
4132
jwt_secret: String,
42-
#[arg(env)]
4333
/// OAuth app client secret
4434
pub gh_client_secret: String,
45-
#[arg(env, default_value = "")]
4635
/// Github organization name
4736
pub gh_org_name: String,
48-
#[arg(env, default_value = "")]
4937
/// Github organization team slug (this team has access to admin dashboard)
5038
pub gh_org_team_slug: String,
51-
#[arg(env, default_value = "")]
5239
/// The usernames of the admins (additional to org team members, comma separated)
5340
pub gh_admin_usernames: String,
54-
#[arg(env, default_value = "")]
5541
/// URL of Slack webhook for sending notifications
5642
pub slack_webhook_url: String,
5743

5844
// Other configs
59-
#[arg(env, default_value = "10")]
6045
/// Maximum number of papers that can be uploaded at a time
6146
pub max_upload_limit: usize,
62-
#[arg(env, default_value = "./log/application.log")]
6347
/// Location where logs are stored
6448
pub log_location: PathBuf,
6549

6650
// Paths
67-
#[arg(env, default_value = "https://static.metakgp.org")]
6851
/// The URL of the static files server (odin's vault)
6952
static_files_url: String,
70-
#[arg(env, default_value = "/srv/static")]
7153
/// The path where static files are served from
7254
static_file_storage_location: PathBuf,
73-
#[arg(env, default_value = "/iqps/uploaded")]
7455
/// The path where uploaded papers are stored temporarily, relative to the `static_file_storage_location`
7556
uploaded_qps_path: PathBuf,
76-
#[arg(env, default_value = "/peqp/qp")]
7757
/// The path where library papers (scrapped) are stored, relative to the `static_file_storage_location`
7858
library_qps_path: PathBuf,
7959

8060
// Server
81-
#[arg(env, default_value = "8080")]
8261
/// The port the server listens on
8362
pub server_port: i32,
8463

8564
// CORS
86-
#[arg(env, default_value = "https://qp.metakgp.org,http://localhost:5173")]
8765
/// List of origins allowed (as a list of values separated by commas `origin1, origin2`)
8866
pub cors_allowed_origins: String,
8967

90-
#[arg(skip)]
9168
/// All paths must be handled using this
9269
pub paths: Paths,
9370
}
9471

95-
impl EnvVars {
72+
impl EnvVars {
73+
/// Parses the environment variables into the struct
74+
pub fn parse() -> Result<Self, Box<dyn std::error::Error>> {
75+
let db_name = std::env::var("DB_NAME")?;
76+
let db_host = std::env::var("DB_HOST")?;
77+
let db_port = std::env::var("DB_PORT")?;
78+
let db_user = std::env::var("DB_USER")?;
79+
let db_password = std::env::var("DB_PASSWORD")?;
80+
let gh_client_id = std::env::var("GH_CLIENT_ID")?;
81+
let gh_org_admin_token = std::env::var("GH_ORG_ADMIN_TOKEN")?;
82+
let jwt_secret = std::env::var("JWT_SECRET")?;
83+
let gh_client_secret = std::env::var("GH_CLIENT_SECRET")?;
84+
let gh_org_name = std::env::var("GH_ORG_NAME").unwrap_or_default();
85+
let gh_org_team_slug = std::env::var("GH_ORG_TEAM_SLUG").unwrap_or_default();
86+
let gh_admin_usernames = std::env::var("GH_ADMIN_USERNAMES").unwrap_or_default();
87+
let slack_webhook_url = std::env::var("SLACK_WEBHOOK_URL").unwrap_or_default();
88+
let max_upload_limit = std::env::var("MAX_UPLOAD_LIMIT")
89+
.unwrap_or_else(|_| "10".to_string())
90+
.parse::<usize>()?;
91+
let log_location = std::env::var("LOG_LOCATION")
92+
.unwrap_or_else(|_| "./log/application.log".to_string())
93+
.into();
94+
let static_files_url = std::env::var("STATIC_FILES_URL")
95+
.unwrap_or_else(|_| "https://static.metakgp.org".to_string());
96+
let static_file_storage_location = std::env::var("STATIC_FILE_STORAGE_LOCATION")
97+
.unwrap_or_else(|_| "/srv/static".to_string())
98+
.into();
99+
let uploaded_qps_path = std::env::var("UPLOADED_QPS_PATH")
100+
.unwrap_or_else(|_| "/iqps/uploaded".to_string())
101+
.into();
102+
let library_qps_path = std::env::var("LIBRARY_QPS_PATH")
103+
.unwrap_or_else(|_| "/peqp/qp".to_string())
104+
.into();
105+
let server_port = std::env::var("SERVER_PORT")
106+
.unwrap_or_else(|_| "8080".to_string())
107+
.parse::<i32>()?;
108+
let cors_allowed_origins = std::env::var("CORS_ALLOWED_ORIGINS")
109+
.unwrap_or_else(|_| "https://qp.metakgp.org,http://localhost:5173".to_string());
110+
Ok(Self {
111+
db_name,
112+
db_host,
113+
db_port,
114+
db_user,
115+
db_password,
116+
gh_client_id,
117+
gh_org_admin_token,
118+
jwt_secret,
119+
gh_client_secret,
120+
gh_org_name,
121+
gh_org_team_slug,
122+
gh_admin_usernames,
123+
slack_webhook_url,
124+
max_upload_limit,
125+
log_location,
126+
static_files_url,
127+
static_file_storage_location,
128+
uploaded_qps_path,
129+
library_qps_path,
130+
server_port,
131+
cors_allowed_origins,
132+
paths: Paths::default(),
133+
})
134+
}
135+
96136
/// Processes the environment variables after reading.
97137
pub fn process(mut self) -> Result<Self, Box<dyn std::error::Error>> {
98138
self.paths = Paths::new(

backend/src/main.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
//!
33
//! The backend is divided into multiple modules. The [`routing`] module contains all the route handlers and the [`db`] module contains all database queries and models. Other modules are utilities used throughout the backend.
44
5-
use clap::Parser;
65
use tracing_appender::rolling::{RollingFileAppender, Rotation};
76
use tracing_subscriber::prelude::*;
87

@@ -22,7 +21,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
2221
}
2322

2423
// Read environment variables
25-
let env_vars = env::EnvVars::parse().process()?;
24+
let env_vars = env::EnvVars::parse()?.process()?;
2625

2726
// Initialize logger
2827
let (append_writer, _guard) = tracing_appender::non_blocking(

frontend/src/components/Common/Common.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import './styles/common_styles.scss';
44
import { IconType } from 'react-icons';
55

66
export function Footer() {
7-
return <h3 className="meta-footer">Made with ❤️ and {"</>"} by <a href="https://github.com/metakgp/iqps-go" target="_blank">MetaKGP</a></h3>;
7+
return <h3 className="meta-footer">
8+
Contribute on <a href="https://github.com/metakgp/iqps-go" target="_blank">GitHub</a> |
9+
Made with ❤️ and {"</>"} by <a href="https://github.com/metakgp" target="_blank">MetaKGP</a>
10+
</h3>;
811
}
912

1013
interface ILinkCommonProps {
@@ -46,4 +49,4 @@ export function Header(props: IHeaderProps) {
4649

4750
export function Navbar() {
4851
return <></>;
49-
}
52+
}

0 commit comments

Comments
 (0)