当前位置: 首页 > news >正文

西部数码网站管理助手 伪静态想做app推广项目在哪找

西部数码网站管理助手 伪静态,想做app推广项目在哪找,内蒙住房和城乡建设部网站首页,wordpress qqwordrsup是使用 rust 编写的一个前端 npm 依赖包管理工具,可以获取到项目中依赖包的最新版本信息,并通过 web 服务的形式提供查看、升级操作等一一系列操作。 在前一篇文章中,记录初始的功能设计,自己的想法实现过程。在自己的使用过…

rsup是使用 rust 编写的一个前端 npm 依赖包管理工具,可以获取到项目中依赖包的最新版本信息,并通过 web 服务的形式提供查看、升级操作等一一系列操作。

在前一篇文章中,记录初始的功能设计,自己的想法实现过程。在自己的使用过程功能中,也会发现一些存在的问题,有一些问题值得记录的再次标记,供大家参考。

rsup 工具安装

在上一篇文章中描写的安装rsup工具部分错误,因为我本地是 macos 系统,

rust 默认执行cargo build构建的是适合 macos 的可执行文件,对于 windows、linux 是不能直接用;还有一个问题,就是rsup-web静态服务资源是不会被编译进工具包的,我本地能用也仅仅是我本地有源代码,它指向静态资源路径的就是我电脑的绝对地址。

可以采取将静态资源链接打包进二进制文件中。

  1. 使用include_bytes!rust 内置的宏将静态文件的内容嵌入到二进制文件
  2. 使用第三方 crate,比如embed-resource或者rust-embed

但是为了方便控制 web 静态资源,比如可以单独更新。采取了静态文件和可执行文件分离的方式,提供下载器同时下载rsup可执行文件和rsup-webweb 静态资源。针对不同的系统定义默认的下载路径,然后通过配置文件读取 web 静态资源提供 web 服务。

rsup工具包包含了配置文件、可执行文件、web 服务文件等。根据不同的系统,提供了三种安装工具包包括 linux、macos、windows。

macos installer

ubuntu instanller

windows instanller

提供了安装脚本文件sh一键下载解压、安装。无需手动配置环境变量。

curl -fsSL https://github.com/ngd-b/rsup/blob/main/install.sh | sh

windows用户需要手动下载安装包,解压后执行installer.exe即可,并且需要手动配置环境变量。

installer子包下载资源

这是为了解决上述问题新增的一个安装器,更友好的交互方式进行安装。也方便后面对下载方式进行更友好的优化。

执行安装器需要使用管理员权限。windows右键以管理员身份执行 exe;类 linux 系统需要使用sudo执行。

在这里插入图片描述

提供了从 github 或者 gitee 下载资源两种方式。使用第三方库 cratedialoguer进行交互选择。 目前只提供了从github下载资源。

在这里插入图片描述

use clap::{Parser, ValueEnum};
use dialoguer::{theme::ColorfulTheme, Select};#[derive(Parser, Debug, Clone, ValueEnum)]
pub enum Origin {Github,Gitee,
}impl Origin {// ...pub fn as_str(&self) -> &'static str {match self {Origin::Github => "github",Origin::Gitee => "gitee",}}/// 将枚举pub fn choices() -> Vec<&'static str> {vec![Origin::Github.as_str(), Origin::Gitee.as_str()]}
}/// 提示用户选择下载源
/// @return 下载源
pub fn prompt_origin() -> Origin {let select = Select::with_theme(&ColorfulTheme::default()).with_prompt("Please select download source...").default(0).items(Origin::choices().as_slice()).interact().unwrap();match select {0 => Origin::Github,1 => Origin::Gitee,_ => unreachable!(),}
}

使用reqwest 下载资源,并将资源保存到默认路径。文件路径output的目录必须要提前创建,而fs::File::create(output)创建了资源文件,如果文件已经存在会直接覆盖。

use reqwest::Client;
use tokio::fs;/// 下载文件
///
async fn download_file(client: &Client, url: &str, output: &str) -> Result<(), Box<dyn Error>> {// 下载地址let res = client.get(url).send().await?;if res.status().is_success() {// 下载成功// 保存文件到指定目录// 文件路径let mut file = fs::File::create(output).await?;// 保存文件let bytes = res.bytes().await?;file.write_all(&bytes).await?;Ok(())} else {let error_message = format!("Request failed with status code: {}", res.status());Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other,error_message,)))}
}

文件下载完成后需要解压。所有的资源文件都是.tar.gz格式的,使用flate2解压文件,并且需要使用tar进行解包提取到指定目录。

