rocket_cli/templates/common/
files.rs1pub const CORS: &str = r#"use rocket::fairing::{Fairing, Info, Kind};
2use rocket::http::Header;
3use rocket::{Request, Response};
4
5pub struct Cors;
6
7#[rocket::async_trait]
8impl Fairing for Cors {
9 fn info(&self) -> Info {
10 Info {
11 name: "Add cors headers to responses",
12 kind: Kind::Response,
13 }
14 }
15
16 async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) {
17 response.set_header(Header::new("Access-Control-Allow-Origin", "*"));
18 response.set_header(Header::new(
19 "Access-Control-Allow-Methods",
20 "POST, PATCH, PUT, DELETE, HEAD, OPTIONS, GET",
21 ));
22 response.set_header(Header::new("Access-Control-Allow-Headers", "*"));
23 response.set_header(Header::new("Access-Control-Allow-Credentials", "true"));
24 }
25}
26"#;
27
28pub const AUTH_GUARD: &str = r#"use rocket::http::Status;
29use rocket::request::{FromRequest, Outcome, Request};
30
31use crate::auth::validate_token;
32
33#[allow(dead_code)]
34pub struct AuthClaims {
35 pub credentials: String,
36}
37
38#[rocket::async_trait]
39impl<'r> FromRequest<'r> for AuthClaims {
40 type Error = AuthError;
41
42 async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
43 let cookie = req.cookies().get("auth_token");
44
45 match cookie {
46 Some(c) => match validate_token(c.value()).await {
47 Ok(credentials) => Outcome::Success(AuthClaims { credentials }),
48 Err(_) => {
49 Outcome::Error((Status::Unauthorized, AuthError::InvalidToken(())))
50 }
51 },
52 None => Outcome::Error((Status::Unauthorized, AuthError::MissingToken)),
53 }
54 }
55}
56
57#[derive(Debug)]
58pub enum AuthError {
59 MissingToken,
60 InvalidToken(()),
61}
62"#;
63
64pub const MIDDLEWARE: &str = r#"/* Middleware goes here */"#;
65
66pub const BASIC_AUTH: &str = r#"use crate::models::{LoginCredentials, User};
67
68use bcrypt::{DEFAULT_COST, hash, verify};
69use chrono::{Duration, Utc};
70use jsonwebtoken::{
71 Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode, errors::ErrorKind,
72};
73
74use serde::{Deserialize, Serialize};
75use sha2::{Digest, Sha256};
76
77use std::collections::HashSet;
78
79/// JWT claims structure, including subject, expiration, and unique nonce.
80#[derive(Debug, Serialize, Deserialize)]
81struct Claims {
82 sub: String, // Subject (user email)
83 exp: usize, // Expiration timestamp
84 nonce: String, // Unique secret marker
85 aud: Vec<String>, // Audience restriction
86 iss: String, // Issuer restriction
87}
88
89/// Authorizes a user by verifying credentials and generating a JWT.
90pub async fn authorize_user(user: &User, credentials: &LoginCredentials) -> Result<String, String> {
91 let auth_key = std::env::var_os("AUTH_KEY")
92 .expect("[AUTH_KEY] must be set...")
93 .into_string()
94 .unwrap();
95
96 // Verify the provided password against the stored hash.
97 if !verify(&credentials.password, &user.password).map_err(|e| e.to_string())? {
98 return Err("Invalid credentials".into());
99 }
100
101 // Generate a unique per-token nonce using user email and secret key.
102 let mut hasher = Sha256::new();
103 hasher.update(format!("{}{}", user.email, auth_key));
104 let nonce = format!("{:x}", hasher.finalize());
105
106 // Set token expiration to 48 hours from now.
107 let expiration = Utc::now()
108 .checked_add_signed(Duration::minutes(2880)) // 2880 minutes = 48 hours
109 .expect("valid timestamp")
110 .timestamp() as usize;
111
112 // Create JWT claims.
113 let claims = Claims {
114 sub: user.email.clone(),
115 exp: expiration,
116 nonce,
117 aud: vec!["".to_string()], // Define your audience
118 iss: "".to_string(), // Define your issuer
119 };
120
121 // Encode claims into a JWT using HS256 algorithm and the secret key.
122 let token = encode(
123 &Header::new(Algorithm::HS256),
124 &claims,
125 &EncodingKey::from_secret(auth_key.as_ref()),
126 )
127 .map_err(|e| e.to_string())?;
128
129 Ok(token)
130}
131
132/// Hashes a given password using bcrypt with default cost.
133pub fn hash_password(password: String) -> Result<String, String> {
134 hash(password, DEFAULT_COST).map_err(|e| e.to_string())
135}
136
137/// Validates session JWTs
138pub async fn validate_token(token: &str) -> Result<String, String> {
139 let auth_key = std::env::var("AUTH_KEY").map_err(|_| "AUTH_KEY must be set".to_string())?;
140
141 let mut validation = Validation::new(Algorithm::HS256);
142 validation.set_audience(&["your-audience"]);
143
144 let mut iss_set = HashSet::new();
145 iss_set.insert("your-issuer".to_string());
146 validation.iss = Some(iss_set);
147
148 let decoded = decode::<Claims>(
149 token,
150 &DecodingKey::from_secret(auth_key.as_bytes()),
151 &validation,
152 )
153 .map_err(|e| match e.kind() {
154 ErrorKind::ExpiredSignature => "Token expired".to_string(),
155 ErrorKind::InvalidToken => "Invalid token".to_string(),
156 _ => format!("Token error: {}", e),
157 })?;
158
159 Ok(decoded.claims.sub)
160}
161"#;
162
163pub const GITIGNORE: &str = r#"/target
164.env
165"#;
166
167pub const ENV: &str = r#"# -------------------------------------------------------------------------
168# Smaple values
169#
170# [Mongo DB]
171# DATABASE_URL=mongodb+srv://<username>:<your-password><your-cluster>.<unique_id>.mongodb.net/?retryWrites=true&w=majority&appName=YourCluster
172# DATABASE=database_name
173#
174#
175# [PostgreSQL]
176# DATABASE_URL="postgres://username:password@host:port/database_name?sslmode=require"
177# DATABASE=database_name
178#
179#
180# DATABASE_URL="postgresql://username:password@localhost:5432/your_database"
181# DATABASE=database_name
182#
183#
184# [MySQL]
185# DATABASE_URL="mysql://username:password@host:port/database_name"
186# DATABASE_URL="mysql://root:password@localhost:3306/your_database"
187# DATABASE=database_name
188#
189#
190# [MS SQL Server]
191# DATABASE_URL="mssql://username:password@host:port/database_name"
192# DATABASE_URL="mssql://username:password@localhost:1433/your_database"
193# DATABASE_URL="mssql://username:password@host\\instance_name/database_name" # For named instances
194# DATABASE=database_name
195#
196#
197# [SQLite]
198# DATABASE_URL="sqlite://./path/to/your/database.db"
199# DATABASE_URL="sqlite://./data/app.db" # Example for a database file in a 'data' folder
200#
201#
202# Hex value, can be generated via Open SSL cli or random keygen
203# AUTH_KEY=qaZxSwefcdVfrtgbHy6HnJUjnmJUikmlo=
204# -----------------------------------------------------------
205
206
207DATABASE_URL=
208DATABASE=
209AUTH_KEY=
210"#;
211
212pub const ROCKET_CONFIG: &str = r#"
213[default]
214# Network settings
215address = "0.0.0.0" # Listen on all network interfaces
216port = 8000 # Port number
217workers = 16 # Number of threads for request handling (adjust to number of CPU cores)
218keep_alive = 5 # Keep-alive timeout in seconds
219max_blocking = 512 # Maximum number of blocking operations allowed simultaneously
220temp_dir = "/tmp" # Directory for temporary files
221ident = "Rocket" # Server identifier in responses
222
223# Logging and debugging
224log_level = "normal" # Logging level: "critical", "normal", "debug"
225cli_colors = true # Enable CLI colors for local logs
226
227# Security
228ip_header = "X-Real-IP" # Use reverse proxy header for client IP detection (set to "false" if unused)
229
230# Resource limits
231[default.limits]
232json = 52428800 # Max size for JSON payloads (10 MB)
233form = 2097152 # Max size for form submissions (2 MB)
234file = 52428800 # Max size for uploaded files (50 MB)
235
236# TLS configuration (uncomment and configure for HTTPS)
237[default.tls]
238certs = "/certs/client_cert.pem" # Path to TLS certificate
239key = "/certs/client_key.pem" # Path to private key
240
241[global]
242# Global overrides for all environments
243address = "0.0.0.0"
244port = 8000
245
246[global.limits]
247json = 52428800
248form = 2097152
249file = 52428800
250"#;