1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ Copyright: (c) 2023, Mike 'PhiSyX' S. (https://github.com/PhiSyX)         ┃
// ┃ SPDX-License-Identifier: MPL-2.0                                          ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃                                                                           ┃
// ┃  This Source Code Form is subject to the terms of the Mozilla Public      ┃
// ┃  License, v. 2.0. If a copy of the MPL was not distributed with this      ┃
// ┃  file, You can obtain one at https://mozilla.org/MPL/2.0/.                ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

use std::ops;
use std::sync::Arc;

use super::{HttpRequest, HttpResponse};

// --------- //
// Interface //
// --------- //

#[async_trait::async_trait]
pub trait HttpContextInterface: Send + Sync {
	type State;

	async fn new(
		extensions: &super::Extensions,
		state: Self::State,
	) -> Option<Self>
	where
		Self: Sized;

	fn shared(self) -> Arc<Self>
	where
		Self: Sized,
	{
		Arc::new(self)
	}
}

// --------- //
// Structure //
// --------- //

pub struct HttpContext<T> {
	pub(crate) context: Arc<T>,
	pub request: HttpRequest<T>,
	pub response: HttpResponse<T>,
	#[cfg(feature = "cookies")]
	pub cookies: super::cookies::Cookies,
	#[cfg(feature = "cookies")]
	pub session: super::session::Session,
}

// -------------- //
// Implémentation //
// -------------- //

impl<T> HttpContext<T> {
	/// Redirige le client sur l'URL précédente. (Se basant sur l'en-tête HTTP
	/// `Referer`).
	pub fn redirect_back(&self) -> axum::response::Redirect {
		assert!(self.request.referer.is_some());
		let referer = self.request.referer.as_ref().expect("Referer");

		fn referer_url(referer: &axum::headers::Referer) -> String {
			let referer_s = format!("{referer:?}");

			let trimmed_referer = referer_s
				.trim_start_matches("Referer(\"")
				.trim_end_matches("\")");

			trimmed_referer.to_owned()
		}

		let uri = referer_url(referer);

		self.response.redirect_to(uri)
	}
}

#[cfg(feature = "auth")]
impl<T> HttpContext<T> {
	/// Déconnecte l'utilisateur de la session, et détruit le cookie de
	/// connexion associé à l'utilisateur.
	pub async fn logout_user(&mut self) {
		let mut session = self.session.write().await;
		session.remove(crate::auth::AUTH_USER_ID_SESSION);
		session.remove(crate::auth::AUTH_ADMIN_ID_SESSION);
		session.regenerate();

		if let Some(mut user_cookie) = self
			.cookies
			.private()
			.get(crate::auth::AUTH_USER_ID_SESSION)
		{
			let expires_in = time::OffsetDateTime::now_utc()
				.checked_sub(time::Duration::days(100));
			user_cookie.set_path("/");
			user_cookie.set_expires(expires_in);
			self.cookies.private().add(user_cookie);
		}

		if let Some(mut adm_cookie) = self
			.cookies
			.private()
			.get(crate::auth::AUTH_ADMIN_ID_SESSION)
		{
			let expires_in = time::OffsetDateTime::now_utc()
				.checked_sub(time::Duration::days(100));
			adm_cookie.set_path("/");
			adm_cookie.set_expires(expires_in);
			self.cookies.private().add(adm_cookie);
		}
	}
}

// -------------- //
// Implémentation // -> Interface
// -------------- //

impl<T> ops::Deref for HttpContext<T> {
	type Target = Arc<T>;

	fn deref(&self) -> &Self::Target {
		&self.context
	}
}