use flate2::read::GzDecoder;
use tar::Archive;/// 解压文件
///
/// @param url 下载地址
/// @param target_dir 保存目录
async fn decompress_file(url: &str, target_dir: &str) -> Result<(), Box<dyn Error>> {let tar_gz = File::open(url)?;let decomppress = GzDecoder::new(tar_gz);let mut archive = Archive::new(decomppress);// 处理解压目录,不存在则创建目录if !Path::new(target_dir).exists() {fs::create_dir_all(target_dir).await?;}archive.unpack(target_dir)?;Ok(())
}

所需要的资源下载解压完成后,现在默认目录下(类 linux 系统下是/opt/rsup)有三个文件

  • rsup 可执行文件
  • config.toml 配置文件
  • web web 静态资源

可以直接去执行rsup可执行文件。但是当前目录下没有package.json文件,我们可以指定参数--dir去访问指定目录下的package.json。为了方便命令的使用,安装时经将命令添加到环境变量中。

针对不同的操作系统,环境变量的配置文件不一样。windows系统需要用户自行配置,macos系统下是.zshrc;其他类系统默认为.bashrc

use std::io::Write;
use std::{error::Error, fs::OpenOptions};/// 提示用户是否添加命令到环境变量
/// 默认添加
pub fn prompt_add_to_env(path: &str) -> Result<(), Box<dyn Error>> {// ... 省略部分代码let home_dir = std::env::var("HOME")?;// 确定系统使用的shelllet shell_file_name = match os {"macos" => ".zshrc",_ => ".bashrc",};// 环境变量配置目录let shell_config_path = format!("{}/{}", home_dir, shell_file_name);// 写入配置let mut file = OpenOptions::new().append(true).open(shell_config_path)?;writeln!(file, "\n# Add rsup to PATH\nexport PATH=\"{}:$PATH\"", path)?;
}

写入配置文件后,需要重新加载配置文件。执行source ~/.zshrc或者.bashrc,这样就可以全局使用rsup命令了。

config子包管理配置文件

配置文件的读取和写入使用config子包,提供配置文件读写操作。installer安装时会默认生成配置文件,在rsup执行时会读取配置文件。为了方便配置文件管理,新增config子包。

使用了 crate toml 对配置文件config.toml进行读写序列化和反序列化。

use std::{error::Error,fs::{self, File},io::{self, Write},path::Path,
};impl Config {/// 读取配置文件///pub async fn read_config() -> Result<(), Box<dyn Error>> {// 读取配置文件let config_dir = Config::get_url();let config_file_dir = format!("{}/config.toml", config_dir);// ... 省略部分代码let config_content = fs::read_to_string(&config_file_dir)?;let config: Config = toml::from_str(&config_content)?;Ok(())}/// 写入配置文件pub async fn write_config() -> Result<Config, Box<dyn Error>> {let config_dir = Config::get_url();// ... 省略部分代码// 配置文件let config_url = format!("{}/config.toml", config_dir);let mut file = File::create(config_url.clone())?;let mut config = Config::default();// 配置文件路径config.dir = config_dir.clone();// 静态文件目录config.web.static_dir = format!("{}/web", &config_dir);let config_content = toml::to_string(&config)?;file.write_all(config_content.as_bytes())?;Ok(config)}
}

在主入口main中执行读取配置文件,然后可以在各个子包中读取。为了方便使用,在config中提供了静态全局变量CONFIG,使用了第三方 crateonce_cell实现。

