use actix_http::header::CONTENT_ENCODING;
use actix_http::ContentEncoding;
use lazy_static::lazy_static;
use qrcode::render::svg;
use qrcode::EcLevel;
use qrcode::QrCode;
use qrcode::Version;
use rand::Rng;
use crate::prelude2::*;
use actix_web::http::header;
use futures_util::TryStreamExt as _;
use image;
use mime::Mime;
use mime::IMAGE_GIF;
use mime::IMAGE_JPEG;
use mime::IMAGE_PNG;
use rqrr;
lazy_static! {
static ref LIMIT_IMAGE_FILE_COUNT: usize = 1; static ref LEGAL_IMAGE_FILETYPES: [Mime; 3] = [IMAGE_PNG, IMAGE_JPEG, IMAGE_GIF];
}
pub async fn detect_grids(
mut payload: actix_multipart::Multipart,
request: HttpRequest,
) -> impl Responder {
let content_length = match request.headers().get(header::CONTENT_LENGTH) {
Some(val) => val.to_str().unwrap_or("0").parse().unwrap(),
None => 0,
};
if content_length == 0 {
return request.json(200, R::failed(400, "上传图片为空!".to_string()));
}
if let Ok(Some(field)) = payload.try_next().await {
let _field_name = field.name().to_owned();
if let Some(file_type) = field.content_type() {
if !LEGAL_IMAGE_FILETYPES.contains(file_type) {
return request.json(
200,
R::failed(400, format!("不支持的图片类型: {}", &file_type)),
);
}
}
let (_file_name, destination, _) = crate::commons::save_upload(field).await?;
let img = image::open(&destination).unwrap().to_luma8();
let mut img = rqrr::PreparedImage::prepare(img);
let grids = img.detect_grids();
let (_meta, content) = grids[0].decode().unwrap();
if let Err(err) = std::fs::remove_file(&destination) {
log::error!(
"clear-files-error: destination={:?}, error={:?}",
destination,
err
)
}
request.json(200, R::ok(content))
} else {
request.json(200, R::failed(400, "请提供上传文件"))
}
}
pub async fn generate_otpcode(
query: web::Query<HashMap<String, String>>,
_request: HttpRequest,
) -> impl Responder {
let name = query.get("name").unwrap();
let mut rng = rand::rng();
let secret: String = std::iter::repeat_with(|| rng.sample(rand::distr::Alphanumeric))
.map(char::from)
.take(32)
.collect();
let period = 30;
let issuer = "zero4rs";
let code = format!("otpauth://totp/{issuer}:{name}?algorithm=SHA1&digits=6&issuer={issuer}&period={period}&secret={secret}");
let version = match code.len() {
0..=50 => Version::Normal(1),
51..=100 => Version::Normal(2),
101..=200 => Version::Normal(3),
_ => Version::Normal(4), };
let ec_level = match code.len() {
0..=50 => EcLevel::L,
51..=200 => EcLevel::M,
_ => EcLevel::Q, };
let image3 = QrCode::with_version(code, version, ec_level)
.map_err(|e| {
log::error!("error={:?}", e);
})
.unwrap()
.render()
.min_dimensions(200, 200)
.dark_color(svg::Color("#000000"))
.light_color(svg::Color("#ffffff"))
.build();
HttpResponse::Ok()
.content_type("image/svg+xml")
.append_header((CONTENT_ENCODING, ContentEncoding::Identity))
.append_header((
"Cache-Control",
"no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0",
))
.body(image3)
}