rs_starter/core/
builtin_handles.rs1use std::{io, time::Duration};
2use actix_web::{http, get, web, error, web::ServiceConfig, Error, web::{Data}, Result, dev::ServiceRequest, HttpRequest, HttpResponse, Responder};
3use actix_cors::Cors;
4use actix_files::NamedFile;
5use actix_extensible_rate_limit::{backend::memory::InMemoryBackend, backend::SimpleInputFunctionBuilder, backend::SimpleInput, backend::SimpleOutput, RateLimiter};
6
7use futures::{future::ok, stream::once};
8use derive_more::{Display, Error};
9use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslAcceptorBuilder};
10
11use tera::{Tera, Context};
12
13use lazy_static::lazy_static;
14use regex::Regex;
15
16
17#[derive(Debug, Display, Error)]
18#[display(fmt = "my error: {}", name)]
19pub struct MyError {
20 name: &'static str,
21}
22
23impl error::ResponseError for MyError {
25
26}
27
28pub fn prettify(input: &str) -> String {
30
31 lazy_static! {
32 static ref OPEN_TAG: Regex = Regex::new("(?P<tag><[A-z])").unwrap();
33 static ref EMPTY_LINE: Regex = Regex::new("(\\s*\n){1,}").unwrap();
34 static ref CLOSE_TAG: Regex = Regex::new("([^>\n]\\s*</)").unwrap();
35 }
36
37 let mut stage1 = input.to_string();
39 stage1 = stage1.replace("<!--", "\n<!--");
40 stage1 = stage1.replace("-->", "-->\n");
41 stage1 = stage1.replace("</", "\n</");
42 stage1 = OPEN_TAG.replace_all(&stage1, "\n$tag").to_string();
43 stage1 = stage1.trim().to_string();
44
45 let mut stage2: Vec<String> = vec![];
47 let mut indent = 0;
48 for line in stage1.split('\n') {
49 let mut post_add = 0;
50 if line.starts_with("</") {
51 indent -= 1;
52 } else if line.ends_with("/>") || line.starts_with("<!DOCTYPE") || line.starts_with("<meta ") {
53 } else if line.starts_with('<') {
56 post_add += 1;
57 }
58
59 stage2.push(format!("{}{}", " ".repeat(indent), line));
60 indent += post_add;
61 }
62
63 let pretty_html1 = stage2.join("\n");
64 pretty_html1
68
69}
70
71
72#[get("/favicon.ico")]
73pub async fn favicon(_req: HttpRequest) -> io::Result<NamedFile> {
74 Ok(NamedFile::open("static/favicon.ico")?)
75}
76
77#[get("/favicon.svg")]
78pub async fn favicon_svg(_req: HttpRequest) -> io::Result<NamedFile> {
79 Ok(NamedFile::open("static/favicon.svg")?)
80}
81
82pub async fn index(tmpl: Data<Tera>) -> impl Responder {
83
84 let mut ctx = Context::new();
85 ctx.insert("name", "啦啦发啦");
86
87 let render_result = tmpl.render("index.html", &ctx);
88
89 match render_result {
90 Ok(rendered) => {
91 HttpResponse::Ok().body(rendered)
92 },
93 Err(err) => HttpResponse::InternalServerError().body(err.to_string()),
94 }
95
96}
97
98pub async fn info() -> impl Responder {
99 HttpResponse::Ok().json("Hello, server is alive and kicking.")
100}
101
102pub async fn readme(_req: HttpRequest) -> io::Result<NamedFile> {
103 Ok(NamedFile::open("README.md")?)
104}
105
106pub async fn about() -> Result<HttpResponse> {
107 Ok(HttpResponse::build(http::StatusCode::OK)
108 .content_type("text/html;charset=utf-8")
109 .body("<h1>About</h1>"))
110}
111
112pub async fn developer(_req: HttpRequest) -> Result<HttpResponse> {
113 Ok(HttpResponse::build(http::StatusCode::OK)
114 .content_type("text/html;charset=utf-8")
115 .body("<h1>Developer</h1>"))
116}
117
118pub async fn stream() -> HttpResponse {
121 let body = once(ok::<_, Error>(web::Bytes::from_static(b"test")));
122 HttpResponse::Ok()
123 .content_type("text/plain;charset=utf-8")
124 .streaming(body)
125}
126
127#[get("/errors")]
128pub async fn errors() -> Result<&'static str, MyError> {
129 Err(MyError { name: "MyError,粗欧文" })
130}
131
132pub async fn throw_error(id: web::Path<u32>) -> Result<HttpResponse, MyError> {
133 let user_id: u32 = id.into_inner();
134 log::info!("userId: {}", user_id);
135 Err(MyError { name: "MyError,粗欧文" })
136}
137
138pub async fn not_found(_request: HttpRequest) -> Result<HttpResponse> {
139 Ok(HttpResponse::build(http::StatusCode::OK)
140 .content_type("text/html;charset=utf-8")
141 .body("<h1>404 - Page not found</h1>"))
142}
143
144pub fn static_handler(config: &mut ServiceConfig) {
145 let static_path = "static";
148 let fs = actix_files::Files::new("/static", &static_path);
149 config.service(fs);
150}
151
152pub fn tls_builder() -> SslAcceptorBuilder {
153 let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
154 builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
155 builder.set_certificate_chain_file("cert.pem").unwrap();
156 return builder
157}
158
159pub fn cors() -> Cors{
160 return Cors::default()
161 .allowed_methods(vec!["GET", "POST", "DELETE", "PUT", "PATCH"])
162 .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
163 .allowed_header(http::header::CONTENT_TYPE)
164 .max_age(3600);
165}
166
167pub fn access_limiter() -> RateLimiter<InMemoryBackend, SimpleOutput, impl Fn(&ServiceRequest) -> std::future::Ready<Result<SimpleInput, Error>>>{
168 return RateLimiter::builder(
169 InMemoryBackend::builder().build(),
170 SimpleInputFunctionBuilder::new(Duration::from_secs(1), 5).real_ip_key().build()
171 ).add_headers().build();
172}