0%

使用 kellnr 搭建私服
https://github.com/kellnr/kellnr

下载安装

1
2
3
4
cd /opt
mkdir kellnr && cd kellnr
wget https://github.com/kellnr/kellnr/releases/download/v5.6.0/kellnr-x86_64-unknown-linux-musl.zip
unzip kellnr-x86_64-unknown-linux-musl.zip && rm kellnr-x86_64-unknown-linux-musl.zip

修改配置

1
vim config/default.toml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#admin用户密码
admin_pwd = "admin123..."

#kdata存储文件夹
data_dir="/opt/kellnr/kdata"

#开启代理 crates.io
[proxy]
enabled = true

# hostname改成你的ip地址
[origin]
hostname = "192.168.254.10"

启动

1
nohup ./kellnr > kellnr.log 2>&1 &

访问: http://{ip}:8000/

阅读全文 »

wrap_fn 方法使用
wrap_fn 允许你以一种简洁的方式,将一个闭包转化为 Transform 类型,并且返回一个能够被 Actix Web 中间件使用的 Service。这个闭包可以接受请求,并返回一个响应。它的作用是简化实现自定义中间件的过程。

修改请求头

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
use actix_web::{
dev::Service,
http::header::{self, HeaderName, HeaderValue},
web, App, HttpResponse, HttpServer, Responder,
};
use futures::FutureExt;
use serde_json::json;

//index
async fn index() -> impl Responder {
HttpResponse::Ok().json(json!({
"code": 1,
"message":"请求完成!",
"data": "index"
}))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
.wrap_fn(|req, srv| {
srv.call(req).map(move |mut res| {
// 修改头部信息
res.as_mut()
.unwrap()
.headers_mut()
.insert(header::SERVER, HeaderValue::from_static("wcharServer"));
//cus-header 自定义头
res.as_mut()
.unwrap()
.headers_mut()
.insert(HeaderName::from_static("cus-header"), HeaderValue::from_static("are you ok?"));

res
})
})
})
.bind("127.0.0.1:8080")?
.run()
.await
}

修改头部

阅读全文 »

使用中间件进行认证

依赖

1
2
3
4
5
6
7
8
9
10
11
[package]
name = "day25"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4.11.0"
dotenv = "0.15.0"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.1"
futures = "0.3.31"
阅读全文 »

rust 递归菜单 遍历菜单

数据库语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE TABLE `sys_menu` (
`menu_id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
`menu_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '菜单名称',
`parent_id` bigint DEFAULT '0' COMMENT '父菜单ID',
`order_num` int DEFAULT '0' COMMENT '显示顺序',
`url` varchar(254) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '请求地址',
`target` tinyint DEFAULT '1' COMMENT '打开方式(1内部,2新窗口)',
`menu_type` tinyint NOT NULL COMMENT '菜单类型(1目录 2菜单 3按钮)',
`visible` tinyint DEFAULT '1' COMMENT '菜单状态(1显示 0隐藏)',
`perms` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '权限标识',
`icon` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单图标',
`create_by` varchar(254) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(254) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(254) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`del_flag` tinyint NOT NULL DEFAULT '1' COMMENT '1正常 0删除',
PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2033 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='菜单权限表'
阅读全文 »

rust grpc upload file

依赖

1
2
3
4
5
6
7
8
9
10
11
12
[package]
name = "grpc"
version = "0.1.0"
edition = "2021"

[dependencies]
tonic = "0.7"
prost = "0.10"
tokio = { version = "1", features = ["full"] }

[build-dependencies]
tonic-build = "0.7"
阅读全文 »

使用 actix-web上传文件

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

use tokio::fs::{create_dir_all, File};
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
use futures_util::StreamExt as _; // for file payload.next()

//上传文件,可以上传多个文件,都是file字段
#[post("/upload")]
async fn upload(mut payload: Multipart) -> impl Responder {
//文件上传根目录
let base_path = "./assets/upload";

//创建根目录
if let Err(e) = create_dir_all(base_path).await {
return HttpResponse::Ok().body(format!("创建文件夹失败,请检查是否有权限! {}".to_string(),e));
}

//多个文件数组
let mut file_paths = Vec::new();

//其他参数 user_id
let mut user_id = String::new();

//其他参数 dept_id
let mut dept_id = String::new();

//遍历 payload 需要引入 use futures_util::StreamExt as _;
while let Some(Ok(mut field)) = payload.next().await {
// 获取字段信息
let content_disposition = field.content_disposition();
let field_name = content_disposition.get_name().unwrap();

//file为上传文件字段
if field_name == "file" {
// Handle file upload
let file_name = content_disposition.get_filename().unwrap_or("default_file");

//完整路径 base_path(根目录) + 要上传的文件
let file_path_local: String = format!("{}/{}", base_path, file_name);
file_paths.push(file_path_local.clone());

let mut file = File::create(file_path_local).await.unwrap();

while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
file.write_all(&data).await.unwrap();
}

//处理其他参数 userId
} else if field_name == "userId" {
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
user_id.push_str(&String::from_utf8_lossy(&data));
}
//处理其他参数 deptId
} else if field_name == "deptId" {
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
dept_id.push_str(&String::from_utf8_lossy(&data));
}
}
}

//file_paths 为路径数组

return HttpResponse::Ok().body("文件上传成功!".to_string());

}

Donuts

博主本项目地址
https://github.com/wchar-net/donuts


前端采用 bootstrap-admin 做的一套后台管理系统

点击访问 bootstrap-admin 前端模板地址

java22+ gradle mysql


目前阶段

国际化配置文件 搜索 个人中心 控制页面权限按钮显示 目前尚未完善

导入数据库文件 doc/sql/sql.sql

博主是后台程序员,前端写的不是很好,感谢 bootstrap-admin 提供的前端模板

请配置文件上传路径,不然头像显示不出来


当前版本

0.0.1-SNAPSHOT

账号/密码

donuts/123456 管理员

user/123456 模拟普通用户

Swagger地址

http://localhost:8080/doc.html

阅读全文 »

docker启动redis哨兵

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
version: '3.8'
services:
redis-master:
container_name: redis-master
image: 'bitnami/redis:latest'
environment:
- REDIS_REPLICATION_MODE=master
- REDIS_PASSWORD=redispassword
ports:
- "6379:6379"
redis-slave:
container_name: slave-redis
image: 'bitnami/redis:latest'
environment:
- REDIS_REPLICATION_MODE=slave
- REDIS_MASTER_HOST=redis-master
- REDIS_MASTER_PASSWORD=redispassword
- REDIS_PASSWORD=redispassword
ports:
- "7000:6379"
depends_on:
- redis-master
redis-sentinel-1:
image: 'bitnami/redis-sentinel:latest'
container_name: sentinel-1
environment:
- REDIS_MASTER_SET=mymaster
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_PASSWORD=redispassword
- REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=10000
depends_on:
- redis-master
- redis-slave
ports:
- "26379:26379"
redis-sentinel-2:
image: 'bitnami/redis-sentinel:latest'
container_name: sentinel-2
environment:
- REDIS_MASTER_SET=mymaster
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_PASSWORD=redispassword
- REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=10000
depends_on:
- redis-master
- redis-slave
ports:
- "26380:26379"
redis-sentinel-3:
image: 'bitnami/redis-sentinel:latest'
container_name: sentinel-3
environment:
- REDIS_MASTER_SET=mymaster
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_PASSWORD=redispassword
- REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=10000
depends_on:
- redis-master
- redis-slave
ports:
- "26381:26379"
1
docker-compose up -d
阅读全文 »