diff --git a/src/main.rs b/src/main.rs
index f166174ed6e71ad5a4a924abbc5e45ae278d6e79..4f4da2ad19517d51ee3016ae9a6135ce3b2a418f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -35,17 +35,15 @@ mod broadcaster;
 mod import_contest;
 mod language;
 mod models;
+mod pages;
 mod queue;
 mod schema;
 mod setup;
-mod pages;
-
-type DbPool = r2d2::Pool<ConnectionManager<PgConnection>>;
 
 async fn update_database(
     mut job_result_receiver: broadcast::Receiver<JobResult>,
-    pool: DbPool,
-) -> Result<(), pages::PageError> {
+    pool: pages::prelude::DbPool,
+) -> Result<(), pages::prelude::PageError> {
     loop {
         let job_result = job_result_receiver.recv().await.unwrap();
         if let JobResult {
@@ -196,12 +194,12 @@ async fn main() -> Result<(), Box<dyn Error>> {
             .app_data(handlebars_ref.clone())
             .service(
                 web::scope("/jughisto")
-                    .service(pages::get_login)
+                    .service(pages::get_login::get_login)
                     .service(pages::get_me)
                     .service(pages::change_password)
                     .service(pages::post_login)
                     .service(pages::post_logout)
-                    .service(pages::get_main)
+                    .service(pages::get_main::get_main)
                     .service(pages::get_contests)
                     .service(pages::get_contest_by_id)
                     .service(pages::get_contest_scoreboard_by_id)
diff --git a/src/pages/get_login.rs b/src/pages/get_login.rs
new file mode 100644
index 0000000000000000000000000000000000000000..10515464416fb9a2a32e3bc2e85c6bffbee93762
--- /dev/null
+++ b/src/pages/get_login.rs
@@ -0,0 +1,10 @@
+use crate::pages::prelude::*;
+
+#[get("/login")]
+pub async fn get_login(base: BaseContext, hb: Data<Handlebars<'_>>) -> PageResult {
+    #[derive(Serialize)]
+    struct Context {
+        base: BaseContext,
+    }
+    render(&hb, "login", &Context { base })
+}
diff --git a/src/pages/get_main.rs b/src/pages/get_main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2fdc5e839a4ac2ca6c4d409c5ed5b18819a80fe3
--- /dev/null
+++ b/src/pages/get_main.rs
@@ -0,0 +1,38 @@
+use chrono_tz::Tz;
+
+use crate::models::submission;
+use crate::pages::prelude::*;
+use crate::pages::{
+    get_formatted_contests, get_formatted_submissions, FormattedContest, FormattedSubmission,
+};
+
+#[get("/")]
+async fn get_main(
+    base: BaseContext,
+    identity: Option<Identity>,
+    pool: Data<DbPool>,
+    hb: Data<Handlebars<'_>>,
+    tz: Data<Tz>,
+) -> PageResult {
+    let logged_user = get_identity(&identity.as_ref());
+
+    #[derive(Serialize)]
+    struct Context {
+        base: BaseContext,
+        contests: Vec<FormattedContest>,
+        submissions: Vec<FormattedSubmission>,
+    }
+
+    let mut connection = pool.get()?;
+    let submissions = submission::get_submissions(&mut connection)?;
+
+    render(
+        &hb,
+        "main",
+        &Context {
+            base,
+            contests: get_formatted_contests(&mut connection, logged_user.map(|u| u.id), &tz)?,
+            submissions: get_formatted_submissions(&tz, &submissions),
+        },
+    )
+}
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index cff4464660a6e6856d3f5cc665fd4e467dde1aa6..3ffd63d4076e2b737d5ca04e1751cd8be6156a51 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -1,11 +1,9 @@
 use std::collections::HashMap;
 use std::convert::TryFrom;
 use std::fs::{create_dir_all, File};
-use std::future::Future;
 use std::io::{Cursor, Read, Write};
 use std::iter::FromIterator;
 use std::path::PathBuf;
-use std::pin::Pin;
 use std::sync::{Arc, Mutex};
 use std::{env, fs, str};
 
@@ -13,12 +11,11 @@ use actix_files::NamedFile;
 use actix_identity::Identity;
 use actix_multipart::Multipart;
 use actix_session::Session;
-use actix_web::dev::Payload;
-use actix_web::http::header::{ContentType, HeaderValue};
+use actix_web::http::header::HeaderValue;
 use actix_web::http::{header, StatusCode};
 use actix_web::middleware::ErrorHandlerResponse;
-use actix_web::{dev, get, post, web, FromRequest, HttpMessage, HttpRequest, HttpResponse};
-use actix_web_flash_messages::{FlashMessage, IncomingFlashMessages};
+use actix_web::{dev, get, post, web, HttpMessage, HttpRequest, HttpResponse};
+use actix_web_flash_messages::FlashMessage;
 use async_channel::Sender;
 use chrono::prelude::*;
 use chrono_tz::Tz;
@@ -26,15 +23,14 @@ use contest::{Contest, ContestWithAcs};
 use dashmap::DashMap;
 use diesel::pg::PgConnection;
 use futures::{StreamExt, TryStreamExt};
-use handlebars::{Handlebars, RenderError};
+use handlebars::Handlebars;
 use itertools::Itertools;
 use lazy_static::lazy_static;
-use log::{error, info};
+use log::info;
 use problem::ProblemByContestWithScore;
 use regex::Regex;
 use serde::{Deserialize, Serialize};
 use submission::{ContestProblem, Submission};
-use thiserror::Error;
 use tokio::sync::broadcast;
 use user::{PasswordMatched, User, UserHashingError};
 use uuid::Uuid;
@@ -43,118 +39,13 @@ use crate::broadcaster::Broadcaster;
 use crate::models::problem::ProblemByContest;
 use crate::models::{contest, problem, submission, user};
 use crate::queue::job_protocol::{job, job_result, Job, JobResult, Language};
-use crate::{import_contest, language, DbPool};
-
-#[derive(Error, Debug)]
-pub enum PageError {
-    #[error("Unauthorized")]
-    Unauthorized(),
-    #[error("Couldn't render: {0}")]
-    Render(#[from] handlebars::RenderError),
-    #[error(transparent)]
-    SessionGet(#[from] actix_session::SessionGetError),
-    #[error("{0}")]
-    Custom(String),
-    #[error("{0}")]
-    Forbidden(String),
-    #[error("{0}")]
-    Validation(String),
-    #[error("Couldn't get connection from pool")]
-    ConnectionPool(#[from] r2d2::Error),
-    #[error("Couldn't hash")]
-    UserHashing(#[from] user::UserHashingError),
-    #[error(transparent)]
-    Web(#[from] actix_web::Error),
-    #[error(transparent)]
-    Queue(#[from] async_channel::SendError<Job>),
-    #[error("Couldn't fetch result from database")]
-    Database(#[from] diesel::result::Error),
-    #[error("couldn't insert session")]
-    SessionInsert(#[from] actix_session::SessionInsertError),
-    #[error("couldn't work with the filesystem")]
-    Io(#[from] std::io::Error),
-    #[error("couldn't work with the zip")]
-    Zip(#[from] zip::result::ZipError),
-}
-
-fn error_response_and_log(me: &impl actix_web::error::ResponseError) -> HttpResponse {
-    error!("{}", me);
-    HttpResponse::build(me.status_code())
-        .insert_header(ContentType::plaintext())
-        .body(me.to_string())
-}
-
-impl actix_web::error::ResponseError for PageError {
-    fn error_response(&self) -> HttpResponse {
-        error_response_and_log(self)
-    }
-
-    fn status_code(&self) -> StatusCode {
-        match *self {
-            PageError::Unauthorized() => StatusCode::UNAUTHORIZED,
-            PageError::Validation(_) => StatusCode::BAD_REQUEST,
-            PageError::Forbidden(_) => StatusCode::FORBIDDEN,
-            PageError::Custom(_)
-            | PageError::SessionInsert(_)
-            | PageError::ConnectionPool(_)
-            | PageError::Web(_)
-            | PageError::Render(_)
-            | PageError::Queue(_)
-            | PageError::SessionGet(_)
-            | PageError::Database(_)
-            | PageError::Io(_)
-            | PageError::UserHashing(_)
-            | PageError::Zip(_) => StatusCode::INTERNAL_SERVER_ERROR,
-        }
-    }
-}
-
-type PageResult = Result<HttpResponse, PageError>;
-
-#[derive(Serialize)]
-pub struct BaseContext {
-    logged_user: Option<LoggedUser>,
-    flash_messages: IncomingFlashMessages,
-    base_url: String,
-}
-
-impl FromRequest for BaseContext {
-    type Error = actix_web::Error;
-    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
-
-    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
-        let req = req.clone();
-        Box::pin(async move {
-            let identity = Option::<Identity>::from_request(&req, &mut Payload::None).await;
-            let logged_user = identity?.and_then(|identity| get_identity(&Some(&identity)));
-            let flash_messages =
-                IncomingFlashMessages::from_request(&req, &mut Payload::None).await?;
-            Ok(BaseContext {
-                logged_user,
-                flash_messages,
-                base_url: env::var("BASE_URL")
-                    .expect("BASE_URL environment variable is not set"),
-            })
-        })
-    }
-}
+use crate::{import_contest, language};
 
-fn render<Err: From<RenderError>, Ctx: serde::Serialize>(
-    hb: &Handlebars,
-    name: &str,
-    context: &Ctx,
-) -> Result<HttpResponse, Err> {
-    Ok(HttpResponse::Ok().body(hb.render(name, context)?))
-}
+pub mod get_login;
+pub mod get_main;
+pub mod prelude;
 
-#[get("/login")]
-async fn get_login(base: BaseContext, hb: web::Data<Handlebars<'_>>) -> PageResult {
-    #[derive(Serialize)]
-    struct Context {
-        base: BaseContext,
-    }
-    render(&hb, "login", &Context { base })
-}
+use prelude::*;
 
 #[get("/me")]
 async fn get_me(
@@ -205,9 +96,7 @@ pub fn render_401<B>(
     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
 }
 
-pub fn render_400<B>(
-    res: dev::ServiceResponse<B>,
-) -> actix_web::Result<ErrorHandlerResponse<B>> {
+pub fn render_400<B>(res: dev::ServiceResponse<B>) -> actix_web::Result<ErrorHandlerResponse<B>> {
     FlashMessage::error("Entrada inválida").send();
     Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
 }
@@ -218,19 +107,6 @@ struct LoginForm {
     password: String,
 }
 
-fn get_identity(identity: &Option<&Identity>) -> Option<LoggedUser> {
-    identity.as_ref().and_then(|identity| {
-        let identity = identity.id();
-        identity
-            .ok()
-            .and_then(|identity| serde_json::from_str(&identity).ok())
-    })
-}
-
-fn require_identity(identity: &Identity) -> Result<LoggedUser, PageError> {
-    get_identity(&Some(identity)).ok_or(PageError::Unauthorized())
-}
-
 fn format_duration(duration: chrono::Duration) -> String {
     format!(
         "{}{}{:02}:{:02}",
@@ -274,11 +150,11 @@ fn get_formatted_problem_by_contest_with_score(
                 None => "*".into(),
             })
             .unwrap_or("".into()),
-        first_ac_submission_minutes: p.first_ac_submission_instant.and_then(|t| match contest
-            .start_instant
-        {
-            Some(cs) => Some((t - cs).num_minutes()),
-            None => None,
+        first_ac_submission_minutes: p.first_ac_submission_instant.and_then(|t| {
+            match contest.start_instant {
+                Some(cs) => Some((t - cs).num_minutes()),
+                None => None,
+            }
         }),
         failed_submissions: p.failed_submissions,
         id: p.id,
@@ -319,11 +195,8 @@ pub async fn get_contest_by_id(
         logged_user.id,
         contest_id,
     )?;
-    let submissions = submission::get_submissions_user_by_contest(
-        &mut connection,
-        logged_user.id,
-        contest_id,
-    )?;
+    let submissions =
+        submission::get_submissions_user_by_contest(&mut connection, logged_user.id, contest_id)?;
 
     render(
         &hb,
@@ -372,11 +245,8 @@ async fn get_contest_scoreboard_by_id(
     let contest = contest::get_contest_by_id(&mut connection, contest_id)?;
     assert_contest_not_started(&logged_user, &contest)?;
     let scores = problem::get_problems_by_contest_id_with_score(&mut connection, contest_id)?;
-    let submissions = submission::get_submissions_user_by_contest(
-        &mut connection,
-        logged_user.id,
-        contest_id,
-    )?;
+    let submissions =
+        submission::get_submissions_user_by_contest(&mut connection, logged_user.id, contest_id)?;
     let mut scores: Vec<_> = scores.into_iter().group_by(|e| e.user_name.as_ref().map(|s| s.clone())).into_iter().map(|(user_name, problems)| {
     let problems: Vec<_> = problems.map(|p| get_formatted_problem_by_contest_with_score(&p, &contest)).collect();
 
@@ -513,13 +383,6 @@ async fn get_contest_problem_by_id_label(
     )
 }
 
-#[derive(Serialize, Deserialize, Clone)]
-struct LoggedUser {
-    id: i32,
-    name: String,
-    is_admin: bool,
-}
-
 #[post("/logout")]
 async fn post_logout(identity: Identity) -> PageResult {
     identity.logout();
@@ -557,9 +420,7 @@ async fn post_login(
                     name: (&logged_user.name).into(),
                     is_admin: logged_user.is_admin,
                 })
-                .map_err(|_| {
-                    PageError::Custom("Usuário no banco de dados inconsistente".into())
-                })?,
+                .map_err(|_| PageError::Custom("Usuário no banco de dados inconsistente".into()))?,
             )
             .map_err(|_| PageError::Custom("Impossível fazer login".into()))?;
             Ok(redirect_to_root())
@@ -773,11 +634,8 @@ async fn get_submissions_me_by_contest_id(
         submissions: Vec<FormattedSubmission>,
     }
 
-    let submissions = submission::get_submissions_user_by_contest(
-        &mut connection,
-        logged_user.id,
-        contest_id,
-    )?;
+    let submissions =
+        submission::get_submissions_user_by_contest(&mut connection, logged_user.id, contest_id)?;
 
     render(
         &hb,
@@ -1029,8 +887,7 @@ async fn create_submission(
                 test_count: metadata.test_count,
                 test_pattern: format!("./{}/{}", metadata.id, metadata.test_pattern).into(),
                 checker_language: metadata.checker_language,
-                checker_source_path: format!("./{}/{}", metadata.id, metadata.checker_path)
-                    .into(),
+                checker_source_path: format!("./{}/{}", metadata.id, metadata.checker_path).into(),
             })),
         })
         .await?;
@@ -1157,37 +1014,6 @@ fn get_formatted_contests(
     })
 }
 
-#[get("/")]
-async fn get_main(
-    base: BaseContext,
-    identity: Option<Identity>,
-    pool: web::Data<DbPool>,
-    hb: web::Data<Handlebars<'_>>,
-    tz: web::Data<Tz>,
-) -> PageResult {
-    let logged_user = get_identity(&identity.as_ref());
-
-    #[derive(Serialize)]
-    struct Context {
-        base: BaseContext,
-        contests: Vec<FormattedContest>,
-        submissions: Vec<FormattedSubmission>,
-    }
-
-    let mut connection = pool.get()?;
-    let submissions = submission::get_submissions(&mut connection)?;
-
-    render(
-        &hb,
-        "main",
-        &Context {
-            base,
-            contests: get_formatted_contests(&mut connection, logged_user.map(|u| u.id), &tz)?,
-            submissions: get_formatted_submissions(&tz, &submissions),
-        },
-    )
-}
-
 #[get("/contests/")]
 async fn get_contests(
     base: BaseContext,
@@ -1282,8 +1108,7 @@ async fn create_contest(
                 form.grade_ratio = parse_field("grade_ratio", &mut cursor)?.parse().ok()
             }
             Some("grade_after_ratio") => {
-                form.grade_after_ratio =
-                    parse_field("grade_after_ratio", &mut cursor)?.parse().ok()
+                form.grade_after_ratio = parse_field("grade_after_ratio", &mut cursor)?.parse().ok()
             }
             Some("polygon_zip") => form.polygon_zip = Some(cursor),
             _ => {}
@@ -1399,9 +1224,7 @@ async fn create_contest(
         fn map_codeforces_language(input: &String) -> Result<String, PageError> {
             Ok(CODEFORCES_LANGUAGE_TO_JUGHISTO
                 .get(input)
-                .ok_or_else(|| {
-                    PageError::Validation(format!("Linguagem {} não suportada", input))
-                })?
+                .ok_or_else(|| PageError::Validation(format!("Linguagem {} não suportada", input)))?
                 .into())
         }
 
diff --git a/src/pages/prelude.rs b/src/pages/prelude.rs
new file mode 100644
index 0000000000000000000000000000000000000000..30b722cbad50f09e529e5e21fc41b3e03fc3c743
--- /dev/null
+++ b/src/pages/prelude.rs
@@ -0,0 +1,141 @@
+pub use actix_identity::Identity;
+pub use actix_web::web::Data;
+pub use actix_web::{get, post, FromRequest, HttpResponse};
+use diesel::r2d2::ConnectionManager;
+use diesel::PgConnection;
+pub use handlebars::{Handlebars, RenderError};
+pub use serde::Serialize;
+
+pub type DbPool = r2d2::Pool<ConnectionManager<PgConnection>>;
+
+use std::env;
+use std::future::Future;
+use std::pin::Pin;
+
+use actix_web::dev::Payload;
+use actix_web::http::header::ContentType;
+use actix_web::http::StatusCode;
+use actix_web::HttpRequest;
+use actix_web_flash_messages::IncomingFlashMessages;
+use log::error;
+use serde::Deserialize;
+use thiserror::Error;
+
+use crate::Job;
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct LoggedUser {
+    pub id: i32,
+    pub name: String,
+    pub is_admin: bool,
+}
+
+impl actix_web::error::ResponseError for PageError {
+    fn error_response(&self) -> HttpResponse {
+        error!("{}", self);
+        HttpResponse::build(self.status_code())
+            .insert_header(ContentType::plaintext())
+            .body(self.to_string())
+    }
+
+    fn status_code(&self) -> StatusCode {
+        match *self {
+            PageError::Unauthorized() => StatusCode::UNAUTHORIZED,
+            PageError::Validation(_) => StatusCode::BAD_REQUEST,
+            PageError::Forbidden(_) => StatusCode::FORBIDDEN,
+            PageError::Custom(_)
+            | PageError::SessionInsert(_)
+            | PageError::ConnectionPool(_)
+            | PageError::Web(_)
+            | PageError::Render(_)
+            | PageError::Queue(_)
+            | PageError::SessionGet(_)
+            | PageError::Database(_)
+            | PageError::Io(_)
+            | PageError::UserHashing(_)
+            | PageError::Zip(_) => StatusCode::INTERNAL_SERVER_ERROR,
+        }
+    }
+}
+
+#[derive(Error, Debug)]
+pub enum PageError {
+    #[error("Unauthorized")]
+    Unauthorized(),
+    #[error("Couldn't render: {0}")]
+    Render(#[from] handlebars::RenderError),
+    #[error(transparent)]
+    SessionGet(#[from] actix_session::SessionGetError),
+    #[error("{0}")]
+    Custom(String),
+    #[error("{0}")]
+    Forbidden(String),
+    #[error("{0}")]
+    Validation(String),
+    #[error("Couldn't get connection from pool")]
+    ConnectionPool(#[from] r2d2::Error),
+    #[error("Couldn't hash")]
+    UserHashing(#[from] crate::user::UserHashingError),
+    #[error(transparent)]
+    Web(#[from] actix_web::Error),
+    #[error(transparent)]
+    Queue(#[from] async_channel::SendError<Job>),
+    #[error("Couldn't fetch result from database")]
+    Database(#[from] diesel::result::Error),
+    #[error("couldn't insert session")]
+    SessionInsert(#[from] actix_session::SessionInsertError),
+    #[error("couldn't work with the filesystem")]
+    Io(#[from] std::io::Error),
+    #[error("couldn't work with the zip")]
+    Zip(#[from] zip::result::ZipError),
+}
+
+pub fn get_identity(identity: &Option<&Identity>) -> Option<LoggedUser> {
+    identity.as_ref().and_then(|identity| {
+        let identity = identity.id();
+        identity
+            .ok()
+            .and_then(|identity| serde_json::from_str(&identity).ok())
+    })
+}
+
+pub fn require_identity(identity: &Identity) -> Result<LoggedUser, PageError> {
+    get_identity(&Some(identity)).ok_or(PageError::Unauthorized())
+}
+
+pub type PageResult = Result<HttpResponse, PageError>;
+
+#[derive(Serialize)]
+pub struct BaseContext {
+    logged_user: Option<LoggedUser>,
+    flash_messages: IncomingFlashMessages,
+    base_url: String,
+}
+
+impl FromRequest for BaseContext {
+    type Error = actix_web::Error;
+    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
+
+    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
+        let req = req.clone();
+        Box::pin(async move {
+            let identity = Option::<Identity>::from_request(&req, &mut Payload::None).await;
+            let logged_user = identity?.and_then(|identity| get_identity(&Some(&identity)));
+            let flash_messages =
+                IncomingFlashMessages::from_request(&req, &mut Payload::None).await?;
+            Ok(BaseContext {
+                logged_user,
+                flash_messages,
+                base_url: env::var("BASE_URL").expect("BASE_URL environment variable is not set"),
+            })
+        })
+    }
+}
+
+pub fn render<Err: From<RenderError>, Ctx: serde::Serialize>(
+    hb: &Handlebars,
+    name: &str,
+    context: &Ctx,
+) -> Result<HttpResponse, Err> {
+    Ok(HttpResponse::Ok().body(hb.render(name, context)?))
+}