CINXE.COM
salvo: Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架
<!DOCTYPE html> <html lang='zh-CN'> <head> <title>salvo: Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架</title> <meta content='on' http-equiv='x-dns-prefetch-control'> <link href='//e.gitee.com' rel='dns-prefetch'> <link href='//files.gitee.com' rel='dns-prefetch'> <link href='//toscode.gitee.com' rel='dns-prefetch'> <link href='https://cn-assets.gitee.com' rel='dns-prefetch'> <link href='https://portrait.gitee.com' rel='dns-prefetch'> <link rel="shortcut icon" type="image/vnd.microsoft.icon" href="https://cn-assets.gitee.com/assets/favicon-9007bd527d8a7851c8330e783151df58.ico" /> <link rel="canonical" href="https://gitee.com/salvo-rs/salvo" /> <meta content='gitee.com/salvo-rs/salvo git https://gitee.com/salvo-rs/salvo.git' name='go-import'> <meta charset='utf-8'> <meta content='always' name='referrer'> <meta content='Gitee' property='og:site_name'> <meta content='Object' property='og:type'> <meta content='https://gitee.com/salvo-rs/salvo' property='og:url'> <meta content='https://foruda.gitee.com/avatar/1668140070725442457/8742731_salvo-rs_1668140070.png' itemprop='image' property='og:image'> <meta content='salvo-rs/salvo' itemprop='name' property='og:title'> <meta content='Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架' property='og:description'> <meta content='salvo,Rust' name='Keywords'> <meta content='Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架' itemprop='description' name='Description'> <meta content='pc,mobile' name='applicable-device'> <meta content="IE=edge" http-equiv="X-UA-Compatible" /> <meta name="csrf-param" content="authenticity_token" /> <meta name="csrf-token" content="4AaQ92jFLiJ6ngInyBZEGRo94bktUD9fzRG/5Tw558VNUzLU5mowPihA+IJZoTpw4qmh0TIavHnFzIhGSKZ6ew==" /> <link rel="stylesheet" media="all" href="https://cn-assets.gitee.com/assets/application-dd3fda160256112913699f4fc9a0b540.css" /> <script> //<![CDATA[ window.gon = {};gon.locale="zh-CN";gon.sentry_dsn=null;gon.baidu_register_hm_push=null;gon.info={"controller_path":"projects","action_name":"show","current_user":false};gon.tour_env={"current_user":null,"action_name":"show","original_url":"https://gitee.com/salvo-rs/salvo","controller_path":"projects"};gon.http_clone="https://gitee.com/salvo-rs/salvo.git";gon.user_project="salvo-rs/salvo";gon.manage_branch="管理分支";gon.manage_tag="管理标签";gon.enterprise_id=0;gon.create_reaction_path="/salvo-rs/salvo/reactions";gon.ipipe_base_url="https://go-api.gitee.com";gon.artifact_base_url="https://go-repo.gitee.com";gon.gitee_go_remote_url="https://go.gitee.com/assets";gon.gitee_go_active=false;gon.current_project_is_mirror=false;gon.show_repo_comment=false;gon.diagram_viewer_path="https://diagram-viewer.giteeusercontent.com";gon.ent_host="e.gitee.com";gon.cp="点击复制";gon.aready_cp="已复制";gon.is_fork=false;gon.is_admin_domain=false;gon.ref="main"; //]]> </script> <script src="https://cn-assets.gitee.com/assets/application-1a4bf2ba7b1e19f0f3f7b1bf63122b0b.js"></script> <script src="https://cn-assets.gitee.com/assets/lib/jquery.timeago.zh-CN-4a4818e98c1978d2419ab19fabcba740.js"></script> <link rel="stylesheet" media="all" href="https://cn-assets.gitee.com/assets/projects/application-46b94c31ba11ae8c37eacce2bdb5603e.css" /> <script src="https://cn-assets.gitee.com/assets/projects/app-f63d225495ca7d7aafcfc0aa0c823a98.js"></script> <script type='text/x-mathjax-config'> MathJax.Hub.Config({ tex2jax: { inlineMath: [['$','$'], ['\\(','\\)']], displayMath: [["$$","$$"],["\\[","\\]"]], processEscapes: true, skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'], ignoreClass: "container|files", processClass: "markdown-body" } }); </script> <script src="https://cn-assets.gitee.com/uploads/resources/MathJax-2.7.2/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> <script> (function () { var messages = { 'zh-CN': { addResult: '增加 <b>{term}</b>', count: '已选择 {count}', maxSelections: '最多 {maxCount} 个选择', noResults: '未找到结果', serverError: '连接服务器时发生错误' }, 'zh-TW': { addResult: '增加 <b>{term}</b>', count: '已選擇 {count}', maxSelections: '最多 {maxCount} 個選擇', noResults: '未找到結果', serverError: '連接服務器時發生錯誤' } } if (messages[gon.locale]) { $.fn.dropdown.settings.message = messages[gon.locale] } }()); </script> <script> var userAgent = navigator.userAgent; var isLessIE11 = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1; if(isLessIE11){ var can_access = "" if (can_access != "true"){ window.location.href = "/incompatible.html"; } } document.addEventListener("error", function (ev) { var elem = ev.target; if (elem.tagName.toLowerCase() === 'img') { elem.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAAAAACIM/FCAAACh0lEQVR4Ae3ch5W0OgyG4dt/mQJ2xgQPzJoM1m3AbALrxzrf28FzsoP0HykJEEAAAUQTBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEkKK0789+GK/I2ezfQB522PnS1qc8pGgXvr4tE4aY0XOUWlGImThWgyCk6DleixzE7qwBkg/MGiDPlVVAyp1VQGrPKiACDhFI6VkF5LmzCki+sg7IwDoglnVAil0IMkeG9CyUiwsxLFUVFzJJOQaKCjFCDN9RXMjIX7W6ztZXZDKKCyn8sWJvH+nca7WHDN9lROlAliPH9iRKCPI4cswFJQWxB46toLQgQ9jhn5QYZA9DOkoMUoQde5YapAxDWkoNYsOQR3KQd9CxUnIQF4S49CB9ENKlBxmDEKsFUgMCCCCAAHIrSF61f6153Ajy8nyiPr8L5MXnmm4CyT2fzN4DUvHZ+ntA2tOQBRBAAAEEEEAAAQQQ7ZBaC6TwSiDUaYHQ2yuB0MN+ft+43whyrs4rgVCjBUKTFshLC6TUAjGA3AxSaYFYLZBOC2RUAsk8h5qTg9QcbEoOsoQhQ2qQhsO5xCD5dgB5JQaZ+KBKGtKecvR81Ic0ZDjByKdDx0rSEDZ/djQbH+bkIdvfJFm98BfV8hD2zprfVdlu9PxVeyYAkciREohRAplJCaRSAplJCcQogTjSAdlyHRBvSAekJR0QRzogA+mADJkOiCPSAPEtqYBshlRAXC43hxix2QiOuEZkVERykGyNo9idIZKE0HO7XrG6OiMShlDWjstVzdPgXtUH9v0CEidAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQP4HgjZxTpdEii0AAAAASUVORK5CYII="; } }, true); </script> </head> <script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script> <script> var title = document.title.replace(/( - Gitee| - 码云)$/, '') imgUrl = ''; document.addEventListener('DOMContentLoaded', function(event) { var imgUrlEl = document.querySelector('.readme-box .markdown-body > img, .readme-box .markdown-body :not(a) > img'); imgUrl = imgUrlEl && imgUrlEl.getAttribute('src'); if (!imgUrl) { imgUrlEl = document.querySelector('meta[itemprop=image]'); imgUrl = imgUrlEl && imgUrlEl.getAttribute('content'); imgUrl = imgUrl || "https://gitee.com/static/images/logo_themecolor.png"; } wx.config({ debug: false, appId: "wxff219d611a159737", timestamp: "1743847033", nonceStr: "07e3fdca7de197d114caba1f21e6bace", signature: "ceeb5e670b6cebb594b065170825f48963480465", jsApiList: [ 'onMenuShareTimeline', 'onMenuShareAppMessage' ] }); wx.ready(function () { wx.onMenuShareTimeline({ title: title, // 分享标题 link: "https://gitee.com/salvo-rs/salvo", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: imgUrl // 分享图标 }); wx.onMenuShareAppMessage({ title: title, // 分享标题 link: "https://gitee.com/salvo-rs/salvo", // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 desc: document.querySelector('meta[name=Description]').getAttribute('content'), imgUrl: imgUrl // 分享图标 }); }); wx.error(function(res){ console.error('err', res) }); }) </script> <body class='git-project lang-zh-CN'> <header class='common-header fixed noborder' id='git-header-nav'> <div class='ui container'> <div class='ui menu header-menu header-container'> <div class='git-nav-expand-bar'> <i class='iconfont icon-mode-table'></i> </div> <div class='gitee-nav__sidebar'> <div class='gitee-nav__sidebar-container'> <div class='gitee-nav__sidebar-top'> <div class='gitee-nav__avatar-box'></div> <div class='gitee-nav__buttons-box'> <a class="ui button small fluid orange" href="/login">登录</a> <a class="ui button small fluid basic is-register" href="/signup">注册</a> </div> </div> <div class='gitee-nav__sidebar-middle'> <div class='gitee-nav__sidebar-list'> <ul> <li class='gitee-nav__sidebar-item'> <a href="/explore"><i class='iconfont icon-ic-discover'></i> <span class='gitee-nav__sidebar-name'>开源</span> </a></li> <li class='gitee-nav__sidebar-item'> <a href="/enterprises"><i class='iconfont icon-ic-enterprise'></i> <span class='gitee-nav__sidebar-name'>企业版</span> </a></li> <li class='gitee-nav__sidebar-item'> <a href="/education"><i class='iconfont icon-ic-education'></i> <span class='gitee-nav__sidebar-name'>高校版</span> </a></li> <li class='gitee-nav__sidebar-item split-line'></li> <li class='gitee-nav__sidebar-item'> <a href="/search"><i class='iconfont icon-ic-search'></i> <span class='gitee-nav__sidebar-name'>搜索</span> </a></li> <li class='gitee-nav__sidebar-item'> <a href="/help"><i class='iconfont icon-help-circle'></i> <span class='gitee-nav__sidebar-name'>帮助中心</span> </a></li> <li class='gitee-nav__sidebar-item'> <a href="/terms"><i class='iconfont icon-file'></i> <span class='gitee-nav__sidebar-name'>使用条款</span> </a></li> <li class='gitee-nav__sidebar-item'> <a href="/about_us"><i class='iconfont icon-issuepx'></i> <span class='gitee-nav__sidebar-name'>关于我们</span> </a></li> </ul> </div> </div> <div class='gitee-nav__sidebar-bottom'> <div class='gitee-nav__sidebar-close-button'> <i class='fa fa-angle-double-left'></i> </div> </div> </div> </div> <!-- /todo 10周年活动结束后 恢复 --> <div class='item gitosc-logo'> <a href="https://gitee.com"><img alt='Gitee - 基于 Git 的代码托管和研发协作平台' class='ui inline image' height='28' src='/static/images/logo.svg?t=158106664' title='Gitee - 基于 Git 的代码托管和研发协作平台' width='95'> <img alt='Gitee - 基于 Git 的代码托管和研发协作平台' class='ui inline black image' height='28' src='/static/images/logo-black.svg?t=158106664' title='Gitee - 基于 Git 的代码托管和研发协作平台' width='95'> </a></div> <a title="开源" class="item " href="/explore">开源 </a><a title="企业版" class="item " href="/enterprises">企业版 </a><a title="高校版" class="item " href="/education">高校版 </a><a title="私有云" class="item" target="_blank" href="https://gitee.cn?utm_source=giteecom">私有云 </a><a title="Gitee AI" class="item mr-3" id="gitee-blog" target="_blank" href="https://ai.gitee.com/?utm_sources=site_nav">模力方舟 <sup class='ui red label' style='right:-36px !important'> Gitee AI </sup> </a><div class='center responsive-logo'> <a href="https://gitee.com"><img alt='Gitee - 基于 Git 的代码托管和研发协作平台' class='ui inline image' height='24' src='/static/images/logo.svg?t=158106664' title='Gitee - 基于 Git 的代码托管和研发协作平台' width='85'> <img alt='Gitee - 基于 Git 的代码托管和研发协作平台' class='ui inline black image' height='24' src='/static/images/logo-black.svg?t=158106664' title='Gitee - 基于 Git 的代码托管和研发协作平台' width='85'> </a></div> <div class='right menu userbar right-header' id='git-nav-user-bar'> <form class="ui item" id="navbar-search-form" data-text-require="搜索关键字不能少于1个" data-text-filter="搜索格式不正确" action="/search" accept-charset="UTF-8" method="get"><input name="utf8" type="hidden" value="✓" /> <input type="hidden" name="type" id="navbar-search-type" /> <input type="hidden" name="fork_filter" id="fork_filter" value="on" /> <div class='ui search header-search'> <input type="text" name="q" id="navbar-search-input" value="" class="prompt" placeholder="搜开源" /> </div> </form> <script> var can_search_in_repo = 1, repo = "VFZScmVFMUVhelJQUkdob1RucFplbHBuUFQxaE56WXpaZz09YTc2M2Y=", reponame = "salvo-rs/salvo"; $(function() { var $search = $('#navbar-search-form .ui.search'); $search.search({ apiSettings: { url: '/search/relative_project?q={query}', onResponse: function (res) { if (res && res.status === 200 && res.data) { var query = htmlSafe($search.search('get value')); res.data.map(function (item) { item.path_ns = '/' + item.path_ns; item.icon = 'iconfont icon-project-public'; }); res.data.unshift({ name_ns: "在全站搜索 <b class='hl'>" + query +"</b> 相关项目", path_ns: '/search?fork_filter=on&q=' + query, icon: 'iconfont icon-search' }); return res; } else { return { data: [] }; } } }, fields: { results: 'data', description: 'name_ns', url: 'path_ns', icon: 'icon' }, minCharacters: 1, maxResults: 10, searchDelay: 250, showNoResults: false, transition: 'fade' }); }); </script> <div class='ui item' id='feature-update-notice'> <div class='notice-update-icon'> <a class="notice-update-popup click-knowed" title="" href="javascript:void(0)"><img alt="功能更新" title="" class="bubl_icon bubl-off-icon" src="https://cn-assets.gitee.com/assets/bulb_off-24ee940be20998aace89a3f040cbc704.svg" /> <img alt="功能更新" title="" class="bubl_icon bubl-on-icon" src="https://cn-assets.gitee.com/assets/bulb_on-3986b1dc417285398e3d15671bd8f261.svg" /> </a></div> <div class='feature-update-notice-panel menu'> <div class='notice-img'> <img alt="" title="" class="notice-img-show" src="" /> </div> <div class='notice-update-title'></div> <div class='notice-update-des'></div> <div class='notice-btn-list d-flex-between'> <button name="button" type="button" class="ui basic orange button btn-notice btn-knowed click-knowed" style="margin-right: 0">我知道了</button> <a class="ui button orange btn-notice btn-details click-knowed" target="_blank" href="">查看详情</a> </div> </div> </div> <a class="item git-nav-user__login-item" href="/login">登录 </a><a class="item git-nav-user__register-item" href="/signup">注册 </a><script> $('.destroy-user-session').on('click', function() { $.cookie('access_token', null, { path: '/' }); }) </script> </div> </div> </div> </header> <script> Gitee.initNavbar() Gitee.initRepoRemoteWay() $.cookie('user_locale',null) </script> <script> var userAgent = navigator.userAgent; var isLessIE11 = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1; if(isLessIE11){ var can_access = "" if (can_access != "true"){ window.location.href = "/incompatible.html"; } } </script> <div class='fixed-notice-infos'> <div class='all-messages'> <div class='ui info message' id='git-bulletin'> <a href=https://www.oschina.net/event/8595459 target='_blank'>4月12日模力方舟 AI 应用沙龙 · 杭州站报名开放,产研前线第一手干货,AI 开发者必冲!</a> <i class='icon remove' id='remove-bulletin'></i> </div> </div> <div class='ui container'> <div class='flash-messages' id='messages-container'></div> </div> <script> (function() { $(function() { var $error_box, alertTip, notify_content, notify_options, template; template = '<div data-notify="container" class="ui {0} message" role="alert">' + '<i data-notify="dismiss" class="close icon"></i>' + '<span data-notify="message">{2}</span>' + '</div>'; notify_content = null; notify_options = {}; alertTip = ''; $error_box = $(".flash_error.flash_error_box"); if (notify_options.type === 'error' && $error_box.length > 0 && !$.isEmptyObject(notify_content.message)) { if (notify_content.message === 'captcha_fail') { alertTip = "验证码不正确"; } else if (notify_content.message === 'captcha_expired') { alertTip = "验证码已过期,请点击刷新"; } else if (notify_content.message === 'not_found_in_database') { alertTip = "帐号或者密码错误"; } else if (notify_content.message === 'not_found_and_show_captcha') { alertTip = "帐号或者密码错误"; } else if (notify_content.message === 'phone_captcha_fail') { alertTip = "手机验证码不通过"; } else { alertTip = notify_content.message; } return $error_box.html(alertTip).show(); } else if (notify_content) { if ("show" === 'third_party_binding') { return $('#third_party_binding-message').html(notify_content.message).addClass('ui message red'); } notify_options.delay = 3000; notify_options.template = template; notify_options.offset = { x: 10, y: 30 }; notify_options.element = '#messages-container'; return $.notify(notify_content, notify_options); } }); }).call(this); </script> </div> <script> (function() { $(function() { var setCookie; setCookie = function(name, value) { $.cookie(name, value, { path: '/', expires: 365 }); }; $('#remove-bulletin, #remove-bulletin-dashboard').on('click', function() { setCookie('remove_bulletin', "gitee-maintain-1742526988"); $('#git-bulletin').hide(); }); $('#remove-member-bulletin').on('click', function() { setCookie('remove_member_bulletin', "gitee_member_bulletin"); $(this).parent().hide(); }); return $('#remove-gift-bulletin').on('click', function() { setCookie('remove_gift_bulletin', "gitee-gift-bulletin"); $(this).parent().hide(); }); }); }).call(this); </script> <script> function closeMessageBanner(pthis, type, val) { var json = {} val = typeof val === 'undefined' ? null : val $(pthis).parent().remove() if (type === 'out_of_enterprise_member') { json = {type: type, data: val} } else if (type === 'enterprise_overdue') { json = {type: type, data: val} } $.post('/profile/close_flash_tip', json) } </script> <div class='site-content'> <div class='git-project-header'> <div class='fixed-notice-infos'> <div class='ui info icon floating message green' id='fetch-ok' style='display: none'> <div class='content'> <div class='header status-title'> <i class='info icon status-icon'></i> 代码拉取完成,页面将自动刷新 </div> </div> </div> <div class='ui info icon floating message error' id='fetch-error' style='display: none'> <div class='content'> <div class='header status-title'> <i class='info icon status-icon'></i> <span class='error_msg'></span> </div> </div> </div> </div> <div class='ui container'> <div class='git-project-categories'> <a href="/explore">开源项目</a> <span class='symbol'>></span> <a href="/explore/web-app-develop">WEB应用开发</a> <span class='symbol'>></span> <a href="/explore/webframework">Web开发框架</a> <span class='symbol and-symbol'>&&</span> </div> <div class='git-project-header-details'> <div class='git-project-header-container'> <div class='git-project-header-actions'> <div class='ui tiny modal project-donate-modal' id='project-donate-modal'> <i class='iconfont icon-close close'></i> <div class='header'>捐赠</div> <div class='content'> 捐赠前请先登录 </div> <div class='actions'> <a class='ui blank button cancel'>取消</a> <a class='ui orange ok button' href='/login'>前往登录</a> </div> </div> <div class='ui small modal wepay-qrcode'> <i class='iconfont icon-close close'></i> <div class='header'> 扫描微信二维码支付 <span class='wepay-cash'></span> </div> <div class='content weqcode-center'> <img id='wepay-qrcode' src=''> </div> <div class='actions'> <div class='ui cancel blank button'>取消</div> <div class='ui ok orange button'>支付完成</div> </div> </div> <div class='ui mini modal' id='confirm-alipay-modal'> <div class='header'>支付提示</div> <div class='content'> 将跳转至支付宝完成支付 </div> <div class='actions'> <div class='ui approve orange button'>确定</div> <div class='ui blank cancel button'>取消</div> </div> </div> <span class='ui buttons basic watch-container'> <div class='ui dropdown button js-project-watch' data-watch-type='unwatch'> <input type='hidden' value=''> <i class='iconfont icon-watch'></i> <div class='text'> Watch </div> <i class='dropdown icon'></i> <div class='menu'> <a data-value="unwatch" class="item" rel="nofollow" data-method="post" href="/salvo-rs/salvo/unwatch"><i class='iconfont icon-msg-read'></i> 不关注 </a><a data-value="watching" class="item" rel="nofollow" data-method="post" href="/salvo-rs/salvo/watch"><i class='iconfont icon-msg-read'></i> 关注所有动态 </a><a data-value="releases_only" class="disabled item" rel="nofollow" data-method="post" href="/salvo-rs/salvo/release_only_watch"><i class='iconfont icon-msg-read'></i> 仅关注版本发行动态 </a><a data-value="ignoring" class="item" rel="nofollow" data-method="post" href="/salvo-rs/salvo/ignoring_watch"><i class='iconfont icon-msg-read'></i> 关注但不提醒动态 </a></div> </div> <style> .js-project-watch .text .iconfont { display: none; } .js-project-watch a, .js-project-watch a:hover { color: #000; } .js-project-watch .item > .iconfont { visibility: hidden; margin-left: -10px; } .js-project-watch .selected .iconfont { visibility: visible; } .js-project-watch .menu { margin-top: 4px !important; } </style> <script> $('.js-project-watch').dropdown({ action: 'select', onChange: function(value, text, $selectedItem) { var type = value === 'unwatch' ? 'Watch' : 'Watching'; $(this).children('.text').text(type); $(this).dropdown('set selected', value) } }); </script> <a class="ui button action-social-count" title="32" href="/salvo-rs/salvo/watchers">32 </a></span> <span class='basic buttons star-container ui'> <a class="ui button star" href="/login"><i class='iconfont icon-star'></i> Star </a><a class="ui button action-social-count " title="326" href="/salvo-rs/salvo/stargazers">326 </a></span> <span class='ui basic buttons fork-container' title='无权 Fork 此仓库'> <a class="ui button fork" title="你必须登录后才可以fork一个仓库" href="/login"><i class='iconfont icon-fork'></i> Fork </a><a class="ui button action-social-count disabled-style" title="37" href="/salvo-rs/salvo/members">37 </a></span> </div> <h2 class='git-project-title mt-0 mb-0'> <a title="GVP - Gitee 最有价值开源项目" class="ui small label git-project-gvp-badge" target="_blank" href="/gvp">GVP</a><a title="salvo-rs" class="author" href="/salvo-rs">salvo-rs</a>/<a title="salvo" class="repository" target="" style="padding-bottom: 0px; margin-right: 4px" href="/salvo-rs/salvo">salvo</a> <input type="hidden" name="recomm_at" id="recomm_at" value="2022-11-11 11:58" /> <input type="hidden" name="project_title" id="project_title" value="salvo-rs/salvo" /> </h2> </div> </div> </div> <script> var title_import_url = "https://github.com/salvo-rs/salvo"; var title_post_url = "/salvo-rs/salvo/update_import"; var title_fork_url = "/salvo-rs/salvo/sync_fork"; var title_project_path = "salvo"; var title_p_name = "salvo"; var title_p_id= "19109888"; var title_description = "Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架"; var title_form_authenticity_token = "g+OAn43c+ulk1kPORTDMAnFadHmuvTKuYeVSPYZjpjoutiK8A3Pk9TYIuWvUh7Jric40EbH3sYhpOGWe8vw7hA=="; var watch_type = "unwatch"; var checkFirst = false; $('.js-project-watch').dropdown('set selected', watch_type); $('.checkbox.sync-wiki').checkbox(); $('.checkbox.sync-prune').checkbox(); $('.checkbox.team-member-checkbox').checkbox(); </script> <style> i.loading, .icon-sync.loading { -webkit-animation: icon-loading 1.2s linear infinite; animation: icon-loading 1.2s linear infinite; } .qrcode_cs { float: left; } .check-sync-wiki { float: left; height: 28px; line-height: 28px; } .sync-wiki-warn { color: #e28560; } </style> <div class='git-project-nav'> <div class='ui container'> <div class='ui secondary pointing menu'> <a class="item active " href="/salvo-rs/salvo"><i class='iconfont icon-code'></i> 代码 </a><a class="item " href="/salvo-rs/salvo/issues"><i class='iconfont icon-task'></i> Issues <span class='ui mini circular label'> 1 </span> </a><a class="item " href="/salvo-rs/salvo/pulls"><i class='iconfont icon-pull-request'></i> Pull Requests <span class='ui mini circular label'> 0 </span> </a><a class="item " href="/salvo-rs/salvo/wikis"><i class='iconfont icon-wiki'></i> Wiki </a><a class="item " href="/salvo-rs/salvo/graph/main"><i class='iconfont icon-statistics'></i> 统计 </a><a class="item " href="/salvo-rs/salvo/gitee_go"><i class='iconfont icon-workflow'></i> 流水线 </a><div class='item'> <div class='ui pointing top right dropdown git-project-service'> <div> <i class='iconfont icon-service'></i> 服务 <i class='dropdown icon'></i> </div> <div class='menu' style='display:none'> <a class="item" href="/salvo-rs/salvo/pages"><img src="/static/images/logo-en.svg" alt="Logo en" /> <div class='item-title'> Gitee Pages </div> </a><a class="item" href="/salvo-rs/salvo/quality_analyses?platform=sonar_qube"><img src="https://cn-assets.gitee.com/assets/sonar_mini-5e1b54bb9f6c951d97fb778ef623afea.png" alt="Sonar mini" /> <div class='item-title'> 质量分析 </div> </a><a class="item" target="_blank" href="https://gitee.com/help/articles/4193"><img src="https://cn-assets.gitee.com/assets/jenkins_for_gitee-554ec65c490d0f1f18de632c48acc4e7.png" alt="Jenkins for gitee" /> <div class='item-title'> Jenkins for Gitee </div> </a><a class="item" target="_blank" href="https://gitee.com/help/articles/4318"><img src="https://cn-assets.gitee.com/assets/cloudbase-1197b95ea3398aff1df7fe17c65a6d42.png?20200925" alt="Cloudbase" /> <div class='item-title'> 腾讯云托管 </div> </a><a class="item" target="_blank" href="https://gitee.com/help/articles/4330"><img src="https://cn-assets.gitee.com/assets/cloud_serverless-686cf926ced5d6d2f1d6e606d270b81e.png" alt="Cloud serverless" /> <div class='item-title'> 腾讯云 Serverless </div> </a><a class="item" href="/salvo-rs/salvo/open_sca"><img src="https://cn-assets.gitee.com/assets/open_sca/logo-9049ced662b2f9936b8001e6f9cc4952.png" alt="Logo" /> <div class='item-title'> 悬镜安全 </div> </a><a class="item" target="_blank" href="https://help.gitee.com/devops/connect/Aliyun-SAE"><img src="https://cn-assets.gitee.com/assets/SAE-f3aa9366a1e2b7fff4747402eb8f10c3.png" alt="Sae" /> <div class='item-title'> 阿里云 SAE </div> </a><a class="item" id="update-codeblitz-link" target="_blank" href="https://codeblitz.cloud.alipay.com/gitee/salvo-rs/salvo/tree/main/"><img style="width:100px;margin-top:4px" src="https://cn-assets.gitee.com/assets/Codeblitz-8824e38875a106e16e29ff57ec977b08.png" alt="Codeblitz" /> <div class='item-title'> Codeblitz </div> </a><button class='ui orange basic button quit-button' id='quiting-button'> 我知道了,不再自动展开 </button> </div> </div> </div> </div> </div> </div> <script> $('.git-project-nav .ui.dropdown').dropdown({ action: 'nothing' }); var gitee_reward_config = JSON.parse(localStorage.getItem('gitee_reward_config') || null) || false var $settingText = $('.setting-text') // 如果没有访问过 if(!gitee_reward_config) $settingText.addClass('red-dot') $('.git-project-service').dropdown({ on: 'click', action: 'nothing', onShow: function () { const branch = 'main' let newUrl = `https://codeblitz.cloud.alipay.com/gitee/salvo-rs/salvo/tree/` const url = decodeURIComponent(window.location.pathname); const startIndex = url.indexOf('main'); if (startIndex !== -1) { newUrl = newUrl + url.substring(startIndex); // 从分支名开始截取 }else{ newUrl = newUrl + branch } const linkElement = document.getElementById("update-codeblitz-link"); linkElement.setAttribute("href", newUrl); }, }) </script> <style> .git-project-nav i.checkmark.icon { color: green; } #quiting-button { display: none; } .git-project-nav .dropdown .menu.hidden:after { visibility: hidden !important; } </style> <script> isSignIn = false isClickGuide = false $('#git-versions.dropdown').dropdown(); $.ajax({ url:"/salvo-rs/salvo/access/add_access_log", type:"GET" }); $('#quiting-button').on('click',function() { $('.git-project-service').click(); if (isSignIn) { $.post("/projects/set_service_guide") } $.cookie("Serve_State", true, { expires: 3650, path: '/'}) $('#quiting-button').hide(); }); if (!(isClickGuide || $.cookie("Serve_State") == 'true')) { $('.git-project-service').click() $('#quiting-button').show() } </script> </div> <div class='ui container'> <div class='register-guide'> <div class='register-container'> <div class='regist'> 加入 Gitee </div> <div class='description'> 与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :) </div> <a class="ui orange button free-registion" href="/signup?from=project-guide">免费加入</a> <div class='login'> 已有帐号? <a href="/login?from=project-guide">立即登录</a> </div> </div> </div> <div class='git-project-content-wrapper'> <div class='ui grid tree-bbb' id='project-wrapper'> <div class='project-left-side-contaner wide column' id='project-left-side-contaner' style='display: none;'> <script src="https://cn-assets.gitee.com/webpacks/vendors_lib-7ff466a6da368d391eda.js" defer="defer"></script> </div> <div class='twelve wide column right-wrapper'> <div id='git-project-search-panel' style='display: none;'> <a href='javascript: void(0);' id='back-to-list'> <i class='angle left icon'></i> 返回 </a> <div class='ui icon input search-input'> <i class='iconfont icon-search icon'></i> <input id='search-name' maxlength='40' placeholder='搜索文件' type='text'> </div> </div> <div class='git-project-content' id='git-project-content'> <div class='git-project-desc-wrapper'> <script> $('.git-project-desc-wrapper .ui.dropdown').dropdown(); if (false) { gon.project_new_blob_path = "/salvo-rs/salvo/new/main" bindShowModal({ el: $('.no-license .project-license__create'), complete: function(data, modal) { if (!data.haveNoChoice && !data.data) { Flash.show('请选择一项开源许可证') } else { location.href = gon.project_new_blob_path + '?license=' + data.data } }, skip: function () { location.href = gon.project_new_blob_path + '?license' } }); } $(".project-admin-action-box .reject").click(function() { var reason = $('[name=review-reject-reason]').val(); if (!reason) { Flash.error('请选择不通过理由') return } $.ajax({ type: 'POST', url: "/admin/shumei_content/shumei_check/reject_project_public", data: { reason: reason, status: 'rejected', project_id: 19109888 }, success: function(result){ if(result.status == 'success'){ window.location.reload(); }else{ Flash.error(result.message) } } }) }) $(".project-admin-action-box .approve").click(function(){ $.ajax({ type: 'POST', url: "/admin/shumei_content/shumei_check/reject_project_public", data: { status: 'approved', project_id: 19109888 }, success: function(result){ if(result.status == 'success'){ window.location.reload(); }else{ Flash.error(result.message) } } }) }) $(".project-admin-action-box .waiting").click(function(){ $.ajax({ type: 'POST', url: "/admin/shumei_content/shumei_check/reject_project_public", data: { status: 'waiting', project_id: 19109888 }, success: function(result){ if(result.status == 'success'){ window.location.reload(); }else{ Flash.error(result.message) } } }) }) $('i.help.circle.icon').popup({ popup: '.no-license .ui.popup', position: 'right center' }); $('#remove-no-license-message').on('click', function() { $.cookie("skip_repo_no_license_message_19109888", 'hide', { expires: 365 }); $('#user-no-license-message').hide(); return; }); </script> </div> <div class='git-project-bread' id='git-project-bread'> <div class='ui horizontal list mr-1' id='git-branch-dropdown' style=''> <div class='item git-project-branch-item'> <input type="hidden" name="path" id="path" value="" /> <div class='ui top left pointing dropdown gradient button dropdown-has-tabs' id='git-project-branch'> <input type="hidden" name="ref" id="ref" value="main" /> <div class='default text'> main </div> <i class='dropdown icon'></i> <div class='menu'> <div class='ui left icon input'> <i class='iconfont icon-search dropdown-search-icon'></i> <input class='search-branch' placeholder='搜索分支' type='text'> </div> <div class='tab-menu project-branch-tab-menu d-flex'> <div class='tab-menu-item' data-placeholder='搜索分支' data-tab='branches'> 分支 (9) </div> <div class='tab-menu-item' data-placeholder='搜索标签' data-tab='tags'> 标签 (145) </div> <div class='d-align-center' style='flex:1;justify-content:end;'> <div class='tab-menu-action' data-tab='branches'> <a class="ui link button" href="/salvo-rs/salvo/branches">管理</a> </div> <div class='tab-menu-action' data-tab='tags'> <a class="ui link button" href="/salvo-rs/salvo/tags">管理</a> </div> </div> </div> <div class='tab scrolling menu' data-tab='branches' id='branches_panel'> <div data-value="main" class="item" title="main"><span>main</span></div> <div data-value="clippy" class="item" title="clippy"><span>clippy</span></div> <div data-value="redme" class="item" title="redme"><span>redme</span></div> <div data-value="fix-oapi" class="item" title="fix-oapi"><span>fix-oapi</span></div> <div data-value="release" class="item" title="release"><span>release</span></div> <div data-value="v0.65.x" class="item" title="v0.65.x"><span>v0.65.x</span></div> <div data-value="v0.63.x" class="item" title="v0.63.x"><span>v0.63.x</span></div> <div data-value="v0.61.x" class="item" title="v0.61.x"><span>v0.61.x</span></div> <div data-value="v0.37.x" class="item" title="v0.37.x"><span>v0.37.x</span></div> </div> <div class='tab scrolling menu' data-tab='tags' id='tags_panel'> <div class='item' data-value='v0.77.1'>v0.77.1</div> <div class='item' data-value='v0.77.0'>v0.77.0</div> <div class='item' data-value='v0.76.2'>v0.76.2</div> <div class='item' data-value='v0.76.1'>v0.76.1</div> <div class='item' data-value='v0.76.0'>v0.76.0</div> <div class='item' data-value='v0.75.0'>v0.75.0</div> <div class='item' data-value='v0.74.3'>v0.74.3</div> <div class='item' data-value='v0.74.2'>v0.74.2</div> <div class='item' data-value='v0.74.1'>v0.74.1</div> <div class='item' data-value='v0.74.0'>v0.74.0</div> <div class='item' data-value='v0.73.0'>v0.73.0</div> <div class='item' data-value='v0.72.4'>v0.72.4</div> <div class='item' data-value='v0.72.3'>v0.72.3</div> <div class='item' data-value='v0.72.2'>v0.72.2</div> <div class='item' data-value='v0.72.1'>v0.72.1</div> <div class='item' data-value='v0.72.0'>v0.72.0</div> <div class='item' data-value='v0.71.1'>v0.71.1</div> <div class='item' data-value='v0.71.0'>v0.71.0</div> <div class='item' data-value='v0.70.0'>v0.70.0</div> <div class='item' data-value='v0.69.0'>v0.69.0</div> </div> </div> </div> <style> .iconfont.icon-shieldlock { color: #8c92a4; } .dropdown-search-icon { position: absolute; top: 8px; left: 11px; } </style> <style> #git-project-branch .project-branch-tab-menu, .project-branch-item .project-branch-tab-menu { padding-left: 0px !important; padding-right: 0px !important; margin: 0 11px !important; border-bottom: 1px solid #dfe3e9 !important; } #git-project-branch .ui.dropdown .menu, .project-branch-item .ui.dropdown .menu { width: 360px !important; } #git-project-branch .ui.dropdown .menu .item, .project-branch-item .ui.dropdown .menu .item { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #git-project-branch .tab-menu-actions, .project-branch-item .tab-menu-actions { position: absolute; right: 0px !important; bottom: 0.357em; } #git-project-branch .tab-menu-action, .project-branch-item .tab-menu-action { position: relative !important; right: 0px !important; bottom: 0px !important; } #git-project-branch .menu::after, .project-branch-item .menu::after { display: none !important; } </style> <script> var $branchesDropdown = $('#branches_panel'); var $tagsDropdown = $('#tags_panel'); var $searchNameInput = $('.search-branch'); var concurrentRequestLock = false; var filterXSS = window.filterXSS; var search_text = ""; var branch_page_number = 1; var branch_total_pager = Math.ceil(9 / 20) || 1 var flag_is_loading = false; var flag_page_number = 1; var flag_total_pager = Math.ceil(145 / 20) || 1 $branchesDropdown.scroll(function() { var branchesPanel = document.getElementById('branches_panel'); var numOfBranches = $branchesDropdown.children().length; if (branchesPanel.clientHeight + branchesPanel.scrollTop + 37 > branchesPanel.scrollHeight && numOfBranches < 9) { debounceLoadMoreBranches.call(); } }); function resetFlagVal() { flag_is_loading = false; flag_page_number = 1; flag_total_pager = 1; concurrentRequestLock = false search_text = ""; branch_page_number = 1; branch_total_pager = 1 } $searchNameInput.on('input', window.globalUtils.debouce(function (e) { resetFlagVal() var $currentTab = $('.tab-menu-action.active'); var numOfBranches = $branchesDropdown.children().length; var searchWord = $searchNameInput.val().trim(); search_text = searchWord if($currentTab.data('tab') === 'branches') { if (searchWord !== "") { loadData(searchWord,1); } else { loadData(); } } var numOfTags = $tagsDropdown.children().length; if($currentTab.data('tab') === 'tags') { if (searchWord !== "") { fetchTags(searchWord,1); } else { fetchTags(); } } }, 500)); function toggleNoResultView($popPanel) { let no_data_html= `<div class='mt-1 mb-1 d-flex-center'> <span>暂无数据</span> </div>` $popPanel.append(no_data_html) } var debounceLoadMoreBranches = window.globalUtils.debouce(function () { if (concurrentRequestLock) return; branch_page_number += 1; if (branch_page_number > branch_total_pager) return; loadData(search_text, branch_page_number); }, 350); function loadData(search, page) { if (concurrentRequestLock) { return; } concurrentRequestLock = true; var searchParams = search || ""; var pageParams = page || 1; $.ajax({ url: "/" + gon.user_project + "/branches/names.json", type: 'GET', data: { search: searchParams, page: pageParams, }, dataType: 'json', success: function (data) { branch_total_pager = data.total_pages; var html = ''; if (pageParams === 1) { $branchesDropdown.empty(); } data.branches.forEach(function (branch) { var protectRule = ''; var branchName = filterXSS(branch.name); if(branch.branch_type.value === 1) { var rule = filterXSS(branch.protection_rule.wildcard); protectRule = `<i class="iconfont icon-shieldlock protected-branch-popup" data-title="受保护分支" data-content='保护规则: ${rule}' > </i>` } html += `<div data-value='${branchName}' class="item"> <span>${branchName}</span> ${protectRule} </div>` }); $branchesDropdown.append(html); $('.protected-branch-popup').popup() if (pageParams === 1 && data.count === 0) { toggleNoResultView($branchesDropdown); } }, complete: function () { concurrentRequestLock = false; } }); } $tagsDropdown.scroll(function() { var tagsPanel = document.getElementById('tags_panel'); var numOfTags = $tagsDropdown.children().length; if (tagsPanel.clientHeight + tagsPanel.scrollTop + 37 > tagsPanel.scrollHeight && numOfTags < 145) { debounceLoadMore.call(); } }); var debounceLoadMore = window.globalUtils.debouce(function () { if (flag_is_loading) return; flag_page_number += 1; if (flag_page_number > flag_total_pager) return; fetchTags(search_text, flag_page_number); }, 350); function fetchTags(search, page) { var searchParams = search || ""; var pageParams = page || 1; if (flag_is_loading) return; flag_is_loading = true; $.ajax({ url: "/" + gon.user_project + "/tags/names.json", data: { search: searchParams, page: pageParams, }, type: "GET", xhrFields: { withCredentials: true, }, success: function (data) { flag_total_pager = data.total_pages; if (pageParams === 1) { $tagsDropdown.html(''); } data.tags.forEach((tag) => { const itemDiv = document.createElement('div'); itemDiv.classList.add('item'); itemDiv.setAttribute('data-value', tag.name); itemDiv.innerText = window.filterXSS(tag.name); $tagsDropdown.append(itemDiv) }); if (pageParams === 1 && data.count === 0) { toggleNoResultView($tagsDropdown); } }, error: function () { }, complete: function () { flag_is_loading = false; }, }); } $('.project-branch-tab-menu').on('click','.tab-menu-item', function (e) { var $currentTab = $(this).data('tab') if($currentTab === 'branches') { $searchNameInput.val('') search_text = ''; loadData() } if($currentTab === 'tags') { $searchNameInput.val('') search_text = ''; fetchTags(); } }) </script> <script> $(function () { var curNode = $('.git-project-branch-item') if (false ){ curNode = $('.left-project-branch-item') }else { curNode = $('.git-project-branch-item') } Gitee.initTabsInDropdown(curNode.find('#git-project-branch').dropdown({ fullTextSearch: true, selectOnKeydown: false, direction: 'downward', action: function (text,value,el) { var oItemOrInitObject = el[0] || el var isNotSelect = oItemOrInitObject.dataset.tab && oItemOrInitObject.dataset.tab === 'branches' if(isNotSelect){ console.warn("You didn't choose a branch") return } var path = $('#path').val(); var href = ['/salvo-rs/salvo/tree', encodeURIComponent(value), path].join('/'); window.location.href = href; return true }, onNoResults: function (searchTerm) { //未找到结果 return true }, })); $('.protected-branch-popup').popup() }) </script> </div> </div> <div class='git-project-right-actions pull-right'> <div class='ui orange button' id='btn-dl-or-clone'> 克隆/下载 <i class='dropdown icon'></i> </div> <div class='ui small modal' id='git-project-download-panel'> <i class='iconfont icon-close close'></i> <div class='header'> 克隆/下载 </div> <div class='content'> <div class='ui secondary pointing menu mb-2 menu-container'> <a class='item active' data-text='' data-type='http' data-url='https://gitee.com/salvo-rs/salvo.git'>HTTPS</a> <a class='item' data-text='' data-type='ssh' data-url='git@gitee.com:salvo-rs/salvo.git'>SSH</a> <a class='item' data-text="该仓库未启用SVN访问,请仓库管理员前往【<a target='_blank' href=/salvo-rs/salvo/settings#function>仓库设置</a>】开启。" data-type='svn' data-url=''>SVN</a> <a class='item' data-text="该仓库未启用SVN访问,请仓库管理员前往【<a target='_blank' href=/salvo-rs/salvo/settings#function>仓库设置</a>】开启。" data-type='svn_ssh' data-url=''>SVN+SSH</a> <a class="ui basic orange button button-box unlogin-download-btn" href="javascript:void(0);"><i class='icon download'></i> 下载ZIP </a><div class='ui custom popup popup'> <div class='popup-container actions'> <div class='content'> 该操作需登录 Gitee 帐号,请先登录后再操作。 </div> <a class="ui orange button ok icon" href="/login?from=download_repository_zip">立即登录 </a><a class="ui blank button cancel" href="/signup?from=download_repository_zip">没有帐号,去注册 </a></div> </div> </div> <div class='ui fluid right labeled small input download-url-panel mb-2'> <input type="text" name="project_url_clone" id="project_url_clone" value="https://gitee.com/salvo-rs/salvo.git" onclick="focus();select()" readonly="readonly" /> <div class='ui basic label copy-icon-box'> <i class='icon iconfont icon-clone mr-0 btn-copy-clone' data-clipboard-target='#project_url_clone' id='btn-copy-project_clone_url1'></i> </div> </div> <div class='tip-box mb-2'> 提示 </div> <div class='mb-1 clone-url-title'> 下载代码请复制以下命令到终端执行 </div> <div class='ui fluid right labeled small input download-url-panel mb-2'> <input type="text" name="project_clone_url" id="project_clone_url" value="https://gitee.com/salvo-rs/salvo.git" onclick="focus();select()" readonly="readonly" /> <div class='ui basic label copy-icon-box'> <i class='icon iconfont icon-clone mr-0 btn-copy-clone' data-clipboard-target='#project_clone_url' id='btn-copy-project_clone_url'></i> </div> </div> <div class='ui fluid right labeled warning-text forbid-warning-text'> </div> <div class='http-ssh-item mb-2'> <div> 为确保你提交的代码身份被 Gitee 正确识别,请执行以下命令完成配置 </div> <div class='textarea-box mt-2'> <textarea class='textarea-content-box' id='global-config-clone' readonly>git config --global user.name userName 
git config --global user.email userEmail</textarea> <i class='icon iconfont icon-clone mr-2 btn-copy-clone text-dark' data-clipboard-target='#global-config-clone' id='btn-copy-global-config'></i> </div> </div> <div class='ssh-item item-panel-box'> <div class='mb-2'> 初次使用 SSH 协议进行代码克隆、推送等操作时,需按下述提示完成 SSH 配置 </div> <div class='mb-1'> <span>1</span> 生成 RSA 密钥 </div> <div class='ui fluid right labeled small input mb-2'> <input type="text" name="ssh_keygen_clone" id="ssh_keygen_clone" value="ssh-keygen -t rsa" onclick="focus();select()" readonly="readonly" /> <div class='ui basic label copy-icon-box'> <i class='icon iconfont icon-clone mr-0 btn-copy-clone' data-clipboard-target='#ssh_keygen_clone' id='btn-copy-ssh_keygen'></i> </div> </div> <div class='mb-1'> <span>2</span> 获取 RSA 公钥内容,并配置到<a href='/profile/sshkeys' target="_blank"> SSH公钥 </a> 中 </div> <div class='ui fluid right labeled small input mb-2'> <input type="text" name="id_rsa_clone" id="id_rsa_clone" value="cat ~/.ssh/id_rsa.pub" onclick="focus();select()" readonly="readonly" /> <div class='ui basic label copy-icon-box'> <i class='icon iconfont icon-clone mr-0 btn-copy-clone' data-clipboard-target='#id_rsa_clone' id='btn-copy-d_rsa'></i> </div> </div> </div> <div class='svn-item item-panel-box'> <div class='mb-1 mt-2'> 在 Gitee 上使用 SVN,请访问<a href='https://help.gitee.com/enterprise/code-manage/%E4%BB%A3%E7%A0%81%E6%89%98%E7%AE%A1/%E4%BB%A3%E7%A0%81%E4%BB%93%E5%BA%93/Gitee%20SVN%E6%94%AF%E6%8C%81' target="_blank"> 使用指南 </a> </div> </div> <div class='http-item item-panel-box'> <div class='mb-2 mt-2'> 使用 HTTPS 协议时,命令行会出现如下账号密码验证步骤。基于安全考虑,Gitee 建议<a href='/profile/personal_access_tokens' target="_blank"> 配置并使用私人令牌 </a>替代登录密码进行克隆、推送等操作 </div> <div>Username for 'https://gitee.com': userName</div> <div class='mb-1'> <span>Password for 'https://userName@gitee.com':</span> <span>#</span> <span> 私人令牌 </span> </div> </div> </div> </div> <style> #git-project-download-panel { top: 90px !important; } #git-project-download-panel input { color: #40485b !important; } #git-project-download-panel .textarea-box { width: 100%; height: 60px; color: #9d9d9d; border-radius: 2px; background-color: #F5F5F5 !important; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-align: center; -ms-flex-align: center; align-items: center; } #git-project-download-panel .menu-container { font-weight: bold; border-color: rgba(0, 0, 0, 0.1) !important; border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; } #git-project-download-panel .menu-container .item { padding: 7px 12px !important; } #git-project-download-panel .hr-item { color: rgba(39, 41, 43, 0.15) !important; } #git-project-download-panel .textarea-content-box { width: 100%; height: 60px; resize: none; border: 0px !important; background-color: #F5F5F5 !important; color: #40485b !important; } #git-project-download-panel .btn-copy-clone { cursor: pointer; color: rgba(0, 0, 0, 0.87) !important; } #git-project-download-panel .copy-icon-box { background-color: #F5F5F5 !important; border-left: 0px !important; } #git-project-download-panel .button-box { border: 0px !important; float: right !important; padding-right: 0 !important; } #git-project-download-panel .tip-box { border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; padding-bottom: 4px; font-weight: 700; } #git-project-download-panel .popup-container { padding: 8px 12px 4px 12px; text-align: center; font-size: 14px; } #git-project-download-panel .popup-container .ok { margin: 12px auto; width: 25%; min-width: 125px; display: block; } #git-project-download-panel .popup-container .cancel { margin-left: 0; } </style> <script> $(function () { var $btnClone = $('#btn-dl-or-clone') var $modalDownload = $('#git-project-download-panel'); var $input = $('#project_clone_url') var $inputUrl = $('#project_url_clone') var cloneUrlTitle= $('.clone-url-title') $('#btn-dl-or-clone').on('click', function (e) { e.preventDefault(); $modalDownload.modal('show'); }) $modalDownload.find('.menu > .item').on('click', function(e) { var $item = $(this).addClass('active'); $item.siblings().removeClass('active'); var dataUrl = $item.attr('data-url'); var cloneUrl = $item.attr('data-url'); var dataType = $item.attr('data-type') var cloneToLocal = '下载代码请复制以下命令到终端执行' if(dataType=='http'){ $modalDownload.find('.http-item').show(); $('.content > .item-panel-box:not(.http-item)').hide(); $modalDownload.find('.http-ssh-item').show(); cloneUrl = 'git clone '+dataUrl }else if(dataType=='ssh'){ $modalDownload.find('.ssh-item').show(); $('.content > .item-panel-box:not(.ssh-item)').hide(); $modalDownload.find('.http-ssh-item').show(); cloneUrl = 'git clone '+dataUrl }else if(dataType=='svn') { $('.content > .item-panel-box:not(.svn-item)').hide(); $modalDownload.find('.svn-item').show(); $modalDownload.find('.http-ssh-item').hide(); cloneUrl = 'svn checkout '+dataUrl }else { $('.content > .item-panel-box:not(.svn-item)').hide(); $modalDownload.find('.svn-item').show(); $modalDownload.find('.http-ssh-item').hide(); cloneUrl = 'svn checkout '+dataUrl } if (dataUrl) { $modalDownload.find('.download-url-panel').show(); $input.val(cloneUrl); $inputUrl.val(dataUrl) cloneUrlTitle.show(); $modalDownload.find('.forbid-warning-text').html(''); } else { $modalDownload.find('.download-url-panel').hide(); //$modalDownload.find('.svn-item').hide(); cloneUrlTitle.hide(); $modalDownload.find('.forbid-warning-text').html($item.attr('data-text') || ''); } $.cookie('remote_way', $item.attr('data-type'), { expires: 365, path: '/' }); }).filter('[data-type="' + ($.cookie('remote_way') || 'http') + '"]').trigger('click'); $('.btn-copy-clone').popup({ content: '点击复制', }).on('click', function(e) { e.stopPropagation(); return false; }).each(function(_, btnCopy) { var $btnCopy = $(btnCopy); new Clipboard(btnCopy).on('success', function() { $btnCopy.popup('destroy').popup({ content: '已复制', on: 'manual' }).popup('show'); setTimeout(function() { $btnCopy.popup('destroy').popup({ content: '点击复制' }); }, 2000); }); }); var $downloadBtn= $('.unlogin-download-btn') var $popupContainer = $('.popup-container') $downloadBtn.popup({ popup : $('.custom.popup'), position : 'bottom right', }).on('click', function(e) { $downloadBtn.popup('destroy').popup({ popup : $('.custom.popup'), on: 'manual', position : 'bottom right', }).popup('show'); setTimeout(function() { $downloadBtn.popup('hide'); }, 2000); }) }) </script> </div> <div class='d-inline pull-right' id='git-project-root-actions'> <div class='ui horizontal list repo-action-list d-flex d-align-center repo-action-list-right'> <div class='item search-box-container'> <div class='ui icon input search-input' id='search-box'> <input class='search-file-name' maxlength='40' placeholder='搜索文件' type='text'> </div> <a class='d-flex d-align-center head-search-file-btn' id='search-file-btn'> <span class='iconify' data-icon='gitee:search' style='font-size: 16px;color:#979CAC;margin-right:10px'></span> </a> <div class='filter-file-container' style='display: none;'></div> </div> <div class='item plus-box'> <div class='ui pointing right top dropdown git-project-file' id='git-project-file'> <span class='iconify' data-icon='gitee:plus' style='font-size: 16px;color:#979CAC'></span> <div class='menu repo-dropdown-box pt-1 pb-1'> <a title="新建文件" id="new_file_bread" class="item repo-action d-flex d-align-center" href="/salvo-rs/salvo/new/main"><span class='iconify' data-icon='gitee:file' style='font-size: 16px;color:#979CAC;margin-right:12px'></span> <span> 新建文件 </span> </a><a title="新建 Diagram 文件" class="item repo-action d-flex d-align-center" href="/salvo-rs/salvo/new/main?ext=drawio"><span class='iconify' data-icon='gitee:lan' style='font-size: 16px;color:#979CAC;margin-right:12px'></span> <span> 新建 Diagram 文件 </span> </a><div class='disabled item d-flex d-align-center'> <span class='iconify' data-icon='gitee:folder-sub' style='font-size: 16px;color:#979CAC;margin-right:12px'></span> <span> 新建子模块 </span> </div> <div class='disabled item d-flex d-align-center'> <span class='iconify' data-icon='gitee:upload' style='font-size: 16px;color:#979CAC;margin-right:12px'></span> <span> 上传文件 </span> </div> </div> </div> </div> <div class='item toschina-content__hidden webIDE-box' data-content='Web IDE'> <a class="ui d-flex d-align-center webide" target="_blank" href="/-/ide/project/salvo-rs/salvo/edit/main/-/"><span class='iconify' data-icon='gitee:computer' style='font-size: 16px;color:#979CAC;margin-right:12px'></span> </a></div> </div> <script> $('.git-project-file').dropdown({ action: 'hide', onHide: function () { $('.plus-box').removeClass('click-active') }, onShow: function () { $('.plus-box').addClass('click-active') } }); </script> <script> $('.webIDE-box').popup() </script> <script src="https://cn-assets.gitee.com/assets/file_search/app-89100712b3bd4fbc0b0eb0aa7d7bf62e.js"></script> <style> .filter-file-container-hide { display: none !important; } </style> </div> <div class='breadcrumb_path path-breadcrumb-contrainer' id='git-project-breadcrumb'> </div> <div class='ui horizontal list repo-action-list branches-tags' style=''> <div class='item'> <a class="ui blank button" href="/salvo-rs/salvo/branches"><i class='iconfont icon-branches'></i> 分支 9 </a></div> <div class='item mr-3'> <a class="ui blank button" href="/salvo-rs/salvo/tags"><i class='iconfont icon-tag'></i> 标签 145 </a></div> </div> </div> <script src="https://cn-assets.gitee.com/webpacks/parse_blob_form_scheme-ea7503b4330a77e025e6.bundle.js"></script> <script> if(window.gon.locale == 'en') $('.branches-tags').css('margin-top', '12px') // 仓库页面切换路径时: 刷新 yaml 错误检查 $(window).on('pjax-complete:file-show', function () { window.parseBlobFormScheme && window.parseBlobFormScheme($('.js-blob-data').data('blob')); }); </script> <style> .ui.dropdown .menu > .header { text-transform: none; } </style> <script> $(function () { var $tip = $('#apk-download-tip'); if (!$tip.length) { return; } $tip.find('.btn-close').on('click', function () { $tip.hide(); }); }); (function(){ function pathAutoRender() { var $parent = $('#git-project-bread'), $child = $('#git-project-bread').children('.ui.horizontal.list'), mainWidth = 0; $child.each(function (i,item) { mainWidth += $(item).width() }); $('.breadcrumb.path.fork-path').remove(); if (mainWidth > 995) { $('#path-breadcrumb').hide(); $parent.append('<div class="ui breadcrumb path fork-path">' + $('#path-breadcrumb').html() + '<div/>') } else { $('#path-breadcrumb').show(); } } window.pathAutoRender = pathAutoRender; pathAutoRender(); })(); </script> <div class='branch-diff-notice-bar hide'> <div class='left-section'></div> <div class='right-section'> <div class='ui button gradient contribute'> 贡献代码 <i class='dropdown icon'></i> </div> <div class='ui button gradient branch-sync hide'> 同步代码 <i class='dropdown icon'></i> </div> </div> </div> <div class='ui popup contribute branch-diff-pop-panel contribute-pop'> <div class='notice-title'></div> <div class='notice-sub-title'></div> <div class='notice-content'></div> <a class='ui button orange fluid disabled create-pr' href='/' target='_blank'> 创建 Pull Request </a> </div> <div class='ui popup branch-diff-pop-panel branch-sync-pop'> <div class='notice-title'></div> <div class='notice-content'></div> <div class='known-more'> <a href='/help/articles/4395' target='_blank'> 了解更多 </a> </div> <div class='btn-group'> <div class='ui button basic red discard-btn hide'></div> <a class='ui button orange basic diff-btn hide' href='/' target='_blank'> 对比差异 </a> <a class='ui button gradient pr-sync-btn hide' href='/' target='_blank'> 通过 Pull Request 同步 </a> <div class='ui buttons basic dropdown-group-btn hide'> <div class='ui button branch-sync-btn'> 同步更新到分支 </div> <div class='ui button dropdown dropdown-create-pr'> <i class='icon dropdown'></i> <div class='menu'> <div class='disabled item'> <div> 通过 Pull Request 同步 <div class='text-muted fs-12 mt-1'> 将会在向当前分支创建一个 Pull <br/>Request,合入后将完成同步 </div> </div> </div> </div> </div> </div> </div> </div> <script> (function () { const i18_compare_current_branch = `当前分支与 <a href="URL">BRANCH</a> 相比` const i18_branch_ahead_commit = `,领先 <a href="URL">NUM 个 Commit</a>` const i18_branch_behind_commit = `,落后 <a href="URL">NUM 个 Commit</a>` const i18_contribute_ahead_title = `当前分支比 BRANCH 领先了 NUM 次提交。` const i18_contribute_pr_create = `创建一个 Pull Request 贡献代码。` const i18_contribute_has_pr = `已创建了 Pull Request` const i18_contribute_no_ahead = `这个分支没有领先于 BRANCH 的代码提交。` const i18_branch_sync_behind = `这个分支已落后于 BRANCH 分支` const i18_branch_sync_behind_desc = `从 BRANCH 分支同步 NUM 个提交来更新分支以保持当前分支代码是最新的。` const i18_branch_sync_out_of_date = `此分支已过时` const i18_branch_sync_out_of_date_desc = `你可以将 BRANCH 分支中的最新更改合并到此分支中。或丢弃当前分支上的提交以使当前分支与 BRANCH 分支匹配。这将从当前分支中删除 NUM 个提交。` const i18_branch_discard_diff = `丢弃 NUM 个提交` const i18_branch_sync_success = `同步成功` const i18_branch_sync_fail = `同步失败` const i18_branch_discard_success = `丢弃成功` const i18_branch_discard_fail = `丢弃失败` window.locale_temple_branch_diff = { i18_compare_current_branch, i18_branch_ahead_commit, i18_branch_behind_commit, i18_contribute_ahead_title, i18_contribute_pr_create, i18_contribute_has_pr, i18_contribute_no_ahead, i18_branch_sync_behind, i18_branch_sync_behind_desc, i18_branch_sync_out_of_date, i18_branch_sync_out_of_date_desc, i18_branch_discard_diff, i18_branch_sync_success, i18_branch_sync_fail, i18_branch_discard_success, i18_branch_discard_fail } })(); </script> <div class='row column tree-holder' id='tree-holder'> <div class='tree-content-holder' id='tree-content-holder'> <div class='ui flat nopadding segment tree-content'> <div class='git-project-recent-commit' id='git-project-info'> <div class='recent-commit'> <a class="commit-author-link js-popover-card " data-username="chrislearn" href="/chrislearn"><img class="avatar circular ui image 20 mini" width="20" alt="" src="https://foruda.gitee.com/avatar/1668551501133662782/1125256_chrislearn_1668551500.png!avatar30" /> <span class="commit-author-name">chrislearn</span></a> <span> <a class="repo-index-commit-msg" title="cargo clippy and code format (#1090)" href="/salvo-rs/salvo/commit/85da88aad5d4faadd3284145380275e90bfb5e83">cargo clippy and code format (#1090)</a> </span> <span>85da88a</span> <span class='timeago' datetime='2025-04-04 07:33' title='2025-04-04 07:33:05 +0800'></span> <check-runs branch='main' commit-id='85da88aad5d4faadd3284145380275e90bfb5e83' project-path='salvo-rs/salvo'></check-runs> <build-status commit-id='85da88aad5d4faadd3284145380275e90bfb5e83'></build-status> </div> <div class='all-commits'> <a href="/salvo-rs/salvo/commits/main"><i class='iconfont icon-commit'></i> 3242 次提交 </a></div> </div> <div class='grid list selection table_da39a3ee5e6b4b0d3255bfef95601890afd80709 tree-table ui' id='tree-slider'> <div class='create-folder-form form ui'> <form id="folder_form-edit" action="/salvo-rs/salvo/new/main" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="authenticity_token" value="njcReKpAvEVAHiCqz9eQqV+ZJrwAliD++LINt4I+GG0zYrNbJO+iWRLA2g9eYO7Apw1m1B/co9jwbzoU9qGF0w==" /> <div class='fields'> <div class='field'> <input type="hidden" name="new_file_path" id="new_file_path" /> <input type="hidden" name="content" id="content" /> <input id='new_folder_path' name='new_folder_path' placeholder='新建文件夹' type='text'> </div> <div class='field'> <button name="button" type="submit" class="ui primary button orange submit field-init-btn js-submit-btn">提交</button> <a class="ui basic white button cancel field-init-btn" href="javascript:void(0)">取消</a> </div> <div class='ui mid-center small message notice'> <strong>提示:</strong> 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件 </div> </div> </form> </div> <div class='file_4c40eab00f24304ca400313319c58d461788ff5e row tree-item' data-branch='main' data-type='folder' file_hex='file_4c40eab00f24304ca400313319c58d461788ff5e'> <div class='five wide column tree-item-file-name tree-folder tree-list-item d-align-center' data-path='.github' data-type='folder'> <i class='iconfont icon-folders file-icon-item'></i> <a class="tree-folder-item" title=".github" data-path=".github" href="/salvo-rs/salvo/tree/main/.github"><span class='simplified-path'></span>.github <div class='js-tree-row-lfs'></div> </a></div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_4c40eab00f24304ca400313319c58d461788ff5e row tree-item tree-item-rename' file_hex='file_4c40eab00f24304ca400313319c58d461788ff5e' style='display:none'> <div class='ui column form' path='tree/main/.github'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='.github'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_3685e330ec5277a9dd5661c61f2bc55811f5a628 row tree-item' data-branch='main' data-type='folder' file_hex='file_3685e330ec5277a9dd5661c61f2bc55811f5a628'> <div class='five wide column tree-item-file-name tree-folder tree-list-item d-align-center' data-path='assets' data-type='folder'> <i class='iconfont icon-folders file-icon-item'></i> <a class="tree-folder-item" title="assets" data-path="assets" href="/salvo-rs/salvo/tree/main/assets"><span class='simplified-path'></span>assets <div class='js-tree-row-lfs'></div> </a></div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_3685e330ec5277a9dd5661c61f2bc55811f5a628 row tree-item tree-item-rename' file_hex='file_3685e330ec5277a9dd5661c61f2bc55811f5a628' style='display:none'> <div class='ui column form' path='tree/main/assets'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='assets'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_b1d786b9fb0f1b9037113f097e900d3e48b5a84d row tree-item' data-branch='main' data-type='folder' file_hex='file_b1d786b9fb0f1b9037113f097e900d3e48b5a84d'> <div class='five wide column tree-item-file-name tree-folder tree-list-item d-align-center' data-path='crates' data-type='folder'> <i class='iconfont icon-folders file-icon-item'></i> <a class="tree-folder-item" title="crates" data-path="crates" href="/salvo-rs/salvo/tree/main/crates"><span class='simplified-path'></span>crates <div class='js-tree-row-lfs'></div> </a></div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_b1d786b9fb0f1b9037113f097e900d3e48b5a84d row tree-item tree-item-rename' file_hex='file_b1d786b9fb0f1b9037113f097e900d3e48b5a84d' style='display:none'> <div class='ui column form' path='tree/main/crates'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='crates'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_99345ce680cd3e48acdb9ab4212e4bd9bf9358b7 row tree-item' data-branch='main' data-type='folder' file_hex='file_99345ce680cd3e48acdb9ab4212e4bd9bf9358b7'> <div class='five wide column tree-item-file-name tree-folder tree-list-item d-align-center' data-path='examples' data-type='folder'> <i class='iconfont icon-folders file-icon-item'></i> <a class="tree-folder-item" title="examples" data-path="examples" href="/salvo-rs/salvo/tree/main/examples"><span class='simplified-path'></span>examples <div class='js-tree-row-lfs'></div> </a></div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_99345ce680cd3e48acdb9ab4212e4bd9bf9358b7 row tree-item tree-item-rename' file_hex='file_99345ce680cd3e48acdb9ab4212e4bd9bf9358b7' style='display:none'> <div class='ui column form' path='tree/main/examples'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='examples'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_a5cc2925ca8258af241be7e5b0381edf30266302 row tree-item' data-branch='main' data-type='file' file_hex='file_a5cc2925ca8258af241be7e5b0381edf30266302'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='.gitignore' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title=".gitignore" data-path=".gitignore" href="/salvo-rs/salvo/blob/main/.gitignore">.gitignore</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_a5cc2925ca8258af241be7e5b0381edf30266302 row tree-item tree-item-rename' file_hex='file_a5cc2925ca8258af241be7e5b0381edf30266302' style='display:none'> <div class='ui column form' path='blob/main/.gitignore'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='.gitignore'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_1b290eb385892bfd4870c08a785598e98c8691b7 row tree-item' data-branch='main' data-type='file' file_hex='file_1b290eb385892bfd4870c08a785598e98c8691b7'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='Cargo.toml' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="Cargo.toml" data-path="Cargo.toml" href="/salvo-rs/salvo/blob/main/Cargo.toml">Cargo.toml</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_1b290eb385892bfd4870c08a785598e98c8691b7 row tree-item tree-item-rename' file_hex='file_1b290eb385892bfd4870c08a785598e98c8691b7' style='display:none'> <div class='ui column form' path='blob/main/Cargo.toml'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='Cargo.toml'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_a43224b0546505fe9cd8ba19122c11e47ca1fdcb row tree-item' data-branch='main' data-type='file' file_hex='file_a43224b0546505fe9cd8ba19122c11e47ca1fdcb'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='ECOSYSTEM.md' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="ECOSYSTEM.md" data-path="ECOSYSTEM.md" href="/salvo-rs/salvo/blob/main/ECOSYSTEM.md">ECOSYSTEM.md</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_a43224b0546505fe9cd8ba19122c11e47ca1fdcb row tree-item tree-item-rename' file_hex='file_a43224b0546505fe9cd8ba19122c11e47ca1fdcb' style='display:none'> <div class='ui column form' path='blob/main/ECOSYSTEM.md'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='ECOSYSTEM.md'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_0d59e2a8b6d66a5b479848f521227a8d7c260f13 row tree-item' data-branch='main' data-type='file' file_hex='file_0d59e2a8b6d66a5b479848f521227a8d7c260f13'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='LICENSE-APACHE' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="LICENSE-APACHE" data-path="LICENSE-APACHE" href="/salvo-rs/salvo/blob/main/LICENSE-APACHE">LICENSE-APACHE</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_0d59e2a8b6d66a5b479848f521227a8d7c260f13 row tree-item tree-item-rename' file_hex='file_0d59e2a8b6d66a5b479848f521227a8d7c260f13' style='display:none'> <div class='ui column form' path='blob/main/LICENSE-APACHE'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='LICENSE-APACHE'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_449e649c3b71c0e0efb38f4a8e4fa5b32e7bbfbf row tree-item' data-branch='main' data-type='file' file_hex='file_449e649c3b71c0e0efb38f4a8e4fa5b32e7bbfbf'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='LICENSE-MIT' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="LICENSE-MIT" data-path="LICENSE-MIT" href="/salvo-rs/salvo/blob/main/LICENSE-MIT">LICENSE-MIT</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_449e649c3b71c0e0efb38f4a8e4fa5b32e7bbfbf row tree-item tree-item-rename' file_hex='file_449e649c3b71c0e0efb38f4a8e4fa5b32e7bbfbf' style='display:none'> <div class='ui column form' path='blob/main/LICENSE-MIT'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='LICENSE-MIT'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d row tree-item' data-branch='main' data-type='file' file_hex='file_8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='README.md' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="README.md" data-path="README.md" href="/salvo-rs/salvo/blob/main/README.md">README.md</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d row tree-item tree-item-rename' file_hex='file_8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d' style='display:none'> <div class='ui column form' path='blob/main/README.md'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='README.md'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_7e4e2587e2a0941dd16d86d2fd5bf11c43d36da4 row tree-item' data-branch='main' data-type='file' file_hex='file_7e4e2587e2a0941dd16d86d2fd5bf11c43d36da4'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='README.zh-hant.md' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="README.zh-hant.md" data-path="README.zh-hant.md" href="/salvo-rs/salvo/blob/main/README.zh-hant.md">README.zh-hant.md</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_7e4e2587e2a0941dd16d86d2fd5bf11c43d36da4 row tree-item tree-item-rename' file_hex='file_7e4e2587e2a0941dd16d86d2fd5bf11c43d36da4' style='display:none'> <div class='ui column form' path='blob/main/README.zh-hant.md'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='README.zh-hant.md'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_933d13cc88d25497145c0e69aa345dbd2174041c row tree-item' data-branch='main' data-type='file' file_hex='file_933d13cc88d25497145c0e69aa345dbd2174041c'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='README.zh.md' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="README.zh.md" data-path="README.zh.md" href="/salvo-rs/salvo/blob/main/README.zh.md">README.zh.md</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_933d13cc88d25497145c0e69aa345dbd2174041c row tree-item tree-item-rename' file_hex='file_933d13cc88d25497145c0e69aa345dbd2174041c' style='display:none'> <div class='ui column form' path='blob/main/README.zh.md'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='README.zh.md'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_7c875fef54268bc433a7fc46aa5fd6e96d58f32f row tree-item' data-branch='main' data-type='file' file_hex='file_7c875fef54268bc433a7fc46aa5fd6e96d58f32f'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='SECURITY.md' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="SECURITY.md" data-path="SECURITY.md" href="/salvo-rs/salvo/blob/main/SECURITY.md">SECURITY.md</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_7c875fef54268bc433a7fc46aa5fd6e96d58f32f row tree-item tree-item-rename' file_hex='file_7c875fef54268bc433a7fc46aa5fd6e96d58f32f' style='display:none'> <div class='ui column form' path='blob/main/SECURITY.md'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='SECURITY.md'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_9b3a1fe94ab84189e548b3d75d926d92c7d3b252 row tree-item' data-branch='main' data-type='file' file_hex='file_9b3a1fe94ab84189e548b3d75d926d92c7d3b252'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='clippy.toml' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="clippy.toml" data-path="clippy.toml" href="/salvo-rs/salvo/blob/main/clippy.toml">clippy.toml</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_9b3a1fe94ab84189e548b3d75d926d92c7d3b252 row tree-item tree-item-rename' file_hex='file_9b3a1fe94ab84189e548b3d75d926d92c7d3b252' style='display:none'> <div class='ui column form' path='blob/main/clippy.toml'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='clippy.toml'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> <div class='file_fa89d2b538300306072cb1b50bfadaa3dd162713 row tree-item' data-branch='main' data-type='file' file_hex='file_fa89d2b538300306072cb1b50bfadaa3dd162713'> <div class='five wide column tree-item-file-name tree-list-item d-align-center' data-path='rustfmt.toml' data-type='file'> <i class="iconfont icon-file"></i> <a class="tree-folder-item" title="rustfmt.toml" data-path="rustfmt.toml" href="/salvo-rs/salvo/blob/main/rustfmt.toml">rustfmt.toml</a> <div class='js-tree-row-lfs'></div> </div> <div class='js-tree-row-commit'></div> </div> <div class='rename-file_fa89d2b538300306072cb1b50bfadaa3dd162713 row tree-item tree-item-rename' file_hex='file_fa89d2b538300306072cb1b50bfadaa3dd162713' style='display:none'> <div class='ui column form' path='blob/main/rustfmt.toml'> <div class='two fields'> <div class='five wide field'> <input class='ui input' name='new_filename' type='text' value='rustfmt.toml'> </div> <div class='five wide field'> <button class='ui blue button popup-save' type='submit'>保存</button> <button class='ui basic button popup-close'>取消</button> </div> </div> </div> </div> </div> </div> <div class='ui tree_progress' data-logs-path='/salvo-rs/salvo/refs/main/logs_tree/'> <div class='ui active inverted dimmer'> <div class='ui small text loader'>Loading...</div> </div> </div> <div class='readme-box' id='git-readme'> <div class='ui flat nopadding segment file_holder'> <div class='file_title d-flex-between file_readme_title'> <div class='d-flex'> <div class='file_title_readme mr-1 pb-1 active-title pr-1 pl-1'> <i class='iconfont icon-readme mr-05'></i> <span class='file_name'>README</span> </div> <div class='file_title_license pb-1 pl-1 pr-1'> <i class='iconfont icon-licence mr-05'></i> <span class='file_name'> Apache-2.0 </span> </div> </div> <div> <div class='readme-edit'> <a class='text-muted edit_path edit-blob' href='/salvo-rs/salvo/edit/main/README.zh.md' title='只有登陆后才可以编辑'> <i class='iconfont icon-edit mr-05'></i> </a> </div> <div class='hide lisence-edit'> <a class='text-muted edit_path edit-blob' href='/salvo-rs/salvo/edit/main/LICENSE-APACHE' title='只有登陆后才可以编辑'> <i class='iconfont icon-edit mr-05'></i> </a> </div> </div> </div> <div class='readme-content'> <div class='file_catalog'> <div class='toggle'> <i class='icon angle left'></i> </div> <div class='scroll-container'> <div class='container'> <div class='skeleton'> <div class='line line1'></div> <div class='line line2'></div> <div class='line line3'></div> <div class='line line1'></div> <div class='line line2'></div> <div class='line line3'></div> </div> </div> </div> </div> <div class='file_content markdown-body'> <blob-markdown-renderer data-dir='' data-path-with-namespace='/salvo-rs/salvo'>
<textarea class='content' style='display:none;'><div align="center">
<p><img alt="Salvo" width="132" style="max-width:40%;min-width:60px;" src="https://salvo.rs/images/logo-text.svg" /></p>
<p>
 <a href="https://github.com/salvo-rs/salvo/blob/main/README.md">English</a>&nbsp;&nbsp;
 <a href="https://github.com/salvo-rs/salvo/blob/main/README.zh.md">简体中文</a>&nbsp;&nbsp;
 <a href="https://github.com/salvo-rs/salvo/blob/main/README.zh-hant.md">繁體中文</a>