use once_cell::sync::OnceCell;// 全局共享配置
pub static CONFIG: OnceCell<Config> = OnceCell::new();impl Config {pub async fn read_config() -> Result<(), Box<dyn Error>> {// ... 省略部分代码// 保存配置数据共享CONFIG.set(config).unwrap();}/// 父级包获取配置pub fn get_config() -> &'static Config {CONFIG.get().unwrap()}
}

这样就可以在其他子包中直接使用config::Config::get_config()获取配置数据了。

配置文件中包含的配置项有:

name = "rsup"
version = "0.3.0"
dir = "/opt/rsup"[web]
port = 8888
static_dir = "/opt/rsup/web"[pkg]
npm_registry = "https://registry.npmmirror.com"

配置文件中的dir字段是安装目录,默认安装在/opt/rsup;web.port字段是 web 服务的端口号,默认8888;pkg.npm_registry字段是 npm 依赖源地址,默认为国内镜像。通常只建议修改pkg.npm_registry设置源地址,方便请求依赖包。

command子包提供命令行交互

提供了新的子包command,用于解析命令行参数。统一管理命令行参数,方便使用。并且提供了一些方法使用。

在使用rsup命令时,可以指定目录使用前端 npm 依赖管理web服务;也可以通过输入自命令进行交互式操作。

子命令包含了两部分:Config 配置命令;Update更新命令。新创建了command子包,在主包解析参数时进行逻辑判断,如果输入命令则执行对应的子命令;未输入子命令则默认执行 web 服务;

#[tokio::main]
async fn main() {let args = Cli::parse();match args.command {Some(Commands::Config { .. }) | Some(Commands::Update { .. }) => {run().await;}_ => {let package = Package::new();// 默认启动pkg解析服务let package_clone = package.clone();task::spawn(async move {pkg::run(args.pkg_args, package_clone).await;});web::run(package.clone()).await;}}
}

执行run()方法调用了子包command中的方法,并解析命令行参数,根据参数执行对应的操作。

pub async fn run() {let cli = Commands::parse();let _ = match cli {Commands::Config { config } => match config {ConfigOptions::List => ConfigOptions::list_config().await,ConfigOptions::Set { key, value } => ConfigOptions::set_config_value(&key, value).await,ConfigOptions::Get { key } => ConfigOptions::get_config_value(&key).await,ConfigOptions::Delete => todo!(),},Commands::Update { update } => {// 获取最新的包地址let (rsup_url, rsup_web_url) = utils::get_pkg_url(None);// 获取命令安装目录let config = external_config::Config::get_config().await;match update {UpdateOptions::Rsup => UpdateOptions::rsup_update(rsup_url, &config.dir).await,UpdateOptions::Web => {UpdateOptions::rsup_web_update(rsup_web_url, &config.dir).await}}}};
}

Config 配置命令

Config配置命令用来管理配置文件,提供交互式操作。我们之前在installer安装时,默认生成配置文件。通过config命令可以查看、修改、删除配置项。

config list 可以展示出配置文件config.toml,在我们安装好rsup命令后,执行rsup config list可以看到配置文件内容。

在这里插入图片描述

config set key value 可以修改配置文件中的值,例如:rsup config set web.port 9999 修改web服务端口号。

对于配置文件的访问、修改,主要是使用了子包config中的方法。为了方便修改,对于子包config的实现进行了调整,文章上面提到的实现为第一版实现,可以做对比差异。

初始实现的需要在core主入口中调用一次读取配置文件,然后在其他子包中通过config::Config::get_config()获取。这种方式在config子包中不方便直接修改配置文件,需要重新读取。

使用tokio::sync::RwLock 实现读写锁,它是线程安全的。使用once_cell::sync::Lazy 实现懒加载,在首次使用时才去读取配置文件。

pub static CONFIG: Lazy<RwLock<Config>> = Lazy::new(|| {// 这里调用初始化let config = Config::read_config().unwrap();RwLock::new(config)
});

在使用set设置配置项时,需要管理员权限,配置更新后会同步更新config.toml配置文件

Update更新命令

rsup工具包含自身和web服务两部分,提供了更新命令,可以更新rsup工具和web服务。

通过rsup update rsup更新工具,通过rsup update web更新web服务。

在这里插入图片描述

utils子包提供公共方法

为了方便子包之间的共用方法的服用,提供了utils子包,提供了一些公共方法。

遇到的问题

记录一下遇到的问题,方便后续查阅。

在使用本地config 模块与配置文件config发生命名冲突

通过extern 明确导入外部模块

// 引入外部crate
extern crate config as external_config;

发布包到crates-io时名称重复,本地引用修改名称

本地开发时使用的名称utils,为了发布到crates-io时,需要修改名称rsup_utils,避免名称重复。然后本地引用时使用package字段指定名称,这样不需要去调整代码里的引用。

[package]
utils = { version = "0.1.0", path = "../utils", package = "rsup_utils" }

下载文件时展示进度条

之前的文件下载时,控制台会陷入长时间的阻塞状态,没有任何反应,为了提供更好的交互体验,使用indicatif展示进度条。

要采用进度条,在下载文件时就要使用流式读取文件,以便更新进度条。

增加两个新的lib库,futures-util提供对于stream的扩展函数。

cargo add indicatif
cargo add futures-util

修改请求reqwest增加特性支持stream

[dependencies]
reqwest = { version = "0.12.9", features = ["stream"] }

修改之前的下载函数download_file,不再使用write_all一次性写入文件,通过分批次读取写入,并同步更新进度条。

/// 下载文件
///
async fn download_file(client: &Client, url: &str, output: &str) -> Result<(), Box<dyn Error>> {// 下载地址let res = client.get(url).send().await?;if res.status().is_success() {// 获取文件大小let content_size = res.content_length().ok_or("无法获取文件大小")?;// 下载成功// 保存文件到指定目录// 文件路径let mut file = fs::File::create(output).await?;// 创建进度条let pb = ProgressBar::new(content_size);pb.set_style(ProgressStyle::default_bar().template("{msg} [{elapsed_precise}] {bar:80} {percent}%")?.progress_chars("##-"),);// 创建流式响应体let mut downloaded = 0;let mut stream = res.bytes_stream();while let Some(item) = stream.next().await {let chunk = item?;file.write_all(&chunk).await?;let len = chunk.len() as u64;downloaded += len;pb.set_position(downloaded);}pb.finish_with_message("下载完成");// 保存文件// let bytes = res.bytes().await?;// file.write_all(&bytes).await?;Ok(())} else {let error_message = format!("Request failed with status code: {}", res.status());Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other,error_message,)))}
}

解决web服务自动后刷新页面加载不到的问题

这是典型的SPA的问题,由于我们使用的是history路由模式,路由由前端控制。我们刷新页面比如http://localhost:8888/home时,会请求http://localhost:8888/home,但是web服务没有这个路由,所以会返回404,导致刷新页面加载不到。

为了处理这个问题,需要增加通配符路由处理跳转route("/{tail:.*}", web::get().to(index)){tail:.*}是一个路径参数,它可以匹配任何路径。

let server = HttpServer::new(move || {//...App::new().app_data(web::Data::new(ms.clone())).route("/", web::get().to(index)).wrap(cors).service(web::scope("/api").configure(api::api_config)).service(Files::new("/static", format!("{}/static/", &static_file_path)).prefer_utf8(true),).route("/ws", web::get().to(socket_index))// SPA fallback route.route("/{tail:.*}", web::get().to(index))
})

windos系统下不同的命令执行名称

windows系统下,我们执行npm -v时,实际内部执行的是npm.cmd -v,而在mac系统下,执行npm -v时,实际内部执行的是npm -v,所以需要根据系统类型,使用不同的命令。

// 判断系统,如果是windows,则使用npm.cmd
let npm_cmd = if cfg!(windows) { "npm.cmd" } else { "npm" };

如果安装时是.exe的话就不需要添加后缀了,直接使用即可。比如node

web服务API参数映射处理

在处理API请求参数时,通过枚举定义了参数类型,然后通过解析匹配到指定的数据结构。

async fn update_pkg(req: web::Json<ReqParams>,data: web::Data<Ms>,
) -> Result<impl Responder, Error> {match &*req {ReqParams::UpdatePkg(params) => {}err => {// ...}
}

如果定义的数据结构字段存在重叠,某个结构完全包含另一个结构的字段,在匹配时就需要将完全包含的结构放在前面,否则可能会匹配到错误的结构。

#[derive(Deserialize, Serialize, Debug)]
#[serde(untagged)]
pub enum ReqParams {UpdatePkg(UpdateParams),// 删除// 目前接受一个nameRemovePkg(RemoveParams),
}

UpdateParamsRemoveParams存在字段重叠,UpdateParams包含了RemoveParams的所有字段,要想匹配到UpdateParams,需要将RemoveParams放在前面。

最后

部署了rsup文档服务网站rsup|Npm Helper

往期rsup文章:

  1. 模式匹配、trait 特征行为、必包、宏
  2. 多线程任务执行
  3. 并发线程间的数据共享
  4. 包、模块,引用路径
  5. 开发一个命令行工具
  6. rust 命令行工具rsup管理前端npm依赖
http://www.fp688.cn/news/158024.html

相关文章:

  • 济南网站建设小程序开发推广普通话的宣传标语
  • 镇江个人网站建设优化大师win10能用吗
  • 洛阳网站建设价格百度官方优化软件
  • 360免费做网站图片外链工具
  • 网络购物网站备案网络推广渠道和方法
  • 基于java的网站开发seo软件推广哪个好
  • 郴州做网站百度官方app免费下载
  • 国外专名做路演的网站营口建网站的公司
  • 园区网站建设社群营销的方法和技巧
  • python培训视频教程常州seo招聘
  • 个性网站设计百度官方网址
  • 周口网站制作公司哪家好网络建站
  • asp网站开发国内外现状竞价交易规则
  • 做外围的都上什么网站找网站怎么宣传
  • wordpress配置ftp服务器配置天津关键词优化网站
  • 淘宝客网站一般用什么做的夫唯seo培训
  • 分享类网站源码网络seo关键词优化技术
  • 泉州服装网站建设友情链接交换网站
  • 做推广有什么好网站男生和女生在一起探讨人生软件
  • 电商网站建设网百度推广账户优化方案
  • wordpress页面生成二维码北京seo优化服务
  • 公司网站首页大图怎么做百度网站关键词排名助手
  • 最简单的做网站的工具最近三天的新闻热点
  • 自己有主机怎么做论坛网站seo短视频
  • 大型购物网站开发自己创建个人免费网站
  • 武汉网站建设设计链接提取视频的网站
  • 微信公众号里面免费做网站广东河源最新疫情
  • 找柳市做网站网络营销主要是学什么的
  • 公安免费网站模板b2b电商平台
  • 深圳包装设计公司排名前十强seo创业