</p>
<p>
<a href="https://github.com/salvo-rs/salvo/actions">
 <img alt="build status" src="https://github.com/salvo-rs/salvo/workflows/ci-linux/badge.svg" />
</a>
<a href="https://github.com/salvo-rs/salvo/actions">
 <img alt="build status" src="https://github.com/salvo-rs/salvo/workflows/ci-macos/badge.svg" />
</a>
<a href="https://github.com/salvo-rs/salvo/actions">
 <img alt="build status" src="https://github.com/salvo-rs/salvo/workflows/ci-windows/badge.svg" />
</a>
<a href="https://codecov.io/gh/salvo-rs/salvo"><img alt="codecov" src="https://codecov.io/gh/salvo-rs/salvo/branch/main/graph/badge.svg" /></a>
<br>
<a href="https://crates.io/crates/salvo"><img alt="crates.io" src="https://img.shields.io/crates/v/salvo" /></a>
<a href="https://docs.rs/salvo"><img alt="Documentation" src="https://docs.rs/salvo/badge.svg" /></a>
<a href="https://crates.io/crates/salvo"><img alt="Download" src="https://img.shields.io/crates/d/salvo.svg" /></a>
<a href="https://github.com/rust-secure-code/safety-dance/"><img alt="unsafe forbidden" src="https://img.shields.io/badge/unsafe-forbidden-success.svg" /></a>
<a href="https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html"><img alt="Rust Version" src="https://img.shields.io/badge/rust-1.85%2B-blue" /></a>
<br>
<a href="https://salvo.rs">
 <img alt="Website" src="https://img.shields.io/badge/https-salvo.rs-%23f00" />
</a>
<a href="https://discord.gg/G8KfmS6ByH">
 <img src="https://img.shields.io/discord/1041442427006890014.svg?logo=discord">
</a>
<a href="https://gitcode.com/salvo-rs/salvo">
 <img src="https://gitcode.com/salvo-rs/salvo/star/badge.svg">
</a>
</p>
</div>

Salvo(赛风) 是一个极其简单且功能强大的 Rust Web 后端框架。仅仅需要基础 Rust 知识即可开发后端服务。

> 中国用户可以添加我微信 (chrislearn), 拉微信讨论群或者直接加入 QQ 群:823441777.
>
> 中国同步仓库:
> - Gitee: https://gitee.com/salvo-rs/salvo
> - Gitcode: https://gitcode.com/salvo-rs/salvo

## 🎯 功能特色

- 基于 [Hyper 1](https://crates.io/crates/hyper), [Tokio](https://crates.io/crates/tokio) 开发;
- 支持 HTTP1, HTTP2 和 **HTTP3**;
- 统一的中间件和句柄接口;
- 路由可以无限嵌套,并且可以在任何路由中附加多个中间件;
- 集成 Multipart 表单处理;
- 支持 WebSocket, WebTransport;
- 支持 OpenAPI;
- 支持 Acme, 自动从 [let's encrypt](https://letsencrypt.org/)获取 TLS 证书。
- 支持 Tower Service 和 Layer.

## ⚡️ 快速开始

你可以查看[实例代码](https://github.com/salvo-rs/salvo/tree/main/examples), 或者访问[官网](https://salvo.rs)。

### 支持 ACME 自动获取证书和 HTTP3 的 Hello World

**只需要几行代码就可以实现一个同时支持 ACME 自动获取证书以及支持 HTTP1,HTTP2,HTTP3 协议的服务器。**

```rust
use salvo::prelude::*;

#[handler]
async fn hello(_req: &mut Request, _depot: &mut Depot, res: &mut Response) {
 res.render(Text::Plain("Hello World"));
}

#[tokio::main]
async fn main() {
 let mut router = Router::new().get(hello);
 let listener = TcpListener::new("0.0.0.0:443")
 .acme()
 .add_domain("test.salvo.rs") // 用你自己的域名替换此域名
 .http01_challenge(&mut router).quinn("0.0.0.0:443");
 let acceptor = listener.join(TcpListener::new("0.0.0.0:80")).bind().await;
 Server::new(acceptor).serve(router).await;
}
```

### 中间件

Salvo 中的中间件其实就是 Handler, 没有其他任何特别之处。**所以书写中间件并不需要像其他某些框架需要掌握泛型关联类型等知识。只要你会写函数就会写中间件,就是这么简单!!!**

```rust
use salvo::http::header::{self, HeaderValue};
use salvo::prelude::*;

#[handler]
async fn add_header(res: &mut Response) {
 res.headers_mut()
 .insert(header::SERVER, HeaderValue::from_static("Salvo"));
}
```

然后将它添加到路由中:

```rust
Router::new().hoop(add_header).get(hello)
```

这就是一个简单的中间件,它向 `Response` 的头部添加了 `Header`, 查看[完整源码](https://github.com/salvo-rs/salvo/blob/main/examples/middleware-add-header/src/main.rs)。

### 可链式书写的树状路由系统

正常情况下我们是这样写路由的:

```rust
Router::with_path("articles").get(list_articles).post(create_article);
Router::with_path("articles/{id}")
 .get(show_article)
 .patch(edit_article)
 .delete(delete_article);
```

往往查看文章和文章列表是不需要用户登录的,但是创建,编辑,删除文章等需要用户登录认证权限才可以。Salvo 中支持嵌套的路由系统可以很好地满足这种需求。我们可以把不需要用户登录的路由写到一起:

```rust
Router::with_path("articles")
 .get(list_articles)
 .push(Router::with_path("{id}").get(show_article));
```

然后把需要用户登录的路由写到一起,并且使用相应的中间件验证用户是否登录:

```rust
Router::with_path("articles")
 .hoop(auth_check)
 .push(Router::with_path("{id}").patch(edit_article).delete(delete_article));
```

虽然这两个路由都有着同样的 `path("articles")`, 然而它们依然可以被同时添加到同一个父路由,所以最后的路由长成了这个样子:

```rust
Router::new()
 .push(
 Router::with_path("articles")
 .get(list_articles)
 .push(Router::with_path("{id}").get(show_article)),
 )
 .push(
 Router::with_path("articles")
 .hoop(auth_check)
 .push(Router::with_path("{id}").patch(edit_article).delete(delete_article)),
 );
```

`{id}`匹配了路径中的一个片段,正常情况下文章的 `id`只是一个数字,这时我们可以使用正则表达式限制 `id`的匹配规则,`r"{id|\d+}"`。

还可以通过 `{**}`, `{*+}` 或者 `{*?}`匹配所有剩余的路径片段。为了代码易读性强些,也可以添加适合的名字,让路径语义更清晰,比如:: `{**file_path}`。

有些用于匹配路径的正则表达式需要经常被使用,可以将它事先注册,比如 GUID:

```rust
PathFilter::register_wisp_regex(
 "guid",
 Regex::new("[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}").unwrap(),
);
```

这样在需要路径匹配时就变得更简洁:

```rust
Router::with_path("{id:guid}").get(index)
```

查看[完整源码](https://github.com/salvo-rs/salvo/blob/main/examples/routing-guid/src/main.rs)

### 文件上传

可以通过 `Request` 中的 `file`异步获取上传的文件:

```rust
#[handler]
async fn upload(req: &mut Request, res: &mut Response) {
 let file = req.file("file").await;
 if let Some(file) = file {
 let dest = format!("temp/{}", file.name().unwrap_or_else(|| "file".into()));
 if let Err(e) = std::fs::copy(&file.path, Path::new(&dest)) {
 res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
 } else {
 res.render("Ok");
 }
 } else {
 res.status_code(StatusCode::BAD_REQUEST);
 }
}
```

### 提取请求数据

可以轻松地从多个不同数据源获取数据,并且组装为你想要的类型。可以先定义一个自定义的类型,比如:

```rust
#[derive(Serialize, Deserialize, Extractible, Debug)]
/// 默认从 body 中获取数据字段值
#[salvo(extract(default_source(from = "body")))]
struct GoodMan<'a> {
 /// 其中, id 号从请求路径参数中获取, 并且自动解析数据为 i64 类型.
 #[salvo(extract(source(from = "param")))]
 id: i64,
 /// 可以使用引用类型, 避免内存复制.
 username: &'a str,
 first_name: String,
 last_name: String,
}
```

然后在 `Handler`中可以这样获取数据:

```rust
#[handler]
async fn edit(req: &mut Request) {
 let good_man: GoodMan<'_> = req.extract().await.unwrap();
}
```

甚至于可以直接把类型作为参数传入函数,像这样:

```rust
#[handler]
async fn edit<'a>(good_man: GoodMan<'a>) {
 res.render(Json(good_man));
}
```

查看[完整源码](https://github.com/salvo-rs/salvo/blob/main/examples/extract-nested/src/main.rs)

### OpenAPI 支持

无需对项目做大的改动,即可实现对 OpenAPI 的完美支持。

```rust
#[derive(Serialize, Deserialize, ToSchema, Debug)]
struct MyObject<T: ToSchema + std::fmt::Debug> {
 value: T,
}

#[endpoint]
async fn use_string(body: JsonBody<MyObject<String>>) -> String {
 format!("{:?}", body)
}
#[endpoint]
async fn use_i32(body: JsonBody<MyObject<i32>>) -> String {
 format!("{:?}", body)
}
#[endpoint]
async fn use_u64(body: JsonBody<MyObject<u64>>) -> String {
 format!("{:?}", body)
}

#[tokio::main]
async fn main() {
 tracing_subscriber::fmt().init();

 let router = Router::new()
 .push(Router::with_path("i32").post(use_i32))
 .push(Router::with_path("u64").post(use_u64))
 .push(Router::with_path("string").post(use_string));

 let doc = OpenApi::new("test api", "0.0.1").merge_router(&router);

 let router = router
 .unshift(doc.into_router("/api-doc/openapi.json"))
 .unshift(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui"));

 let acceptor = TcpListener::new("127.0.0.1:5800").bind().await;
 Server::new(acceptor).serve(router).await;
}
```

### 🛠️ Salvo CLI

Salvo CLI 是一个命令行工具,可以简化创建新的 Salvo 项目的过程,支持 Web API、网站、数据库(包括通过 SQLx、SeaORM、Diesel、Rbatis 支持的 SQLite、PostgreSQL、MySQL)和基本的中间件的模板。
你可以使用 [salvo-cli](https://github.com/salvo-rs/salvo-cli)来创建一个新的 Salvo 项目:

#### 安装

```bash
cargo install salvo-cli
```

#### 创建一个 Salvo 项目

```bash
salvo new project_name
```

___

### 更多示例

您可以从 [examples](./examples/)文件夹下查看更多示例代码,您可以通过以下命令运行这些示例:

```bash
cd examples
cargo run --bin example-basic-auth
```

您可以使用任何你想运行的示例名称替代这里的 `basic-auth`。

## 🚀 性能

Benchmark 测试结果可以从这里查看:

[https://web-frameworks-benchmark.netlify.app/result?l=rust](https://web-frameworks-benchmark.netlify.app/result?l=rust)

[https://www.techempower.com/benchmarks/#section=data-r22](https://www.techempower.com/benchmarks/#section=data-r22)

## 🩸 贡献者

<a href="https://github.com/salvo-rs/salvo/graphs/contributors">
 <img src="https://contrib.rocks/image?repo=salvo-rs/salvo" />
</a>

## ☕ 捐助

`Salvo`是一个开源项目,如果想支持本项目,可以 ☕ [**请我喝杯咖啡**](https://ko-fi.com/chrislearn)。
<p style="text-align: center;">
<img src="https://salvo.rs/images/alipay.png" alt="Alipay" width="180"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="https://salvo.rs/images/weixin.png" alt="Weixin" width="180"/>
</p>

## ⚠️ 开源协议

Salvo 项目采用以下开源协议:

- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))

- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT))</textarea>
<div class='loader-wrapper'>
<div class='ui inline mini active loader'></div>
</div></blob-markdown-renderer>
</div> <div class='file_line'></div> </div> <div class='hide lisence-content'> <div class='file_content code'> <div class='d-block lines mb-1 ml-3 mr-3 mt-1 white'> <div class='preformatted'>
 Apache License
 Version 2.0, January 2004
 http://www.apache.org/licenses/

 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

 1. Definitions.

 "License" shall mean the terms and conditions for use, reproduction,
 and distribution as defined by Sections 1 through 9 of this document.

 "Licensor" shall mean the copyright owner or entity authorized by
 the copyright owner that is granting the License.

 "Legal Entity" shall mean the union of the acting entity and all
 other entities that control, are controlled by, or are under common
 control with that entity. For the purposes of this definition,
 "control" means (i) the power, direct or indirect, to cause the
 direction or management of such entity, whether by contract or
 otherwise, or (ii) ownership of fifty percent (50%) or more of the
 outstanding shares, or (iii) beneficial ownership of such entity.

 "You" (or "Your") shall mean an individual or Legal Entity
 exercising permissions granted by this License.

 "Source" form shall mean the preferred form for making modifications,
 including but not limited to software source code, documentation
 source, and configuration files.

 "Object" form shall mean any form resulting from mechanical
 transformation or translation of a Source form, including but
 not limited to compiled object code, generated documentation,
 and conversions to other media types.

 "Work" shall mean the work of authorship, whether in Source or
 Object form, made available under the License, as indicated by a
 copyright notice that is included in or attached to the work
 (an example is provided in the Appendix below).

 "Derivative Works" shall mean any work, whether in Source or Object
 form, that is based on (or derived from) the Work and for which the
 editorial revisions, annotations, elaborations, or other modifications
 represent, as a whole, an original work of authorship. For the purposes
 of this License, Derivative Works shall not include works that remain
 separable from, or merely link (or bind by name) to the interfaces of,
 the Work and Derivative Works thereof.

 "Contribution" shall mean any work of authorship, including
 the original version of the Work and any modifications or additions
 to that Work or Derivative Works thereof, that is intentionally
 submitted to Licensor for inclusion in the Work by the copyright owner
 or by an individual or Legal Entity authorized to submit on behalf of
 the copyright owner. For the purposes of this definition, "submitted"
 means any form of electronic, verbal, or written communication sent
 to the Licensor or its representatives, including but not limited to
 communication on electronic mailing lists, source code control systems,
 and issue tracking systems that are managed by, or on behalf of, the
 Licensor for the purpose of discussing and improving the Work, but
 excluding communication that is conspicuously marked or otherwise
 designated in writing by the copyright owner as "Not a Contribution."

 "Contributor" shall mean Licensor and any individual or Legal Entity
 on behalf of whom a Contribution has been received by Licensor and
 subsequently incorporated within the Work.

 2. Grant of Copyright License. Subject to the terms and conditions of
 this License, each Contributor hereby grants to You a perpetual,
 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 copyright license to reproduce, prepare Derivative Works of,
 publicly display, publicly perform, sublicense, and distribute the
 Work and such Derivative Works in Source or Object form.

 3. Grant of Patent License. Subject to the terms and conditions of
 this License, each Contributor hereby grants to You a perpetual,
 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 (except as stated in this section) patent license to make, have made,
 use, offer to sell, sell, import, and otherwise transfer the Work,
 where such license applies only to those patent claims licensable
 by such Contributor that are necessarily infringed by their
 Contribution(s) alone or by combination of their Contribution(s)
 with the Work to which such Contribution(s) was submitted. If You
 institute patent litigation against any entity (including a
 cross-claim or counterclaim in a lawsuit) alleging that the Work
 or a Contribution incorporated within the Work constitutes direct
 or contributory patent infringement, then any patent licenses
 granted to You under this License for that Work shall terminate
 as of the date such litigation is filed.

 4. Redistribution. You may reproduce and distribute copies of the
 Work or Derivative Works thereof in any medium, with or without
 modifications, and in Source or Object form, provided that You
 meet the following conditions:

 (a) You must give any other recipients of the Work or
 Derivative Works a copy of this License; and

 (b) You must cause any modified files to carry prominent notices
 stating that You changed the files; and

 (c) You must retain, in the Source form of any Derivative Works
 that You distribute, all copyright, patent, trademark, and
 attribution notices from the Source form of the Work,
 excluding those notices that do not pertain to any part of
 the Derivative Works; and

 (d) If the Work includes a "NOTICE" text file as part of its
 distribution, then any Derivative Works that You distribute must
 include a readable copy of the attribution notices contained
 within such NOTICE file, excluding those notices that do not
 pertain to any part of the Derivative Works, in at least one
 of the following places: within a NOTICE text file distributed
 as part of the Derivative Works; within the Source form or
 documentation, if provided along with the Derivative Works; or,
 within a display generated by the Derivative Works, if and
 wherever such third-party notices normally appear. The contents
 of the NOTICE file are for informational purposes only and
 do not modify the License. You may add Your own attribution
 notices within Derivative Works that You distribute, alongside
 or as an addendum to the NOTICE text from the Work, provided
 that such additional attribution notices cannot be construed
 as modifying the License.

 You may add Your own copyright statement to Your modifications and
 may provide additional or different license terms and conditions
 for use, reproduction, or distribution of Your modifications, or
 for any such Derivative Works as a whole, provided Your use,
 reproduction, and distribution of the Work otherwise complies with
 the conditions stated in this License.

 5. Submission of Contributions. Unless You explicitly state otherwise,
 any Contribution intentionally submitted for inclusion in the Work
 by You to the Licensor shall be under the terms and conditions of
 this License, without any additional terms or conditions.
 Notwithstanding the above, nothing herein shall supersede or modify
 the terms of any separate license agreement you may have executed
 with Licensor regarding such Contributions.

 6. Trademarks. This License does not grant permission to use the trade
 names, trademarks, service marks, or product names of the Licensor,
 except as required for reasonable and customary use in describing the
 origin of the Work and reproducing the content of the NOTICE file.

 7. Disclaimer of Warranty. Unless required by applicable law or
 agreed to in writing, Licensor provides the Work (and each
 Contributor provides its Contributions) on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 implied, including, without limitation, any warranties or conditions
 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 PARTICULAR PURPOSE. You are solely responsible for determining the
 appropriateness of using or redistributing the Work and assume any
 risks associated with Your exercise of permissions under this License.

 8. Limitation of Liability. In no event and under no legal theory,
 whether in tort (including negligence), contract, or otherwise,
 unless required by applicable law (such as deliberate and grossly
 negligent acts) or agreed to in writing, shall any Contributor be
 liable to You for damages, including any direct, indirect, special,
 incidental, or consequential damages of any character arising as a
 result of this License or out of the use or inability to use the
 Work (including but not limited to damages for loss of goodwill,
 work stoppage, computer failure or malfunction, or any and all
 other commercial damages or losses), even if such Contributor
 has been advised of the possibility of such damages.

 9. Accepting Warranty or Additional Liability. While redistributing
 the Work or Derivative Works thereof, You may choose to offer,
 and charge a fee for, acceptance of support, warranty, indemnity,
 or other liability obligations and/or rights consistent with this
 License. However, in accepting such obligations, You may act only
 on Your own behalf and on Your sole responsibility, not on behalf
 of any other Contributor, and only if You agree to indemnify,
 defend, and hold each Contributor harmless for any liability
 incurred by, or claims asserted against, such Contributor by reason
 of your accepting any such warranty or additional liability.

 END OF TERMS AND CONDITIONS

 APPENDIX: How to apply the Apache License to your work.

 To apply the Apache License to your work, attach the following
 boilerplate notice, with the fields enclosed by brackets "{}"
 replaced with your own identifying information. (Don't include
 the brackets!) The text should be enclosed in the appropriate
 comment syntax for the file format. We also recommend that a
 file or class name and description of purpose be included on the
 same "printed page" as the copyright notice for easier
 identification within third-party archives.

 Copyright (c) 2019-present, The `salvo` Developers

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

</div></div> </div> </div> </div> </div> <script> "use strict"; if ($('.markdown-body').children("style").length != 0) { var i, array = $('.markdown-body').children("style"); array.first().attr('scoped',''); $.scoped(); } $('.appeal_message').removeClass('text-center') $(".file_title_readme").click(function(event) { $('.lisence-content').hide() $('.readme-content').show() $('.lisence-edit').hide() $('.readme-edit').show() $('.file_title_readme').addClass('active-title') $('.file_title_license').removeClass('active-title') }); $(".file_title_license").click(function(event) { $('.lisence-content').show() $('.readme-content').hide() $('.lisence-edit').show() $('.readme-edit').hide() $('.file_title_license').addClass('active-title') $('.file_title_readme').removeClass('active-title') }); try { if((gon.wait_fork!=undefined && gon.wait_fork==true) || (gon.wait_fetch!=undefined && gon.wait_fetch==true)){ $('.edit-blob').popup({content:"当前仓库正在后台处理中,暂时无法编辑", on: 'hover', delay: { show: 200, hide: 200 }}); $('.edit-blob').click(function(e){ e.preventDefault(); }) } }catch (error) {} $('.disabled-edit-readonly').popup({ content: "只读文件不可编辑", className: { popup: 'ui popup', }, position: 'bottom center', }) $('.disabled-edit-readonly, .disabled-edit-status').click(function() { return false }) $('.has_tooltip').popup({ position: 'top center' }); </script> <style> .txt-style { background: #FFF !important; padding: 0 !important; } .file_readme_title { padding-bottom: 0 !important; cursor: pointer; } .active-title { border-bottom: 2px solid #fe7300; } .preformatted { font-family: monospace; word-wrap: break-word; white-space: pre-wrap; word-break: keep-all; } .disabled-edit-readonly, .has_tooltip { color: #40485b !important; cursor: default !important; opacity: 0.3 !important; } </style> </div> <div class='project__footer-container'> <div class='actions'> <div class='item star-container'> <div class='unstar'> <a href="/login"><div class='circle'> <i class='iconfont icon-star-solid'></i> </div> </a><a class="content" href="/salvo-rs/salvo/stargazers"><div class='title'> Starred </div> <div class='desc action-social-count'> 326 </div> </a></div> <div class='star'> <a href="/login"><div class='circle'> <i class='iconfont icon-star'></i> </div> </a><a class="content" href="/salvo-rs/salvo/stargazers"><div class='title'> Star </div> <div class='desc action-social-count'> 326 </div> </a></div> </div> <div class='item fork-container'> <a href="/login"><div class='circle'> <i class='iconfont icon-fork'></i> </div> </a><a href="/salvo-rs/salvo/members"><div class='content'> <div class='title'> Fork </div> <div class='desc'> 37 </div> </div> </a></div> </div> </div> <script id='tree-item-context-menu-template' type='text/plain'> <div class='ui menu compact vertical tree-context'> <a class='btn-open-new-tab item tree-operation'> <i class='file outline icon'></i> 新标签打开 </a> <a class='btn-copy item tree-operation'> <i class='copy icon'></i> 复制 </a> <a class='btn-rename item tree-operation'> <i class='edit icon'></i> 重命名 </a> <a class='btn-delete item tree-operation'> <i class='trash icon'></i> 删除 </a> </div> </script> <script id='tree-item-submodule-context-menu-template' type='text/plain'> <div class='ui menu compact vertical tree-context'> <a class='btn-copy item tree-operation'> <i class='copy icon'></i> 复制 </a> <a class='btn-compact-edit item tree-operation submodule_item'> <i class='edit icon'></i> 编辑 </a> <a class='btn-submodule-delete item tree-operation submodule_item'> <i class='trash icon'></i> 删除 </a> </div> </script> <script src="/static/javascripts/file-icons.js"></script> <script> $(function() { $(".tree-list-item").each(function(){ $this = $(this) var path = $this.attr('data-path') var type = $this.attr('data-type') $icon = $this.find('i') if(type==='file'){ let iconClass=''; try { iconClass = FileIcons.getClassWithColor(path) || 'file-generic'; } catch (err) {} if(iconClass) $icon.attr('class', "file-icon-item iconfont icon-file ".concat(iconClass)); } }) var $createFolderForm = $('.create-folder-form'), $createFolderSubmitBtn = $('.create-folder-form .js-submit-btn') $folderPath = $('#new_folder_path'), $message = $('.create-folder-form .notice'), folders = [".github","assets","crates","examples"], folderReg = new RegExp(/\/+/); var INDEX_BEGIN_WITH_READONLY_DIR = 0 var gitGCModal = new GiteeModalHelper({ approveText: "确认", cancelText: "取消", okText: "确认", }) function checkFolder(folder) { var i, itemArr; if (folder == '') { return false; } for (i = 0; i< folders.length; i++) { itemArr = folders[i].split('/'); if (itemArr[0] == folder){ return true } } return false; } function createFolderShow () { $createFolderForm.show(); $folderPath.focus(); } $('.create-folder-form .cancel').click(function () { $createFolderForm.hide(); }) $folderPath.on('input', function (e) { if($(this).parent().hasClass('error')) { $message.removeClass('warn').html("<strong>提示:</strong> 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件"); $(this).parent().removeClass('error'); } $createFolderSubmitBtn.removeClass('disabled'); }) $createFolderForm.submit(function (e) { var value = $folderPath.val(); if (!folderReg.test(value) && !checkFolder(value) && value) { $('#new_file_path').val(value + '/.keep'); } else { $message.addClass('warn').html("文件夹名不为空,不含有字符(/)且不能与当前目录文件夹同名"); $folderPath.parent().addClass('error'); e.preventDefault(); } $createFolderSubmitBtn.addClass('disabled'); }) $('.create-folder').click(createFolderShow) $('#create-folder').click(createFolderShow); window.createFolderShow = createFolderShow; window.hasTreeContextMenu = false; var contextMenuTemplate = $('#tree-item-context-menu-template').html(); var submoduleContextMenuTemplate = $("#tree-item-submodule-context-menu-template").html(); if ("true" == "false" || "true" == "false" || "false" == "false") { return; } function createContextMenu($item, x, y) { if (hasTreeContextMenu) { $('.tree-context').remove(); hasTreeContextMenu = false; return; } hasTreeContextMenu = true; var isSubmodule = $item.hasClass('tree-item-submodule-name') var path = $item.parent().find('a').attr('href'); var $menu = isSubmodule ? $(submoduleContextMenuTemplate).appendTo('body') : $(contextMenuTemplate).appendTo('body'); var readonly = $item.attr('data-readonly') !== undefined var submoduleEditUrl = $item.closest('.tree-item').attr('edit_url') var submoduleDeleteUrl = $item.closest('.tree-item').attr('delete_url') var hasNormal = $item.closest('.tree-item')[0].hasAttribute('normal') if ($('.btn-readonly')[0]) { if (readonly) { $('.btn-readonly')[0].children[0].className = 'icon unlock' $('.btn-readonly')[0].lastChild.data = "取消只读" } else { $('.btn-readonly')[0].lastChild.data = "标记为只读" } } $menu.css({ left: x, top: y , 'min-width': '90px'}); $menu.find('.btn-open-new-tab').attr({ href: path, target: '_blank' }); window.Clipboard && new Clipboard('.btn-copy', { text: function () { return $item.text().trim(); } }); // submodule 菜单事件 // 编辑子模块 if (!hasNormal) { $menu.find('.btn-compact-edit').addClass('disabled') } $menu.find('.btn-compact-edit').on('click', function(event) { if (!hasNormal) { return } window.location.href = submoduleEditUrl }); // 删除子模块 $menu.find('.btn-submodule-delete').on('click', function(event) { removeSubmodule(submoduleDeleteUrl, $item) }); // 普通文件 菜单事件 $menu.find('.btn-rename').on('click', function(event) { rename($item.parent().attr('file_hex')); }); $menu.find('.btn-delete').on('click', function(event) { removeFile($item.parent().next().find('.ui.form').attr('path').replace(/\+/g, '%20'), $item.find('a').text()); }) $menu.find('.btn-readonly').on('click', function(event) { var obj = $item.parent().next().find('.ui.form').attr('path').replace(/\+/g, '%20') var readonly = $item.attr('data-readonly') !== undefined if (readonly) { removeReadonlyMark(obj, $item) } else { addReadonlyMark(obj, $item) } }) $menu.find('.btn-readonly-is-uncharged-enterprise').popup({ content: "仅付费企业版可使用文件只读功能", className: { popup: 'ui popup', }, position: 'bottom center' }) $menu.find('.btn-readonly-is-open-svn').popup({ content: "仓库已开启 SVN 支持,无法标记只读文件/目录", className: { popup: 'ui popup', }, position: 'bottom center' }) } // submodule 右键菜单事件 var editing = false $(document).on('click',function(event){ if (hasTreeContextMenu && event.button !== 2) { setTimeout(function () { $('.tree-context').remove(); hasTreeContextMenu = false; }, 200); } }) $('.tree-item').on('click',function(event){ if (hasTreeContextMenu) { event.preventDefault(); } }) function isReadonly(path) { if (!gon.readonlyItems) { return false } else { var item_path = getItemPath(path, 'folder') return isTreeItemReadonly(item_path, gon.readonlyItems) } } function isParentsReadonly(path) { var self_path = path.replace(/\/$/, '') var parentPathArray = self_path.split('/') if (parentPathArray.length < 2) { return false } parentPathArray.pop() var parentPath = parentPathArray.join('/') + '/' return isReadonly(parentPath) } // 删除只读标记 function removeReadonlyMark(path, $item) { var $icon = $($item.context.children[0]) var $parent = $item.parent('.tree-item') var type = $parent.data('type') var branch = $parent.data('branch') var readonly_item = $item.data('path') if (type !== 'file') { readonly_item += "/" } var confirmString = "\n <p>所属分支: <code class=\"readonly-branch\">".concat(htmlSafe(branch), "</code></p> <p>文件路径: <code class=\"readonly-path\">").concat(htmlSafe(readonly_item), "</code></p> <strong>确认取消分支上这个路径的只读标记?</strong>\n "); gitGCModal.confirm("取消只读", confirmString, function() { var parentsReadonly = isParentsReadonly(readonly_item) if (parentsReadonly) { return gitGCModal.alert('提示', '只读记录不存在,或父级目录为只读',function () { location.reload() }) } $.ajax({ url: "/salvo-rs/salvo/readonly", type: 'DELETE', data: { branch: branch, path: readonly_item }, success: function(res) { if (res.code !== 0) { gitGCModal.alert("提示", res.msg, function() { location.reload(); }) }else { $parent.removeClass('readonly-item') if (gon.readonlyItems) { gon.readonlyItems.splice(gon.readonlyItems.indexOf(readonly_item), 1) } if (type === 'file') { $icon.removeClass('icon-file-readonly readonly-icon') $icon.addClass('icon-file') } else { $icon.removeClass('icon-folder-readonly readonly-icon') $icon.addClass('icon-folders') } $icon.popup('destroy') $item.removeAttr('data-readonly') location.reload(); } } }) }) } // 只读过滤 function isTreeItemReadonly(path, readonlyItems) { var item = readonlyItems.find(function(item) { if (item.slice(-1) === '/') { return path.indexOf(item) === INDEX_BEGIN_WITH_READONLY_DIR } else { return path === item } }) return item !== undefined } function getItemPath(path) { var path_type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'file'; return path_type === 'file' ? path : "".concat(path, "/"); } // 启用异步只读差异化的次数 var is_can_readonly = false // 异步对文件树进行只读差异化显示 function markTree() { $.ajax({ url: "/salvo-rs/salvo/readonly", method: 'get', data: { "branch": "main" }, success:function(result) { var readonlyItems = result.readonly_items gon.readonlyItems = readonlyItems $(".tree-list-item").each(function(){ $this = $(this) var path = $this.attr('data-path') var type = $this.attr('data-type') var item_path = getItemPath(path, type) var readonly = isTreeItemReadonly(item_path, readonlyItems) $icon = $this.find('i') $parent = $this.parent('.tree-item') if (readonly) { $parent.addClass('readonly-item') $this.attr('data-readonly', '') $icon.attr('class', "iconfont icon-".concat(type, "-readonly readonly-icon")); $icon.attr('data-readonly', '') $icon.popup({ content: "只读", className : { popup: 'ui popup dark', }, position: 'top center' }) } else { var className = type === 'file' ? 'file' : 'folders' $this.removeAttr('data-readonly') $icon.attr('class', "iconfont icon-".concat(className)); $icon.removeAttr('data-readonly') } }) } }) } // 若已开启只读功能,对目录进行只读差异化标记 if (is_can_readonly) { markTree() } // 添加只读标记 function addReadonlyMark(path, $item) { var $icon = $($item.context.children[0]) var $parent = $item.parent('.tree-item') var type = $parent.data('type') var branch = $parent.data('branch') var readonly_item = $item.data('path') if (type !== 'file') { readonly_item += "/" } var confirmString = "\n <p>所属分支: <code class=\"readonly-branch\">".concat(htmlSafe(branch), "</code></p> <p>文件路径: <code class=\"readonly-path\">").concat(htmlSafe(readonly_item), "</code></p> <strong>确认将分支上的这个路径标记为只读?</strong>\n "); gitGCModal.confirm("标记只读", confirmString,function (){ $.ajax({ url: "/salvo-rs/salvo/readonly", type: 'POST', data: { branch: branch, path: readonly_item, }, success: function(res) { if (res.code !== 0) { gitGCModal.alert("提示", res.msg,function (){ location.reload(); }) } else { // 之前未开启只读功能 if (!is_can_readonly) { is_can_readonly = true return markTree() } $parent.addClass('readonly-item') var existReadonlyItems = gon.readonlyItems || [] existReadonlyItems.push(readonly_item) gon.readonlyItems = existReadonlyItems $icon.popup({ content: "只读", className : { popup: 'ui popup dark', }, position: 'top center' }) if (type === 'file') { $icon.removeClass('icon-file') $icon.addClass('icon-file-readonly readonly-icon') } else { $icon.removeClass('icon-folders') $icon.addClass('icon-folder-readonly readonly-icon') } $item.attr('data-readonly', '') location.reload(); } } }) }) } function removeFile(path, file_name) { var file_name = file_name || path var content = "确定要删除 %{name} 吗?".replace('%{name}', htmlSafe(file_name)); gitGCModal.confirm("删除", content, function() { $.ajax({ type: "DELETE", dataType: "JSON", url: "/salvo-rs/salvo/delete/" + path, success: function(res) { if (res.status != 1) { var alert_message = res.message || "删除失败" return gitGCModal.alert("提示", alert_message) } if ($('.tree-item-file-name').length == 2) { window.location.href = '/' + gon.user_project; if(file_name.toUpperCase()==='LICENSE'){ window.location.reload(); } } else { window.location.href = window.location.pathname; if(file_name.toUpperCase()==='LICENSE'){ window.location.reload(); } } } }); }) } // 删除子模块 function removeSubmodule(deleteUrl, $item) { if (!deleteUrl) return var file_name = $item.text().trim() var content = "此操作无法恢复,确定要删除子模块%{name}?".replace('%{name}', htmlSafe(file_name)); new GiteeModalHelper({ approveText: "删除并提交推送", cancelText: "取消", okText: "删除并提交推送", }).confirm("删除", content, function() { $.ajax({ type: "DELETE", dataType: "JSON", url: deleteUrl, success: function(res) { if (res.status == 200) { $item.closest('.row.tree-item').remove(); window.location.reload(); } else { Flash.error(res.message, 5000) } }, error: function (err) { err.responseJSON && Flash.error(err.responseJSON.message); }, }); }) } function rename(file_hex) { $(".row.tree-item").show(); $(".tree-item-rename").hide(); if($("."+file_hex).is(':hidden') == true) return; $("."+file_hex).hide(); var _t = $(".rename-"+file_hex); src = _t.attr('src_text') if(src==undefined){ newName = _t.find("[name='new_filename']").val() _t.attr('src_text',newName) }else{ _t.find("[name='new_filename']").val(_t.attr('src_text')) } $(".rename-"+file_hex).css("display",""); } setTimeout(function(){ $(".popup-save").click(function(){ form = $(this).parents(".ui.form") file_hex = $(this).parents(".row.tree-item").attr("file_hex"); new_filename = $.trim(form.find("[name='new_filename']").val()) overwrite = false $('.tree-item-file-name').find('a').each(function(a){ title = $(this).attr('title'); if(title != undefined){ if (title.split('/')[0] == new_filename) { overwrite = true; } } }); if(overwrite){ form.find("[name='new_filename']").focus(); gitGCModal.alert("提示", "存在相同的文件名,请修改后重试") return; } $.ajax({ type: 'POST', url: "/salvo-rs/salvo/rename/"+ form.attr('path').replace(/\+/g, '%20'), data: { new_filename: new_filename }, success: function(o){ if(o.status == 1){ href = window.location.href.split('?')[0] window.location.href = href; }else { var alert_message = o.message || "重命名失败" gitGCModal.alert("提示", alert_message) } }, dataType: "json" }); }) $(".popup-close").click(function(){ $(".row.tree-item").show(); $(".tree-item-rename").hide(); }) $('.tree-item-file-name, .tree-item-submodule-name').each(function() { var $this = $(this); if (typeof $this.parent().attr('file_hex') === 'undefined') { return; } $this.on('contextmenu', function(event) { event.preventDefault(); createContextMenu($this, event.pageX, event.pageY); }) }) },1000) }) </script> <style> .readonly-item { background-color: #FBFBFB !important; } .readonly-path,.readonly-branch { display: block; white-space: normal; word-break: break-word; line-height: 1.8; margin-top: 1em; } .btn-readonly-is-uncharged-enterprise, .btn-readonly-is-open-svn { background-color: #f5f5f5 !important; color: #757575 !important; } </style> <div class='complaint'> <div class='ui modal small form' id='landing-comments-complaint-modal'> <i class='iconfont icon-close close'></i> <div class='header'> 举报 </div> <div class='content'> <div class='appeal-success-tip hide'> <i class='iconfont icon-ic_msg_success'></i> <div class='appeal-success-text'> 举报成功 </div> <span> 我们将于2个工作日内通过站内信反馈结果给你! </span> </div> <div class='appeal-tip'> 请认真填写举报原因,尽可能描述详细。 </div> <div class='ui form appeal-form'> <div class='inline field'> <label class='left-part appeal-type-wrap'> 举报类型 </label> <div class='ui dropdown selection' id='appeal-comments-types'> <div class='text default'> 请选择举报类型 </div> <i class='dropdown icon'></i> <div class='menu'></div> </div> </div> <div class='inline field'> <label class='left-part'> 举报原因 </label> <textarea class='appeal-reason' id='appeal-comment-reason' name='msg' placeholder='请说明举报原因' rows='3'></textarea> </div> <div class='ui message callback-msg hide'></div> <div class='ui small error text message exceeded-size-tip'></div> </div> </div> <div class='actions'> <div class='ui button blank cancel'> 取消 </div> <div class='ui orange icon button disabled ok' id='complaint-comment-confirm'> 发送 </div> </div> </div> <script> var $complaintCommentsModal = $('#landing-comments-complaint-modal'), $complainCommentType = $complaintCommentsModal.find('#appeal-comments-types'), $complaintModalTip = $complaintCommentsModal.find('.callback-msg'), $complaintCommentsContent = $complaintCommentsModal.find('.appeal-reason'), $complaintCommentBtn = $complaintCommentsModal.find('#complaint-comment-confirm'), complaintSending = false, initedCommentsType = false; function initCommentsTypeList() { if (!initedCommentsType) { $.ajax({ url: "/appeals/fetch_types", method: 'get', data: {'type': 'comment'}, success: function (data) { var result = ''; for (var i = 0; i < data.length; i++) { result = result + "<div class='item' data-value='" + data[i].id + "'>" + data[i].name + "</div>"; } $complainCommentType.find('.menu').html(result); } }); $complainCommentType.dropdown({showOnFocus: false}); initedCommentsType = true; } } $complainCommentType.on('click', function() { $complaintCommentsModal.modal({ autofocus: false, onApprove: function() { return false; }, onHidden: function() { restoreCommonentDefault(); } }).modal('show'); }); $complaintCommentsContent.on('change keyup', function(e) { var content = $(this).val(); if ($.trim(content).length > 0 && $complainCommentType.dropdown('get value').length > 0 ) { $complaintCommentBtn.removeClass('disabled'); return; } $complaintCommentBtn.addClass('disabled'); }); $complainCommentType.dropdown({ showOnFocus: false, onChange: function(value, text, $selectedItem) { if (value.length > 0 && $.trim($complaintCommentsContent.val()).length > 0) { $complaintCommentBtn.removeClass('disabled'); return } $complaintCommentBtn.addClass('disabled'); } }); function restoreCommonentDefault() { $complainCommentType.dropdown('restore defaults'); $complaintCommentsContent.val(''); $('.exceeded-size-tip').text('').hide(); $complaintModalTip.text('').hide(); setTimeout(function() { setCommentSendTip(false); }, 1500); } $complaintCommentBtn.on('click',function(e){ var reason = $complaintCommentsContent.val(); var appealableId = $('#landing-comments-complaint-modal').attr('data-id'); if (complaintSending) { return; } var appealType = $complainCommentType.dropdown('get value'); var formData = new FormData(); formData.append('appeal_type_id', appealType); formData.append('reason', reason); formData.append('appeal_type','Note'); formData.append('target_id',appealableId); $.ajax({ type: 'POST', url: "/appeals", cache: false, contentType: false, processData: false, data: formData, beforeSend: function() { setCommentSendStatus(true); }, success: function(res) { if (res.status == 200) { setCommentSendTip(true); setTimeout(function() { $complaintCommentsModal.modal('hide'); restoreCommonentDefault(); }, 3000); } setCommentSendStatus(false); }, error: function(err) { showCommonTips(err.responseJSON.message, 'error'); setCommentSendStatus(false); } }) }); function showCommonTips(text, type) { $complaintModalTip.text(text).show(); if (type == 'error') { $complaintModalTip.removeClass('success').addClass('error'); } else { $complaintModalTip.removeClass('error').addClass('success'); } } function setCommentSendStatus(value) { complaintSending = value; if (complaintSending) { $complaintCommentBtn.addClass('loading'); $complaintCommentsContent.attr('readonly', true); $complainCommentType.attr('readonly', true); } else { $complaintCommentBtn.removeClass('loading'); $complaintCommentsContent.attr('readonly', false); $complainCommentType.attr('readonly', false); } } function setCommentSendTip(value) { if (value) { $('.appeal-success-tip').removeClass('hide'); $('.appeal-tip').addClass('hide'); $('.appeal-form').addClass('hide'); $('#landing-comments-complaint-modal .actions').addClass('hide'); } else { $('.appeal-success-tip').addClass('hide'); $('.appeal-tip').removeClass('hide'); $('.appeal-form').removeClass('hide'); $('#landing-comments-complaint-modal .actions').removeClass('hide'); } } </script> <div class='ui small modal' id='misjudgment_appeal_modal'> <i class='close icon'></i> <div class='header dividing ui'> 误判申诉 </div> <div class='content'> <p>此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。</p> <p>如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。</p> <div class='buttons'> <div class='ui button blank cancel'>取消</div> <div class='ui button orange submit'>提交</div> </div> </div> </div> <style> #misjudgment_appeal_modal .buttons { float: right; margin-top: 30px; margin-bottom: 20px; } #misjudgment_appeal_modal .buttons .cancel { margin-right: 20px; } </style> <script> var $misjudgmentAppealModal = $('#misjudgment_appeal_modal'); $('.cancel').on('click',function(){ $misjudgmentAppealModal.modal('hide'); }); var $jsSubmitAppeal = $misjudgmentAppealModal.find('.submit') $jsSubmitAppeal.on('click', function(e) { e.preventDefault(); $(this).addClass('loading').addClass('disabled'); var type = $(this).attr('data-type'); var id = $(this).attr('data-id'); var projectId = $(this).attr('data-project-id'); var appealType = $(this).attr('data-appeal-type'); $.ajax({ type: "PUT", url: "/misjudgment_appeal", data: { type: type, id: id, project_id: projectId, appeal_type: appealType }, success: function(data) { Flash.info('提交成功'); $jsSubmitAppeal.removeClass('loading'); $misjudgmentAppealModal.modal('hide'); location.reload() }, error: function(e) { Flash.error('提交失败:'+e.responseText); $jsSubmitAppeal.removeClass('loading').removeClass('disabled'); location.reload() } }); }) </script> </div> <script> "use strict"; $('.js-check-star').checkbox('set unchecked') </script> </div> <script> (function() { $(function() { Tree.init(); return TreeCommentActions.init(); }); }).call(this); </script> </div> </div> <div class='four wide column' style=''> <div class='project__right-side'> <div class='side-item intro'> <div class='header'> <h4>简介</h4> </div> <div class='content'> <span class='git-project-desc-text'>Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架</span> <a class='hide spread' href='javascript:void(0);'> 展开 <i class='caret down icon'></i> </a> <a class='retract hide' href='javascript:void(0);'> 收起 <i class='caret up icon'></i> </a> <div class='intro-list'> <div class='blank d-flex d-flex-between dropdown item js-project-label_show label-list-line-feed project-label-list ui' data-labels='[]' data-url='/salvo-rs/salvo/update_description'> <div class='mixed-label'> </div> <div class='default'>暂无标签</div> </div> <div class='item'> <i class='iconfont icon-link'></i> <span class='git-project-homepage'> <a rel="nofollow" id="homepage" target="_blank" href="https://gitee.com/link?target=https%3A%2F%2Fsalvo.rs">https://salvo.rs</a> </span> </div> <div class='item'> <i class='iconfont icon-tag-program'></i> <span class='summary-languages'> Rust <span class='text-muted'> 等 3 种语言 <i class='icon dropdown'></i> </span> </span> <div class='ui popup summary-languages-popup'> <div class='row'> <div class='lang'> <a href="/explore/all?lang=Rust">Rust</a> </div> <div class='lang-bar'> <div class='bar' style='width: 99.8%;'></div> </div> <a class="percentage" href="/explore/all?lang=Rust">99.8%</a> </div> <div class='row'> <div class='lang'> <a href="/explore/all?lang=Shell">Shell</a> </div> <div class='lang-bar'> <div class='bar' style='width: 0.1%;'></div> </div> <a class="percentage" href="/explore/all?lang=Shell">0.1%</a> </div> <div class='row'> <div class='lang'> <a href="/explore/all?lang=HTML">HTML</a> </div> <div class='lang-bar'> <div class='bar' style='width: 0.1%;'></div> </div> <a class="percentage" href="/explore/all?lang=HTML">0.1%</a> </div> </div> </div> <div class='item box-licence'> <i class='iconfont icon-licence'></i> <span id='license-popup'> Apache-2.0 </span> <div class='ui popup dark'>使用 Apache-2.0 开源许可协议</div> </div> <!-- - page = @project.page --> <!-- - if page&.status? --> <!-- .item --> <!-- %i.iconfont.icon-giteepage --> <!-- Pages: --> <!-- = link_to page.domain_url, page.domain_url, target: '_blank' --> </div> </div> <div class='content intro-form'> <div class='ui small input'> <textarea name='project[description]' placeholder='描述' rows='5'></textarea> </div> <div class='ui small input'> <input data-regex-value='(^$)|(^(http|https):\/\/(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).*)|(^(http|https):\/\/[a-zA-Z0-9]+([_\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,10}(:[0-9]{1,10})?(\?.*)?(\/.*)?$)' name='project[homepage]' placeholder='主页(eg: https://gitee.com)' type='text'> </div> <button class='ui orange button mt-1 btn-save'> 保存更改 </button> <div class='ui blank button btn-cancel-edit'> 取消 </div> </div> </div> <div class='side-item release'> <div class='header'> <h4>发行版</h4> </div> <div class='content'> <span class='text-muted'> 暂无发行版 </span> </div> </div> <div class='side-item compass'> <div class='header mb-1 d-align-center'> <h4 class='limit-length compass-label mr-1'></h4> <a class="ui link button compass-qa" href="https://compass.gitee.com/zh/docs/dimensions-define"><i class='iconfont icon-help-circle'></i> </a></div> <div class='content'> <div class='compass-echart-container'> <div data-url='/salvo-rs/salvo/compass/chart_data' id='compass-metrics'> <div class='wrap'></div> <div class='ui popup radar-popup'> <h4 class='title'>开源评估指数源自 OSS-Compass 评估体系,评估体系围绕以下三个维度对项目展开评估:</h4> <div class='project-radar-list'> <div class='descript-contianer'> <div class='descript-title'> <p class='mb-1'>1. 开源生态</p> <ul class='mb-1 mt-1'> <li>生产力:来评估开源项目输出软件制品和开源价值的能力。</li> <li>创新力:用于评估开源软件及其生态系统的多样化程度。</li> <li>稳健性:用于评估开源项目面对多变的发展环境,抵御内外干扰并自我恢复的能力。</li> </ul> <p>2. 协作、人、软件</p> <ul> <li>协作:代表了开源开发行为中协作的程度和深度。</li> <li>人:观察开源项目核心人员在开源项目中的影响力,并通过第三方视角考察用户和开发者对开源项目的评价。</li> <li>软件:从开源项目对外输出的制品评估其价值最终落脚点。也是开源评估最“古老”的主流方向之一“开源软件” 的具体表现。</li> </ul> <p>3. 评估模型</p> <ul> 基于“开源生态”与“协作、人、软件”的维度,找到与该目标直接或间接相关的可量化指标,对开源项目健康与生态进行量化评估,最终形成开源评估指数。 </ul> </div> </div> </div> <div class='finaltime'></div> </div> <div class='legend-box ml-1'> <div class='dimension d-flex'></div> <div class='compass-type d-flex'></div> </div> </div> </div> <script src="/static/javascripts/echarts.min.js"></script> <script src="/static/javascripts/echarts-gl.min.js"></script> <script src="https://cn-assets.gitee.com/assets/skill_radar/rep_compass_chart-a170f1ecfff8cd448229c0a3b82b074a.js"></script> </div> </div> <div class='side-item contrib' data-url='/salvo-rs/salvo/contributors_count?ref=main' id='contributor'> <div class='header'> <h4> 贡献者 <span class='text-muted' id='contributor-count'></span> </h4> <a class="ui link button pull-right" href="/salvo-rs/salvo/contributors?ref=main">全部</a> </div> <div class='content' id='contributor-list'></div> <div class='ui active centered inline loader' id='contributor-loader'></div> </div> <div class='side-item events' data-url='/salvo-rs/salvo/events.json' id='proj-events'> <div class='header'> <h4>近期动态</h4> </div> <div class='content'> <div class='ui comments' id='event-list'></div> <a class="loadmore hide" href="javascript:void(0);">加载更多 <i class='icon dropdown'></i> </a><center> <div class='text-muted nomore hide'>不能加载更多了</div> <div class='ui inline loader active'></div> </center> </div> </div> </div> <div class='ui modal tiny' id='edit-project-description'> <i class='iconfont icon-close close'></i> <div class='header'>编辑仓库简介</div> <div class='content'> <div class='item mb-2'> <div class='title label'>简介内容</div> <div class='ui small input'> <textarea maxlength='200' name='project[description]' placeholder='描述' rows='5'>Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架</textarea> </div> </div> <div class='item mb-2'> <div class='title label'>主页</div> <div class='ui small input'> <input data-regex-value='(^$)|(^(http|https):\/\/(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).*)|(^(http|https):\/\/[a-zA-Z0-9]+([_\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,10}(:[0-9]{1,10})?(\?.*)?(\/.*)?$)' name='project[homepage]' placeholder='主页(eg: https://gitee.com)' type='text' value='https://salvo.rs'> </div> </div> </div> <div class='actions'> <button class='ui button blank cancel'>取消</button> <button class='ui button orange btn-save'>保存更改</button> </div> </div> <style> #license-popup { color: #005980; cursor: pointer; } </style> <script> window.gon.projectRightSide = { homepage: "https://salvo.rs", description: "Salvo 是一个极其简单易用却又功能强大的 Rust Web 后端框架", url: '/salvo-rs/salvo/update_description', i18n: { invalidHomepage: '不是有效的 http 地址', descriptionLimitExceeded: '简介长度不得超过%{limit}个字符', noDescription: '暂无描述', noPermission: '无权限操作!', requestError: '修改发生错误,请稍后重试!' } } window.gon.cloneArrSelectedLabel = [] || [] $(function () { var $editModal = $('#edit-project-description') $editModal.modal({ onShow: function () { window.globalUtils.getFocus($editModal.find('textarea')) } }) $('.project__right-side').on('click', '.header .btn-edit', function () { $editModal.modal('show') }) $('#license-popup').popup({ position: 'bottom center', lastResort: 'bottom center' }) $('.js-project-label_show').projectLabel({ i18n: { empty: "标签名不能为空", verify: "标签名只允许包含中文、字母、数字或者中划线(-),不能以中划线开头,且长度少于35个字符", max: "最多选择 5 个标签" } }) }) </script> </div> <div class='project-right-side-contaner' id='code-parsing'> <div class='d-flex-between mb-2'> <div class='title fs-16 d-align-center'> <img class='mr-1' height='32' src='/static/images/mjc_icon@2x.png' width='32'> <span class='ai-file-name'>马建仓 AI 助手</span> </div> <div> <i class='iconfont icon-close close gitee-icon-close'></i> </div> </div> <div class='code-parsing-content'> <div class='sub_title'></div> <div class='markdown-body'></div> <div class='bottom-content'> <div class='js-code-parsing-img'></div> <div class='ai_code_btns_simple'> <div class='ai_code_btns_simple_container'> <div class='mr-1 test-more'>尝试更多</div> <div class='btn_box' data-text='代码解读' data-value='parsing'> <div class='btn_box_title'>代码解读</div> </div> <div class='btn_box' data-text='代码找茬' data-value='analysis'> <div class='btn_box_title'>代码找茬</div> </div> <div class='btn_box' data-text='代码优化' data-value='optimize'> <div class='btn_box_title'>代码优化</div> </div> </div> </div> </div> </div> <div class='skeleton'> <div class='line line1'></div> <div class='line line2'></div> <div class='line line3'></div> <div class='line line4'></div> <div class='line line1'></div> <div class='line line2'></div> <div class='line line3'></div> <div class='line line4'></div> <div class='line line1'></div> <div class='line line2'></div> <div class='line line3'></div> <div class='line line4'></div> </div> <div class='resize-handle'> <div class='resize-handle-line'></div> </div> <script src="/static/javascripts/markdown-it.min.js"></script> <script src="https://cn-assets.gitee.com/assets/ai_code_parsing/app-667254dc80e793cb047ec2e07574f422.js"></script> <script> $(function() { var maxWidthPercentage = 0.5; $("#code-parsing").resizable({ handles: 'e, w', // 通过左边调整大小 minWidth: 350, // 设置 代码解析框 的最小宽度 resize: function(event, ui) { var parentWidth = $(this).parent().width(); var newWidthDiv2 = ui.size.width; var newWidthDiv1 = parentWidth - newWidthDiv2; // 计算最大宽度 var maxWidthDiv2 = parentWidth * maxWidthPercentage; // 确保 代码解析框 不超过最大宽度 newWidthDiv2 = Math.min(newWidthDiv2, maxWidthDiv2); // 确保 文件详情 至少有最小宽度 newWidthDiv1 = Math.max(parentWidth - newWidthDiv2, 750); var percentageCode = (newWidthDiv2 / parentWidth) * 100; var percentageProject = (newWidthDiv1 / parentWidth) * 100; $('#code-parsing').css('width',percentageCode+"%") $('.git-project-content-wrapper').find('#sixteen').attr('style', 'width: ' + percentageProject + '% !important;'); $('.right-wrapper').attr('style', 'width: ' + percentageProject + '% !important;'); $('.project-conter-container').attr('style', 'width: ' + percentageProject + '% !important;'); } }); }) </script> </div> </div> </div> <style> .team-member-checkbox .ui.radio.checkbox.checked label:after { top: 7px !important; } </style> <script> function scrollToReadmeBox() { var readmeBox = document.getElementById('git-readme'); if (readmeBox) { const topPos = readmeBox.offsetTop; window.scrollTo({ top: topPos, behavior: "smooth" }); } } $(".box-licence").click(function(event) { $('.lisence-content').show() $('.lisence-edit').show() $('.readme-content').hide() $('.readme-edit').hide() $('#git-readme').removeClass('unshow') $('.file_title_license').addClass('active-title') $('.file_title_readme').removeClass('active-title') scrollToReadmeBox() }); // 防止二次挂载 if (false) { window.gon.tree_left_side_loaded = true; } </script> <link rel="stylesheet" media="all" href="https://cn-assets.gitee.com/assets/markdown_preview-001478f1b12f2725f1b1f76f36b9ce4e.css" /> <script src="https://cn-assets.gitee.com/assets/markdown_preview-772822b3442a45aaf94af83c38fdbaf4.js"></script> <script src="https://cn-assets.gitee.com/webpacks/markdown_render-13d3d4c9beaea8f7006b.bundle.js" defer="defer"></script> </div> <script> (function() { var donateModal; Gitee.modalHelper = new GiteeModalHelper({ alertText: '提示', okText: '确定' }); donateModal = new ProjectDonateModal({ el: '#project-donate-modal', alipayUrl: '/salvo-rs/salvo/alipay', wepayUrl: '/salvo-rs/salvo/wepay', nameIsBlank: '名称不能为空', nameTooLong: '名称过长(最多为 36 个字符)', modalHelper: Gitee.modalHelper }); if (null === 'true') { donateModal.show(); } $('#project-donate').on('click', function() { return donateModal.show(); }); }).call(this); </script> <script> Tree.initHighlightTheme('white') </script> </div> <div class='gitee-project-extension'> <div class='extension lang'>Rust</div> <div class='extension public'>1</div> <div class='extension https'>https://gitee.com/salvo-rs/salvo.git</div> <div class='extension ssh'>git@gitee.com:salvo-rs/salvo.git</div> <div class='extension namespace'>salvo-rs</div> <div class='extension repo'>salvo</div> <div class='extension name'>salvo</div> <div class='extension branch'>main</div> </div> <script> $(function() { GitLab.GfmAutoComplete.dataSource = "/salvo-rs/salvo/autocomplete_sources" GitLab.GfmAutoComplete.Emoji.assetBase = '/assets/emoji' GitLab.GfmAutoComplete.setup(); }); </script> <footer id='git-footer-main'> <div class='ui container'> <div class='logo-row'> <a href="https://gitee.com"><img alt='Gitee - 基于 Git 的代码托管和研发协作平台' class='logo-img' src='/static/images/logo-black.svg?t=158106666'> </a></div> <div class='name-important'> 深圳市奥思网络科技有限公司版权所有 </div> <div class='ui two column grid d-flex-center'> <div class='eight wide column git-footer-left'> <div class='ui four column grid' id='footer-left'> <div class='column'> <div class='ui link list'> <div class='item'> <a class="item" href="/all-about-git">Git 大全</a> </div> <div class='item'> <a class="item" rel="nofollow" href="https://help.gitee.com/learn-Git-Branching/">Git 命令学习</a> </div> <div class='item'> <a class="item" rel="nofollow" href="https://copycat.gitee.com/">CopyCat 代码克隆检测</a> </div> <div class='item'> <a class="item" href="/appclient">APP与插件下载</a> </div> </div> </div> <div class='column'> <div class='ui link list'> <div class='item'> <a class="item" href="/gitee_reward">Gitee Reward</a> </div> <div class='item'> <a class="item" href="/gitee-stars">Gitee 封面人物</a> </div> <div class='item'> <a class="item" href="/gvp">GVP 项目</a> </div> <div class='item'> <a class="item" rel="nofollow" href="https://blog.gitee.com/">Gitee 博客</a> </div> <div class='item'> <a class="item" href="/enterprises#nonprofit-plan">Gitee 公益计划</a> </div> <div class='item'> <a class="item" href="https://gitee.com/features/gitee-go">Gitee 持续集成</a> </div> </div> </div> <div class='column'> <div class='ui link list'> <div class='item'> <a class="item" href="/api/v5/swagger">OpenAPI</a> </div> <div class='item'> <a class="item" href="https://gitee.com/oschina/mcp-gitee">MCP Server</a> </div> <div class='item'> <a class="item" href="https://help.gitee.com">帮助文档</a> </div> <div class='item'> <a class="item" href="/self_services">在线自助服务</a> </div> <div class='item'> <a class="item" href="/help/articles/4378">更新日志</a> </div> </div> </div> <div class='column'> <div class='ui link list'> <div class='item'> <a class="item" href="/about_us">关于我们</a> </div> <div class='item'> <a class="item" rel="nofollow" href="https://www.oschina.net/news/131099/oschina-hiring">加入我们</a> </div> <div class='item'> <a class="item" href="/terms">使用条款</a> </div> <div class='item'> <a class="item" href="/oschina/git-osc/issues">意见建议</a> </div> <div class='item'> <a class="item" href="/links.html">合作伙伴</a> </div> </div> </div> </div> </div> <div class='eight wide column right aligned followus git-footer-right'> <div class='qrcode mr-1'> <div class='qrcode-box'> <img alt="技术交流QQ群" src="https://cn-assets.gitee.com/assets/contact_qr-5e2c2a8da453396590e56a545bce4974.jpg" /> </div> <p class='mt-1 mini_app-text'>技术交流QQ群</p> </div> <div class='qrcode'> <div class='qrcode-box'> <img alt="微信服务号" class="weixin-qr" src="https://cn-assets.gitee.com/assets/qrcode-weixin@2x-b74cc97a2ea80123ea53a737f709836d.png" /> </div> <p class='mt-1 weixin-text'>微信服务号</p> </div> <div class='phone-and-qq column'> <div class='ui list official-support-container'> <div class='item'></div> <div class='item mail-and-zhihu'> <a rel="nofollow" href="mailto: client@oschina.cn"><i class='iconfont icon-msg-mail'></i> <span id='git-footer-email'>client#oschina.cn</span> </a></div> <div class='item tel'> <a> <i class='iconfont icon-tel'></i> <span>企业版在线使用:400-606-0201</span> </a> </div> <div class='item tel'> <a class='d-flex'> <i class='iconfont icon-tel mt-05 mr-05'></i> <span>专业版私有部署:</span> <div> <div>13670252304</div> <div>13352947997</div> </div> </a> </div> </div> </div> </div> </div> </div> <div class='bottombar'> <div class='ui container'> <div class='ui d-flex d-flex-between'> <div class='seven wide column partner d-flex'> <div class='open-atom d-flex-center'> <img class="logo-openatom mr-1" alt="开放原子开源基金会" src="https://cn-assets.gitee.com/assets/logo-openatom-d083391cc8a54e283529f3fc11cc38ca.svg" /> <a target="_blank" rel="nofollow" href="https://www.openatom.org/">开放原子开源基金会</a> <div class='sub-title ml-1'>合作代码托管平台</div> </div> <div class='report-12377 d-flex-center ml-3'> <img class="report-12377__logo mr-1" alt="违法和不良信息举报中心" src="https://cn-assets.gitee.com/assets/12377@2x-1aa42ed2d2256f82a61ecf57be1ec244.png" /> <a target="_blank" rel="nofollow" href="https://12377.cn">违法和不良信息举报中心</a> </div> <div class='copyright ml-3'> <a rel="nofollow" href="http://beian.miit.gov.cn/">粤ICP备12009483号</a> </div> </div> <div class='nine wide column right aligned'> <i class='icon world'></i> <a href="/language/zh-CN">简 体</a> / <a href="/language/zh-TW">繁 體</a> / <a href="/language/en">English</a> </div> </div> </div> </div> </footer> <script> var officialEmail = $('#git-footer-email').text() $('#git-footer-main .icon-popup').popup({ position: 'bottom center' }) $('#git-footer-email').text(officialEmail.replace('#', '@')) window.gon.popover_card_locale = { follow:"关注", unfollow:"已关注", gvp_title: "GVP - Gitee 最有价值开源项目", project: "项目", org: "开源组织", member: "", author: "作者", user_blocked: "该用户已被屏蔽或已注销", net_error: "网络错误", unknown_exception: "未知异常" } window.gon.select_message = { placeholder: "请输入个人空间地址或完整的邮箱地址" } </script> <script src="https://cn-assets.gitee.com/webpacks/popover_card-ca6b9a20ba5353733d61.bundle.js"></script> <link rel="stylesheet" media="all" href="https://cn-assets.gitee.com/webpacks/css/gitee_nps-ae0dbee40f6ddc72015a.css" /> <script src="https://cn-assets.gitee.com/webpacks/gitee_nps-30c2a3673b61c539fcf7.bundle.js"></script> <script src="https://cn-assets.gitee.com/webpacks/gitee_icons-0340f9dc8fc1dfbb9937.bundle.js"></script> <div class='side-toolbar'> <div class='button toolbar-help'> <i class='iconfont icon-help'></i> </div> <div class='ui popup left center dark'>点此查找更多帮助</div> <div class='toolbar-help-dialog'> <div class='toolbar-dialog-header'> <h3 class='toolbar-dialog-title'>搜索帮助</h3> <form class="toolbar-help-search-form" action="/help/load_keywords_data" accept-charset="UTF-8" method="get"><input name="utf8" type="hidden" value="✓" /> <div class='ui icon input fluid toolbar-help-search'> <input name='keywords' placeholder='请输入产品名称或问题' type='text'> <i class='icon search'></i> </div> </form> <i class='iconfont icon-close toolbar-dialog-close-icon'></i> </div> <div class='toolbar-dialog-content'> <div class='toolbar-help-hot-search'> <div class='toolbar-roll'> <a class="init active" title="Git 命令在线学习" href="https://oschina.gitee.io/learn-git-branching/?utm_source==gitee-help-widget"><i class='Blue icon icon-command iconfont'></i> <span>Git 命令在线学习</span> </a><a class="init " title="如何在 Gitee 导入 GitHub 仓库" href="https://gitee.com/help/articles/4261?utm_source==gitee-help-widget"><i class='icon icon-clipboard iconfont orange'></i> <span>如何在 Gitee 导入 GitHub 仓库</span> </a></div> <div class='toolbar-list'> <div class='toolbar-list-item'> <a href="/help/articles/4114">Git 仓库基础操作</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4166">企业版和社区版功能对比</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4191">SSH 公钥设置</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4194">如何处理代码冲突</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4232">仓库体积过大,如何减小?</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4279">如何找回被删除的仓库数据</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4283">Gitee 产品配额说明</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4284">GitHub仓库快速导入Gitee及同步更新</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4328">什么是 Release(发行版)</a> </div> <div class='toolbar-list-item'> <a href="/help/articles/4354">将 PHP 项目自动发布到 packagist.org</a> </div> </div> </div> <div class='toolbar-help-search-reseult'></div> </div> </div> <script> var opt = { position: 'left center'}; var $helpSideToolbar = $('.button.toolbar-help'); var $toolbarRoll = $('.toolbar-roll'); $(function() { if (false) { $helpSideToolbar.popup(opt).popup({lastResort:'left center'}) } else { $helpSideToolbar.popup({lastResort:'left center'}).popup('show', opt); setTimeout(function() { $helpSideToolbar.popup('hide', opt); }, 3000); } if ($toolbarRoll.length) { setInterval(function() { var $nextActiveLink = $toolbarRoll.find('a.active').next(); if (!$nextActiveLink.length) { $nextActiveLink = $toolbarRoll.find('a:first-child'); } $nextActiveLink.attr('class', 'active').siblings().removeClass('active init'); }, 5000); } }) </script> <div class='popup button' id='home-comment'> <i class='iconfont icon-comment'></i> </div> <div class='ui popup dark'>评论</div> <div class='toolbar-appeal popup button'> <i class='iconfont icon-report'></i> </div> <div class='ui popup dark'> 仓库举报 </div> <script> $('.toolbar-appeal').popup({ position: 'left center' }); </script> <div class='button gotop popup' id='gotop'> <i class='iconfont icon-top'></i> </div> <div class='ui popup dark'>回到顶部</div> </div> <div class='form modal normal-modal tiny ui' id='unlanding-complaint-modal'> <i class='iconfont icon-close close'></i> <div class='header'> 登录提示 </div> <div class='container actions'> <div class='content'> 该操作需登录 Gitee 帐号,请先登录后再操作。 </div> <div class='ui orange icon large button ok'> 立即登录 </div> <div class='ui button blank cancel'> 没有帐号,去注册 </div> </div> </div> <script> var $elm = $('.toolbar-appeal'); $elm.on('click', function() { var modals = $("#unlanding-complaint-modal.normal-modal"); if (modals.length > 1) { modals.eq(0).modal('show'); } else { modals.modal('show'); } }) $("#unlanding-complaint-modal.normal-modal").modal({ onDeny: function() { window.location.href = "/signup?from="; }, onApprove: function() { window.location.href = "/login?from="; } }) </script> <style> .side-toolbar .bdsharebuttonbox a { font-size: 24px; color: white !important; opacity: 0.9; margin: 6px 6px 0px 6px; background-image: none; text-indent: 0; height: auto; width: auto; } </style> <style> #udesk_btn a { margin: 0px 20px 167px 0px !important; } </style> <script> (function() { $('#project-user-message').popup({ position: 'left center' }); }).call(this); </script> <script> Gitee.initSideToolbar({ hasComment: true, commentUrl: '/salvo-rs/salvo#tree_comm_title' }) </script> <script> (function() { this.__gac = { domain: 'www.oschina.net' }; }).call(this); </script> <script src="https://cn-assets.gitee.com/assets/bdstatic/app-070a9e339ac82bf2bf7ef20375cd4121.js"></script> <script src="https://cn-assets.gitee.com/webpacks/build_status-7effc7b22ddf8e90b34c.bundle.js"></script> <script src="https://cn-assets.gitee.com/webpacks/scan_status-7a19032286b03591b228.bundle.js"></script> <script src="https://cn-assets.gitee.com/webpacks/mermaid_render-669e055f02596b5d8886.bundle.js"></script> <script src="https://cn-assets.gitee.com/webpacks/check_runs-19ec228348982570119a.bundle.js"></script> </body> </html>