Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b31d3229a | ||
|
|
591e3598e6 | ||
|
|
0fbc727e0c | ||
|
|
e91f2d40d4 | ||
|
|
097c8f7676 | ||
|
|
150eec4585 | ||
|
|
ead80a58d7 | ||
|
|
a960361348 | ||
|
|
c80aff05bc | ||
|
|
694c5e35bd | ||
|
|
c236378f01 | ||
|
|
3e75e7c6b6 | ||
|
|
2bff3ecf9d | ||
|
|
6651db33a7 | ||
|
|
e7bccbaf6e | ||
|
|
bc91a9834f | ||
|
|
81edced29d | ||
|
|
1ef873dcc3 | ||
|
|
8d49f7045d | ||
|
|
dad3deb482 | ||
|
|
cd13c08a2f | ||
|
|
045ec5f44e | ||
|
|
094211f28f | ||
|
|
93c9739292 | ||
|
|
3a468d6af5 | ||
|
|
77adc02bd9 | ||
|
|
7ccea91046 | ||
|
|
6f264ce8d3 | ||
|
|
b108b6feff | ||
|
|
1692fff007 | ||
|
|
4f503d358a | ||
|
|
6c44959c49 | ||
|
|
e0fe48bebf | ||
|
|
f23da8a9b9 | ||
|
|
fcdcf124d5 | ||
|
|
fea6a0efa9 | ||
|
|
9f22ad048c | ||
|
|
ac7c858520 | ||
|
|
5d0a7f7a90 | ||
|
|
d4eeee14da | ||
|
|
09019fe38b | ||
|
|
bec7c51e9e | ||
|
|
9aceca264c | ||
|
|
8ef0770db2 | ||
|
|
ef8d0eefc4 | ||
|
|
1c67f1acc8 | ||
|
|
8e5d3a11a9 | ||
|
|
55fd344735 | ||
|
|
5a9a24d01f | ||
|
|
e59fe81244 | ||
|
|
ed6795b4cc | ||
|
|
d65df2a57f | ||
|
|
574e600974 | ||
|
|
e1d0b4058b | ||
|
|
397091015b | ||
|
|
341806a3bd | ||
|
|
d25144fe9d | ||
|
|
7a980b4b34 | ||
|
|
2c75cf70d5 | ||
|
|
24e55faeb8 | ||
|
|
78e73497b0 | ||
|
|
12e6ede7be | ||
|
|
d91b651273 | ||
|
|
1fb34fc49a | ||
|
|
39e93fad1c | ||
|
|
413453a1e7 | ||
|
|
9312ef2f48 | ||
|
|
f8d5861e5a | ||
|
|
99b09d8597 | ||
|
|
c9c9544d22 | ||
|
|
e7a7976774 | ||
|
|
a215268a10 | ||
|
|
88bfec45cd | ||
|
|
6d1c328402 | ||
|
|
2259c9984e | ||
|
|
50373094ad | ||
|
|
5930692edc | ||
|
|
58d57ee33a | ||
|
|
9c987a4bc7 | ||
|
|
ef5269f634 | ||
|
|
9166f87178 | ||
|
|
8963fdd7bf | ||
|
|
77f94765c6 | ||
|
|
34e08e7918 | ||
|
|
dc4fb861ef | ||
|
|
5e86861510 | ||
|
|
088d502d4b | ||
|
|
93ccb1e1f6 | ||
|
|
4abcbc03ae | ||
|
|
44487b3a8b | ||
|
|
f4dbf3bde5 | ||
|
|
bcdd0be188 | ||
|
|
c0b0bc6547 | ||
|
|
09505941db | ||
|
|
162f76a737 | ||
|
|
e2bcabff20 | ||
|
|
eaac017f82 | ||
|
|
ed838e6052 | ||
|
|
8814a0bb6f | ||
|
|
f8c63f7d86 | ||
|
|
124ec73684 | ||
|
|
2dbe8cd35e | ||
|
|
f896a21a9d | ||
|
|
e59e1da238 | ||
|
|
34c830d7cd | ||
|
|
3df9012906 | ||
|
|
6850295f78 | ||
|
|
ced4a0f4a0 | ||
|
|
045577348f | ||
|
|
b6297f289e | ||
|
|
2becd43537 | ||
|
|
1b58308ba6 | ||
|
|
37cbd0d4dd | ||
|
|
9d51bc9ea3 | ||
|
|
6504339a03 |
1
.gitignore
vendored
@@ -2,5 +2,6 @@
|
||||
.DS_Store
|
||||
*.zip
|
||||
*.tar
|
||||
*.tar.gz
|
||||
build.sh
|
||||
compose.yml
|
||||
|
||||
155
CHANGELOG.md
@@ -1,17 +1,164 @@
|
||||
# SAFELINE-CE CHANGELOG
|
||||
|
||||
### [3.14.0] - 2023-11-30
|
||||
## [4.1.1] - 2024-01-11
|
||||
|
||||
#### 新增
|
||||
### 修复
|
||||
|
||||
- 修复 IP 组在线订阅失败时会保存错误内容的问题
|
||||
|
||||
## [4.1.0] - 2024-01-11
|
||||
|
||||
### 新增
|
||||
|
||||
- 拦截日志一键复制为 cURL ([#531](https://github.com/chaitin/SafeLine/issues/531))
|
||||
|
||||
### 优化
|
||||
|
||||
- IP 组若为在线订阅,显示更新时间([#574](https://github.com/chaitin/SafeLine/issues/574))
|
||||
- 优化 safeline-fvm 容器重启速度,重启时间减少 10s
|
||||
- 优化 safeline-mgt 容器镜像层数,从 39 层下降到 24 层
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复日志列表 IP 来源地区未翻译国家编号的问题([#578](https://github.com/chaitin/SafeLine/issues/578))
|
||||
- 修复英文翻译问题([#591](https://github.com/chaitin/SafeLine/issues/591))
|
||||
- 修复雷池管理后台证书更新后未自动重启问题
|
||||
|
||||
## [4.0.2] - 2024-01-06
|
||||
|
||||
### 修复
|
||||
|
||||
- 管理后台 mgt 启动时提示证书异常
|
||||
- 统计页面中 QPS 数据统计方法由窗口时间(5s)改为按秒计算平均值
|
||||
|
||||
## [4.0.1] - 2024-01-05
|
||||
|
||||
### 修复
|
||||
|
||||
- safeline-luigi 容器打印与功能无关的错误日志
|
||||
- 统计页面中不显示 QPS 数据
|
||||
|
||||
## [4.0.0] - 2024-01-05
|
||||
|
||||
### 新增
|
||||
|
||||
- 完整支持 **流式语义分析检测**,包含 协议解析、解码、模式匹配 三个阶段的改造,解决经典 “大包绕过” 问题
|
||||
- IP 组支持通过 URL 在线订阅内容([#414](https://github.com/chaitin/SafeLine/issues/414))
|
||||
- 新增 “搜索引擎爬虫 IP”,包含 Google、Bing、百度、360 的爬虫 IP([#374](https://github.com/chaitin/SafeLine/issues/374)、[#399](https://github.com/chaitin/SafeLine/issues/399))
|
||||
- 出厂预置 “搜索引擎爬虫白名单” 和 “长亭社区恶意 IP 情报黑名单”,方便配置
|
||||
|
||||
### 优化
|
||||
- 支持类 ChatGPT 应用的流式 HTTP 响应([#513](https://github.com/chaitin/SafeLine/issues/513))
|
||||
- 在 证书管理 编辑证书后,若证书正被站点使用,自动重启 nginx 使新证书生效([#534](https://github.com/chaitin/SafeLine/issues/534))
|
||||
- safeline-fvm 容器体积减小 60%
|
||||
- safeline-mgt 服务减少宿主机文件依赖
|
||||
- safeline-mgt 服务日志全部写入 docker 标准输出,默认仅输出启动信息和错误日志,减小磁盘占用
|
||||
- safeline-mgt 服务、safeline-tengine 服务支持运行时日志输出范围设置,方便问题调试
|
||||
- 更新 compose.yaml 文件配置,移除非必要环境变量配置,规范环境变量名称,移除非必要卷配置
|
||||
- 增加新统计服务 safeline-luigi,为更精细的统计能力做准备
|
||||
- 优化若干 UI 交互、文字描述、英文翻译的细节(感谢国际友人的帮助)
|
||||
- 修复 3.16 以及之前版本的一些问题:
|
||||
- safeline-tcd 启动时因启动顺序导致输出错误提示
|
||||
- http 强制跳转到 https 功能未生效
|
||||
- 修复 4.0.0-beta.x 版本中的一些问题:
|
||||
- 登录雷池失败,提示 HTTP/2 协议错误([#564](https://github.com/chaitin/SafeLine/issues/564))
|
||||
- 升级脚本未正常检测到雷池安装目录([#561](https://github.com/chaitin/SafeLine/pull/561),感谢热心网友 nmgliangwei)
|
||||
- safeline-mgt 持续输出版本号错误日志
|
||||
- 拦截页面未显示时间
|
||||
|
||||
|
||||
## [4.0.0-beta.3] - 2023-12-28
|
||||
|
||||
### 优化
|
||||
|
||||
- 支持类 ChatGPT 应用的流式 HTTP 响应([#513](https://github.com/chaitin/SafeLine/issues/513))
|
||||
- 更新流式检测引擎到 (20231228) 版本
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复由于服务启动顺序导致输出非必要的错误日志
|
||||
|
||||
## [4.0.0-beta.2] - 2023-12-22
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复 safeline-tcd 启动时因启动顺序导致输出错误提示信息
|
||||
- 修复 safeline-mgt 在 beta 版本下持续输出版本号错误日志
|
||||
- 修复 http 强制跳转到 https 功能未生效问题
|
||||
|
||||
### 优化
|
||||
|
||||
- 更新流式检测引擎版本到 20231222 版本
|
||||
|
||||
## [4.0.0-beta.1] - 2023-12-21
|
||||
|
||||
### 新增
|
||||
|
||||
- 完整支持 **流式语义分析检测**,包含 协议解析、解码、模式匹配 三个阶段的改造,解决经典 “大包绕过” 问题
|
||||
|
||||
### 优化
|
||||
|
||||
- safeline-fvm 容器体积减小 60%
|
||||
- safeline-mgt 服务减少宿主机文件依赖
|
||||
- safeline-mgt 服务日志全部写入 docker 标准输出,默认仅输出启动信息和错误日志,减小磁盘占用
|
||||
- safeline-mgt 服务、safeline-tengine 服务支持运行时日志输出范围设置,方便问题调试
|
||||
- 更新 compose.yaml 文件配置,移除非必要环境变量配置,规范环境变量名称,移除非必要卷配置
|
||||
- 增加新统计服务 safeline-luigi,为更精细的统计能力做准备
|
||||
- 美化 502/504 页面
|
||||
- 优化频率限制配置的英文翻译(感谢国际友人的提示)
|
||||
|
||||
## [3.16.1] - 2023-12-15
|
||||
|
||||
### 新增
|
||||
|
||||
- 右上角增加 “更多工具”,方便快速访问牧云主机助手、百川网站监测等常用运维管理工具
|
||||
|
||||
### 优化
|
||||
|
||||
- 登录时若验证码错误,不再自动清空内容,方便修改([#449](https://github.com/chaitin/SafeLine/issues/449))
|
||||
- 精简 docker 镜像文件,safeline-mgt-api 体积减小 90%
|
||||
- 获取站点的 Favicon 和标题时,增加浏览器 UserAgent,避免被上游服务拒绝
|
||||
- 数据统计页 4xx 和 5xx 错误率的默认显示方式从 “-%” 改为 “0%” ([#517](https://github.com/chaitin/SafeLine/issues/517))
|
||||
- 优化控制台和 502 、504 页面的一些样式细节
|
||||
- 未登录时,不显示任何前端页面内容,避免被报告安全问题
|
||||
|
||||
## [3.15.3] - 2023-12-08
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复 403 拦截页面没有展示拦截页面附加说明的问题
|
||||
|
||||
## [3.15.2] - 2023-12-07
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 502、504 页面。网站服务器异常、配置有误时,能给网站用户提供更清晰友好的说明
|
||||
- 拦截页面支持英文,根据客户端语言自动切换
|
||||
|
||||
### 优化
|
||||
|
||||
- 单个 IP 组内的 IP 数量,增加 1w 行的上限。避免更新配置时系统异常
|
||||
- 修复创建或修改站点时,端口占用检查没有生效的问题
|
||||
- 略微提高流量检测和配置修改时的执行效率
|
||||
|
||||
## [3.14.1] - 2023-11-30
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复日志服务 CPU 占用过高问题
|
||||
|
||||
## [3.14.0] - 2023-11-30
|
||||
|
||||
### 新增
|
||||
|
||||
- 增加黑白名单正则表达式校验,避免 “_url_” 这类错误正则
|
||||
- 事件列表固定表头 ([#443](https://github.com/chaitin/SafeLine/issues/443))
|
||||
|
||||
#### 优化
|
||||
### 优化
|
||||
|
||||
- 移除 redis 依赖,减少运行容器数量
|
||||
|
||||
#### 修复
|
||||
### 修复
|
||||
|
||||
- 修复黑白名单 CIDR 格式校验提示文案未翻译的问题
|
||||
- 修复 safeline-tengine 容器提示缺少 MGT_API 的问题 ([#468](https://github.com/chaitin/SafeLine/issues/468))
|
||||
|
||||
37
Dockerfile
@@ -1,15 +1,15 @@
|
||||
FROM golang:1.21 as go-builder
|
||||
|
||||
WORKDIR /work
|
||||
COPY backend .
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
RUN go mod tidy
|
||||
COPY backend/go.mod .
|
||||
COPY backend/go.sum .
|
||||
RUN go mod download
|
||||
COPY backend .
|
||||
RUN CGO_ENABLED=0 go build -a -v -ldflags="-w" -o server .
|
||||
|
||||
FROM node:20.5-alpine
|
||||
|
||||
ARG telemetry
|
||||
|
||||
RUN apk update
|
||||
RUN apk add nginx supervisor curl
|
||||
|
||||
@@ -17,12 +17,6 @@ RUN echo -e "
|
||||
server { \n\
|
||||
listen 80; \n\
|
||||
\n\
|
||||
location /api/count { \n\
|
||||
proxy_pass $telemetry; \n\
|
||||
} \n\
|
||||
location /api/exist { \n\
|
||||
proxy_pass $telemetry; \n\
|
||||
} \n\
|
||||
location /api/ { \n\
|
||||
proxy_pass http://localhost:8080; \n\
|
||||
} \n\
|
||||
@@ -31,11 +25,26 @@ server {
|
||||
} \n\
|
||||
location /blazehttp { \n\
|
||||
root /app/; \n\
|
||||
try_files \$uri =404; \n\
|
||||
try_files \$uri =404; \n\
|
||||
} \n\
|
||||
location /release { \n\
|
||||
root /app/; \n\
|
||||
try_files \$uri =404; \n\
|
||||
try_files \$uri =404; \n\
|
||||
} \n\
|
||||
location /sitemap.xml { \n\
|
||||
root /srv/website/public; \n\
|
||||
} \n\
|
||||
location /sitemap-0.xml { \n\
|
||||
root /srv/website/public; \n\
|
||||
} \n\
|
||||
location /robots.txt { \n\
|
||||
root /srv/website/public; \n\
|
||||
} \n\
|
||||
location /googlef97f8402f9139518.html { \n\
|
||||
root /srv/website/public; \n\
|
||||
} \n\
|
||||
location /BingSiteAuth.xml { \n\
|
||||
root /srv/website/public; \n\
|
||||
} \n\
|
||||
location / { \n\
|
||||
rewrite /posts/guide_introduction /docs/ permanent; \n\
|
||||
@@ -94,13 +103,13 @@ COPY --from=go-builder /work/server /srv/server
|
||||
|
||||
COPY documents /srv/documents
|
||||
WORKDIR /srv/documents
|
||||
RUN npm i; npm run build
|
||||
RUN npm ci; npm run build
|
||||
# npm run serve
|
||||
|
||||
ENV TARGET=http://localhost:8080
|
||||
COPY website /srv/website
|
||||
WORKDIR /srv/website
|
||||
RUN npm i; npm run build
|
||||
RUN npm ci; npm run build
|
||||
# npm start
|
||||
|
||||
COPY release /app/release
|
||||
|
||||
8
FAQ.md
@@ -24,9 +24,9 @@ As shown, you shall install `docker` first. Try `curl -fLsS https://get.docker.c
|
||||
|
||||
As shown, you shall install `docker compose v2`. Try `[Install Docker Compose](https://docs.docker.com/compose/install/)`
|
||||
|
||||
### safeline-postgres: Operation not permitted
|
||||
### safeline-pg: Operation not permitted
|
||||
|
||||
`docker logs -f safeline-postgres` with error `Operation not permitted`
|
||||
`docker logs -f safeline-pg` with error `Operation not permitted`
|
||||
|
||||
Upgrade your docker engine and retry.
|
||||
|
||||
@@ -94,9 +94,9 @@ As shown, you shall start docker first. Try `systemctl start docker`.
|
||||
|
||||
端口冲突,根据报错信息中的端口号,排查是哪个服务占用了,手动处理冲突。
|
||||
|
||||
### safeline-postgres 出现 Operation not permitted
|
||||
### safeline-pg 出现 Operation not permitted
|
||||
|
||||
`docker logs -f safeline-postgres` 容器日志中看到 `Operation not permitted` 报错
|
||||
`docker logs -f safeline-pg` 容器日志中看到 `Operation not permitted` 报错
|
||||
|
||||
可能是您的 docker 版本过低,升级 docker 到最新版本尝试一下。
|
||||
|
||||
|
||||
17
README.md
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img src="https://waf-ce.chaitin.cn/images/403.svg" width="120">
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/403.svg" width="120">
|
||||
</p>
|
||||
<h1 align="center">雷池 - 广受好评的社区 WAF</h1>
|
||||
<br>
|
||||
@@ -20,9 +20,14 @@
|
||||
|
||||
一款足够简单、足够好用、足够强的免费 WAF。基于业界领先的语义引擎检测技术,作为反向代理接入,保护你的网站不受黑客攻击。
|
||||
|
||||
- **累计安装**超过 60000 台
|
||||
- **保护网站**超过 500,000 个
|
||||
- 每天**处理 HTTP 请求**超过 20,000,000,000 次
|
||||
- 每天**拦截攻击**超过 10,000,000 次
|
||||
|
||||
核心检测能力由智能语义分析算法驱动,专为社区而生,不让黑客越雷池半步。
|
||||
|
||||
<img src="https://waf-ce.chaitin.cn/images/album/0.png" />
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/album/0.png" />
|
||||
|
||||
<h4 align="center">相关源码仓库</h4>
|
||||
<p align="center">
|
||||
@@ -75,13 +80,13 @@ bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
|
||||
浏览器打开后台管理页面 `https://<waf-ip>:9443`。根据界面提示,使用 **支持 TOTP 的认证软件** 扫描二维码,然后输入动态口令登录:
|
||||
|
||||

|
||||

|
||||
|
||||
### 配置防护站点
|
||||
|
||||
雷池以反向代理方式接入,优先于网站服务器接收流量,对流量中的攻击行为进行检测和清洗,将清洗过后的流量转发给网站服务器。
|
||||
|
||||

|
||||

|
||||
|
||||
<font color=grey>💡 TIPS: 添加后,执行 `curl -H "Host: <域名>" http://<WAF IP>:<端口>` 应能获取到业务网站的响应。</font>
|
||||
|
||||
@@ -92,7 +97,7 @@ bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
- 浏览器访问 `http://<IP或域名>:<端口>/?id=1%20AND%201=1`
|
||||
- 浏览器访问 `http://<IP或域名>:<端口>/?a=<script>alert(1)</script>`
|
||||
|
||||

|
||||

|
||||
|
||||
> 如果你需要进行深度测试,请参考 <a href="https://waf-ce.chaitin.cn/posts/guide_test">测试防护效果</a>
|
||||
|
||||
@@ -109,7 +114,7 @@ bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
1. 可以通过 GitHub Issue 直接进行 Bug 反馈和功能建议
|
||||
2. 可以扫描下方二维码加入雷池社区版用户讨论群
|
||||
|
||||
<img src="https://waf-ce.chaitin.cn/images/wechat-230825.png" width="30%" />
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/wechat-230825.png" width="30%" />
|
||||
|
||||
## Star History <a name="star-history"></a>
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ For examples:
|
||||
1. You can make bug feedback and feature suggestions directly through GitHub Issues.
|
||||
2. By scanning the QR code below (use wechat or qq), you can join the discussion group of SafeLine users for detailed discussions.
|
||||
|
||||
<img src="https://waf-ce.chaitin.cn/images/wechat-230825.png" width="30%" />
|
||||
<img src="https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/wechat-230825.png" width="30%" />
|
||||
|
||||
## ✨ CTStack
|
||||
<img src="https://ctstack-oss.oss-cn-beijing.aliyuncs.com/CT%20Stack-2.png" width="30%" />
|
||||
|
||||
@@ -15,6 +15,40 @@ const docTemplate = `{
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/exist": {
|
||||
"post": {
|
||||
"description": "get ip if id exist",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Safeline"
|
||||
],
|
||||
"summary": "get ip if id exist",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "body",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ExistReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/discussions": {
|
||||
"get": {
|
||||
"description": "get discussions from GitHub",
|
||||
@@ -49,6 +83,29 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/info": {
|
||||
"get": {
|
||||
"description": "get repo info from GitHub",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"GitHub"
|
||||
],
|
||||
"summary": "get repo info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/service.Repo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/issues": {
|
||||
"get": {
|
||||
"description": "get issues from GitHub",
|
||||
@@ -82,17 +139,68 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/safeline/count": {
|
||||
"get": {
|
||||
"description": "get installer count",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Safeline"
|
||||
],
|
||||
"summary": "get installer count",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/service.InstallerCount"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"handler.ExistReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.Category": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"emoji": {
|
||||
"type": "string"
|
||||
},
|
||||
"emoji_html": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.Discussion": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"author": {
|
||||
"$ref": "#/definitions/service.User"
|
||||
},
|
||||
"category_name": {
|
||||
"type": "string"
|
||||
"category": {
|
||||
"$ref": "#/definitions/service.Category"
|
||||
},
|
||||
"comment_count": {
|
||||
"type": "integer"
|
||||
@@ -126,6 +234,17 @@ const docTemplate = `{
|
||||
},
|
||||
"upvote_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.InstallerCount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -172,6 +291,17 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.Repo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"star_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -4,6 +4,40 @@
|
||||
"contact": {}
|
||||
},
|
||||
"paths": {
|
||||
"/exist": {
|
||||
"post": {
|
||||
"description": "get ip if id exist",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Safeline"
|
||||
],
|
||||
"summary": "get ip if id exist",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "body",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ExistReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/discussions": {
|
||||
"get": {
|
||||
"description": "get discussions from GitHub",
|
||||
@@ -38,6 +72,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/info": {
|
||||
"get": {
|
||||
"description": "get repo info from GitHub",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"GitHub"
|
||||
],
|
||||
"summary": "get repo info",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/service.Repo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/repos/issues": {
|
||||
"get": {
|
||||
"description": "get issues from GitHub",
|
||||
@@ -71,17 +128,68 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/safeline/count": {
|
||||
"get": {
|
||||
"description": "get installer count",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Safeline"
|
||||
],
|
||||
"summary": "get installer count",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/service.InstallerCount"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"handler.ExistReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.Category": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"emoji": {
|
||||
"type": "string"
|
||||
},
|
||||
"emoji_html": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.Discussion": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"author": {
|
||||
"$ref": "#/definitions/service.User"
|
||||
},
|
||||
"category_name": {
|
||||
"type": "string"
|
||||
"category": {
|
||||
"$ref": "#/definitions/service.Category"
|
||||
},
|
||||
"comment_count": {
|
||||
"type": "integer"
|
||||
@@ -115,6 +223,17 @@
|
||||
},
|
||||
"upvote_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.InstallerCount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -161,6 +280,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.Repo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"star_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.User": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
definitions:
|
||||
handler.ExistReq:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
token:
|
||||
type: string
|
||||
type: object
|
||||
service.Category:
|
||||
properties:
|
||||
emoji:
|
||||
type: string
|
||||
emoji_html:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
service.Discussion:
|
||||
properties:
|
||||
author:
|
||||
$ref: '#/definitions/service.User'
|
||||
category_name:
|
||||
type: string
|
||||
category:
|
||||
$ref: '#/definitions/service.Category'
|
||||
comment_count:
|
||||
type: integer
|
||||
comment_users:
|
||||
@@ -27,6 +45,13 @@ definitions:
|
||||
type: string
|
||||
upvote_count:
|
||||
type: integer
|
||||
url:
|
||||
type: string
|
||||
type: object
|
||||
service.InstallerCount:
|
||||
properties:
|
||||
total:
|
||||
type: integer
|
||||
type: object
|
||||
service.Issue:
|
||||
properties:
|
||||
@@ -56,6 +81,13 @@ definitions:
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
service.Repo:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
star_count:
|
||||
type: integer
|
||||
type: object
|
||||
service.User:
|
||||
properties:
|
||||
avatar_url:
|
||||
@@ -66,6 +98,28 @@ definitions:
|
||||
info:
|
||||
contact: {}
|
||||
paths:
|
||||
/exist:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: get ip if id exist
|
||||
parameters:
|
||||
- description: body
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handler.ExistReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
summary: get ip if id exist
|
||||
tags:
|
||||
- Safeline
|
||||
/repos/discussions:
|
||||
get:
|
||||
consumes:
|
||||
@@ -88,6 +142,21 @@ paths:
|
||||
summary: get discussions
|
||||
tags:
|
||||
- GitHub
|
||||
/repos/info:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: get repo info from GitHub
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/service.Repo'
|
||||
summary: get repo info
|
||||
tags:
|
||||
- GitHub
|
||||
/repos/issues:
|
||||
get:
|
||||
consumes:
|
||||
@@ -110,4 +179,19 @@ paths:
|
||||
summary: get issues
|
||||
tags:
|
||||
- GitHub
|
||||
/safeline/count:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: get installer count
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/service.InstallerCount'
|
||||
summary: get installer count
|
||||
tags:
|
||||
- Safeline
|
||||
swagger: "2.0"
|
||||
|
||||
@@ -60,3 +60,20 @@ func (h *GitHubHandler) GetDiscussions(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, discussions)
|
||||
}
|
||||
|
||||
// GetRepo handles GET requests for fetching GitHub repo info.
|
||||
// @Summary get repo info
|
||||
// @Description get repo info from GitHub
|
||||
// @Tags GitHub
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} service.Repo
|
||||
// @Router /repos/info [get]
|
||||
func (h *GitHubHandler) GetRepo(c *gin.Context) {
|
||||
repo, err := h.gitHubService.GetRepo(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, repo)
|
||||
}
|
||||
|
||||
64
backend/internal/handler/safeline.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/chaitin/SafeLine/internal/service"
|
||||
)
|
||||
|
||||
type SafelineHandler struct {
|
||||
safelineService *service.SafelineService
|
||||
}
|
||||
|
||||
func NewSafelineHandler(safelineService *service.SafelineService) *SafelineHandler {
|
||||
return &SafelineHandler{
|
||||
safelineService: safelineService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetInstallerCount
|
||||
// @Summary get installer count
|
||||
// @Description get installer count
|
||||
// @Tags Safeline
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} service.InstallerCount
|
||||
// @Router /safeline/count [get]
|
||||
func (h *SafelineHandler) GetInstallerCount(c *gin.Context) {
|
||||
count, err := h.safelineService.GetInstallerCount(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(200, count)
|
||||
}
|
||||
|
||||
type ExistReq struct {
|
||||
Id string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// Exist return ip if id exist
|
||||
// @Summary get ip if id exist
|
||||
// @Description get ip if id exist
|
||||
// @Tags Safeline
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body ExistReq true "body"
|
||||
// @Success 200 {object} string
|
||||
// @Router /exist [post]
|
||||
func (h *SafelineHandler) Exist(c *gin.Context) {
|
||||
req := &ExistReq{}
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
ip, err := h.safelineService.GetExist(c, req.Id, req.Token)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"ip": ip})
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -11,6 +13,22 @@ import (
|
||||
"github.com/shurcooL/githubv4"
|
||||
)
|
||||
|
||||
type LabelName = string
|
||||
|
||||
const (
|
||||
LabelNameEnhancement LabelName = "enhancement"
|
||||
LabelNameInProgress LabelName = "in progress"
|
||||
LabelNameReleased LabelName = "released"
|
||||
)
|
||||
|
||||
type RoadmapLabelName = string
|
||||
|
||||
const (
|
||||
RoadmapLabelNameInConsideration RoadmapLabelName = "in_consideration"
|
||||
RoadmapLabelNameInProgress RoadmapLabelName = "in_progress"
|
||||
RoadmapLabelNameReleased RoadmapLabelName = "released"
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
@@ -21,17 +39,58 @@ type User struct {
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
type IssueState = string
|
||||
|
||||
const (
|
||||
IssueStateOpened IssueState = "OPEN"
|
||||
IssueStateClosed IssueState = "CLOSED"
|
||||
)
|
||||
|
||||
// Issue represents a GitHub issue with minimal fields.
|
||||
type Issue struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"-"`
|
||||
Url string `json:"url"`
|
||||
Labels []Label `json:"labels"`
|
||||
CommentCount int `json:"comment_count"`
|
||||
ThumbsUpCount int `json:"thumbs_up"`
|
||||
Author User `json:"author"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"-"`
|
||||
State IssueState `json:"state"`
|
||||
Url string `json:"url"`
|
||||
Labels []Label `json:"labels"`
|
||||
CommentCount int `json:"comment_count"`
|
||||
ThumbsUpCount int `json:"thumbs_up"`
|
||||
Author User `json:"author"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (i Issue) InConsideration() bool {
|
||||
if i.State != IssueStateOpened {
|
||||
return false
|
||||
}
|
||||
if !slices.Contains(i.LabelNames(), LabelNameEnhancement) {
|
||||
return false
|
||||
}
|
||||
if slices.Contains(i.LabelNames(), LabelNameInProgress) {
|
||||
return false
|
||||
}
|
||||
if slices.Contains(i.LabelNames(), LabelNameReleased) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (i Issue) InProgress() bool {
|
||||
return i.State == IssueStateOpened && slices.Contains(i.LabelNames(), LabelNameInProgress)
|
||||
}
|
||||
|
||||
func (i Issue) Released() bool {
|
||||
return slices.Contains(i.LabelNames(), LabelNameReleased)
|
||||
}
|
||||
|
||||
func (i Issue) LabelNames() []string {
|
||||
var names []string
|
||||
for _, v := range i.Labels {
|
||||
names = append(names, v.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// Discussion represents a GitHub discussion.
|
||||
@@ -58,6 +117,11 @@ type Category struct {
|
||||
EmojiHTML string `json:"emoji_html" graphql:"emojiHTML"`
|
||||
}
|
||||
|
||||
type Repo struct {
|
||||
ID string `json:"id"`
|
||||
StarCount int `json:"star_count"`
|
||||
}
|
||||
|
||||
type GitHubAPI interface {
|
||||
Query(ctx context.Context, q interface{}, variables map[string]interface{}) error
|
||||
}
|
||||
@@ -155,33 +219,63 @@ func (s *GitHubService) refreshCache() {
|
||||
return
|
||||
}
|
||||
s.cache.Store("discussions", discussions)
|
||||
|
||||
repo, err := s.fetchRepo(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("failed to fetch repo %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
s.cache.Store("repo", repo)
|
||||
}
|
||||
|
||||
// GetIssues tries to get the issues from cache; if not available, fetches from GitHub API.
|
||||
func (s *GitHubService) GetIssues(ctx context.Context, filter string) (issues []*Issue, err error) {
|
||||
func (s *GitHubService) GetIssues(ctx context.Context, filter string) (map[string][]*Issue, error) {
|
||||
cachedIssues, found := s.cache.Load("issues")
|
||||
if found {
|
||||
return s.filterIssues(cachedIssues.([]*Issue), filter)
|
||||
}
|
||||
|
||||
issues, err = s.fetchIssues(ctx, nil)
|
||||
issues, err := s.fetchIssues(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.filterIssues(issues, filter)
|
||||
}
|
||||
|
||||
func (s *GitHubService) filterIssues(issues []*Issue, filter string) ([]*Issue, error) {
|
||||
func (s *GitHubService) filterIssues(issues []*Issue, filter string) (map[string][]*Issue, error) {
|
||||
filteredIssues := issues
|
||||
if filter != "" {
|
||||
filteredIssues := make([]*Issue, 0)
|
||||
filteredIssues = make([]*Issue, 0)
|
||||
for _, issue := range issues {
|
||||
if strings.Contains(issue.Title, filter) || strings.Contains(issue.Body, filter) {
|
||||
filteredIssues = append(filteredIssues, issue)
|
||||
}
|
||||
}
|
||||
return filteredIssues, nil
|
||||
}
|
||||
return issues, nil
|
||||
out := make(map[string][]*Issue)
|
||||
for _, issue := range filteredIssues {
|
||||
if issue.InConsideration() {
|
||||
out[RoadmapLabelNameInConsideration] = append(out[RoadmapLabelNameInConsideration], issue)
|
||||
}
|
||||
if issue.InProgress() {
|
||||
out[RoadmapLabelNameInProgress] = append(out[RoadmapLabelNameInProgress], issue)
|
||||
}
|
||||
if issue.Released() {
|
||||
out[RoadmapLabelNameReleased] = append(out[RoadmapLabelNameReleased], issue)
|
||||
}
|
||||
}
|
||||
sort.Slice(out[RoadmapLabelNameInConsideration], func(i, j int) bool {
|
||||
return out[RoadmapLabelNameInConsideration][i].ThumbsUpCount > out[RoadmapLabelNameInConsideration][j].ThumbsUpCount
|
||||
})
|
||||
sort.Slice(out[RoadmapLabelNameInProgress], func(i, j int) bool {
|
||||
return out[RoadmapLabelNameInProgress][i].ThumbsUpCount > out[RoadmapLabelNameInProgress][j].ThumbsUpCount
|
||||
})
|
||||
sort.Slice(out[RoadmapLabelNameReleased], func(i, j int) bool {
|
||||
return out[RoadmapLabelNameReleased][i].UpdatedAt > out[RoadmapLabelNameReleased][j].UpdatedAt
|
||||
})
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetRepositoryIssues queries GitHub for issues of a repository.
|
||||
@@ -194,7 +288,9 @@ func (s *GitHubService) fetchIssues(ctx context.Context, afterCursor *githubv4.S
|
||||
Title string
|
||||
Body string
|
||||
Url string
|
||||
State string
|
||||
CreatedAt githubv4.DateTime
|
||||
UpdatedAt githubv4.DateTime
|
||||
Author User
|
||||
Labels struct {
|
||||
Nodes []struct {
|
||||
@@ -213,7 +309,7 @@ func (s *GitHubService) fetchIssues(ctx context.Context, afterCursor *githubv4.S
|
||||
EndCursor githubv4.String
|
||||
HasNextPage bool
|
||||
}
|
||||
} `graphql:"issues(first: 100, after: $afterCursor, states: OPEN, orderBy: {field: CREATED_AT, direction: DESC})"`
|
||||
} `graphql:"issues(first: 100, after: $afterCursor, orderBy: {field: UPDATED_AT, direction: DESC})"`
|
||||
} `graphql:"repository(owner: $owner, name: $name)"`
|
||||
}
|
||||
variables := map[string]interface{}{
|
||||
@@ -230,19 +326,21 @@ func (s *GitHubService) fetchIssues(ctx context.Context, afterCursor *githubv4.S
|
||||
issues := make([]*Issue, 0)
|
||||
for _, node := range query.Repository.Issues.Nodes {
|
||||
issue := &Issue{
|
||||
ID: node.ID,
|
||||
Title: node.Title,
|
||||
Body: node.Body,
|
||||
Url: node.Url,
|
||||
ID: node.ID,
|
||||
Title: node.Title,
|
||||
Body: node.Body,
|
||||
Url: node.Url,
|
||||
State: node.State,
|
||||
CreatedAt: node.CreatedAt.Unix(),
|
||||
UpdatedAt: node.UpdatedAt.Unix(),
|
||||
Author: node.Author,
|
||||
CommentCount: node.Comments.TotalCount,
|
||||
ThumbsUpCount: node.Reactions.TotalCount,
|
||||
}
|
||||
issue.Labels = make([]Label, len(node.Labels.Nodes))
|
||||
for i, label := range node.Labels.Nodes {
|
||||
issue.Labels[i] = Label{Name: label.Name, Color: label.Color}
|
||||
}
|
||||
issue.CommentCount = node.Comments.TotalCount
|
||||
issue.ThumbsUpCount = node.Reactions.TotalCount
|
||||
issue.Author = node.Author
|
||||
issue.CreatedAt = node.CreatedAt.Unix()
|
||||
issues = append(issues, issue)
|
||||
}
|
||||
|
||||
@@ -374,3 +472,36 @@ func (s *GitHubService) fetchDiscussions(ctx context.Context, afterCursor *githu
|
||||
|
||||
return discussions, nil
|
||||
}
|
||||
|
||||
func (s *GitHubService) GetRepo(ctx context.Context) (*Repo, error) {
|
||||
if cachedData, found := s.cache.Load("repo"); found {
|
||||
return cachedData.(*Repo), nil
|
||||
}
|
||||
|
||||
repo, err := s.fetchRepo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.cache.Store("repo", repo)
|
||||
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
func (s *GitHubService) fetchRepo(ctx context.Context) (*Repo, error) {
|
||||
var query struct {
|
||||
Repository struct {
|
||||
ID string
|
||||
StargazerCount int
|
||||
} `graphql:"repository(owner: $owner, name: $name)"`
|
||||
}
|
||||
variables := map[string]interface{}{
|
||||
"owner": githubv4.String(s.owner),
|
||||
"name": githubv4.String(s.repo),
|
||||
}
|
||||
err := s.request(ctx, &query, variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Repo{ID: query.Repository.ID, StarCount: query.Repository.StargazerCount}, nil
|
||||
}
|
||||
|
||||
83
backend/internal/service/safeline.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cacheCount InstallerCount
|
||||
|
||||
type InstallerCount struct {
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type SafelineService struct {
|
||||
client *http.Client
|
||||
APIHost string
|
||||
}
|
||||
|
||||
func NewSafelineService(host string) *SafelineService {
|
||||
return &SafelineService{
|
||||
APIHost: host,
|
||||
client: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SafelineService) GetInstallerCount(ctx context.Context) (InstallerCount, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, s.APIHost+"/api/v1/public/safeline/count", nil)
|
||||
if err != nil {
|
||||
return cacheCount, err
|
||||
}
|
||||
res, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return cacheCount, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
var r map[string]interface{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
|
||||
return cacheCount, err
|
||||
}
|
||||
if r["code"].(float64) != 0 {
|
||||
return cacheCount, nil
|
||||
}
|
||||
cacheCount = InstallerCount{
|
||||
Total: int(r["data"].(map[string]interface{})["total"].(float64)),
|
||||
}
|
||||
return cacheCount, nil
|
||||
}
|
||||
|
||||
// GetExist return ip if id exist
|
||||
func (s *SafelineService) GetExist(ctx context.Context, id string, token string) (string, error) {
|
||||
body := fmt.Sprintf(`{"id":"%s", "token": "%s"}`, id, token)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.APIHost+"/api/v1/public/safeline/exist", strings.NewReader(body))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
res, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
raw, _ := io.ReadAll(res.Body)
|
||||
return "", errors.New(string(raw))
|
||||
}
|
||||
var r map[string]interface{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r["code"].(float64) != 0 {
|
||||
return "", nil
|
||||
}
|
||||
return r["data"].(map[string]interface{})["ip"].(string), nil
|
||||
}
|
||||
@@ -16,10 +16,6 @@ import (
|
||||
func main() {
|
||||
viper.AutomaticEnv()
|
||||
|
||||
// variables that must be set
|
||||
viper.SetDefault("GITHUB_TOKEN", "")
|
||||
|
||||
// optional variables to set
|
||||
viper.SetDefault("GITHUB_CACHE_TTL", 10) // cache timeout in minutes
|
||||
viper.SetDefault("LISTEN_ADDR", ":8080") // api server addr
|
||||
|
||||
@@ -27,6 +23,12 @@ func main() {
|
||||
if githubToken == "" {
|
||||
log.Fatal("GITHUB_TOKEN must be set")
|
||||
}
|
||||
|
||||
telemetryHost := viper.GetString("TELEMETRY_HOST")
|
||||
if telemetryHost == "" {
|
||||
log.Fatal("TELEMETRY_HOST must be set")
|
||||
}
|
||||
|
||||
listenAddr := viper.GetString("LISTEN_ADDR")
|
||||
|
||||
r := gin.Default()
|
||||
@@ -45,6 +47,13 @@ func main() {
|
||||
v1 := r.Group("/api")
|
||||
v1.GET("/repos/issues", gitHubHandler.GetIssues)
|
||||
v1.GET("/repos/discussions", gitHubHandler.GetDiscussions)
|
||||
v1.GET("/repos/info", gitHubHandler.GetRepo)
|
||||
|
||||
// Initialize the SafelineService.
|
||||
safelineService := service.NewSafelineService(telemetryHost)
|
||||
safelineHandler := handler.NewSafelineHandler(safelineService)
|
||||
v1.GET("/safeline/count", safelineHandler.GetInstallerCount)
|
||||
v1.POST("/exist", safelineHandler.Exist)
|
||||
|
||||
docs.SwaggerInfo.BasePath = v1.BasePath()
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
|
||||
161
blockpage/502.html
Normal file
162
blockpage/504.html
Normal file
@@ -228,20 +228,24 @@
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="intercepted">请求存在威胁,已被拦截</div>
|
||||
<div class="intercepted" id="block-title">
|
||||
请求存在威胁,已被拦截
|
||||
</div>
|
||||
<div class="intercepted-tips" id="intercepted-tips"></div>
|
||||
<div class="intercepted-item" id="EventID"></div>
|
||||
<div class="intercepted-item" id="TYPE"></div>
|
||||
<div class="intercepted-item">拦截时间: <span id="now"></span></div>
|
||||
<div class="intercepted-item">
|
||||
<span id="time">拦截时间</span>: <span id="now"></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="footer">
|
||||
安全检测能力由
|
||||
<a class="footer-waflink" href="https://waf-ce.chaitin.cn"
|
||||
>长亭雷池 WAF</a
|
||||
>
|
||||
驱动
|
||||
<span id="powered-by">安全检测能力由</span>
|
||||
<a class="footer-waflink" href="https://waf-ce.chaitin.cn">
|
||||
<span id="waf-title">长亭雷池 WAF</span>
|
||||
</a>
|
||||
<span id="powered-by-tail">驱动</span>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
@@ -307,5 +311,21 @@
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function R(id, text) {
|
||||
var el = document.getElementById(id);
|
||||
if (el) el.innerText = text;
|
||||
}
|
||||
if (!navigator.language.startsWith("zh")) {
|
||||
document.title = "Request has been denied";
|
||||
|
||||
R("block-title", "Threat has been detected, request denied");
|
||||
R("time", "Denied at");
|
||||
R("waf-title", "Safeline WAF");
|
||||
R("powered-by", "Powered by");
|
||||
R("powered-by-tail", "");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -9,7 +9,7 @@ slug: /
|
||||
|
||||
雷池(SafeLine)是长亭科技耗时近 10 年倾情打造的 WAF,核心检测能力由智能语义分析算法驱动。
|
||||
|
||||
Slogan: 不让黑客越雷池半步。
|
||||
Slogan: 不让黑客越雷池一步。
|
||||
|
||||
## 什么是 WAF
|
||||
|
||||
|
||||
@@ -17,15 +17,23 @@ title: "安装雷池"
|
||||
|
||||
复制以下命令执行,即可完成安装
|
||||
|
||||
```shell
|
||||
```sh
|
||||
bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
```
|
||||
如果需要使用华为云加速,可使用
|
||||
```sh
|
||||
CDN=1 bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
```
|
||||
如果需要安装最新版本流式检测模式,可使用
|
||||
```sh
|
||||
STREAM=1 bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/setup.sh)"
|
||||
```
|
||||
|
||||
**若安装失败,请参考 [安装问题](/faq/install)**
|
||||
|
||||
### 在线安装演示
|
||||
|
||||
<iframe src="//player.bilibili.com/player.html?aid=236214137&bvid=BV1Je411f7hQ&cid=1339309164&p=1" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
<iframe src="//player.bilibili.com/player.html?aid=236214137&bvid=BV1Je411f7hQ&cid=1339636220&p=1&autoplay=0" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
style={{ width: '100%', height: '350px' }}
|
||||
>
|
||||
</iframe>
|
||||
@@ -58,8 +66,8 @@ style={{ width: '100%', height: '350px' }}
|
||||
IMAGE_TAG=latest
|
||||
MGT_PORT=9443
|
||||
POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)
|
||||
REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)
|
||||
SUBNET_PREFIX=172.22.222
|
||||
IMAGE_PREFIX=chaitin
|
||||
EOF
|
||||
```
|
||||
|
||||
@@ -77,7 +85,7 @@ style={{ width: '100%', height: '350px' }}
|
||||
|
||||
### 离线安装演示
|
||||
|
||||
<iframe src="//player.bilibili.com/player.html?aid=278701847&bvid=BV1gw411P7om&cid=1339618895&p=1" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
<iframe src="//player.bilibili.com/player.html?aid=278701847&bvid=BV1gw411P7om&cid=1339618895&p=1&autoplay=0" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
style={{ width: '100%', height: '350px' }}
|
||||
>
|
||||
</iframe>
|
||||
@@ -92,7 +100,7 @@ style={{ width: '100%', height: '350px' }}
|
||||
|
||||
### 助手安装演示
|
||||
|
||||
<iframe src="//player.bilibili.com/player.html?aid=613778738&bvid=BV1sh4y1t7Pk&cid=1134834926&p=1" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen="{true}"
|
||||
<iframe src="//player.bilibili.com/player.html?aid=613778738&bvid=BV1sh4y1t7Pk&cid=1134834926&p=1&autoplay=0" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen="{true}"
|
||||
style={{ width: '100%', height: '350px' }}
|
||||
> </iframe>
|
||||
|
||||
@@ -121,7 +129,7 @@ lscpu | grep ssse3 # 确认CPU是否支持 ssse3 指令
|
||||
|
||||
### 配置检测演示
|
||||
|
||||
<iframe src="//player.bilibili.com/player.html?aid=918634668&bvid=BV1Uu4y1L7Ko&cid=1339439164&p=1" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
<iframe src="//player.bilibili.com/player.html?aid=918634668&bvid=BV1Uu4y1L7Ko&cid=1339439164&p=1&autoplay=0" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
style={{ width: '100%', height: '350px' }}
|
||||
></iframe>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ title: "登录雷池"
|
||||
|
||||
根据界面提示,使用 **支持 TOTP 的认证软件或者小程序** 扫描二维码,然后输入动态口令登录:
|
||||
|
||||
<iframe src="//player.bilibili.com/player.html?aid=748637002&bvid=BV1wC4y177zN&cid=1339420830&p=1" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
<iframe src="//player.bilibili.com/player.html?aid=748637002&bvid=BV1wC4y177zN&cid=1339420830&p=1&autoplay=0" scrolling="no" border="0" frameBorder="no" framespacing="0" allowFullScreen='{true}'
|
||||
style={{ width: '100%', height: '350px' }}
|
||||
></iframe>
|
||||
|
||||
@@ -20,7 +20,7 @@ style={{ width: '100%', height: '350px' }}
|
||||
|
||||
1.服务器和 totp 应用的**时间必须保持一致**,否则无法验证通过
|
||||
|
||||
2.跳转到登录页后,**无法回退查看二维码**,需使用页面提供的方法重置
|
||||
2.完成首次登录后,**无法回退查看二维码**,使用页面提供的方法重置
|
||||
|
||||
## 常见登录问题
|
||||
|
||||
|
||||
@@ -8,11 +8,9 @@ title: "配置站点"
|
||||
|
||||
## 工作原理
|
||||
|
||||
雷池社区版主要以 **反向代理** 的方式工作,类似于一台 nginx 服务。
|
||||
雷池社区版主要以 **反向代理** 的方式工作,类似nginx。
|
||||
|
||||
**部署时,需要让网站流量先抵达雷池,经过雷池检测和过滤后,再转给原来的网站业务。**
|
||||
|
||||
建议优先熟悉反向代理概念再继续配置
|
||||
**让网站流量先抵达雷池,经过雷池检测和过滤后,再转给原来的网站业务。**
|
||||
|
||||
## 配置界面
|
||||
|
||||
@@ -23,22 +21,22 @@ title: "配置站点"
|
||||
### 开始配置
|
||||
|
||||
```shell
|
||||
环境信息:
|
||||
环境信息:
|
||||
网站服务器:IPA,对外端口80,域名‘example.com’
|
||||
部署雷池的服务器:IPB
|
||||
目的:使用雷池的80端口接受请求进行防护
|
||||
雷池服务器:IPB
|
||||
|
||||
步骤:
|
||||
1. 必须将网站流量指向雷池的IPB。例如修改域名解析服务的配置,将域名解析到雷池IPB
|
||||
2. 具体配置参考下图
|
||||
3. 禁止网站服务器(IPA)上,所有除了雷池之外的访问。例如配置防火墙
|
||||
步骤:
|
||||
1.将原网站流量指向雷池的IPB(必须)。例如修改域名解析服务,将域名解析到IPB
|
||||
2.参考配置如下图
|
||||
3.禁止网站服务器上,除雷池之外的访问。例如配置防火墙
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 配置完成
|
||||
|
||||
浏览器访问`example.com:80`,若能获取到业务网站的响应,并且站点上 “今日访问量” 增加,则代表配置成功。
|
||||
如果浏览器访问`example.com:80`能获取到业务网站的响应,并且数据统计页的 “今日请求数” 增加,代表配置成功。
|
||||
|
||||
|
||||
效果大致如下:
|
||||
|
||||
@@ -51,11 +49,10 @@ title: "配置站点"
|
||||
### 开始配置
|
||||
|
||||
```shell
|
||||
参考场景:
|
||||
环境信息:
|
||||
网站服务器:IPA,对外端口80,域名‘example.com’
|
||||
服务器上部署雷池:雷池页面端口9443
|
||||
目的:继续使用网站的80端口接受请求,进行防护
|
||||
步骤:
|
||||
|
||||
步骤:
|
||||
1.需要原网站的监听修改为端口A,使80端口变成未使用状态,再进行配置
|
||||
2.具体配置参考下图
|
||||
```
|
||||
@@ -70,7 +67,7 @@ title: "配置站点"
|
||||
|
||||
### 配置完成
|
||||
|
||||
浏览器访问`example.com:80`,若能获取到业务网站的响应,并且站点上 “今日访问量” 增加,则代表配置成功。
|
||||
如果浏览器访问`example.com:80`能获取到业务网站的响应,并且数据统计页的 “今日请求数” 增加,代表配置成功。
|
||||
|
||||
效果大致如图:
|
||||
|
||||
@@ -85,28 +82,26 @@ title: "配置站点"
|
||||
### 开始配置
|
||||
|
||||
```shell
|
||||
环境信息:
|
||||
网站服务器:IPA,对外端口80,域名‘example.com’
|
||||
部署雷池的服务器:IPB
|
||||
上游nginx:IPC,端口C
|
||||
下游nginx:IPD
|
||||
环境信息:
|
||||
网站服务器:IPA
|
||||
雷池服务器:IPB
|
||||
上游服务器:IPC,端口C
|
||||
下游服务器:IPD,域名‘example.com’
|
||||
|
||||
目的:使用雷池的80端口接受请求进行防护
|
||||
|
||||
步骤:
|
||||
1. 将下游nginx的流量指向雷池的IPB,访问端口指向80。
|
||||
2. 具体配置参考下图
|
||||
步骤:
|
||||
1.将下游nginx的流量指向雷池的IPC,访问端口指向80。
|
||||
2.具体配置参考下图
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 配置完成
|
||||
|
||||
浏览器访问`example.com:80`,若能获取到业务网站的响应,并且站点上 “今日访问量” 增加,则代表配置成功。
|
||||
如果浏览器访问`example.com:80`能获取到业务网站的响应,并且数据统计页的 “今日请求数” 增加,代表配置成功。
|
||||
|
||||
效果大致如图:
|
||||
|
||||

|
||||

|
||||
|
||||
## 常见配置问题
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ title: "配置其他"
|
||||
|
||||
### 人机验证
|
||||
|
||||
人机验证的有效时间默认是一个小时,未来可能会支持配置,敬请期待
|
||||
|
||||
详情查看 [人机验证 2.0](/about/challenge)
|
||||
|
||||
### 语义分析
|
||||
@@ -40,7 +42,7 @@ title: "配置其他"
|
||||
|
||||
### 证书管理
|
||||
|
||||
管理需要使用的正式,点击添加证书添加
|
||||
管理需要使用的证书,点击添加证书添加
|
||||
|
||||
### 其他
|
||||
|
||||
|
||||
@@ -10,25 +10,34 @@ title: "升级雷池"
|
||||
|
||||
## 在线升级
|
||||
|
||||
执行以下命令进行升级。
|
||||
执行以下命令进行升级,升级不会清除历史数据。
|
||||
|
||||
```
|
||||
```sh
|
||||
bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/upgrade.sh)"
|
||||
```
|
||||
|
||||
[可选] 执行以下命令删除旧版本 Docke 镜像,释放磁盘空间。
|
||||
[可选] 执行以下命令删除旧版本 Docker 镜像,释放磁盘空间。
|
||||
|
||||
```
|
||||
```sh
|
||||
docker rmi $(docker images | grep "safeline" | grep "none" | awk '{print $3}')
|
||||
```
|
||||
|
||||
> 有部分环境的默认 SafeLine 安装路径是在 `/data/safeline-ce`,安装之后可能会发现需要重新绑定 OTP、配置丢失等情况,可以修改 .env 的 `SAFELINE_DIR` 变量,指向 `/data/safeline-ce`
|
||||
|
||||
如果需要使用华为云加速,可使用
|
||||
```sh
|
||||
CDN=1 bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/upgrade.sh)"
|
||||
```
|
||||
如果需要升级到最新版本流式检测模式,可使用
|
||||
```sh
|
||||
STREAM=1 bash -c "$(curl -fsSLk https://waf-ce.chaitin.cn/release/latest/upgrade.sh)"
|
||||
```
|
||||
|
||||
## 离线镜像
|
||||
|
||||
适用于 docker hub 拉取镜像失败的场景,手动更新镜像。
|
||||
|
||||
```
|
||||
```sh
|
||||
# cd /path/to/safeline
|
||||
|
||||
mv compose.yaml compose.yaml.old
|
||||
@@ -42,8 +51,8 @@ grep "SAFELINE_DIR" ".env" > /dev/null || echo "SAFELINE_DIR=$(pwd)" >> ".env"
|
||||
grep "IMAGE_TAG" ".env" > /dev/null || echo "IMAGE_TAG=latest" >> ".env"
|
||||
grep "MGT_PORT" ".env" > /dev/null || echo "MGT_PORT=9443" >> ".env"
|
||||
grep "POSTGRES_PASSWORD" ".env" > /dev/null || echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> ".env"
|
||||
grep "REDIS_PASSWORD" ".env" > /dev/null || echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> ".env"
|
||||
grep "SUBNET_PREFIX" ".env" > /dev/null || echo "SUBNET_PREFIX=172.22.222" >> ".env"
|
||||
grep "IMAGE_PREFIX" ".env" >/dev/null || echo "IMAGE_PREFIX=chaitin" >>".env"
|
||||
```
|
||||
|
||||
下载 [雷池社区版镜像包](https://demo.waf-ce.chaitin.cn/image.tar.gz) 并传输到需要安装雷池的服务器上,执行以下命令加载镜像
|
||||
|
||||
@@ -61,9 +61,9 @@ security_opt:
|
||||
- seccomp=./seccomp.json
|
||||
```
|
||||
|
||||
#### 报错:safeline-postgres 出现 Operation not permitted
|
||||
#### 报错:safeline-pg 出现 Operation not permitted
|
||||
|
||||
`docker logs -f safeline-postgres` 容器日志中看到 `Operation not permitted` 报错。
|
||||
`docker logs -f safeline-pg` 容器日志中看到 `Operation not permitted` 报错。
|
||||
|
||||
可能是您的 docker 版本过低,升级 docker 到最新版本尝试一下。
|
||||
|
||||
@@ -101,7 +101,11 @@ security_opt:
|
||||
|
||||
## 如何卸载
|
||||
|
||||
在安装目录(默认 safeline)下执行 `docker compose down`
|
||||
在安装目录(默认 safeline)下
|
||||
|
||||
根据本地的compose版本,执行 `docker compose down` 或者 `docker-compose down`
|
||||
|
||||
|
||||
|
||||
## 问题无法解决
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ TOTP 动态口令只有 30 秒的有效期,如果认证失败,请在动态
|
||||
命令执行完成后打开雷池页面重新绑定即可。
|
||||
|
||||
```
|
||||
docker exec safeline-mgt-api resetadmin
|
||||
docker exec safeline-mgt resetadmin
|
||||
```
|
||||
|
||||
**注意:重置动态口令后要尽快完成绑定,别被其他人捷足先登了。**
|
||||
@@ -39,7 +39,7 @@ docker exec safeline-mgt-api resetadmin
|
||||
|
||||
如果之前未保存绑定二维码,想多人使用雷池社区版,只需要以下 3 步:
|
||||
|
||||
1. 重置动态口令(参考 [重置认证](#重置认证))
|
||||
1. 重置动态口令(参考 [重新绑定动态口令](#重新绑定动态口令))
|
||||
2. 进入登录页面,这时会自动跳转到 TOTP 绑定页面,保存 “绑定二维码”(注意,非 “认证二维码”)
|
||||
3. 将 “绑定二维码” 分享给其他人进行绑定(“绑定二维码” 无绑定次数限制,无时效限制)
|
||||
|
||||
|
||||
@@ -20,23 +20,21 @@ title: "配置问题"
|
||||
|
||||
4. 同时存在其他错误的配置可能会导致新的配置一直不生效,检查有没有存在其他错误的配置
|
||||
|
||||
## 排查步骤:
|
||||
## 排查步骤
|
||||
|
||||
1. 明确 “网站无法访问” 的具体表现:
|
||||
|
||||
- 如果 `502 Bad Gateway tengine`:
|
||||
|
||||

|
||||
大概率是是雷池的上游服务器配置不正确,或者雷池无法访问到上游服务器,请继续按下面步骤排查。
|
||||
|
||||
大概率是是雷池的上游服务器配置不正确,或者雷池无法访问到上游服务器。请继续按下面步骤排查,重点排查步骤 6、7
|
||||

|
||||
|
||||
- 如果请求能够返回但是十分缓慢
|
||||
|
||||
- 首先确认服务器负载是否正常
|
||||
- 确认服务器负载是否正常,检查服务器的 CPU、内存、带宽使用情况
|
||||
|
||||
- 检查服务器的 CPU、内存、带宽使用情况
|
||||
|
||||
- 在客户端执行命令,检查雷池服务器与上游服务器的网络:`curl -H "Host: <SafeLine-IP>" -vv -o /dev/null -s -w 'time_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n' http://<上游服务器地址>`
|
||||
- 在客户端执行命令,检查雷池服务器与上游服务器的网络:`curl -H "Host: <雷池 IP>" -vv -o /dev/null -s -w 'time_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n' http://<上游服务器地址>`
|
||||
|
||||
- 如果 time_namelookup 时间过大,请检查 dns server 配置
|
||||
- 如果 time_connect 时间过大,请检查雷池与上游服务器之间的网络状态
|
||||
@@ -45,12 +43,14 @@ title: "配置问题"
|
||||
- 如果不是以上情况,继续下一步
|
||||
|
||||
2. 在客户端执行 `curl -v -H "Host: <域名或者IP>" http://<雷池 IP>:<雷池监听端口>` 。如能获取到业务网站的响应,如图,并且站点的 “今日访问量” +1,说明雷池配置正确,网络正常
|
||||
|
||||

|
||||
|
||||
- 如果浏览器无法访问,但这一步正常获取到响应,大概率是因为:
|
||||
如果浏览器无法访问,但这一步正常获取到响应,大概率是因为:
|
||||
|
||||
- 网站域名还没有切到雷池,浏览器测试时访问的是 `http(s)://<雷池 IP>`,恰好业务服务上有 Host 验证,所以拒绝了该请求。这种情况需要修改本机 host,把域名解析到雷池 IP,再访问 `http(s)://<域名>`,才能准确测试
|
||||
- 网站业务做了其他一些特殊处理。例如访问后 301 跳转到了其他地址,需要具体排查网站业务的响应内容
|
||||
- 如果不能获取到响应,继续下一步
|
||||
- 如果不能获取到响应,继续下一步
|
||||
|
||||
3. 在雷池设备上执行 `curl -v -H "Host: <域名或者IP>" http://<雷池 IP>:<雷池监听端口>`。如能获取到业务网站的响应,并且站点上 “今日访问量” +1,说明雷池配置正确
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ location /xxx {
|
||||
**_注意:该操作会清除所有日志信息,且不可恢复_**
|
||||
|
||||
```shell
|
||||
docker exec safeline-mgt-api cleanlogs
|
||||
docker exec safeline-mgt cleanlogs
|
||||
```
|
||||
|
||||
## 将雷池的日志导出到 XXX
|
||||
@@ -55,7 +55,7 @@ docker exec safeline-mgt-api cleanlogs
|
||||
<source>
|
||||
@type sql
|
||||
|
||||
host safeline-postgres // 默认数据库地址,如果在 compose.yml 中改过,请使用改后值
|
||||
host safeline-pg // 默认数据库地址,如果在 compose.yml 中改过,请使用改后值
|
||||
port 5432
|
||||
database safeline-ce // 数据库名
|
||||
adapter postgresql
|
||||
@@ -186,13 +186,8 @@ real_ip_header X-Forwarded-For;
|
||||
|
||||
## 是否支持 WebSocket ?
|
||||
|
||||
如果需要支持 WebSocket,需要参考 [自定义站点-nginx-conf](#自定义站点-nginx-conf),增加下面的配置
|
||||
默认支持
|
||||
|
||||
```
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
```
|
||||
|
||||
## 问题无法解决
|
||||
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
---
|
||||
title: "可用性监控"
|
||||
title: "百川网站监控"
|
||||
---
|
||||
|
||||
# 网站可用性监控
|
||||
# 百川网站监控
|
||||
|
||||
除了对网站的安全防护以外,不少站长还对网站的可用性、稳定性健康监测和敏感内容监测也有强烈需求,有此类需求的站长可以搭配使用 [长亭百川网站监测](https://rivers.chaitin.cn/landing/radar) 产品。
|
||||
安装雷池社区版,领取长亭网站监控产品 100 元体验金
|
||||
|
||||
### 关于长亭百川网站监测
|
||||
## 关于网站监控
|
||||
|
||||
[长亭百川网站监测](https://rivers.chaitin.cn/landing/radar) 是一款专门为网站管理员打造的网站监测工具,能够有效监测站点可用性、SSL 证书合法性、网站敏感内容等信息。
|
||||
### 什么是网站监控
|
||||
|
||||
交互界面简洁直观、操作上手轻松、注重用户体验,实时监测每个页面的状态和详细信息,让你对网站运行状态了如指掌。
|
||||
**除了对网站的安全防护以外,不少站长还对网站的可用性、稳定性健康监测和敏感内容监测也有强烈需求。**
|
||||
|
||||
[长亭百川网站监测](https://rivers.chaitin.cn/landing/radar) 是一款优秀的网站监测工具,能够有效监测站点可用性、SSL 证书合法性、网站敏感内容等信息。
|
||||
|
||||
交互界面简洁直观、操作上手轻松,实时监测每个页面的状态和详细信息,让你对网站运行状态了如指掌。
|
||||
|
||||

|
||||
|
||||
### 雷池用户福利
|
||||
### 领取方式
|
||||
|
||||
凡是安装了雷池社区版的用户,可凭借雷池设备码领取长亭网站监控产品 100 元体验金一份
|
||||
1. 安装雷池社区版,查看雷池设备码
|
||||
|
||||
2. 登录网站监测,在网站监测页输入雷池设备码,地址:[长亭百川网站监测](https://rivers.chaitin.cn/landing/radar)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: "检测效果对比"
|
||||
title: "WAF检测效果对比"
|
||||
---
|
||||
|
||||
# 检测效果对比
|
||||
# WAF检测效果对比
|
||||
|
||||
雷池社区版与其他 WAF 的检测能力对比
|
||||
|
||||
|
||||
@@ -6,6 +6,157 @@ title: "版本更新记录"
|
||||
|
||||
[版本升级方法](/guide/upgrade)
|
||||
|
||||
### [4.1.1] - 2024-01-11
|
||||
|
||||
#### 修复
|
||||
|
||||
- 修复 IP 组在线订阅失败时会保存错误内容的问题
|
||||
|
||||
### [4.1.0] - 2024-01-11
|
||||
|
||||
#### 新增
|
||||
|
||||
- 拦截日志一键复制为 cURL ([#531](https://github.com/chaitin/SafeLine/issues/531))
|
||||
|
||||
#### 优化
|
||||
|
||||
- IP 组若为在线订阅,显示更新时间([#574](https://github.com/chaitin/SafeLine/issues/574))
|
||||
- 优化 safeline-fvm 容器重启速度,重启时间减少 10s
|
||||
- 优化 safeline-mgt 容器镜像层数,从 39 层下降到 24 层
|
||||
|
||||
#### 修复
|
||||
|
||||
- 修复日志列表 IP 来源地区未翻译国家编号的问题([#578](https://github.com/chaitin/SafeLine/issues/578))
|
||||
- 修复英文翻译问题([#591](https://github.com/chaitin/SafeLine/issues/591))
|
||||
- 修复雷池管理后台证书更新后未自动重启问题
|
||||
|
||||
### [4.0.2] - 2024-01-06
|
||||
|
||||
#### 修复
|
||||
|
||||
- 管理后台 mgt 启动时提示证书异常
|
||||
- 统计页面中 QPS 数据统计方法由窗口时间(5s)改为按秒计算平均值
|
||||
|
||||
### [4.0.1] - 2024-01-05
|
||||
|
||||
#### 修复
|
||||
|
||||
- safeline-luigi 容器打印与功能无关的错误日志
|
||||
- 统计页面中不显示 QPS 数据
|
||||
|
||||
### [4.0.0] - 2024-01-05
|
||||
|
||||
#### 新增
|
||||
|
||||
- 完整支持 **流式语义分析检测**,包含 协议解析、解码、模式匹配 三个阶段的改造,解决经典 “大包绕过” 问题
|
||||
- IP 组支持通过 URL 在线订阅内容([#414](https://github.com/chaitin/SafeLine/issues/414)):
|
||||
- 
|
||||
- 新增 “搜索引擎爬虫 IP”,包含 Google、Bing、百度、360 的爬虫 IP([#374](https://github.com/chaitin/SafeLine/issues/374)、[#399](https://github.com/chaitin/SafeLine/issues/399)):
|
||||

|
||||
- 出厂预置 “搜索引擎爬虫白名单” 和 “长亭社区恶意 IP 情报黑名单”,方便配置
|
||||
|
||||
#### 优化
|
||||
|
||||
- 支持类 ChatGPT 应用的流式 HTTP 响应([#513](https://github.com/chaitin/SafeLine/issues/513))
|
||||
- 在 证书管理 编辑证书后,若证书正被站点使用,自动重启 nginx 使新证书生效([#534](https://github.com/chaitin/SafeLine/issues/534))
|
||||
- safeline-fvm 容器体积减小 60%
|
||||
- safeline-mgt 服务减少宿主机文件依赖
|
||||
- safeline-mgt 服务日志全部写入 docker 标准输出,默认仅输出启动信息和错误日志,减小磁盘占用
|
||||
- safeline-mgt 服务、safeline-tengine 服务支持运行时日志输出范围设置,方便问题调试
|
||||
- 更新 compose.yaml 文件配置,移除非必要环境变量配置,规范环境变量名称,移除非必要卷配置
|
||||
- 增加新统计服务 safeline-luigi,为更精细的统计能力做准备
|
||||
- 优化若干 UI 交互、文字描述、英文翻译的细节(感谢国际友人的帮助)
|
||||
- 修复 3.16 以及之前版本的一些问题:
|
||||
- safeline-tcd 启动时因启动顺序导致输出错误提示
|
||||
- http 强制跳转到 https 功能未生效
|
||||
- 修复 4.0.0-beta.x 版本中的一些问题:
|
||||
- 登录雷池失败,提示 HTTP/2 协议错误([#564](https://github.com/chaitin/SafeLine/issues/564))
|
||||
- 升级脚本未正常检测到雷池安装目录([#561](https://github.com/chaitin/SafeLine/pull/561),感谢热心网友 nmgliangwei)
|
||||
- safeline-mgt 持续输出版本号错误日志
|
||||
- 拦截页面未显示时间
|
||||
|
||||
### [4.0.0-beta.3] - 2023-12-28
|
||||
|
||||
#### 优化
|
||||
|
||||
- 支持类 ChatGPT 应用的流式 HTTP 响应([#513](https://github.com/chaitin/SafeLine/issues/513))
|
||||
- 更新流式检测引擎到 (20231228) 版本
|
||||
|
||||
#### 修复
|
||||
|
||||
- 修复由于服务启动顺序导致输出非必要的错误日志
|
||||
|
||||
### [4.0.0-beta.2] - 2023-12-22
|
||||
|
||||
#### 修复
|
||||
|
||||
- 修复 safeline-tcd 启动时因启动顺序导致输出错误提示信息
|
||||
- 修复 safeline-mgt 在 beta 版本下持续输出版本号错误日志
|
||||
- 修复 http 强制跳转到 https 功能未生效问题
|
||||
|
||||
#### 优化
|
||||
|
||||
- 更新流式检测引擎版本到 20231222 版本
|
||||
|
||||
### [4.0.0-beta.1] - 2023-12-21
|
||||
|
||||
#### 新增
|
||||
|
||||
- 完整支持 **流式语义分析检测**,包含 协议解析、解码、模式匹配 三个阶段的改造,解决经典 “大包绕过” 问题
|
||||
|
||||
#### 优化
|
||||
|
||||
- safeline-fvm 容器体积减小 60%
|
||||
- safeline-mgt 服务减少宿主机文件依赖
|
||||
- safeline-mgt 服务日志全部写入 docker 标准输出,默认仅输出启动信息和错误日志,减小磁盘占用
|
||||
- safeline-mgt 服务、safeline-tengine 服务支持运行时日志输出范围设置,方便问题调试
|
||||
- 更新 compose.yaml 文件配置,移除非必要环境变量配置,规范环境变量名称,移除非必要卷配置
|
||||
- 增加新统计服务 safeline-luigi,为更精细的统计能力做准备
|
||||
- 美化 502/504 页面
|
||||
- 优化频率限制配置的英文翻译(感谢国际友人的提示)
|
||||
|
||||
### [3.16.1] - 2023-12-15
|
||||
|
||||
#### 新增
|
||||
|
||||
- 右上角增加 “更多工具”,方便快速访问牧云主机助手、百川网站监测等常用运维管理工具
|
||||
- 
|
||||
|
||||
#### 优化
|
||||
|
||||
- 登录时若验证码错误,不再自动清空内容,方便修改([#449](https://github.com/chaitin/SafeLine/issues/449))
|
||||
- 精简 docker 镜像文件,safeline-mgt-api 体积减小 90%
|
||||
- 获取站点的 Favicon 和标题时,增加浏览器 UserAgent,避免被上游服务拒绝
|
||||
- 数据统计页 4xx 和 5xx 错误率的默认显示方式从 “-%” 改为 “0%” ([#517](https://github.com/chaitin/SafeLine/issues/517))
|
||||
- 优化控制台和 502 、504 页面的一些样式细节
|
||||
- 未登录时,不显示任何前端页面内容,避免被报告安全问题
|
||||
|
||||
### [3.15.3] - 2023-12-08
|
||||
|
||||
#### 修复
|
||||
|
||||
- 修复 403 拦截页面没有展示拦截页面附加说明的问题
|
||||
|
||||
### [3.15.2] - 2023-12-07
|
||||
|
||||
#### 新增
|
||||
|
||||
- 新增 502、504 页面。网站服务器异常、配置有误时,能给网站用户提供更清晰友好的说明
|
||||
- 
|
||||
- 拦截页面支持英文,根据客户端语言自动切换
|
||||
|
||||
#### 优化
|
||||
|
||||
- 单个 IP 组内的 IP 数量,增加 1w 行的上限。避免更新配置时系统异常
|
||||
- 修复创建或修改站点时,端口占用检查没有生效的问题
|
||||
- 略微提高流量检测和配置修改时的执行效率
|
||||
|
||||
### [3.14.1] - 2023-11-30
|
||||
|
||||
#### 修复
|
||||
|
||||
- 修复日志服务 CPU 占用过高问题
|
||||
|
||||
### [3.14.0] - 2023-11-30
|
||||
|
||||
#### 新增
|
||||
|
||||
@@ -12,14 +12,13 @@ title: "雷池技术架构"
|
||||
|
||||
| 名称 | 定义 | 详情 |
|
||||
| ----------------- | ------------ | ------------------------------------------------------- |
|
||||
| safeline-mgt-api | 管理容器 | 接收管理后台行为,向其他服务或容器推送消息 |
|
||||
| safeline-mgt | 管理容器 | 接收管理后台行为,向其他服务或容器推送消息 |
|
||||
| safeline-detector | 检测容器 | 执行检测的容器,从 Tengine 进入的流量会转发到该节点检测 |
|
||||
| safeline-mario | 日志容器 | 记录与统计恶意行为的节点 |
|
||||
| safeline-tengine | 网关 | 转发网关,有简单的过滤功能 |
|
||||
| safeline-postgres | 关系型数据库 | 存储攻击日志、保护站点、黑白名单配置的数据库 |
|
||||
| safeline-redis | 缓存数据库 | - |
|
||||
| safeline-pg | 关系型数据库 | 存储攻击日志、保护站点、黑白名单配置的数据库 |
|
||||
|
||||
对于后台管理人员,可以直接通信的节点为管理服务 `safeline-mgt-api`,该节点负责:
|
||||
对于后台管理人员,可以直接通信的节点为管理服务 `safeline-mgt`,该节点负责:
|
||||
|
||||
- 向 Tengine 网关推送自定义配置并利用 NGINX 命令进行 reload 热更新
|
||||
- 自定义检测规则(黑白名单等)并向检测引擎 `safeline-detector` 推送
|
||||
@@ -36,7 +35,6 @@ echo "SAFELINE_DIR=$(pwd)" >> .env # 设置当前路径为雷池社区版的根
|
||||
echo "IMAGE_TAG=latest" >> .env # 设置镜像的 tag
|
||||
echo "MGT_PORT=9443" >> .env # 管理容器服务使用的端口
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env # /dev/urandom是一个很长的随机数文本,tr -dc 命令用于删除非字母、非数字的字符,用于生成随机的 postgres 密码
|
||||
echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env # 同上,用于生成随机的 redis 密码
|
||||
echo "SUBNET_PREFIX=172.22.222" >> .env # 定义 docker 虚拟网卡的子网前缀
|
||||
```
|
||||
|
||||
@@ -74,21 +72,6 @@ services:
|
||||
cap_drop:
|
||||
- net_raw
|
||||
command: [postgres, -c, max_connections=200] # 设置 postgres 的最大连接数
|
||||
redis:
|
||||
container_name: safeline-redis
|
||||
restart: always
|
||||
image: redis:7.0.11
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/redis/data:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
|
||||
networks:
|
||||
safeline-ce: # 使用上文的 safeline-ce 网络,ip 为172.22.222.3
|
||||
ipv4_address: ${SUBNET_PREFIX}.3
|
||||
cap_drop:
|
||||
- net_raw
|
||||
sysctls:
|
||||
net.core.somaxconn: "511"
|
||||
management:
|
||||
container_name: safeline-mgt-api
|
||||
restart: always
|
||||
@@ -137,7 +120,6 @@ services:
|
||||
- LOG_DIR=/logs/mario
|
||||
- GOGC=100
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
|
||||
- REDIS_URL=redis://:${REDIS_PASSWORD}@safeline-redis:6379/0
|
||||
networks:
|
||||
safeline-ce: # 使用上文的 safeline-ce 网络,IP 为172.22.222.6
|
||||
ipv4_address: ${SUBNET_PREFIX}.6
|
||||
|
||||
@@ -18,7 +18,7 @@ apisix:https://github.com/apache/apisix
|
||||
|
||||
### 安装 APISIX
|
||||
|
||||
> 注意,要使用 APISIX 3.5.0 及以上版本的 APISIX
|
||||
> 注意,要使用 APISIX 3.5.0 及以上版本
|
||||
|
||||
本文使用 apisix 的 docker 版本来做演示,克隆 apisix-docker 仓库,运行以下命令来安装:
|
||||
|
||||
@@ -55,23 +55,14 @@ bash -c "$(curl -fsSLk <https://waf-ce.chaitin.cn/release/latest/setup.sh>)"
|
||||
cd /data/safeline/resources/detector/
|
||||
```
|
||||
|
||||
用文本编辑器打开目录里的 snserver.yml 文件,寻找这样的三行内容:
|
||||
用文本编辑器打开目录里的 detector.yml 文件,我们需要将 bind 方式从 unix socket 改为 tcp,添加如下配置:
|
||||
|
||||
```
|
||||
bind_addr: unix:///resources/detector/snserver.sock
|
||||
# bind_addr: 0.0.0.0
|
||||
# listen_port: 8000
|
||||
```
|
||||
|
||||
找到以后,我们需要将 bind 方式从 unix socket 改为 tcp,将这三行修改为以下内容即可:
|
||||
|
||||
```
|
||||
# bind_addr: unix:///resources/detector/snserver.sock
|
||||
bind_addr: 0.0.0.0
|
||||
listen_port: 8000
|
||||
```
|
||||
|
||||
这样我们就把雷池引擎的服务监听到了 8000 端口,现在只需要把容器内的 8000 端口映射到宿主机即可。
|
||||
detector配置的属性值将覆盖容器内默认配置文件的同名属性值。这样我们就把雷池引擎的服务监听到了 8000 端口,现在只需要把容器内的 8000 端口映射到宿主机即可。
|
||||
|
||||
进入雷池的安装目录
|
||||
|
||||
|
||||
@@ -102,11 +102,11 @@ const config = {
|
||||
to: "https://waf-ce.chaitin.cn",
|
||||
},
|
||||
{
|
||||
label: "社区",
|
||||
label: "开发计划",
|
||||
to: "https://waf-ce.chaitin.cn/community",
|
||||
},
|
||||
{
|
||||
label: "版本对比",
|
||||
label: "付费版本",
|
||||
to: "https://waf-ce.chaitin.cn/version",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -16,6 +16,7 @@ a:hover {
|
||||
:root {
|
||||
--ifm-color-primary: #0fc6c2;
|
||||
--ifm-breadcrumb-color-active: #0fc6c2;
|
||||
--ifm-menu-color: #000;
|
||||
--ifm-menu-color-active: #0fc6c2;
|
||||
--ifm-link-hover-color: #0fc6c2;
|
||||
--ifm-footer-link-hover-color: #0fc6c2;
|
||||
@@ -34,12 +35,12 @@ aside.theme-doc-sidebar-container {
|
||||
width: 240px !important;
|
||||
}
|
||||
|
||||
.navbar__toggle.clean-btn svg {
|
||||
/* .navbar__toggle.clean-btn svg {
|
||||
color: white;
|
||||
}
|
||||
} */
|
||||
|
||||
@media (max-width: 996px) {
|
||||
:root {
|
||||
--ifm-menu-color: white;
|
||||
--ifm-menu-color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
documents/static/images/docs/about_changelog/502_page.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 62 KiB |
BIN
documents/static/images/docs/about_changelog/ip_group_url.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
documents/static/images/docs/about_changelog/moretools.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 232 KiB |
9025
documents/yarn.lock
Normal file
118
release/beta.bak/compose.yaml
Normal file
@@ -0,0 +1,118 @@
|
||||
networks:
|
||||
safeline-ce:
|
||||
name: safeline-ce
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- gateway: ${SUBNET_PREFIX:?SUBNET_PREFIX required}.1
|
||||
subnet: ${SUBNET_PREFIX}.0/24
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: safeline-ce
|
||||
|
||||
services:
|
||||
postgres:
|
||||
container_name: safeline-pg
|
||||
restart: always
|
||||
image: postgres:15.2
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- POSTGRES_USER=safeline-ce
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?postgres password required}
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.2
|
||||
command: [postgres, -c, max_connections=200]
|
||||
mgt:
|
||||
container_name: safeline-mgt
|
||||
restart: always
|
||||
image: chaitin/safeline-mgt:${IMAGE_TAG:?image tag required}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${SAFELINE_DIR}/resources/mgt:/app/data
|
||||
ports:
|
||||
- ${MGT_PORT:-9443}:1443
|
||||
environment:
|
||||
- MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
|
||||
- MGT_LICENSE_SERVER=https://safeline-ce-4463.rivers.chaitin.cn/
|
||||
depends_on:
|
||||
- postgres
|
||||
- fvm
|
||||
dns:
|
||||
- 119.29.29.29
|
||||
- 223.5.5.5
|
||||
- 180.76.76.76
|
||||
- 1.2.4.8
|
||||
- 114.114.114.114
|
||||
- 8.8.8.8
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.4
|
||||
detect:
|
||||
container_name: safeline-detector
|
||||
restart: always
|
||||
image: chaitin/safeline-detector:${IMAGE_TAG}
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/detector:/resources/detector
|
||||
- ${SAFELINE_DIR}/logs/detector:/logs/detector
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- LOG_DIR=/logs/detector
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.5
|
||||
mario:
|
||||
container_name: safeline-mario
|
||||
restart: always
|
||||
image: chaitin/safeline-mario:${IMAGE_TAG}
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/mario:/resources/mario
|
||||
- ${SAFELINE_DIR}/logs/mario:/logs/mario
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
- LOG_DIR=/logs/mario
|
||||
- GOGC=100
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.6
|
||||
tengine:
|
||||
container_name: safeline-tengine
|
||||
restart: always
|
||||
image: chaitin/safeline-tengine:${IMAGE_TAG}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/resolv.conf:/etc/resolv.conf
|
||||
- ${SAFELINE_DIR}/resources/nginx:/etc/nginx
|
||||
- ${SAFELINE_DIR}/resources/detector:/resources/detector
|
||||
- ${SAFELINE_DIR}/logs/nginx:/var/log/nginx
|
||||
- ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
|
||||
environment:
|
||||
- TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
|
||||
- TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
|
||||
# deprecated
|
||||
- SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
|
||||
ulimits:
|
||||
nofile: 131072
|
||||
network_mode: host
|
||||
luigi:
|
||||
container_name: safeline-luigi
|
||||
restart: always
|
||||
image: chaitin/safeline-luigi:${IMAGE_TAG}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${SAFELINE_DIR}/resources/luigi:/app/data
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.7
|
||||
fvm:
|
||||
container_name: safeline-fvm
|
||||
restart: always
|
||||
image: chaitin/safeline-fvm:${IMAGE_TAG}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.8
|
||||
222
release/beta.bak/setup.sh
Executable file
@@ -0,0 +1,222 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "
|
||||
____ __ _ _
|
||||
/ ___| __ _ / _| ___ | | (_) _ __ ___
|
||||
\___ \ / _\` | | |_ / _ \ | | | | | '_ \ / _ \\
|
||||
___) | | (_| | | _| | __/ | |___ | | | | | | | __/
|
||||
|____/ \__,_| |_| \___| |_____| |_| |_| |_| \___|
|
||||
"
|
||||
|
||||
qrcode() {
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "████ ▄▄▄▄▄ █▀ █▀▀██▀▄▀▀▄▀▄▀▄██ ▄▄▄▄▄ ████"
|
||||
echo "████ █ █ █▀ ▄ █▀▄▄▀▀ ▄█▄ ▀█ █ █ ████"
|
||||
echo "████ █▄▄▄█ █▀█ █▄█▄▀▀▄▀▄ ▀▀▄▄█ █▄▄▄█ ████"
|
||||
echo "████▄▄▄▄▄▄▄█▄█▄█ █▄▀ █ ▀▄▀ █▄█▄▄▄▄▄▄▄████"
|
||||
echo "████▄ ▄▄ █▄▄ ▄█▄▄▄▄▀▄▀▀▄██ ▄▄▀▄█▄▀ ▀████"
|
||||
echo "████▄ ▄▀▄ ▄▀▄ ▀ ▄█▀ ▀▄ █▀▀ ▀█▀▄██▄▀▄█████"
|
||||
echo "█████ ▀▄█ ▄ ▄▄▀▄▀▀█▄▀▄▄▀▄▀▄ ▄ ▀▄▄▄█▀▀████"
|
||||
echo "████ █▀▄▀ ▄▀▄▄▀█▀ ▄▄ █▄█▀▀▄▀▀█▄█▄█▀▄█████"
|
||||
echo "████ █ ▀ ▄▀▀ ██▄█▄▄▄▄▄▀▄▀▀▀▄▄▀█▄▀█ ▀████"
|
||||
echo "████ █ ▀▄ ▄██▀▀ ▄█▀ ▀███▄ ▀▄▀▄▄ ▄▀▄█████"
|
||||
echo "████▀▄▄█ ▄▀▄▀ ▄▀▀▀▄▀▄▀ ▄▀▄ ▄▀ ▄▀█ ▀████"
|
||||
echo "████ █ █ █▄▀ █▄█▀ ▄▄███▀▀▀▄█▀▄ ▀ ▀▄█████"
|
||||
echo "████▄███▄█▄▄▀▄ █▄█▄▄▄▄▀▀▄█▀▀ ▄▄▄ ▀█ ████"
|
||||
echo "████ ▄▄▄▄▄ █▄▀█ ▄█▀▄ █▀█▄ ▀ █▄█ ▀▄▀████"
|
||||
echo "████ █ █ █ █▄▀▀▀▄▄▄▀▀▀▀▀▀ ▄▄ ▀█ ████"
|
||||
echo "████ █▄▄▄█ █ ▀█▀ ▄▄▄▄ ▀█ ▀▀▄▀ ▀▀ ▀██████"
|
||||
echo "████▄▄▄▄▄▄▄█▄▄██▄█▄▄█▄██▄██▄▄█▄▄█▄█▄█████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
|
||||
echo
|
||||
echo "微信扫描上方二维码加入雷池项目讨论组"
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" 2>&1
|
||||
}
|
||||
|
||||
space_left() {
|
||||
dir="$1"
|
||||
while [ ! -d "$dir" ]; do
|
||||
dir=`dirname "$dir"`;
|
||||
done
|
||||
echo `df -h "$dir" --output='avail' | tail -n 1`
|
||||
}
|
||||
|
||||
start_docker() {
|
||||
systemctl start docker && systemctl enable docker
|
||||
}
|
||||
|
||||
confirm() {
|
||||
echo -e -n "\033[34m[SafeLine] $* \033[1;36m(Y/n)\033[0m"
|
||||
read -n 1 -s opt
|
||||
|
||||
[[ "$opt" == $'\n' ]] || echo
|
||||
|
||||
case "$opt" in
|
||||
'y' | 'Y' ) return 0;;
|
||||
'n' | 'N' ) return 1;;
|
||||
*) confirm "$1";;
|
||||
esac
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "\033[37m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "\033[33m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
abort() {
|
||||
qrcode
|
||||
echo -e "\033[31m[SafeLine] $*\033[0m"
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap 'onexit' INT
|
||||
onexit() {
|
||||
echo
|
||||
abort "用户手动结束安装"
|
||||
}
|
||||
|
||||
# CPU ssse3 指令集检查
|
||||
support_ssse3=1
|
||||
lscpu | grep ssse3 > /dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
echo "not found info in lscpu"
|
||||
support_ssse3=0
|
||||
fi
|
||||
|
||||
cat /proc/cpuinfo | grep ssse3 > /dev/null 2>&1
|
||||
if [ $support_ssse3 -eq "0" -a $? -ne "0" ]; then
|
||||
abort "雷池需要运行在支持 ssse3 指令集的 CPU 上,虚拟机请自行配置开启 CPU ssse3 指令集支持"
|
||||
fi
|
||||
|
||||
safeline_path='/data/safeline'
|
||||
|
||||
if [ -z "$BASH" ]; then
|
||||
abort "请用 bash 执行本脚本,请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ ! -t 0 ]; then
|
||||
abort "STDIN 不是标准的输入设备,请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$#" -ne "0" ]; then
|
||||
abort "当前脚本无需任何参数,请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$EUID" -ne "0" ]; then
|
||||
abort "请以 root 权限运行"
|
||||
fi
|
||||
info "脚本调用方式确认正常"
|
||||
|
||||
if [ -z `command_exists docker` ]; then
|
||||
warning "缺少 Docker 环境"
|
||||
if confirm "是否需要自动安装 Docker"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 安装失败"
|
||||
fi
|
||||
info "Docker 安装完成"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
fi
|
||||
info "发现 Docker 环境: '`command -v docker`'"
|
||||
|
||||
start_docker
|
||||
docker version > /dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 服务工作异常"
|
||||
fi
|
||||
info "Docker 工作状态正常"
|
||||
|
||||
compose_command="docker compose"
|
||||
if $compose_command version; then
|
||||
info "发现 Docker Compose Plugin"
|
||||
else
|
||||
warning "未发现 Docker Compose Plugin"
|
||||
compose_command="docker-compose"
|
||||
if [ -z `command_exists "docker-compose"` ]; then
|
||||
warning "未发现 docker-compose 组件"
|
||||
if confirm "是否需要自动安装 Docker Compose Plugin"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker Compose Plugin 安装失败"
|
||||
fi
|
||||
info "Docker Compose Plugin 安装完成"
|
||||
compose_command="docker compose"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
else
|
||||
info "发现 docker-compose 组件: '`command -v docker-compose`'"
|
||||
fi
|
||||
fi
|
||||
|
||||
while true; do
|
||||
echo -e -n "\033[34m[SafeLine] 雷池安装目录 (留空则为 '$safeline_path'): \033[0m"
|
||||
read input_path
|
||||
[[ -z "$input_path" ]] && input_path=$safeline_path
|
||||
|
||||
if [[ ! $input_path == /* ]]; then
|
||||
warning "'$input_path' 不是合法的绝对路径"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -f "$input_path" ] || [ -d "$input_path" ]; then
|
||||
warning "'$input_path' 路径已经存在,请换一个"
|
||||
continue
|
||||
fi
|
||||
|
||||
safeline_path=$input_path
|
||||
|
||||
if confirm "目录 '$safeline_path' 当前剩余存储空间为 `space_left \"$safeline_path\"` ,雷池至少需要 5G,是否确定"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$safeline_path"
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "创建安装目录 '$safeline_path' 失败"
|
||||
fi
|
||||
info "创建安装目录 '$safeline_path' 成功"
|
||||
cd "$safeline_path"
|
||||
|
||||
curl -sS -k "https://waf-ce.chaitin.cn/release/beta/compose.yaml" -o compose.yaml
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
fi
|
||||
info "下载 compose.yaml 脚本成功"
|
||||
|
||||
touch ".env"
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "创建 .env 脚本失败"
|
||||
fi
|
||||
info "创建 .env 脚本成功"
|
||||
|
||||
echo "SAFELINE_DIR=$safeline_path" >> .env
|
||||
echo "IMAGE_TAG=beta-stream" >> .env
|
||||
echo "MGT_PORT=9443" >> .env
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "SUBNET_PREFIX=172.22.222" >> .env
|
||||
|
||||
info "即将开始下载 Docker 镜像"
|
||||
|
||||
$compose_command up -d
|
||||
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "启动 Docker 容器失败"
|
||||
fi
|
||||
|
||||
qrcode
|
||||
|
||||
warning "雷池 WAF 社区版安装成功,请访问以下地址访问控制台"
|
||||
warning "https://0.0.0.0:9443/"
|
||||
|
||||
236
release/beta.bak/upgrade.sh
Executable file
@@ -0,0 +1,236 @@
|
||||
#! /bin/bash
|
||||
|
||||
echo "
|
||||
____ __ _ _
|
||||
/ ___| __ _ / _| ___ | | (_) _ __ ___
|
||||
\___ \ / _\` | | |_ / _ \ | | | | | '_ \ / _ \\
|
||||
___) | | (_| | | _| | __/ | |___ | | | | | | | __/
|
||||
|____/ \__,_| |_| \___| |_____| |_| |_| |_| \___|
|
||||
"
|
||||
|
||||
echo $1
|
||||
|
||||
qrcode() {
|
||||
echo
|
||||
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "████ ▄▄▄▄▄ █▀ █▀▀██▀▄▀▀▄▀▄▀▄██ ▄▄▄▄▄ ████"
|
||||
echo "████ █ █ █▀ ▄ █▀▄▄▀▀ ▄█▄ ▀█ █ █ ████"
|
||||
echo "████ █▄▄▄█ █▀█ █▄█▄▀▀▄▀▄ ▀▀▄▄█ █▄▄▄█ ████"
|
||||
echo "████▄▄▄▄▄▄▄█▄█▄█ █▄▀ █ ▀▄▀ █▄█▄▄▄▄▄▄▄████"
|
||||
echo "████▄ ▄▄ █▄▄ ▄█▄▄▄▄▀▄▀▀▄██ ▄▄▀▄█▄▀ ▀████"
|
||||
echo "████▄ ▄▀▄ ▄▀▄ ▀ ▄█▀ ▀▄ █▀▀ ▀█▀▄██▄▀▄█████"
|
||||
echo "█████ ▀▄█ ▄ ▄▄▀▄▀▀█▄▀▄▄▀▄▀▄ ▄ ▀▄▄▄█▀▀████"
|
||||
echo "████ █▀▄▀ ▄▀▄▄▀█▀ ▄▄ █▄█▀▀▄▀▀█▄█▄█▀▄█████"
|
||||
echo "████ █ ▀ ▄▀▀ ██▄█▄▄▄▄▄▀▄▀▀▀▄▄▀█▄▀█ ▀████"
|
||||
echo "████ █ ▀▄ ▄██▀▀ ▄█▀ ▀███▄ ▀▄▀▄▄ ▄▀▄█████"
|
||||
echo "████▀▄▄█ ▄▀▄▀ ▄▀▀▀▄▀▄▀ ▄▀▄ ▄▀ ▄▀█ ▀████"
|
||||
echo "████ █ █ █▄▀ █▄█▀ ▄▄███▀▀▀▄█▀▄ ▀ ▀▄█████"
|
||||
echo "████▄███▄█▄▄▀▄ █▄█▄▄▄▄▀▀▄█▀▀ ▄▄▄ ▀█ ████"
|
||||
echo "████ ▄▄▄▄▄ █▄▀█ ▄█▀▄ █▀█▄ ▀ █▄█ ▀▄▀████"
|
||||
echo "████ █ █ █ █▄▀▀▀▄▄▄▀▀▀▀▀▀ ▄▄ ▀█ ████"
|
||||
echo "████ █▄▄▄█ █ ▀█▀ ▄▄▄▄ ▀█ ▀▀▄▀ ▀▀ ▀██████"
|
||||
echo "████▄▄▄▄▄▄▄█▄▄██▄█▄▄█▄██▄██▄▄█▄▄█▄█▄█████"
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
|
||||
echo
|
||||
echo "微信扫描上方二维码加入雷池项目讨论组"
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" 2>&1
|
||||
}
|
||||
|
||||
space_left() {
|
||||
dir="$1"
|
||||
while [ ! -d "$dir" ]; do
|
||||
dir=$(dirname "$dir")
|
||||
done
|
||||
echo $(df -h "$dir" --output='avail' | tail -n 1)
|
||||
}
|
||||
|
||||
confirm() {
|
||||
echo -e -n "\033[34m[SafeLine] $* \033[1;36m(Y/n)\033[0m"
|
||||
read -n 1 -s opt
|
||||
|
||||
[[ "$opt" == $'\n' ]] || echo
|
||||
|
||||
case "$opt" in
|
||||
'y' | 'Y') return 0 ;;
|
||||
'n' | 'N') return 1 ;;
|
||||
*) confirm "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "\033[37m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "\033[33m[SafeLine] $*\033[0m"
|
||||
}
|
||||
|
||||
abort() {
|
||||
qrcode
|
||||
echo -e "\033[31m[SafeLine] $*\033[0m"
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap 'onexit' INT
|
||||
onexit() {
|
||||
echo
|
||||
abort "用户手动结束升级"
|
||||
}
|
||||
|
||||
# CPU ssse3 指令集检查
|
||||
support_ssse3=1
|
||||
lscpu | grep ssse3 >/dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
echo "not found info in lscpu"
|
||||
support_ssse3=0
|
||||
fi
|
||||
|
||||
cat /proc/cpuinfo | grep ssse3 >/dev/null 2>&1
|
||||
if [ $support_ssse3 -eq "0" -a $? -ne "0" ]; then
|
||||
abort "雷池需要运行在支持 ssse3 指令集的 CPU 上,虚拟机请自行配置开启 CPU ssse3 指令集支持"
|
||||
fi
|
||||
|
||||
if [ -z "$BASH" ]; then
|
||||
abort "请用 bash 执行本脚本, 请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ ! -t 0 ]; then
|
||||
abort "STDIN 不是标准的输入设备, 请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$#" -ne "0" ]; then
|
||||
abort "当前脚本无需任何参数, 请参考最新的官方技术文档 https://waf-ce.chaitin.cn/"
|
||||
fi
|
||||
|
||||
if [ "$EUID" -ne "0" ]; then
|
||||
abort "请以 root 权限运行"
|
||||
fi
|
||||
info "脚本调用方式确认正常"
|
||||
|
||||
if [ -z $(command_exists docker) ]; then
|
||||
warning "缺少 Docker 环境"
|
||||
if confirm "是否需要自动安装 Docker"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 安装失败"
|
||||
fi
|
||||
info "Docker 安装完成"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
fi
|
||||
info "发现 Docker 环境: '$(command -v docker)'"
|
||||
|
||||
docker version >/dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 服务工作异常"
|
||||
fi
|
||||
info "Docker 工作状态正常"
|
||||
|
||||
compose_command="docker compose"
|
||||
if $compose_command version; then
|
||||
info "发现 Docker Compose Plugin"
|
||||
else
|
||||
warning "未发现 Docker Compose Plugin"
|
||||
compose_command="docker-compose"
|
||||
if [ -z $(command_exists "docker-compose") ]; then
|
||||
warning "未发现 docker-compose 组件"
|
||||
if confirm "是否需要自动安装 Docker Compose Plugin"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker Compose Plugin 安装失败"
|
||||
fi
|
||||
info "Docker Compose Plugin 安装完成"
|
||||
compose_command="docker compose"
|
||||
else
|
||||
abort "中止安装"
|
||||
fi
|
||||
else
|
||||
info "发现 docker-compose 组件: '$(command -v docker-compose)'"
|
||||
fi
|
||||
fi
|
||||
|
||||
container_id=$(docker ps -n 1 --filter name=.*safeline-mgt.* --format '{{.ID}}')
|
||||
safeline_path=$(docker inspect --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' $container_id)
|
||||
|
||||
while [ -z "$safeline_path" ]; do
|
||||
echo -e -n "\033[34m[SafeLine] 未发现正在运行的雷池,请输入雷池安装路径 (留空则为 '$(pwd)'): \033[0m"
|
||||
read input_path
|
||||
[[ -z "$input_path" ]] && input_path=$(pwd)
|
||||
|
||||
if [[ ! $input_path == /* ]]; then
|
||||
warning "'$input_path' 不是合法的绝对路径"
|
||||
continue
|
||||
fi
|
||||
|
||||
safeline_path=$input_path
|
||||
done
|
||||
|
||||
cd "$safeline_path"
|
||||
|
||||
grep COLLIE .env >/dev/null 2>&1
|
||||
if [ $? -eq "0" ]; then
|
||||
abort "检测到你的环境通过牧云主机助手安装,请使用牧云主机助手-应用市场进行升级."
|
||||
fi
|
||||
|
||||
compose_name=$(ls docker-compose.yaml compose.yaml 2>/dev/null)
|
||||
compose_path=$safeline_path/$compose_name
|
||||
|
||||
if [ -f "$compose_path" ]; then
|
||||
info "发现位于 '$safeline_path' 的雷池环境"
|
||||
else
|
||||
abort "没有发现位于 $safeline_path 的雷池环境"
|
||||
fi
|
||||
|
||||
mv $compose_name $compose_name.old
|
||||
|
||||
curl "https://waf-ce.chaitin.cn/release/beta/compose.yaml" -sSLk -o $compose_name
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
fi
|
||||
info "下载 compose.yaml 脚本成功"
|
||||
|
||||
sed -i "s/IMAGE_TAG=.*/IMAGE_TAG=beta-stream/g" ".env"
|
||||
|
||||
grep "SAFELINE_DIR" ".env" >/dev/null || echo "SAFELINE_DIR=$(pwd)" >>".env"
|
||||
grep "IMAGE_TAG" ".env" >/dev/null || echo "IMAGE_TAG=beta-stream" >>".env"
|
||||
grep "MGT_PORT" ".env" >/dev/null || echo "MGT_PORT=9443" >>".env"
|
||||
grep "POSTGRES_PASSWORD" ".env" >/dev/null || echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >>".env"
|
||||
grep "SUBNET_PREFIX" ".env" >/dev/null || echo "SUBNET_PREFIX=172.22.222" >>".env"
|
||||
|
||||
info "升级 .env 脚本成功"
|
||||
|
||||
info "即将开始下载新版本 Docker 镜像"
|
||||
|
||||
$compose_command pull
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载新版本 Docker 镜像失败"
|
||||
fi
|
||||
info "下载新版本 Docker 镜像成功"
|
||||
|
||||
info "即将开始替换 Docker 容器"
|
||||
|
||||
# 升级到 3.14.0 版本时,移除了 safeline-redis 容器,需要删除容器,否则无法启动新 compose 网络
|
||||
docker rm -f safeline-redis &>/dev/null
|
||||
# 升级到 4.0.0 ,重命名了 mgt、fvm、pg 几个容器
|
||||
docker rm -f safeline-mgt-api &>/dev/null
|
||||
docker rm -f safeline-fvm-manager &>/dev/null
|
||||
docker rm -f safeline-postgres &>/dev/null
|
||||
|
||||
$compose_command down --remove-orphans && $compose_command up -d
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "替换 Docker 容器失败"
|
||||
fi
|
||||
info "雷池升级成功"
|
||||
|
||||
qrcode
|
||||
|
||||
warning "雷池 WAF 社区版安装成功, 请访问以下地址访问控制台"
|
||||
warning "https://0.0.0.0:9443/"
|
||||
@@ -1,5 +1,3 @@
|
||||
# http://www.so.com/help/spider_ip.html
|
||||
|
||||
180.153.232.0/24
|
||||
180.153.234.0/24
|
||||
180.153.236.0/24
|
||||
|
||||
18
release/ipgroup/crawler/baidu.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
117.34.74.0/24
|
||||
118.122.188.0/24
|
||||
119.63.196.0/24
|
||||
121.14.89.0/24
|
||||
123.125.66.0/24
|
||||
123.125.68.0/24
|
||||
123.125.71.0/24
|
||||
123.181.108.77
|
||||
125.39.78.0/24
|
||||
159.226.50.0/24
|
||||
180.76.5.0/24
|
||||
203.208.60.0/24
|
||||
210.72.225.0/24
|
||||
220.181.108.0/24
|
||||
220.181.158.107
|
||||
220.181.68.0/24
|
||||
220.181.7.0/24
|
||||
61.135.165.0/24
|
||||
26
release/ipgroup/crawler/bing.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
157.55.39.0/24
|
||||
207.46.13.0/24
|
||||
40.77.167.0/24
|
||||
13.66.139.0/24
|
||||
13.66.144.0/24
|
||||
52.167.144.0/24
|
||||
13.67.10.16/28
|
||||
13.69.66.240/28
|
||||
13.71.172.224/28
|
||||
139.217.52.0/28
|
||||
191.233.204.224/28
|
||||
20.36.108.32/28
|
||||
20.43.120.16/28
|
||||
40.79.131.208/28
|
||||
40.79.186.176/28
|
||||
52.231.148.0/28
|
||||
20.79.107.240/28
|
||||
51.105.67.0/28
|
||||
20.125.163.80/28
|
||||
40.77.188.0/22
|
||||
65.55.210.0/24
|
||||
199.30.24.0/23
|
||||
40.77.202.0/24
|
||||
40.77.139.0/25
|
||||
20.74.197.0/28
|
||||
20.15.133.160/27
|
||||
@@ -1,5 +1,12 @@
|
||||
# sync from https://www.cloudflare.com/ips-v4
|
||||
|
||||
# ipv6
|
||||
2400:cb00::/32
|
||||
2606:4700::/32
|
||||
2803:f800::/32
|
||||
2405:b500::/32
|
||||
2405:8100::/32
|
||||
2a06:98c0::/29
|
||||
2c0f:f248::/32
|
||||
# ipv4
|
||||
173.245.48.0/20
|
||||
103.21.244.0/22
|
||||
103.22.200.0/22
|
||||
|
||||
203
release/ipgroup/crawler/default.txt
Normal file
@@ -0,0 +1,203 @@
|
||||
# baidu
|
||||
117.34.74.0/24
|
||||
118.122.188.0/24
|
||||
119.63.196.0/24
|
||||
121.14.89.0/24
|
||||
123.125.66.0/24
|
||||
123.125.68.0/24
|
||||
123.125.71.0/24
|
||||
123.181.108.77
|
||||
125.39.78.0/24
|
||||
159.226.50.0/24
|
||||
180.76.5.0/24
|
||||
203.208.60.0/24
|
||||
210.72.225.0/24
|
||||
220.181.108.0/24
|
||||
220.181.158.107
|
||||
220.181.68.0/24
|
||||
220.181.7.0/24
|
||||
61.135.165.0/24
|
||||
|
||||
# 360
|
||||
180.153.232.0/24
|
||||
180.153.234.0/24
|
||||
180.153.236.0/24
|
||||
180.163.220.0/24
|
||||
42.236.101.0/24
|
||||
42.236.102.0/24
|
||||
42.236.103.0/24
|
||||
42.236.10.0/24
|
||||
42.236.12.0/24
|
||||
42.236.13.0/24
|
||||
42.236.14.0/24
|
||||
42.236.15.0/24
|
||||
42.236.16.0/24
|
||||
42.236.17.0/24
|
||||
42.236.46.0/24
|
||||
42.236.48.0/24
|
||||
42.236.49.0/24
|
||||
42.236.50.0/24
|
||||
42.236.51.0/24
|
||||
42.236.52.0/24
|
||||
42.236.53.0/24
|
||||
42.236.54.0/24
|
||||
42.236.55.0/24
|
||||
42.236.99.0/24
|
||||
|
||||
# bing
|
||||
157.55.39.0/24
|
||||
207.46.13.0/24
|
||||
40.77.167.0/24
|
||||
13.66.139.0/24
|
||||
13.66.144.0/24
|
||||
52.167.144.0/24
|
||||
13.67.10.16/28
|
||||
13.69.66.240/28
|
||||
13.71.172.224/28
|
||||
139.217.52.0/28
|
||||
191.233.204.224/28
|
||||
20.36.108.32/28
|
||||
20.43.120.16/28
|
||||
40.79.131.208/28
|
||||
40.79.186.176/28
|
||||
52.231.148.0/28
|
||||
20.79.107.240/28
|
||||
51.105.67.0/28
|
||||
20.125.163.80/28
|
||||
40.77.188.0/22
|
||||
65.55.210.0/24
|
||||
199.30.24.0/23
|
||||
40.77.202.0/24
|
||||
40.77.139.0/25
|
||||
20.74.197.0/28
|
||||
20.15.133.160/27
|
||||
|
||||
# google
|
||||
192.178.5.0/27
|
||||
34.100.182.96/28
|
||||
34.101.50.144/28
|
||||
34.118.254.0/28
|
||||
34.118.66.0/28
|
||||
34.126.178.96/28
|
||||
34.146.150.144/28
|
||||
34.147.110.144/28
|
||||
34.151.74.144/28
|
||||
34.152.50.64/28
|
||||
34.154.114.144/28
|
||||
34.155.98.32/28
|
||||
34.165.18.176/28
|
||||
34.175.160.64/28
|
||||
34.176.130.16/28
|
||||
34.22.85.0/27
|
||||
34.64.82.64/28
|
||||
34.65.242.112/28
|
||||
34.80.50.80/28
|
||||
34.88.194.0/28
|
||||
34.89.10.80/28
|
||||
34.89.198.80/28
|
||||
34.96.162.48/28
|
||||
35.247.243.240/28
|
||||
66.249.64.0/27
|
||||
66.249.64.128/27
|
||||
66.249.64.160/27
|
||||
66.249.64.192/27
|
||||
66.249.64.224/27
|
||||
66.249.64.32/27
|
||||
66.249.64.64/27
|
||||
66.249.64.96/27
|
||||
66.249.65.0/27
|
||||
66.249.65.160/27
|
||||
66.249.65.192/27
|
||||
66.249.65.224/27
|
||||
66.249.65.32/27
|
||||
66.249.65.64/27
|
||||
66.249.65.96/27
|
||||
66.249.66.0/27
|
||||
66.249.66.128/27
|
||||
66.249.66.160/27
|
||||
66.249.66.192/27
|
||||
66.249.66.32/27
|
||||
66.249.66.64/27
|
||||
66.249.66.96/27
|
||||
66.249.68.0/27
|
||||
66.249.68.32/27
|
||||
66.249.68.64/27
|
||||
66.249.69.0/27
|
||||
66.249.69.128/27
|
||||
66.249.69.160/27
|
||||
66.249.69.192/27
|
||||
66.249.69.224/27
|
||||
66.249.69.32/27
|
||||
66.249.69.64/27
|
||||
66.249.69.96/27
|
||||
66.249.70.0/27
|
||||
66.249.70.128/27
|
||||
66.249.70.160/27
|
||||
66.249.70.192/27
|
||||
66.249.70.224/27
|
||||
66.249.70.32/27
|
||||
66.249.70.64/27
|
||||
66.249.70.96/27
|
||||
66.249.71.0/27
|
||||
66.249.71.128/27
|
||||
66.249.71.160/27
|
||||
66.249.71.192/27
|
||||
66.249.71.224/27
|
||||
66.249.71.32/27
|
||||
66.249.71.64/27
|
||||
66.249.71.96/27
|
||||
66.249.72.0/27
|
||||
66.249.72.128/27
|
||||
66.249.72.160/27
|
||||
66.249.72.192/27
|
||||
66.249.72.224/27
|
||||
66.249.72.32/27
|
||||
66.249.72.64/27
|
||||
66.249.72.96/27
|
||||
66.249.73.0/27
|
||||
66.249.73.128/27
|
||||
66.249.73.160/27
|
||||
66.249.73.192/27
|
||||
66.249.73.224/27
|
||||
66.249.73.32/27
|
||||
66.249.73.64/27
|
||||
66.249.73.96/27
|
||||
66.249.74.0/27
|
||||
66.249.74.128/27
|
||||
66.249.74.32/27
|
||||
66.249.74.64/27
|
||||
66.249.74.96/27
|
||||
66.249.75.0/27
|
||||
66.249.75.128/27
|
||||
66.249.75.160/27
|
||||
66.249.75.192/27
|
||||
66.249.75.224/27
|
||||
66.249.75.32/27
|
||||
66.249.75.64/27
|
||||
66.249.75.96/27
|
||||
66.249.76.0/27
|
||||
66.249.76.128/27
|
||||
66.249.76.160/27
|
||||
66.249.76.192/27
|
||||
66.249.76.224/27
|
||||
66.249.76.32/27
|
||||
66.249.76.64/27
|
||||
66.249.76.96/27
|
||||
66.249.77.0/27
|
||||
66.249.77.128/27
|
||||
66.249.77.160/27
|
||||
66.249.77.192/27
|
||||
66.249.77.224/27
|
||||
66.249.77.32/27
|
||||
66.249.77.64/27
|
||||
66.249.77.96/27
|
||||
66.249.78.0/27
|
||||
66.249.78.32/27
|
||||
66.249.79.0/27
|
||||
66.249.79.128/27
|
||||
66.249.79.160/27
|
||||
66.249.79.192/27
|
||||
66.249.79.224/27
|
||||
66.249.79.32/27
|
||||
66.249.79.64/27
|
||||
66.249.79.96/27
|
||||
235
release/ipgroup/crawler/google.txt
Normal file
@@ -0,0 +1,235 @@
|
||||
# ipv6
|
||||
2001:4860:4801:10::/64
|
||||
2001:4860:4801:11::/64
|
||||
2001:4860:4801:12::/64
|
||||
2001:4860:4801:13::/64
|
||||
2001:4860:4801:14::/64
|
||||
2001:4860:4801:15::/64
|
||||
2001:4860:4801:16::/64
|
||||
2001:4860:4801:17::/64
|
||||
2001:4860:4801:18::/64
|
||||
2001:4860:4801:19::/64
|
||||
2001:4860:4801:1a::/64
|
||||
2001:4860:4801:1b::/64
|
||||
2001:4860:4801:1c::/64
|
||||
2001:4860:4801:1d::/64
|
||||
2001:4860:4801:1e::/64
|
||||
2001:4860:4801:20::/64
|
||||
2001:4860:4801:21::/64
|
||||
2001:4860:4801:22::/64
|
||||
2001:4860:4801:23::/64
|
||||
2001:4860:4801:24::/64
|
||||
2001:4860:4801:25::/64
|
||||
2001:4860:4801:26::/64
|
||||
2001:4860:4801:27::/64
|
||||
2001:4860:4801:28::/64
|
||||
2001:4860:4801:29::/64
|
||||
2001:4860:4801:2::/64
|
||||
2001:4860:4801:2a::/64
|
||||
2001:4860:4801:2b::/64
|
||||
2001:4860:4801:2c::/64
|
||||
2001:4860:4801:2d::/64
|
||||
2001:4860:4801:2e::/64
|
||||
2001:4860:4801:2f::/64
|
||||
2001:4860:4801:30::/64
|
||||
2001:4860:4801:31::/64
|
||||
2001:4860:4801:32::/64
|
||||
2001:4860:4801:33::/64
|
||||
2001:4860:4801:34::/64
|
||||
2001:4860:4801:35::/64
|
||||
2001:4860:4801:36::/64
|
||||
2001:4860:4801:37::/64
|
||||
2001:4860:4801:38::/64
|
||||
2001:4860:4801:39::/64
|
||||
2001:4860:4801:3::/64
|
||||
2001:4860:4801:3a::/64
|
||||
2001:4860:4801:3b::/64
|
||||
2001:4860:4801:3c::/64
|
||||
2001:4860:4801:3d::/64
|
||||
2001:4860:4801:3e::/64
|
||||
2001:4860:4801:40::/64
|
||||
2001:4860:4801:41::/64
|
||||
2001:4860:4801:42::/64
|
||||
2001:4860:4801:43::/64
|
||||
2001:4860:4801:44::/64
|
||||
2001:4860:4801:45::/64
|
||||
2001:4860:4801:46::/64
|
||||
2001:4860:4801:47::/64
|
||||
2001:4860:4801:48::/64
|
||||
2001:4860:4801:49::/64
|
||||
2001:4860:4801:4a::/64
|
||||
2001:4860:4801:50::/64
|
||||
2001:4860:4801:51::/64
|
||||
2001:4860:4801:53::/64
|
||||
2001:4860:4801:54::/64
|
||||
2001:4860:4801:55::/64
|
||||
2001:4860:4801:60::/64
|
||||
2001:4860:4801:61::/64
|
||||
2001:4860:4801:62::/64
|
||||
2001:4860:4801:63::/64
|
||||
2001:4860:4801:64::/64
|
||||
2001:4860:4801:65::/64
|
||||
2001:4860:4801:66::/64
|
||||
2001:4860:4801:67::/64
|
||||
2001:4860:4801:68::/64
|
||||
2001:4860:4801:69::/64
|
||||
2001:4860:4801:6a::/64
|
||||
2001:4860:4801:6b::/64
|
||||
2001:4860:4801:6c::/64
|
||||
2001:4860:4801:6d::/64
|
||||
2001:4860:4801:6e::/64
|
||||
2001:4860:4801:6f::/64
|
||||
2001:4860:4801:70::/64
|
||||
2001:4860:4801:71::/64
|
||||
2001:4860:4801:72::/64
|
||||
2001:4860:4801:73::/64
|
||||
2001:4860:4801:74::/64
|
||||
2001:4860:4801:75::/64
|
||||
2001:4860:4801:76::/64
|
||||
2001:4860:4801:77::/64
|
||||
2001:4860:4801:78::/64
|
||||
2001:4860:4801:79::/64
|
||||
2001:4860:4801:80::/64
|
||||
2001:4860:4801:81::/64
|
||||
2001:4860:4801:82::/64
|
||||
2001:4860:4801:83::/64
|
||||
2001:4860:4801:84::/64
|
||||
2001:4860:4801:85::/64
|
||||
2001:4860:4801:86::/64
|
||||
2001:4860:4801:87::/64
|
||||
2001:4860:4801:88::/64
|
||||
2001:4860:4801:90::/64
|
||||
2001:4860:4801:91::/64
|
||||
2001:4860:4801:92::/64
|
||||
2001:4860:4801:93::/64
|
||||
2001:4860:4801:c::/64
|
||||
2001:4860:4801:f::/64
|
||||
# ipv4
|
||||
192.178.5.0/27
|
||||
34.100.182.96/28
|
||||
34.101.50.144/28
|
||||
34.118.254.0/28
|
||||
34.118.66.0/28
|
||||
34.126.178.96/28
|
||||
34.146.150.144/28
|
||||
34.147.110.144/28
|
||||
34.151.74.144/28
|
||||
34.152.50.64/28
|
||||
34.154.114.144/28
|
||||
34.155.98.32/28
|
||||
34.165.18.176/28
|
||||
34.175.160.64/28
|
||||
34.176.130.16/28
|
||||
34.22.85.0/27
|
||||
34.64.82.64/28
|
||||
34.65.242.112/28
|
||||
34.80.50.80/28
|
||||
34.88.194.0/28
|
||||
34.89.10.80/28
|
||||
34.89.198.80/28
|
||||
34.96.162.48/28
|
||||
35.247.243.240/28
|
||||
66.249.64.0/27
|
||||
66.249.64.128/27
|
||||
66.249.64.160/27
|
||||
66.249.64.192/27
|
||||
66.249.64.224/27
|
||||
66.249.64.32/27
|
||||
66.249.64.64/27
|
||||
66.249.64.96/27
|
||||
66.249.65.0/27
|
||||
66.249.65.160/27
|
||||
66.249.65.192/27
|
||||
66.249.65.224/27
|
||||
66.249.65.32/27
|
||||
66.249.65.64/27
|
||||
66.249.65.96/27
|
||||
66.249.66.0/27
|
||||
66.249.66.128/27
|
||||
66.249.66.160/27
|
||||
66.249.66.192/27
|
||||
66.249.66.32/27
|
||||
66.249.66.64/27
|
||||
66.249.66.96/27
|
||||
66.249.68.0/27
|
||||
66.249.68.32/27
|
||||
66.249.68.64/27
|
||||
66.249.69.0/27
|
||||
66.249.69.128/27
|
||||
66.249.69.160/27
|
||||
66.249.69.192/27
|
||||
66.249.69.224/27
|
||||
66.249.69.32/27
|
||||
66.249.69.64/27
|
||||
66.249.69.96/27
|
||||
66.249.70.0/27
|
||||
66.249.70.128/27
|
||||
66.249.70.160/27
|
||||
66.249.70.192/27
|
||||
66.249.70.224/27
|
||||
66.249.70.32/27
|
||||
66.249.70.64/27
|
||||
66.249.70.96/27
|
||||
66.249.71.0/27
|
||||
66.249.71.128/27
|
||||
66.249.71.160/27
|
||||
66.249.71.192/27
|
||||
66.249.71.224/27
|
||||
66.249.71.32/27
|
||||
66.249.71.64/27
|
||||
66.249.71.96/27
|
||||
66.249.72.0/27
|
||||
66.249.72.128/27
|
||||
66.249.72.160/27
|
||||
66.249.72.192/27
|
||||
66.249.72.224/27
|
||||
66.249.72.32/27
|
||||
66.249.72.64/27
|
||||
66.249.72.96/27
|
||||
66.249.73.0/27
|
||||
66.249.73.128/27
|
||||
66.249.73.160/27
|
||||
66.249.73.192/27
|
||||
66.249.73.224/27
|
||||
66.249.73.32/27
|
||||
66.249.73.64/27
|
||||
66.249.73.96/27
|
||||
66.249.74.0/27
|
||||
66.249.74.128/27
|
||||
66.249.74.32/27
|
||||
66.249.74.64/27
|
||||
66.249.74.96/27
|
||||
66.249.75.0/27
|
||||
66.249.75.128/27
|
||||
66.249.75.160/27
|
||||
66.249.75.192/27
|
||||
66.249.75.224/27
|
||||
66.249.75.32/27
|
||||
66.249.75.64/27
|
||||
66.249.75.96/27
|
||||
66.249.76.0/27
|
||||
66.249.76.128/27
|
||||
66.249.76.160/27
|
||||
66.249.76.192/27
|
||||
66.249.76.224/27
|
||||
66.249.76.32/27
|
||||
66.249.76.64/27
|
||||
66.249.76.96/27
|
||||
66.249.77.0/27
|
||||
66.249.77.128/27
|
||||
66.249.77.160/27
|
||||
66.249.77.192/27
|
||||
66.249.77.224/27
|
||||
66.249.77.32/27
|
||||
66.249.77.64/27
|
||||
66.249.77.96/27
|
||||
66.249.78.0/27
|
||||
66.249.78.32/27
|
||||
66.249.79.0/27
|
||||
66.249.79.128/27
|
||||
66.249.79.160/27
|
||||
66.249.79.192/27
|
||||
66.249.79.224/27
|
||||
66.249.79.32/27
|
||||
66.249.79.64/27
|
||||
66.249.79.96/27
|
||||
@@ -12,9 +12,9 @@ networks:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
container_name: safeline-postgres
|
||||
container_name: safeline-pg
|
||||
restart: always
|
||||
image: postgres:15.2
|
||||
image: swr.cn-east-3.myhuaweicloud.com/chaitin-safeline/postgres:15.2
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/postgres/data:/var/lib/postgresql/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
@@ -24,43 +24,22 @@ services:
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.2
|
||||
cap_drop:
|
||||
- net_raw
|
||||
command: [postgres, -c, max_connections=200]
|
||||
redis:
|
||||
container_name: safeline-redis
|
||||
mgt:
|
||||
container_name: safeline-mgt
|
||||
restart: always
|
||||
image: redis:7.0.10
|
||||
image: ${IMAGE_PREFIX}/safeline-mgt:${IMAGE_TAG:?image tag required}
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/redis/data:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.3
|
||||
cap_drop:
|
||||
- net_raw
|
||||
sysctls:
|
||||
net.core.somaxconn: "511"
|
||||
management:
|
||||
container_name: safeline-mgt-api
|
||||
restart: always
|
||||
image: chaitin/safeline-mgt-api:${IMAGE_TAG:?image tag required}
|
||||
volumes:
|
||||
- ${SAFELINE_DIR?safeline dir required}/resources/management:/resources/management
|
||||
- ${SAFELINE_DIR}/resources/nginx:/resources/nginx
|
||||
- ${SAFELINE_DIR}/logs:/logs
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${SAFELINE_DIR}/resources/mgt:/app/data
|
||||
ports:
|
||||
- ${MGT_PORT:-9443}:1443
|
||||
environment:
|
||||
- MANAGEMENT_RESOURCES_DIR=/resources/management
|
||||
- NGINX_RESOURCES_DIR=/resources/nginx
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
|
||||
- MARIO_URL=http://safeline-mario:3335
|
||||
- FVM_MANAGER_URL=safeline-fvm-manager:9004
|
||||
- REDIS_URL=redis://:${REDIS_PASSWORD}@safeline-redis:6379/0
|
||||
- MANAGEMENT_LOGS_DIR=/logs/management
|
||||
- MGT_PG=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce?sslmode=disable
|
||||
- MGT_LICENSE_SERVER=https://safeline-ce-4463.rivers.chaitin.cn/
|
||||
depends_on:
|
||||
- postgres
|
||||
- fvm
|
||||
dns:
|
||||
- 119.29.29.29
|
||||
- 223.5.5.5
|
||||
@@ -71,12 +50,10 @@ services:
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.4
|
||||
cap_drop:
|
||||
- net_raw
|
||||
detector:
|
||||
detect:
|
||||
container_name: safeline-detector
|
||||
restart: always
|
||||
image: chaitin/safeline-detector:${IMAGE_TAG}
|
||||
image: ${IMAGE_PREFIX}/safeline-detector:${IMAGE_TAG}
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/detector:/resources/detector
|
||||
- ${SAFELINE_DIR}/logs/detector:/logs/detector
|
||||
@@ -86,12 +63,10 @@ services:
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.5
|
||||
cap_drop:
|
||||
- net_raw
|
||||
mario:
|
||||
container_name: safeline-mario
|
||||
restart: always
|
||||
image: chaitin/safeline-mario:${IMAGE_TAG}
|
||||
image: ${IMAGE_PREFIX}/safeline-mario:${IMAGE_TAG}
|
||||
volumes:
|
||||
- ${SAFELINE_DIR}/resources/mario:/resources/mario
|
||||
- ${SAFELINE_DIR}/logs/mario:/logs/mario
|
||||
@@ -99,43 +74,45 @@ services:
|
||||
environment:
|
||||
- LOG_DIR=/logs/mario
|
||||
- GOGC=100
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-postgres/safeline-ce
|
||||
- REDIS_URL=redis://:${REDIS_PASSWORD}@safeline-redis:6379/0
|
||||
- DATABASE_URL=postgres://safeline-ce:${POSTGRES_PASSWORD}@safeline-pg/safeline-ce
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.6
|
||||
cap_drop:
|
||||
- net_raw
|
||||
tengine:
|
||||
container_name: safeline-tengine
|
||||
restart: always
|
||||
image: chaitin/safeline-tengine:${IMAGE_TAG}
|
||||
image: ${IMAGE_PREFIX}/safeline-tengine:${IMAGE_TAG}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/resolv.conf:/etc/resolv.conf
|
||||
- ${SAFELINE_DIR}/resources/nginx:/etc/nginx
|
||||
- ${SAFELINE_DIR}/resources/management:/resources/management
|
||||
- ${SAFELINE_DIR}/resources/detector:/resources/detector
|
||||
- ${SAFELINE_DIR}/logs/nginx:/var/log/nginx
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${SAFELINE_DIR}/resources/cache:/usr/local/nginx/cache
|
||||
- /etc/resolv.conf:/etc/resolv.conf
|
||||
environment:
|
||||
- REDIS_URL=redis://:${REDIS_PASSWORD}@${SUBNET_PREFIX}.3:6379/0
|
||||
- MGT_ADDR=${SUBNET_PREFIX}.4:9002
|
||||
- TCD_MGT_API=https://${SUBNET_PREFIX}.4:1443/api/open/publish/server
|
||||
- TCD_SNSERVER=${SUBNET_PREFIX}.5:8000
|
||||
# deprecated
|
||||
- SNSERVER_ADDR=${SUBNET_PREFIX}.5:8000
|
||||
ulimits:
|
||||
nofile: 131072
|
||||
network_mode: host
|
||||
fvm-manager:
|
||||
container_name: safeline-fvm-manager
|
||||
luigi:
|
||||
container_name: safeline-luigi
|
||||
restart: always
|
||||
image: chaitin/safeline-fvm-manager:${IMAGE_TAG}
|
||||
environment:
|
||||
- FVM_LOGS_DIR=/logs/management
|
||||
- DETECTOR_URL=http://safeline-detector:8001
|
||||
image: ${IMAGE_PREFIX}/safeline-luigi:${IMAGE_TAG}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${SAFELINE_DIR}/logs:/logs
|
||||
- ${SAFELINE_DIR}/resources/luigi:/app/data
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.8
|
||||
cap_drop:
|
||||
- net_raw
|
||||
ipv4_address: ${SUBNET_PREFIX}.7
|
||||
fvm:
|
||||
container_name: safeline-fvm
|
||||
restart: always
|
||||
image: ${IMAGE_PREFIX}/safeline-fvm:${IMAGE_TAG}
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
networks:
|
||||
safeline-ce:
|
||||
ipv4_address: ${SUBNET_PREFIX}.8
|
||||
@@ -8,6 +8,9 @@ echo "
|
||||
|____/ \__,_| |_| \___| |_____| |_| |_| |_| \___|
|
||||
"
|
||||
|
||||
export STREAM=${STREAM:-0}
|
||||
export CDN=${CDN:-0}
|
||||
|
||||
qrcode() {
|
||||
echo "█████████████████████████████████████████"
|
||||
echo "█████████████████████████████████████████"
|
||||
@@ -189,7 +192,8 @@ fi
|
||||
info "创建安装目录 '$safeline_path' 成功"
|
||||
cd "$safeline_path"
|
||||
|
||||
curl -sS -k "https://waf-ce.chaitin.cn/release/latest/compose.yaml" -o compose.yaml
|
||||
curl "https://waf-ce.chaitin.cn/release/latest/compose.yaml" -sSLk -o compose.yaml
|
||||
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
fi
|
||||
@@ -202,12 +206,23 @@ fi
|
||||
info "创建 .env 脚本成功"
|
||||
|
||||
echo "SAFELINE_DIR=$safeline_path" >> .env
|
||||
echo "IMAGE_TAG=latest" >> .env
|
||||
|
||||
if [ $STREAM -eq 1 ]; then
|
||||
echo "IMAGE_TAG=latest-stream" >>".env"
|
||||
else
|
||||
echo "IMAGE_TAG=latest" >>".env"
|
||||
fi
|
||||
|
||||
echo "MGT_PORT=9443" >> .env
|
||||
echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> .env
|
||||
echo "SUBNET_PREFIX=172.22.222" >> .env
|
||||
|
||||
if [ $CDN -eq 0 ]; then
|
||||
echo "IMAGE_PREFIX=chaitin" >>".env"
|
||||
else
|
||||
echo "IMAGE_PREFIX=swr.cn-east-3.myhuaweicloud.com/chaitin-safeline" >>".env"
|
||||
fi
|
||||
|
||||
info "即将开始下载 Docker 镜像"
|
||||
|
||||
$compose_command up -d
|
||||
|
||||
@@ -8,6 +8,9 @@ echo "
|
||||
|____/ \__,_| |_| \___| |_____| |_| |_| |_| \___|
|
||||
"
|
||||
|
||||
export STREAM=${STREAM:-0}
|
||||
export CDN=${CDN:-0}
|
||||
|
||||
echo $1
|
||||
|
||||
qrcode() {
|
||||
@@ -40,15 +43,15 @@ qrcode() {
|
||||
}
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" 2>&1
|
||||
command -v "$1" 2>&1
|
||||
}
|
||||
|
||||
space_left() {
|
||||
dir="$1"
|
||||
while [ ! -d "$dir" ]; do
|
||||
dir=`dirname "$dir"`;
|
||||
dir=$(dirname "$dir")
|
||||
done
|
||||
echo `df -h "$dir" --output='avail' | tail -n 1`
|
||||
echo $(df -h "$dir" --output='avail' | tail -n 1)
|
||||
}
|
||||
|
||||
confirm() {
|
||||
@@ -58,9 +61,9 @@ confirm() {
|
||||
[[ "$opt" == $'\n' ]] || echo
|
||||
|
||||
case "$opt" in
|
||||
'y' | 'Y' ) return 0;;
|
||||
'n' | 'N' ) return 1;;
|
||||
*) confirm "$1";;
|
||||
'y' | 'Y') return 0 ;;
|
||||
'n' | 'N') return 1 ;;
|
||||
*) confirm "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -86,13 +89,13 @@ onexit() {
|
||||
|
||||
# CPU ssse3 指令集检查
|
||||
support_ssse3=1
|
||||
lscpu | grep ssse3 > /dev/null 2>&1
|
||||
lscpu | grep ssse3 >/dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
echo "not found info in lscpu"
|
||||
support_ssse3=0
|
||||
fi
|
||||
|
||||
cat /proc/cpuinfo | grep ssse3 > /dev/null 2>&1
|
||||
cat /proc/cpuinfo | grep ssse3 >/dev/null 2>&1
|
||||
if [ $support_ssse3 -eq "0" -a $? -ne "0" ]; then
|
||||
abort "雷池需要运行在支持 ssse3 指令集的 CPU 上,虚拟机请自行配置开启 CPU ssse3 指令集支持"
|
||||
fi
|
||||
@@ -114,7 +117,7 @@ if [ "$EUID" -ne "0" ]; then
|
||||
fi
|
||||
info "脚本调用方式确认正常"
|
||||
|
||||
if [ -z `command_exists docker` ]; then
|
||||
if [ -z $(command_exists docker) ]; then
|
||||
warning "缺少 Docker 环境"
|
||||
if confirm "是否需要自动安装 Docker"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
@@ -126,9 +129,9 @@ if [ -z `command_exists docker` ]; then
|
||||
abort "中止安装"
|
||||
fi
|
||||
fi
|
||||
info "发现 Docker 环境: '`command -v docker`'"
|
||||
info "发现 Docker 环境: '$(command -v docker)'"
|
||||
|
||||
docker version > /dev/null 2>&1
|
||||
docker version >/dev/null 2>&1
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "Docker 服务工作异常"
|
||||
fi
|
||||
@@ -140,7 +143,7 @@ if $compose_command version; then
|
||||
else
|
||||
warning "未发现 Docker Compose Plugin"
|
||||
compose_command="docker-compose"
|
||||
if [ -z `command_exists "docker-compose"` ]; then
|
||||
if [ -z $(command_exists "docker-compose") ]; then
|
||||
warning "未发现 docker-compose 组件"
|
||||
if confirm "是否需要自动安装 Docker Compose Plugin"; then
|
||||
curl -sSLk https://get.docker.com/ | bash
|
||||
@@ -153,18 +156,17 @@ else
|
||||
abort "中止安装"
|
||||
fi
|
||||
else
|
||||
info "发现 docker-compose 组件: '`command -v docker-compose`'"
|
||||
info "发现 docker-compose 组件: '$(command -v docker-compose)'"
|
||||
fi
|
||||
fi
|
||||
|
||||
container_id=`docker ps --filter ancestor=chaitin/safeline-mgt-api --format '{{.ID}}'`
|
||||
mount_path=`docker inspect --format '{{range .Mounts}}{{if eq .Destination "/logs"}}{{.Source}}{{end}}{{end}}' $container_id`
|
||||
safeline_path=`dirname $mount_path`
|
||||
container_id=$(docker ps -n 1 --filter name=.*safeline-mgt.* --format '{{.ID}}')
|
||||
safeline_path=$(docker inspect --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' $container_id)
|
||||
|
||||
while [ -z "$safeline_path" ]; do
|
||||
echo -e -n "\033[34m[SafeLine] 未发现正在运行的雷池,请输入雷池安装路径 (留空则为 '`pwd`'): \033[0m"
|
||||
echo -e -n "\033[34m[SafeLine] 未发现正在运行的雷池,请输入雷池安装路径 (留空则为 '$(pwd)'): \033[0m"
|
||||
read input_path
|
||||
[[ -z "$input_path" ]] && input_path=`pwd`
|
||||
[[ -z "$input_path" ]] && input_path=$(pwd)
|
||||
|
||||
if [[ ! $input_path == /* ]]; then
|
||||
warning "'$input_path' 不是合法的绝对路径"
|
||||
@@ -176,12 +178,12 @@ done
|
||||
|
||||
cd "$safeline_path"
|
||||
|
||||
grep COLLIE .env > /dev/null 2>&1
|
||||
grep COLLIE .env >/dev/null 2>&1
|
||||
if [ $? -eq "0" ]; then
|
||||
abort "检测到你的环境通过牧云主机助手安装,请使用牧云主机助手-应用市场进行升级."
|
||||
fi
|
||||
|
||||
compose_name=`ls docker-compose.yaml compose.yaml 2>/dev/null`
|
||||
compose_name=$(ls docker-compose.yaml compose.yaml 2>/dev/null)
|
||||
compose_path=$safeline_path/$compose_name
|
||||
|
||||
if [ -f "$compose_path" ]; then
|
||||
@@ -190,23 +192,45 @@ else
|
||||
abort "没有发现位于 $safeline_path 的雷池环境"
|
||||
fi
|
||||
|
||||
|
||||
mv $compose_name $compose_name.old
|
||||
|
||||
curl "https://waf-ce.chaitin.cn/release/latest/compose.yaml" -sSLk -o $compose_name
|
||||
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "下载 compose.yaml 脚本失败"
|
||||
fi
|
||||
info "下载 compose.yaml 脚本成功"
|
||||
|
||||
sed -i "s/IMAGE_TAG=.*/IMAGE_TAG=latest/g" ".env"
|
||||
if [ $STREAM -eq 1 ]; then
|
||||
sed -i "s/IMAGE_TAG=.*/IMAGE_TAG=latest-stream/g" ".env"
|
||||
else
|
||||
sed -i "s/IMAGE_TAG=.*/IMAGE_TAG=latest/g" ".env"
|
||||
fi
|
||||
|
||||
grep "SAFELINE_DIR" ".env" > /dev/null || echo "SAFELINE_DIR=$(pwd)" >> ".env"
|
||||
grep "IMAGE_TAG" ".env" > /dev/null || echo "IMAGE_TAG=latest" >> ".env"
|
||||
grep "MGT_PORT" ".env" > /dev/null || echo "MGT_PORT=9443" >> ".env"
|
||||
grep "POSTGRES_PASSWORD" ".env" > /dev/null || echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> ".env"
|
||||
grep "REDIS_PASSWORD" ".env" > /dev/null || echo "REDIS_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >> ".env"
|
||||
grep "SUBNET_PREFIX" ".env" > /dev/null || echo "SUBNET_PREFIX=172.22.222" >> ".env"
|
||||
grep "SAFELINE_DIR" ".env" >/dev/null || echo "SAFELINE_DIR=$(pwd)" >>".env"
|
||||
|
||||
if [ $STREAM -eq 1 ]; then
|
||||
grep "IMAGE_TAG" ".env" >/dev/null || echo "IMAGE_TAG=latest-stream" >>".env"
|
||||
else
|
||||
grep "IMAGE_TAG" ".env" >/dev/null || echo "IMAGE_TAG=latest" >>".env"
|
||||
fi
|
||||
|
||||
grep "MGT_PORT" ".env" >/dev/null || echo "MGT_PORT=9443" >>".env"
|
||||
grep "POSTGRES_PASSWORD" ".env" >/dev/null || echo "POSTGRES_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" >>".env"
|
||||
grep "SUBNET_PREFIX" ".env" >/dev/null || echo "SUBNET_PREFIX=172.22.222" >>".env"
|
||||
|
||||
|
||||
if [ $CDN -eq 0 ]; then
|
||||
sed -i "s/IMAGE_PREFIX=.*/IMAGE_PREFIX=chaitin/g" ".env"
|
||||
else
|
||||
sed -i "s/IMAGE_PREFIX=.*/IMAGE_PREFIX=swr.cn-east-3.myhuaweicloud.com\/chaitin-safeline/g" ".env"
|
||||
fi
|
||||
|
||||
if [ $CDN -eq 0 ]; then
|
||||
grep "IMAGE_PREFIX" ".env" >/dev/null || echo "IMAGE_PREFIX=chaitin" >>".env"
|
||||
else
|
||||
grep "IMAGE_PREFIX" ".env" >/dev/null || echo "IMAGE_PREFIX=swr.cn-east-3.myhuaweicloud.com/chaitin-safeline" >>".env"
|
||||
fi
|
||||
|
||||
info "升级 .env 脚本成功"
|
||||
|
||||
@@ -220,7 +244,10 @@ info "下载新版本 Docker 镜像成功"
|
||||
|
||||
info "即将开始替换 Docker 容器"
|
||||
|
||||
$compose_command down && $compose_command up -d
|
||||
# 升级到 3.14.0 版本时,移除了 safeline-redis 容器,需要删除容器,否则无法启动新 compose 网络
|
||||
docker rm -f safeline-redis &>/dev/null
|
||||
|
||||
$compose_command down --remove-orphans && $compose_command up -d
|
||||
if [ $? -ne "0" ]; then
|
||||
abort "替换 Docker 容器失败"
|
||||
fi
|
||||
@@ -230,4 +257,3 @@ qrcode
|
||||
|
||||
warning "雷池 WAF 社区版安装成功, 请访问以下地址访问控制台"
|
||||
warning "https://0.0.0.0:9443/"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"latest_version": "v3.14.0",
|
||||
"rec_version": "v3.8.2"
|
||||
"latest_version": "v4.1.1",
|
||||
"rec_version": "v3.11.1"
|
||||
}
|
||||
44
website/next-sitemap.config.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/** @type {import('next-sitemap').IConfig} */
|
||||
const nextSiteMapConfig = {
|
||||
siteUrl: 'https://waf-ce.chaitin.cn',
|
||||
generateRobotsTxt: true,
|
||||
robotsTxtOptions: {
|
||||
policies: [{ userAgent: '*', allow: '/', disallow: '' }],
|
||||
},
|
||||
sitemap: {
|
||||
// path: '/sitemap.xml',
|
||||
routes: {
|
||||
'/community': {
|
||||
changefreq: 'always',
|
||||
},
|
||||
},
|
||||
},
|
||||
autoLastmod: true,
|
||||
priority: 1,
|
||||
changefreq: 'daily',
|
||||
sitemapSize: 5000,
|
||||
transform: async (config, path) => {
|
||||
if (!path) {
|
||||
return null
|
||||
}
|
||||
const customFields = config.sitemap.routes[path] || {}
|
||||
|
||||
return {
|
||||
loc: path,
|
||||
changefreq: customFields.changefreq || config.changefreq,
|
||||
priority: config.priority,
|
||||
lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
|
||||
alternateRefs: config.alternateRefs ?? [],
|
||||
}
|
||||
},
|
||||
additionalPaths: (config) => {
|
||||
const paths = ['/docs']
|
||||
const result = []
|
||||
paths.forEach(async (item) => {
|
||||
result.push(await config.transform(config, item))
|
||||
})
|
||||
return result
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextSiteMapConfig
|
||||
@@ -6,12 +6,12 @@ const nextConfig = {
|
||||
// These rewrites are checked after both pages/public files
|
||||
// and dynamic routes are checked
|
||||
{
|
||||
source: '/api/count',
|
||||
destination: 'https://waf-ce.chaitin.cn/api/count',
|
||||
source: '/api/safeline/count',
|
||||
destination: 'http://121.199.46.182/api/safeline/count',
|
||||
},
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: 'http://10.10.4.142:8080/api/:path*',
|
||||
destination: 'http://121.199.46.182/api/:path*',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
60
website/package-lock.json
generated
@@ -15,6 +15,7 @@
|
||||
"@mui/material": "5.14.3",
|
||||
"countup.js": "2.7.0",
|
||||
"next": "14.0.1",
|
||||
"next-sitemap": "^4.2.3",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
},
|
||||
@@ -252,6 +253,11 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@corex/deepmerge": {
|
||||
"version": "4.0.43",
|
||||
"resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-4.0.43.tgz",
|
||||
"integrity": "sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ=="
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz",
|
||||
@@ -992,7 +998,6 @@
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "2.0.5",
|
||||
"run-parallel": "^1.1.9"
|
||||
@@ -1005,7 +1010,6 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
@@ -1014,7 +1018,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": "2.1.5",
|
||||
"fastq": "^1.6.0"
|
||||
@@ -1505,7 +1508,6 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
@@ -2403,7 +2405,6 @@
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.1.tgz",
|
||||
"integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
@@ -2419,7 +2420,6 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
@@ -2443,7 +2443,6 @@
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz",
|
||||
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
@@ -2464,7 +2463,6 @@
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -2931,7 +2929,6 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -2961,7 +2958,6 @@
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
@@ -2988,7 +2984,6 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -3303,7 +3298,6 @@
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
@@ -3312,7 +3306,6 @@
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz",
|
||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"braces": "^3.0.2",
|
||||
"picomatch": "^2.3.1"
|
||||
@@ -3336,8 +3329,7 @@
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
@@ -3418,6 +3410,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-sitemap": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.2.3.tgz",
|
||||
"integrity": "sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==",
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/iamvishnusankar/next-sitemap.git"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@corex/deepmerge": "^4.0.43",
|
||||
"@next/env": "^13.4.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
"minimist": "^1.2.8"
|
||||
},
|
||||
"bin": {
|
||||
"next-sitemap": "bin/next-sitemap.mjs",
|
||||
"next-sitemap-cjs": "bin/next-sitemap.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/next-sitemap/node_modules/@next/env": {
|
||||
"version": "13.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz",
|
||||
"integrity": "sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw=="
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.13.tgz",
|
||||
@@ -3680,7 +3703,6 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
}
|
||||
@@ -3838,8 +3860,7 @@
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
@@ -3972,7 +3993,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz",
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"iojs": ">=1.0.0",
|
||||
"node": ">=0.10.0"
|
||||
@@ -3994,7 +4014,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
@@ -4396,7 +4415,6 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start -p 3001",
|
||||
"lint": "next lint"
|
||||
"lint": "next lint",
|
||||
"sitemap_build": "next-sitemap"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "11.11.1",
|
||||
@@ -16,6 +17,7 @@
|
||||
"@mui/material": "5.14.3",
|
||||
"countup.js": "2.7.0",
|
||||
"next": "14.0.1",
|
||||
"next-sitemap": "^4.2.3",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
},
|
||||
|
||||
4
website/public/BingSiteAuth.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0"?>
|
||||
<users>
|
||||
<user>3EF7E30C5A378E54709FA57D67499571</user>
|
||||
</users>
|
||||
1
website/public/googlef97f8402f9139518.html
Normal file
@@ -0,0 +1 @@
|
||||
google-site-verification: googlef97f8402f9139518.html
|
||||
BIN
website/public/images/community-banner-mobile.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 64 KiB |
BIN
website/public/images/version-banner-mobile.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
10
website/public/robots.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
# *
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow:
|
||||
|
||||
# Host
|
||||
Host: https://waf-ce.chaitin.cn
|
||||
|
||||
# Sitemaps
|
||||
Sitemap: https://waf-ce.chaitin.cn/sitemap.xml
|
||||
7
website/public/sitemap-0.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
|
||||
<url><loc>https://waf-ce.chaitin.cn</loc><lastmod>2023-12-12T07:31:45.326Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||
<url><loc>https://waf-ce.chaitin.cn/community</loc><lastmod>2023-12-12T07:31:45.326Z</lastmod><changefreq>always</changefreq><priority>1</priority></url>
|
||||
<url><loc>https://waf-ce.chaitin.cn/version</loc><lastmod>2023-12-12T07:31:45.326Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||
<url><loc>https://waf-ce.chaitin.cn/docs</loc><lastmod>2023-12-12T07:31:45.326Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||
</urlset>
|
||||
4
website/public/sitemap.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>https://waf-ce.chaitin.cn/sitemap-0.xml</loc></sitemap>
|
||||
</sitemapindex>
|
||||
@@ -2,6 +2,7 @@ export {
|
||||
getSetupCount,
|
||||
getDiscussions,
|
||||
getIssues,
|
||||
getReposInfo,
|
||||
};
|
||||
|
||||
const BASE_API = "/api";
|
||||
@@ -11,7 +12,11 @@ export {
|
||||
}
|
||||
|
||||
function getSetupCount() {
|
||||
return fetch("https://waf-ce.chaitin.cn/api/count").then((res) => res.json());
|
||||
return fetch(getAPIUrl() + "/safeline/count").then((res) => res.json());
|
||||
}
|
||||
|
||||
function getReposInfo() {
|
||||
return fetch(getAPIUrl() + "/repos/info").then((res) => res.json());
|
||||
}
|
||||
|
||||
function getDiscussions(query: string) {
|
||||
|
||||
@@ -45,3 +45,10 @@ export function formatDate(time: number): string {
|
||||
const day = date.getDate();
|
||||
return `${month} ${day}`;
|
||||
}
|
||||
|
||||
export function formatStarNumber(v: number) {
|
||||
if (v < 1000) {
|
||||
return v;
|
||||
}
|
||||
return Math.round(v / 100) / 10;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import Image from 'next/image'
|
||||
import { Box, Grid, Typography, Stack, SxProps, Container, Link } from '@mui/material';
|
||||
import WafTitle from './home/WafTitle'
|
||||
|
||||
const LINKS = [
|
||||
{
|
||||
@@ -24,7 +23,6 @@ const LINKS = [
|
||||
to: "/docs/about/changelog",
|
||||
},
|
||||
],
|
||||
xs: 6,
|
||||
},
|
||||
{
|
||||
title: "关于我们",
|
||||
@@ -38,13 +36,12 @@ const LINKS = [
|
||||
to: "https://stack.chaitin.cn/",
|
||||
},
|
||||
],
|
||||
xs: 12,
|
||||
},
|
||||
];
|
||||
|
||||
export const items = [
|
||||
{ to: "/community", label: "社区" },
|
||||
{ to: "/version", label: "版本对比" },
|
||||
{ to: "/community", label: "开发计划" },
|
||||
{ to: "/version", label: "付费版本" },
|
||||
{ to: "", label: "用户协议" },
|
||||
];
|
||||
|
||||
@@ -64,7 +61,29 @@ export default function Footer() {
|
||||
spacing={4}
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<WafTitle title="雷池 SafeLine" sx={{ marginLeft: '16px' }} />
|
||||
<Link href="/">
|
||||
<Grid container flexDirection="row" display="flex" alignItems="center" sx={{ marginTop: 0 }}>
|
||||
<Image
|
||||
src="/images/safeline.svg"
|
||||
alt="SafeLine Logo"
|
||||
width={40}
|
||||
height={43}
|
||||
/>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
color: "common.white",
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
marginLeft: '16px',
|
||||
fontSize: { xs: "40px", md: "28px" },
|
||||
position: "relative",
|
||||
bottom: 5,
|
||||
}}
|
||||
>
|
||||
雷池 SafeLine
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Link>
|
||||
<Box>
|
||||
{items.map((item, index) => (
|
||||
<Box key={index} component="span" mr={5}>
|
||||
@@ -83,7 +102,7 @@ export default function Footer() {
|
||||
</Stack>
|
||||
</Grid>
|
||||
{LINKS.map((link) => (
|
||||
<Grid item xs={12} md={5} my={{ xs: 4, md: 0 }} key={link.title}>
|
||||
<Grid item xs={8} md={5} my={{ xs: 4, md: 0 }} key={link.title}>
|
||||
<Stack
|
||||
id="groupchat"
|
||||
spacing={1}
|
||||
@@ -92,7 +111,7 @@ export default function Footer() {
|
||||
<Title title={link.title} />
|
||||
<Grid container>
|
||||
{link.items.map((item, index) => (
|
||||
<Grid key={index} item xs={link.xs} md={12}>
|
||||
<Grid key={index} item xs={12}>
|
||||
<Link sx={{ fontSize: '14px', color: "common.white", opacity: 0.5, fontWeight: 400, lineHeight: "38px" }} href={item.to} target="_blank" rel={item.label}>
|
||||
{item.label}
|
||||
</Link>
|
||||
@@ -102,7 +121,7 @@ export default function Footer() {
|
||||
</Stack>
|
||||
</Grid>
|
||||
))}
|
||||
<Grid item xs={24} md={4} display={'flex'} justifyContent={{ xs: "center", lg: "flex-end" }}>
|
||||
<Grid item xs={8} md={4} my={{ xs: 4, md: 0 }} display="flex" justifyContent={{ xs: "center", lg: "flex-end" }}>
|
||||
<Stack
|
||||
id="groupchat"
|
||||
spacing={2}
|
||||
@@ -118,7 +137,7 @@ export default function Footer() {
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container sx={{ pb: 1.5 }}>
|
||||
<Grid container sx={{ pb: 1.5 }} justifyContent={{ xs: "center", md: "flex-start" }}>
|
||||
<Typography
|
||||
variant="inherit"
|
||||
sx={{ fontSize: "12px", color: "rgba(255,255,255,0.26)", fontWeight: 400 }}
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { AppBar, Grid, Toolbar, Typography, Button, Box, Container, Link } from '@mui/material';
|
||||
import { AppBar, Drawer, Grid, Toolbar, Typography, Button, Box, Container, Link, List, ListItem, ListItemText, Stack, IconButton } from '@mui/material';
|
||||
import Image from 'next/image';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Icon from "@/components/Icon";
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import usePopupState, { bindPopover, bindHover } from '@/components/Popover/usePopupState'
|
||||
import HoverPopover from '@/components/Popover/HoverPopover'
|
||||
|
||||
const navs = [
|
||||
{ to: "/docs", label: "帮助文档", target: "_blank" },
|
||||
{ to: "/community", label: "社区", target: "_self" },
|
||||
{ to: "/version", label: "版本对比", target: "_self" },
|
||||
{ to: "/community", label: "开发计划", target: "_self" },
|
||||
{ to: "/version", label: "付费版本", target: "_self" },
|
||||
];
|
||||
|
||||
const menus = [
|
||||
...navs,
|
||||
{ to: "https://github.com/chaitin/SafeLine", label: "GitHub", target: "_blank" },
|
||||
{ to: "https://demo.waf-ce.chaitin.cn:9443/dashboard", label: "演示 Demo", target: "_blank" },
|
||||
];
|
||||
|
||||
const HoverPopover = dynamic(() => import('@/components/Popover/HoverPopover'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function NavBar() {
|
||||
const [isSticky, setIsSticky] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const popoverState = usePopupState({
|
||||
popupId: "wechat-qrcode-popover"
|
||||
@@ -32,116 +44,177 @@ export default function NavBar() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AppBar position='fixed' color='transparent'
|
||||
sx={{
|
||||
boxShadow: 'none',
|
||||
...(isSticky ? { backdropFilter: 'blur(8px)', background: 'rgba(255,255,255,0.8)' } : {}),
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="lg" sx={{ mx: 0 }}>
|
||||
<Toolbar sx={{ backgroundColor: 'transparent', py: 1, ml: 2, px: { sm: 0 } }}>
|
||||
<Grid container justifyContent="space-around">
|
||||
<Grid item xs={12} md={6} display="flex">
|
||||
<Box display="flex" alignItems="center">
|
||||
<Link href="/">
|
||||
<Grid container flexDirection="row" display="flex" spacing={2} sx={{ marginTop: '0px' }}>
|
||||
<Image
|
||||
src="/images/safeline.svg"
|
||||
alt="Waf Logo"
|
||||
width={24}
|
||||
height={26}
|
||||
/>
|
||||
<>
|
||||
<AppBar position='fixed' color='transparent'
|
||||
sx={{
|
||||
boxShadow: 'none',
|
||||
...(isSticky ? { backdropFilter: 'blur(8px)', background: 'rgba(255,255,255,0.8)' } : {}),
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="lg" sx={{ mx: 0 }}>
|
||||
<Toolbar sx={{ backgroundColor: 'transparent', py: 1, ml: 2, px: { sm: 0 } }}>
|
||||
<Grid container justifyContent="space-around">
|
||||
<Grid item xs={10} md={6} display="flex">
|
||||
<Box display="flex" alignItems="center">
|
||||
<SafelineTitle />
|
||||
<Box display={{ xs: 'none', md: 'flex' }} alignItems="center">
|
||||
{navs.map((nav, index) => (
|
||||
<Box component="span" key={index} mr={3.5}>
|
||||
<Link key={index} href={nav.to} sx={{ color: "common.black" }} target={nav.target}>{nav.label}</Link>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={2} md={6} display="flex" justifyContent="flex-end">
|
||||
<Box sx={{ fontSize: "16px", display: { xs: "none", md: "flex" }, alignItems: "center" }}>
|
||||
<Link
|
||||
href="https://github.com/chaitin/SafeLine"
|
||||
target="_blank"
|
||||
sx={{
|
||||
color: "common.black",
|
||||
display: "flex",
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
},
|
||||
}}
|
||||
mr={3.5}
|
||||
>
|
||||
<Icon type="icon-github-fill" sx={{ mr: 1 }} />
|
||||
GitHub
|
||||
</Link>
|
||||
<Box mr={3.5}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
{...bindHover(popoverState as any)}
|
||||
variant='body1'
|
||||
sx={{
|
||||
ml: 1,
|
||||
mr: 7,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
color: popoverState.isOpen ? 'primary.main' : 'common.black',
|
||||
cursor: "pointer",
|
||||
'&:hover': {
|
||||
color: "primary.main",
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
transition: 'unset',
|
||||
}}
|
||||
>
|
||||
雷池 SafeLine
|
||||
微信讨论组
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Link>
|
||||
<Box display={{ xs: 'none', md: 'flex' }} alignItems="center">
|
||||
{navs.map((nav, index) => (
|
||||
<Box component="span" key={index} mr={3.5}>
|
||||
<Link key={index} href={nav.to} sx={{ color: "common.black" }} target={nav.target}>{nav.label}</Link>
|
||||
</Box>
|
||||
))}
|
||||
<HoverPopover
|
||||
{...bindPopover(popoverState)}
|
||||
anchorOrigin={{
|
||||
vertical: 42,
|
||||
horizontal: 'left',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
marginThreshold={16}
|
||||
>
|
||||
<Image
|
||||
src="/images/wechat-230825.png"
|
||||
alt="wechat"
|
||||
width={259}
|
||||
height={259}
|
||||
/>
|
||||
</HoverPopover>
|
||||
</Box>
|
||||
<Link href="https://demo.waf-ce.chaitin.cn:9443/dashboard" sx={{ color: "common.black" }} mr={3.5} target="_blank">演示 Demo</Link>
|
||||
<Button
|
||||
variant="contained"
|
||||
target="_blank"
|
||||
sx={{ width: { xs: "100%", sm: "auto" } }}
|
||||
href="/docs/guide/install"
|
||||
>
|
||||
立即安装
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={0} md={6} display={{ xs: 'none', md: 'flex' }} justifyContent={'flex-end'}>
|
||||
<Box sx={{ fontSize: "16px", display: "flex", alignItems: "center" }}>
|
||||
<Link
|
||||
href="https://github.com/chaitin/SafeLine"
|
||||
target="_blank"
|
||||
sx={{
|
||||
color: "common.black",
|
||||
display: "flex",
|
||||
"&:hover": {
|
||||
color: "primary.main",
|
||||
},
|
||||
}}
|
||||
mr={3.5}
|
||||
>
|
||||
<Icon type="icon-github-fill" sx={{ mr: 1 }} />
|
||||
GitHub
|
||||
</Link>
|
||||
<Box mr={3.5}>
|
||||
<Typography
|
||||
{...bindHover(popoverState as any)}
|
||||
variant='body1'
|
||||
<Stack justifyContent="center">
|
||||
<Icon
|
||||
type="icon-caidan"
|
||||
color="common.black"
|
||||
sx={{
|
||||
color: popoverState.isOpen ? 'primary.main' : 'common.black',
|
||||
cursor: "pointer",
|
||||
'&:hover': {
|
||||
color: "primary.main",
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
transition: 'unset',
|
||||
display: { xs: "flex", md: "none" },
|
||||
fontSize: "26px",
|
||||
}}
|
||||
>
|
||||
微信讨论组
|
||||
</Typography>
|
||||
<HoverPopover
|
||||
{...bindPopover(popoverState)}
|
||||
anchorOrigin={{
|
||||
vertical: 42,
|
||||
horizontal: 'left',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
marginThreshold={16}
|
||||
>
|
||||
<Image
|
||||
src="/images/wechat-230825.png"
|
||||
alt="wechat"
|
||||
width={259}
|
||||
height={259}
|
||||
/>
|
||||
</HoverPopover>
|
||||
</Box>
|
||||
<Link href="https://demo.waf-ce.chaitin.cn:9443/dashboard" sx={{ color: "common.black" }} mr={3.5} target="_blank">演示 demo</Link>
|
||||
<Button
|
||||
variant="contained"
|
||||
target="_blank"
|
||||
sx={{ width: { xs: "100%", sm: "auto" } }}
|
||||
href="/docs/guide/install"
|
||||
>
|
||||
立即安装
|
||||
</Button>
|
||||
</Box>
|
||||
onClick={() => setOpen(true)}
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
<Drawer
|
||||
anchor='right'
|
||||
sx={{ width: "100%" }}
|
||||
variant="temporary"
|
||||
open={open}
|
||||
PaperProps={{
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<Stack direction="row" justifyContent="space-between" pl={4} pr={0.5} py={1} sx={{ boxShadow: "rgba(0, 0, 0, 0.1) 0px 1px 2px 0px" }}>
|
||||
<Box>
|
||||
<SafelineTitle />
|
||||
</Box>
|
||||
<IconButton onClick={() => setOpen(false)}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
<List>
|
||||
{menus.map((menu) => (
|
||||
<Link key={menu.label} href={menu.to} target={menu.target}>
|
||||
<ListItem>
|
||||
<ListItemText primary={menu.label} />
|
||||
</ListItem>
|
||||
</Link>
|
||||
))}
|
||||
</List>
|
||||
<Box ml={2}>
|
||||
<Image
|
||||
src="/images/wechat-230825.png"
|
||||
alt="wechat"
|
||||
width={160}
|
||||
height={160}
|
||||
/>
|
||||
</Box>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const SafelineTitle: React.FC = () => {
|
||||
return (
|
||||
<Link href="/">
|
||||
<Grid container flexDirection="row" display="flex" spacing={2} sx={{ marginTop: '0px', minWidth: "192px" }}>
|
||||
<Box width={{ xs: "40px", md: "24px" }} height={{ xs: "43px", md: "26px" }} position="relative">
|
||||
<Image
|
||||
src="/images/safeline.svg"
|
||||
alt="SafeLine Logo"
|
||||
layout="responsive"
|
||||
width={40}
|
||||
height={43}
|
||||
/>
|
||||
</Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
ml: { xs: 2, md: 1 },
|
||||
mr: { xs: 0, md: 7 },
|
||||
fontSize: { xs: "24px", md: "16px" },
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
}}
|
||||
>
|
||||
雷池 SafeLine
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -32,9 +32,9 @@ const Illustrate = ({ text }: { text: string }) => {
|
||||
};
|
||||
|
||||
const versions = [
|
||||
{ title: '社区版', key: 'experience', width: '33%' },
|
||||
// { title: '专业版', key: 'major', width: '25%' },
|
||||
{ title: '企业版', key: 'basics', width: '33%' },
|
||||
{ title: '社区版', key: 'experience' },
|
||||
// { title: '专业版', key: 'major' },
|
||||
{ title: '企业版', key: 'basics' },
|
||||
]
|
||||
|
||||
const colors = ['light', 'main'] // 'lighter',
|
||||
@@ -150,7 +150,7 @@ const FunctionTable = () => {
|
||||
{
|
||||
name: "多设备集中管理",
|
||||
tip: "",
|
||||
experience: <Support />,
|
||||
experience: <NotSupport />,
|
||||
major: <Support />,
|
||||
basics: <Support />,
|
||||
},
|
||||
@@ -271,9 +271,9 @@ const FunctionTable = () => {
|
||||
>
|
||||
<TableHead sx={{ background: "transparent" }}>
|
||||
<TableRow sx={{ border: "0" }}>
|
||||
<TableCell sx={{ width: "33%" }} />
|
||||
<TableCell sx={{ width: { xs: "50%", sm: "33%" }}}/>
|
||||
{versions.map((item, index) => (
|
||||
<TableCell key={item.title} align="center" sx={{ width: item.width, fontSize: "16px" }}>
|
||||
<TableCell key={item.title} align="center" sx={{ width: { xs: "25%", sm: "33%" }, fontSize: "16px" }}>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
display: "flex",
|
||||
@@ -354,13 +354,13 @@ const FunctionTable = () => {
|
||||
>
|
||||
{data.data.map((item) => (
|
||||
<TableRow key={item.name}>
|
||||
<TableCell sx={{ width: "33%" }}>
|
||||
<TableCell sx={{ width: { xs: "50%", sm: "33%" }}}>
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 400 }}>{item.name}</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
{versions.map((v, index) => (
|
||||
<TableCell key={index} sx={{ width: v.width }} align="center">
|
||||
<TableCell key={index} sx={{ width: { xs: "25%", sm: "33%" } }} align="center">
|
||||
{item[v.key as 'experience' | 'major' | 'basics']}
|
||||
</TableCell>
|
||||
))}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import React from "react";
|
||||
import { alpha, Box, Button, Typography } from "@mui/material";
|
||||
import dynamic from 'next/dynamic';
|
||||
import FunctionTable from "./FunctionTable";
|
||||
import Consultation from "./Consultation";
|
||||
|
||||
const Consultation = dynamic(() => import('./Consultation'), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const VERSION_LIST = [
|
||||
{
|
||||
@@ -118,7 +122,7 @@ const Version = () => {
|
||||
py: 2,
|
||||
borderRadius: '12px 12px 0px 0px',
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "right bottom",
|
||||
backgroundPosition: "center center",
|
||||
textAlign: "center",
|
||||
backgroundImage: `url(${item.name_bg})`,
|
||||
}}
|
||||
@@ -147,7 +151,6 @@ const Version = () => {
|
||||
</Typography>
|
||||
)}
|
||||
</Typography>
|
||||
{/* <Description content={item.desc} /> */}
|
||||
<Box>{item.operation}</Box>
|
||||
<FunctionItems items={item.functions} />
|
||||
</Box>
|
||||
@@ -156,7 +159,11 @@ const Version = () => {
|
||||
</Box>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{ fontSize: "48px", fontWeight: 600, mt: "180px !important" }}
|
||||
sx={{
|
||||
fontSize: "48px",
|
||||
fontWeight: 600,
|
||||
mt: { xs: "64px !important", md: "180px !important" },
|
||||
}}
|
||||
>
|
||||
细节对比
|
||||
</Typography>
|
||||
|
||||
@@ -31,7 +31,7 @@ type User = {
|
||||
login: string
|
||||
}
|
||||
|
||||
type Discussion = {
|
||||
export type Discussion = {
|
||||
id: string
|
||||
labels: { name: string, color: string }[]
|
||||
thumbs_up: number
|
||||
@@ -74,12 +74,12 @@ export default function DiscussionList({ value }: DiscussionListProps) {
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={4} display="flex" alignItems="center">
|
||||
<Grid item xs={12} sm={4} mb={{ xs: 4, sm: 0 }} display="flex" justifyContent={{ xs: "center", sm: "flex-start" }}>
|
||||
<Stack direction="row">
|
||||
<Typography variant="h6" sx={{ mr: 2 }}>Discussions</Typography>
|
||||
<Typography variant="h5" sx={{ mr: 2 }}>讨论</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
<Grid item xs={12} sm={8}>
|
||||
<Stack direction="row" justifyContent={{ xs: "flex-start", md: "flex-end" }}>
|
||||
<Paper
|
||||
sx={{
|
||||
@@ -107,11 +107,10 @@ export default function DiscussionList({ value }: DiscussionListProps) {
|
||||
variant="contained"
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "102px" },
|
||||
ml: { xs: 2 },
|
||||
ml: 2 ,
|
||||
minWidth: "80px",
|
||||
height: "36px",
|
||||
whiteSpace: 'nowrap',
|
||||
fontSize: { xs: '12px', md: '14px' }
|
||||
}}
|
||||
href="https://github.com/chaitin/SafeLine/discussions"
|
||||
>
|
||||
|
||||
@@ -29,63 +29,36 @@ type Issue = {
|
||||
url: string
|
||||
comment_count: number
|
||||
created_at: number
|
||||
updated_at: number
|
||||
author: {
|
||||
avatar_url: string,
|
||||
login: string,
|
||||
}
|
||||
}
|
||||
|
||||
export type Issues = {
|
||||
in_consideration?: Issue[]
|
||||
in_progress?: Issue[]
|
||||
released?: Issue[]
|
||||
}
|
||||
|
||||
interface IssueListProps {
|
||||
value: Issue[];
|
||||
value: Issues;
|
||||
}
|
||||
|
||||
const ROADMAP_TABS = [
|
||||
{ title: '正在考虑', key: 'inConsideration', color: '#FFBF00' },
|
||||
{ title: '进行中', key: 'inProgress', color: '#0FC6C2' },
|
||||
{ title: '正在考虑', key: 'in_consideration', color: '#FFBF00' },
|
||||
{ title: '进行中', key: 'in_progress', color: '#0FC6C2' },
|
||||
{ title: '最近完成', key: 'released', color: '#245CFF' },
|
||||
]
|
||||
|
||||
const isExistInLabels = (labels: Issue['labels'], label: string) => {
|
||||
return !!labels?.find((item: { name: string }) => item.name.includes(label))
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param issues
|
||||
* @returns
|
||||
* 正在考虑 = 带 enhancement,且没有 in progress 和 released,且 open
|
||||
* 进行中 = 带 enhancement 和 in progress,且 open
|
||||
* 最近完成 = 带 enhancement 和 released,且 open
|
||||
* 按点赞数量降序排序
|
||||
*/
|
||||
const handleSortIssues = (issues: Issue[]) => {
|
||||
const list = issues.filter((item: Issue) => isExistInLabels(item.labels, 'enhancement')).sort((item1, item2) => item2.thumbs_up - item1.thumbs_up)
|
||||
const inConsideration: Issue[] = []
|
||||
const inProgress: Issue[] = []
|
||||
const released: Issue[] = []
|
||||
list.forEach((item: Issue) => {
|
||||
const { labels } = item
|
||||
if (isExistInLabels(labels, 'in progress')) {
|
||||
inProgress.push(item)
|
||||
} else if (isExistInLabels(labels, 'released')) {
|
||||
released.push(item)
|
||||
} else {
|
||||
inConsideration.push(item)
|
||||
}
|
||||
})
|
||||
return {
|
||||
inConsideration,
|
||||
inProgress,
|
||||
released,
|
||||
}
|
||||
}
|
||||
|
||||
function IssueList({ value }: IssueListProps) {
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const [issues, setIssues] = useState<Record<string, Array<Issue>>>(handleSortIssues(value || []))
|
||||
const [issues, setIssues] = useState<Record<string, Array<Issue>>>(value || {})
|
||||
|
||||
const handleSearch = async() => {
|
||||
const result = await getIssues(searchText)
|
||||
setIssues(handleSortIssues(result || []))
|
||||
setIssues(result || {})
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: any) => {
|
||||
@@ -98,12 +71,12 @@ function IssueList({ value }: IssueListProps) {
|
||||
<>
|
||||
<Box sx={{ pb: 3 }}>
|
||||
<Grid container>
|
||||
<Grid item xs={4} display="flex" alignItems="center">
|
||||
<Grid item xs={12} sm={4} mb={{ xs: 4, sm: 0 }} display="flex" justifyContent={{ xs: "center", sm: "flex-start" }}>
|
||||
<Stack direction="row">
|
||||
<Typography variant="h6" sx={{ mr: 2 }}>Roadmap</Typography>
|
||||
<Typography variant="h5" sx={{ mr: 2 }}>开发计划</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
<Grid item xs={12} sm={8}>
|
||||
<Stack direction="row" justifyContent={{ xs: "flex-start", md: "flex-end" }}>
|
||||
<Paper
|
||||
sx={{
|
||||
@@ -131,11 +104,10 @@ function IssueList({ value }: IssueListProps) {
|
||||
variant="contained"
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "102px" },
|
||||
ml: { xs: 2 },
|
||||
minWidth: "80px",
|
||||
height: "36px",
|
||||
whiteSpace: 'nowrap',
|
||||
fontSize: { xs: '12px', md: '14px' }
|
||||
}}
|
||||
href="https://github.com/chaitin/SafeLine/issues"
|
||||
>
|
||||
@@ -166,10 +138,13 @@ function IssueList({ value }: IssueListProps) {
|
||||
}}
|
||||
>
|
||||
<Box px={2} py={2}>
|
||||
<Typography variant="h6" color={tab.color}>{tab.title}</Typography>
|
||||
<List sx={{ py: 0, maxHeight: "830px", overflowY: "auto" }}>
|
||||
{issues[tab.key].map((issue) => (
|
||||
<ListItem key={issue.id} sx={{ pt: 2, pb: 0, px: 0 }}>
|
||||
<Typography variant="h6" color={tab.color} mb={2}>
|
||||
{tab.title}
|
||||
({issues[tab.key]?.length || 0})
|
||||
</Typography>
|
||||
<List sx={{ py: 0, maxHeight: "790px", overflowY: "auto" }}>
|
||||
{issues[tab.key]?.map((issue, index) => (
|
||||
<ListItem key={issue.id} sx={{ pt: index > 0 ? 2 : 0, pb: 0, px: 0 }}>
|
||||
<IssueItem issue={issue} />
|
||||
</ListItem>
|
||||
))}
|
||||
|
||||
@@ -61,30 +61,29 @@ const Abilities = () => {
|
||||
sx={{
|
||||
background: "#111227",
|
||||
color: "common.white",
|
||||
pt: 18,
|
||||
pb: 27,
|
||||
pt: { xs: 8, md: 18 },
|
||||
pb: { xs: 8, md: 27 },
|
||||
px: 2,
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="lg">
|
||||
<Grid container alignItems="center">
|
||||
<Grid item xs={12} sm={12} md={6}>
|
||||
<Typography variant="h2" mb={4.5}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="h2" mb={4.5} textAlign={{ xs: "center", md: "left" }} fontSize={{ xs: "32px", md: "48px" }}>
|
||||
多维能力拓展
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
{ABILITY_LIST.map((ability) => (
|
||||
<AbilityItem
|
||||
key={ability.title}
|
||||
title={ability.title}
|
||||
img={ability.img}
|
||||
href={ability.href}
|
||||
ability={ability}
|
||||
hoveredUrl={hoveredUrl}
|
||||
handleIconHover={handleIconHover}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={6}>
|
||||
<Grid item xs={0} md={6} display={{ xs: "none", md: "block" }}>
|
||||
<Box sx={{ width: { xs: "100%", sm: "100%" } }}>
|
||||
{ABILITY_LIST.map((ability) => (
|
||||
<Image
|
||||
@@ -107,16 +106,14 @@ const Abilities = () => {
|
||||
export default Abilities;
|
||||
|
||||
interface ItemProps {
|
||||
title: string;
|
||||
href?: string;
|
||||
img?: string;
|
||||
ability: any
|
||||
hoveredUrl?: string;
|
||||
handleIconHover: Function;
|
||||
}
|
||||
|
||||
const AbilityItem: React.FC<ItemProps> = ({
|
||||
title,
|
||||
href,
|
||||
img,
|
||||
ability,
|
||||
hoveredUrl,
|
||||
handleIconHover,
|
||||
}) => {
|
||||
return (
|
||||
@@ -128,12 +125,12 @@ const AbilityItem: React.FC<ItemProps> = ({
|
||||
borderRadius: "12px",
|
||||
width: { xs: "100%", lg: "274px" },
|
||||
}}
|
||||
onMouseEnter={() => handleIconHover(img)}
|
||||
onMouseEnter={() => handleIconHover(ability.img)}
|
||||
onMouseLeave={() => {}}
|
||||
onClick={() => handleIconHover(img)}
|
||||
onClick={() => handleIconHover(ability.img)}
|
||||
>
|
||||
{href ? (
|
||||
<Link href={href} target="_blank" rel={title}>
|
||||
{ability.href ? (
|
||||
<Link href={ability.href} target="_blank" rel={ability.title}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
px={3}
|
||||
@@ -152,7 +149,7 @@ const AbilityItem: React.FC<ItemProps> = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
{ability.title}
|
||||
<Icon type="icon-youjiantouxian" />
|
||||
</Typography>
|
||||
</Link>
|
||||
@@ -168,7 +165,7 @@ const AbilityItem: React.FC<ItemProps> = ({
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
{ability.title}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -19,7 +19,7 @@ const FEATURE_LIST = [
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} ml={2} mt={14}>
|
||||
<Grid item xs={12} md={4} ml={2} mt={{ xs: 3, md: 14 }}>
|
||||
<List>
|
||||
<ListItem sx={{ mb: 1 }}>
|
||||
<ListItemText sx={{ textIndent: '-0.75rem' }} primary="· 国内首创、业内领先的智能语义分析算法" />
|
||||
@@ -37,9 +37,10 @@ const FEATURE_LIST = [
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "146px" },
|
||||
height: "50px",
|
||||
height: { xs: "72px", sm: "50px" },
|
||||
ml: { xs: 0, sm: 2 },
|
||||
mb: { xs: 2, sm: 0 },
|
||||
fontSize: { xs: "24px", sm: "14px" },
|
||||
}}
|
||||
href="/docs/about/syntaxanalysis"
|
||||
>
|
||||
@@ -61,7 +62,7 @@ const FEATURE_LIST = [
|
||||
/>
|
||||
<Image
|
||||
src="/images/feature1.svg"
|
||||
alt=""
|
||||
alt="SQL注入,CSRF,XSS,SSRF,..."
|
||||
layout="responsive"
|
||||
width={100}
|
||||
height={100}
|
||||
@@ -77,11 +78,11 @@ const FEATURE_LIST = [
|
||||
desc: "轻松上手,实现躺平式管理",
|
||||
icon: "/images/feature2-icon.png",
|
||||
content: (
|
||||
<Grid container position={'relative'} sx={{ mb: 5 }}>
|
||||
<Grid container position="relative" sx={{ mb: 5 }} ml={{ xs: 3, md: 0 }}>
|
||||
<Grid container>
|
||||
<Grid item xs={12} md={6} mt={7}>
|
||||
<Grid item xs={12} md={6} mt={7} order={{ xs: 2, md: 1 }}>
|
||||
<Box
|
||||
position={'relative'}
|
||||
position="relative"
|
||||
>
|
||||
<Image
|
||||
src="/images/feature2-bg.png"
|
||||
@@ -94,7 +95,7 @@ const FEATURE_LIST = [
|
||||
/>
|
||||
<Image
|
||||
src="/images/feature2.svg"
|
||||
alt=""
|
||||
alt="开箱即用,轻松上手,适配多种运行环境"
|
||||
layout="responsive"
|
||||
width={100}
|
||||
height={100}
|
||||
@@ -103,7 +104,7 @@ const FEATURE_LIST = [
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} mt={14}>
|
||||
<Grid item xs={12} md={4} mt={{ xs: 3, md: 14 }} order={{ xs: 1, md: 2 }}>
|
||||
<List>
|
||||
<ListItem sx={{ mb: 1 }}>
|
||||
<ListItemText sx={{ textIndent: '-0.75rem' }} primary="· 一键安装,容器式管理,适配多种运行环境" />
|
||||
@@ -135,7 +136,7 @@ const FEATURE_LIST = [
|
||||
desc: "无规则引擎,线性安全检测算法",
|
||||
icon: "/images/feature3-icon.png",
|
||||
content: (
|
||||
<Grid container display={'flex'} justifyContent={'flex-end'} position={'relative'} sx={{ mb: 10 }}>
|
||||
<Grid container display="flex" justifyContent="flex-end" position="relative" sx={{ mb: 10 }} ml={{ xs: 3, md: 0 }}>
|
||||
<Grid item xs={0} md={2} sx={{ display: { xs: 'none', md: 'block' }, position: 'absolute', left: 0 }}>
|
||||
<Box>
|
||||
<Image
|
||||
@@ -146,7 +147,7 @@ const FEATURE_LIST = [
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} mt={14}>
|
||||
<Grid item xs={12} md={4} mt={{ xs: 3, md: 14 }}>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemText sx={{ textIndent: '-0.75rem' }} primary="· 平均检测延迟 < 1 毫秒,单核轻松检测 2000+ TPS 并发" />
|
||||
@@ -157,10 +158,7 @@ const FEATURE_LIST = [
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} mt={7}>
|
||||
<Box
|
||||
position={'relative'}
|
||||
width={{ xs: "80%", md: "100%" }}
|
||||
>
|
||||
<Box position={'relative'}>
|
||||
<Image
|
||||
src="/images/feature3-bg.png"
|
||||
alt=""
|
||||
@@ -170,7 +168,7 @@ const FEATURE_LIST = [
|
||||
/>
|
||||
<Image
|
||||
src="/images/feature3.svg"
|
||||
alt=""
|
||||
alt="性能,服务可用性99.99%"
|
||||
layout="responsive"
|
||||
width={100}
|
||||
height={100}
|
||||
@@ -194,7 +192,11 @@ const Features = () => {
|
||||
<Grid container>
|
||||
{FEATURE_LIST.map((feature, index) => (
|
||||
<Grid item xs={12} key={feature.title}>
|
||||
<Box display={{ xs: "block", md: index % 2 == 1 ? 'flex' : ''}} alignItems={'flex-end'} flexDirection={index % 2 == 1 ? 'column' : 'row'}>
|
||||
<Box
|
||||
display={{ xs: "block", md: index % 2 == 1 ? 'flex' : ''}}
|
||||
alignItems="flex-end"
|
||||
flexDirection={index % 2 == 1 ? 'column' : 'row'}
|
||||
>
|
||||
<Box>
|
||||
<Image
|
||||
src={feature.icon}
|
||||
|
||||
@@ -53,15 +53,15 @@ const ARTICLE_LIST = [
|
||||
|
||||
const Partner = () => {
|
||||
return (
|
||||
<Container>
|
||||
<Container className='relative'>
|
||||
<Box
|
||||
sx={{
|
||||
px: 5,
|
||||
px: { xs: 0, sm: 5 },
|
||||
pt: 19,
|
||||
}}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
<Typography variant="h2" mb={2}>
|
||||
<Typography variant="h2" mb={2} fontSize={{ xs: "32px", md: "48px" }}>
|
||||
优秀企业的信赖之选
|
||||
</Typography>
|
||||
{ARTICLE_LIST.map((item) => (
|
||||
@@ -69,26 +69,27 @@ const Partner = () => {
|
||||
key={item}
|
||||
variant='h5'
|
||||
sx={{ color: "#86909C", fontWeight: 500, wordBreak: "break-word" }}
|
||||
mt={2}
|
||||
mt={{ xs: 1, md: 2 }}
|
||||
fontSize={{ xs: "18px", md: "24px" }}
|
||||
>
|
||||
<Icon type="icon-jingxuan" className="mr-3 relative top-1" sx={{ fontSize: "1.3em" }} />
|
||||
{item}
|
||||
</Typography>
|
||||
))}
|
||||
<Grid container mt={4} spacing={3} display="flex" justifyContent="center">
|
||||
<Grid container mt={4} display="flex" justifyContent="center">
|
||||
{PARTNER_LIST.map((item) => (
|
||||
<Grid item key={item.title} mb={1}>
|
||||
<Grid item key={item.title} mb={2} mr={{ xs: 2, md: 3 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: { sx: "140px", md: "160px" },
|
||||
height: { sx: "auto", md: "95px" },
|
||||
width: { xs: "108px", md: "160px" },
|
||||
height: { xs: "auto", md: "95px" },
|
||||
backgroundColor: "common.white",
|
||||
boxShadow: "0px 8px 24px -4px rgba(145,158,171,0.1)",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
className="flex justify-center items-center"
|
||||
>
|
||||
<Box sx={{ width: "140px" }}>
|
||||
<Box sx={{ width: { xs: "108px", md: "140px" }}}>
|
||||
<Image
|
||||
src={`/images/logo/${item.icon}`}
|
||||
alt={item.title}
|
||||
@@ -101,21 +102,22 @@ const Partner = () => {
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
<Box mt={6}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "146px" },
|
||||
height: "50px",
|
||||
ml: { xs: 2, sm: 2 },
|
||||
mb: { xs: 2, sm: 0 },
|
||||
}}
|
||||
href="https://www.chaitin.cn/zh/"
|
||||
>
|
||||
了解详情
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box mt={6} textAlign="center">
|
||||
<Button
|
||||
variant="outlined"
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "146px" },
|
||||
height: { xs: "72px", sm: "50px" },
|
||||
ml: { xs: 0, sm: 2 },
|
||||
mb: { xs: 2, sm: 0 },
|
||||
fontSize: { xs: "24px", sm: "14px" },
|
||||
}}
|
||||
href="https://www.chaitin.cn/zh/"
|
||||
>
|
||||
了解详情
|
||||
</Button>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
|
||||
73
website/src/components/home/Version.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import React from 'react'
|
||||
import { Box, Button, Container, Stack, Typography } from "@mui/material";
|
||||
import Image from 'next/image'
|
||||
|
||||
const Version = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: { xs: "205px", md: "343px" },
|
||||
mt: 19,
|
||||
backgroundImage: "url(/images/enterprise-bg.svg)",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
>
|
||||
<Container className="relative h-full" sx={{ px: 5 }}>
|
||||
<Stack justifyContent="center" className="h-full">
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
fontWeight: 400,
|
||||
color: "common.white",
|
||||
fontSize: { xs: "24px", md: "28px" },
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
letterSpacing: "2px",
|
||||
}}
|
||||
>
|
||||
欢迎使用雷池其他版本
|
||||
</Typography>
|
||||
<Button
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "384px", md: "146px" },
|
||||
height: { xs: "72px", md: "50px" },
|
||||
mt: 4,
|
||||
backgroundColor: "common.white",
|
||||
fontSize: { xs: "24px", md: "16px" },
|
||||
"&:hover": {
|
||||
color: "#0A8A87",
|
||||
backgroundColor: "common.white",
|
||||
},
|
||||
}}
|
||||
href="/version"
|
||||
>
|
||||
付费版本
|
||||
</Button>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: { xs: 0, md: -96 },
|
||||
top: { xs: -32, md: -65 },
|
||||
display: { xs: "none", sm: "block" },
|
||||
}}
|
||||
>
|
||||
<Box width={{ xs: 208, md: 417 }}>
|
||||
<Image
|
||||
src="/images/shield.png"
|
||||
alt="雷池"
|
||||
layout="responsive"
|
||||
width={417}
|
||||
height={359}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Version;
|
||||
@@ -1,31 +0,0 @@
|
||||
import React from "react";
|
||||
import Image from 'next/image'
|
||||
import { Typography, SxProps, Grid, Link } from "@mui/material";
|
||||
|
||||
interface TitleProps {
|
||||
title: string;
|
||||
sx?: SxProps;
|
||||
}
|
||||
|
||||
const Title: React.FC<TitleProps> = ({ title, sx }) => {
|
||||
return (
|
||||
<Link href="/">
|
||||
<Grid container flexDirection="row" display="flex" alignItems="center" sx={{ marginTop: 0 }}>
|
||||
<Image
|
||||
src="/images/safeline.svg"
|
||||
alt="Waf Logo"
|
||||
width={40}
|
||||
height={43}
|
||||
/>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{ color: "common.white", fontFamily: "AlimamaShuHeiTi-Bold", ...sx }}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default Title;
|
||||
@@ -2,27 +2,30 @@ import React from "react";
|
||||
import { Box, Grid, Button, Typography, Container, Stack } from "@mui/material";
|
||||
import Image from 'next/image';
|
||||
import Head from 'next/head';
|
||||
import DiscussionList from '@/components/community/DiscussionList';
|
||||
import IssueList from '@/components/community/IssueList';
|
||||
import DiscussionList, { Discussion } from '@/components/community/DiscussionList';
|
||||
import IssueList, { Issues } from '@/components/community/IssueList';
|
||||
import { getDiscussions, getIssues } from "@/api";
|
||||
|
||||
type CommunityPropsType = {
|
||||
discussions: any[];
|
||||
issues: any[];
|
||||
discussions: Discussion[];
|
||||
issues: Issues;
|
||||
};
|
||||
|
||||
export async function getServerSideProps() {
|
||||
let discussions = []
|
||||
let issues = []
|
||||
let discussions: Discussion[] = []
|
||||
let issues: Issues = {}
|
||||
|
||||
const promises = [
|
||||
getDiscussions('').then((result) => discussions = result || []),
|
||||
getIssues('').then((result) => issues = result || {}),
|
||||
];
|
||||
try {
|
||||
discussions = await getDiscussions('');
|
||||
issues = await getIssues('');
|
||||
|
||||
await Promise.allSettled(promises)
|
||||
} finally {
|
||||
return {
|
||||
props: {
|
||||
discussions: discussions || [],
|
||||
issues: issues || [],
|
||||
discussions: discussions,
|
||||
issues: issues,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -40,97 +43,91 @@ function Community({ discussions, issues }: CommunityPropsType) {
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "380px",
|
||||
backgroundImage: "url(/images/community-banner.png)",
|
||||
backgroundSize: "cover",
|
||||
height: { xs: "866px", md: "380px" },
|
||||
position: 'relative',
|
||||
backgroundImage: { xs: "url(/images/community-banner-mobile.png)", md: "url(/images/community-banner.png)" },
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: 'center center',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<Box pt={23}>
|
||||
<Typography variant="h2" sx={{ fontFamily: "AlimamaShuHeiTi-Bold" }}>通过社区获取更多帮助</Typography>
|
||||
<Container className="relative">
|
||||
<Box
|
||||
pt={{ xs: 19, md: 23 }}
|
||||
textAlign={{ xs: "center", md: "left" }}
|
||||
>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
fontSize: { xs: "32px", md: "48px" }
|
||||
}}>
|
||||
勇往直前,开创无限可能的未来
|
||||
</Typography>
|
||||
<Typography variant="subtitle1" sx={{ opacity: 0.5 }} mt={1}>同样欢迎你的参与</Typography>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
<Container sx={{ pt: 6, mb: 18 }}>
|
||||
<DiscussionList value={discussions} />
|
||||
</Container>
|
||||
<Container>
|
||||
<IssueList value={issues} />
|
||||
</Container>
|
||||
<Container>
|
||||
<Box
|
||||
px={6}
|
||||
py={6}
|
||||
sx={{
|
||||
mt: 19,
|
||||
background: "#111227",
|
||||
borderRadius: "16px",
|
||||
}}
|
||||
className="flex flex-col justify-center"
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Stack sx={{ color: "common.white" }}>
|
||||
<Typography variant="h3" sx={{ fontSize: "36px" }}>绕过反馈</Typography>
|
||||
<Typography mt={1} variant="body1" sx={{ opacity: 0.5 }}>向 CT Stack 安全社区提交雷池 XSS、SQL 绕过,可获取积分奖励</Typography>
|
||||
<Button
|
||||
variant="outlined"
|
||||
target="_blank"
|
||||
<Container sx={{ position: "relative", bottom: { xs: 124, md: 0 }, mb: { xs: "-124px", md: 0 } }}>
|
||||
<Container sx={{ pt: 6, mb: { xs: 10, sm: 18 }}}>
|
||||
<IssueList value={issues} />
|
||||
</Container>
|
||||
<Container>
|
||||
<DiscussionList value={discussions} />
|
||||
</Container>
|
||||
<Container>
|
||||
<Box
|
||||
px={6}
|
||||
py={6}
|
||||
sx={{
|
||||
mt: { xs: 10, sm: 19 },
|
||||
mb: { xs: 10, sm: 26 },
|
||||
background: "#111227",
|
||||
borderRadius: "16px",
|
||||
}}
|
||||
className="flex flex-col justify-center"
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Stack sx={{ color: "common.white" }}>
|
||||
<Typography variant="h3" sx={{ fontSize: "36px" }}>绕过反馈</Typography>
|
||||
<Typography mt={1} variant="body1" sx={{ opacity: 0.5 }}>向长亭提交雷池 XSS、SQL 绕过,您将获得现金和实物奖励</Typography>
|
||||
<Button
|
||||
variant="outlined"
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "146px" },
|
||||
height: "50px",
|
||||
mt: { xs: 4, sm: 10 },
|
||||
fontSize: "16px",
|
||||
backgroundColor: "common.white",
|
||||
}}
|
||||
href="https://stack.chaitin.com/security-challenge/safeline/index"
|
||||
>
|
||||
去提交
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6} display="flex" justifyContent={{ xs: 'center', md: 'flex-end' }} mt={{ xs: 2, md: 0 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: { xs: "146px" },
|
||||
height: "50px",
|
||||
mt: 10,
|
||||
fontSize: "16px",
|
||||
backgroundColor: "common.white",
|
||||
width: '367px',
|
||||
height: '169px',
|
||||
mt: { xs: 2, sm: 0 },
|
||||
}}
|
||||
href="https://stack.chaitin.com/security-challenge/safeline/index"
|
||||
>
|
||||
去提交
|
||||
</Button>
|
||||
</Stack>
|
||||
<Image
|
||||
src="/images/feedback.png"
|
||||
alt="XSS 挑战入口,SQL 挑战入口"
|
||||
layout="responsive"
|
||||
width={100}
|
||||
height={100}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6} display="flex" justifyContent={{ sx: 'flex-start', md: 'flex-end' }} mt={{ xs: 2, md: 0 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: { xs: '100%', md: '367px' },
|
||||
height: { xs: 'auto', md: '206px' }
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/images/feedback.png"
|
||||
alt=""
|
||||
layout="responsive"
|
||||
width={100}
|
||||
height={100}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Container>
|
||||
<Container>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundImage: "url(/images/partner-bg.png)",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: 'center center',
|
||||
backgroundRepeat: 'no-repeat'
|
||||
}}
|
||||
>
|
||||
<Box textAlign="center" pt={13} pb={12}>
|
||||
<Image
|
||||
src="/images/wechat-230825.png"
|
||||
alt="wechat"
|
||||
width={300}
|
||||
height={300}
|
||||
/>
|
||||
<Typography variant="h4" mt={3}>微信讨论组</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
</Container>
|
||||
</Box>
|
||||
</main>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { getSetupCount } from "@/api";
|
||||
import { getReposInfo, getSetupCount } from "@/api";
|
||||
import Features from "@/components/home/Features";
|
||||
import Abilities from "@/components/home/Abilities";
|
||||
import Partner from "@/components/home/Partner";
|
||||
import Version from "@/components/home/Version";
|
||||
import { formatStarNumber } from "@/common/utils";
|
||||
import {
|
||||
Box,
|
||||
Grid,
|
||||
@@ -15,17 +17,17 @@ import {
|
||||
import Image from "next/image";
|
||||
|
||||
const ARTICLES = [
|
||||
"《阮一峰·科技爱好者周刊》",
|
||||
"《Hello Github 月刊》",
|
||||
"《码农出击》",
|
||||
"《GitHub Daily》",
|
||||
"《Open Github 社区》",
|
||||
"《科技 lion》",
|
||||
{ title: '《阮一峰·科技爱好者周刊》', href: "https://www.ruanyifeng.com/blog/2023/11/weekly-issue-276.html", width: 250 },
|
||||
{ title: '《Hello Github 月刊》', href: "https://hellogithub.com/repository/0d07cfe266af4c25ba3eadf2c3d06f50", width: 206 },
|
||||
{ title: '《Apache APISIX》', href: "https://zhuanlan.zhihu.com/p/655041825", width: 186 },
|
||||
{ title: '《科技 lion》', href: "https://kejilion.blogspot.com/2023/11/npm-waf.html", width: 250 },
|
||||
{ title: '《Github 爱好者》', href: "https://mp.weixin.qq.com/s/CO-k2nv-PK0Ij-V5lTbUEQ", width: 206 },
|
||||
{ title: '《GitHub Daily》', href: "https://zhuanlan.zhihu.com/p/656047298", width: 186 },
|
||||
];
|
||||
|
||||
const totalSx = {
|
||||
color: "primary.main",
|
||||
fontSize: { xs: "58px", md: "70px" },
|
||||
fontSize: "70px",
|
||||
background: "linear-gradient(90deg, #8FE5D7 0%, #0FC6C2 100%)",
|
||||
"-webkit-background-clip": "text",
|
||||
"-webkit-text-fill-color": "transparent",
|
||||
@@ -33,27 +35,32 @@ const totalSx = {
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
};
|
||||
|
||||
const textAligns = ["left", "center", "right"];
|
||||
const justifyContents = ["flex-start", "center", "flex-end"];
|
||||
|
||||
export async function getServerSideProps() {
|
||||
let total = 46151;
|
||||
let total = 48750;
|
||||
let starCount = 6.5;
|
||||
const promises = [
|
||||
getSetupCount().then((result) => total = result.total),
|
||||
getReposInfo().then((result) => starCount = formatStarNumber(result.star_count)),
|
||||
];
|
||||
try {
|
||||
const result = await getSetupCount();
|
||||
total = result.total;
|
||||
await Promise.allSettled(promises)
|
||||
} finally {
|
||||
return {
|
||||
props: {
|
||||
total,
|
||||
starCount,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default function Home({ total }: { total: number }) {
|
||||
export default function Home({ total, starCount }: { total: number, starCount: number }) {
|
||||
const totalRef = useRef(null);
|
||||
const startRef = useRef(null);
|
||||
|
||||
const initTotal = async (n: number) => {
|
||||
const initTotal = async (n: number, starCount: number) => {
|
||||
const countUpModule = await import("countup.js");
|
||||
const anim = new countUpModule.CountUp(totalRef.current!, Math.max(0, n), {
|
||||
duration: 2,
|
||||
@@ -61,7 +68,7 @@ export default function Home({ total }: { total: number }) {
|
||||
anim.start();
|
||||
const startAnim = new countUpModule.CountUp(
|
||||
startRef.current!,
|
||||
Math.max(0, 6.4),
|
||||
Math.max(0, starCount),
|
||||
{
|
||||
duration: 2,
|
||||
decimalPlaces: 1,
|
||||
@@ -71,8 +78,8 @@ export default function Home({ total }: { total: number }) {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initTotal(total);
|
||||
}, [total]);
|
||||
initTotal(total, starCount);
|
||||
}, [total, starCount]);
|
||||
|
||||
return (
|
||||
<main className="flex flex-col justify-between" title="雷池 WAF 社区版">
|
||||
@@ -82,26 +89,33 @@ export default function Home({ total }: { total: number }) {
|
||||
width: "100%",
|
||||
height: "866px",
|
||||
position: "relative",
|
||||
backgroundImage: "url(/images/home-banner.png)",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
>
|
||||
<Box pt={26.5}>
|
||||
<Stack alignItems="center">
|
||||
<Image
|
||||
src="/images/home-banner.png"
|
||||
alt="雷池 SafeLine 主页背景"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
objectPosition="center"
|
||||
quality={100}
|
||||
// unoptimized={true}
|
||||
/>
|
||||
<Box pt={{ xs: 21, md: 26.5 }} className="relative" display={{ xs: "none", sm: "block" }}>
|
||||
<Box alignItems="center">
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
color: "#86909C",
|
||||
letterSpacing: 8,
|
||||
letterSpacing: { xs: 4, md: 8 },
|
||||
}}
|
||||
justifyContent="center"
|
||||
>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
mr: 35,
|
||||
mr: { xs: 22, md: 36.5 },
|
||||
fontWeight: 400,
|
||||
fontSize: { xs: "16px", md: "24px" },
|
||||
}}
|
||||
>
|
||||
基于智能语义分析的
|
||||
@@ -110,6 +124,7 @@ export default function Home({ total }: { total: number }) {
|
||||
variant="h5"
|
||||
sx={{
|
||||
fontWeight: 400,
|
||||
fontSize: { xs: "16px", md: "24px" },
|
||||
}}
|
||||
>
|
||||
下一代 Web 应用防火墙
|
||||
@@ -118,11 +133,11 @@ export default function Home({ total }: { total: number }) {
|
||||
<Stack
|
||||
direction="row"
|
||||
mt={2}
|
||||
justifyContent="center"
|
||||
sx={{
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
letterSpacing: 10,
|
||||
background:
|
||||
"linear-gradient(90deg, #160847 0%, #0A7977 100%)",
|
||||
letterSpacing: { xs: 5, md: 10 },
|
||||
background: "linear-gradient(90deg, #160847 0%, #0A7977 100%)",
|
||||
"-webkit-background-clip": "text",
|
||||
"-webkit-text-fill-color": "transparent",
|
||||
}}
|
||||
@@ -130,29 +145,53 @@ export default function Home({ total }: { total: number }) {
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
mr: 15.5,
|
||||
mr: { xs: 13.5, md: 15.5 },
|
||||
fontSize: { xs: "48px", md: "80px" },
|
||||
}}
|
||||
>
|
||||
不让黑客
|
||||
</Typography>
|
||||
<Typography variant="h1" sx={{}}>
|
||||
<Typography variant="h1" sx={{ fontSize: { xs: "48px", md: "80px" }, }}>
|
||||
越雷池一步
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box pt={{ xs: 16 }} className="relative" display={{ xs: "block", sm: "none" }}>
|
||||
<Stack alignItems="center">
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontSize: "32px",
|
||||
}}
|
||||
>
|
||||
不让黑客,越雷池一步
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
fontWeight: 400,
|
||||
mt: 2,
|
||||
fontSize: "16px",
|
||||
color: "#86909C",
|
||||
}}
|
||||
>
|
||||
基于智能语义分析的,下一代 Web 应用防火墙
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
bottom: 351,
|
||||
bottom: { xs: 436, sm: 472, md: 351 },
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ width: "369px" }}>
|
||||
<Box width={{ xs: "209px", sm: "269px", md: "369px" }}>
|
||||
<Image
|
||||
src="/images/gif/waf-logo.gif"
|
||||
alt="WAf logo"
|
||||
alt="SafeLine logo"
|
||||
layout="responsive"
|
||||
width={369}
|
||||
height={369}
|
||||
@@ -161,7 +200,9 @@ export default function Home({ total }: { total: number }) {
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{ position: "relative", bottom: "360px", marginBottom: "-360px" }}
|
||||
sx={{ position: "relative" }}
|
||||
bottom={{ xs: 448, md: 360 }}
|
||||
mb={{ xs: -56, md: -45 }}
|
||||
>
|
||||
<Container>
|
||||
<Box sx={{ display: "flex", justifyContent: "center" }}>
|
||||
@@ -170,10 +211,10 @@ export default function Home({ total }: { total: number }) {
|
||||
target="_blank"
|
||||
sx={{
|
||||
width: { xs: "100%", sm: "188px" },
|
||||
height: "60px",
|
||||
height: { xs: "80px", sm: "60px" },
|
||||
ml: { xs: 0, sm: 0 },
|
||||
mb: { xs: 0, sm: 0 },
|
||||
fontSize: "20px",
|
||||
fontSize: { xs: "32px", sm: "20px" },
|
||||
boxShadow: "0px 15px 25px 0px rgba(15,198,194,0.3)",
|
||||
}}
|
||||
href="/docs/guide/install"
|
||||
@@ -185,7 +226,7 @@ export default function Home({ total }: { total: number }) {
|
||||
<Container>
|
||||
<Box mt={7.5}>
|
||||
<Grid container justifyContent="center">
|
||||
<Grid item xs={12} md={6}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Stack spacing={2} alignItems="center">
|
||||
<Typography
|
||||
variant="h1"
|
||||
@@ -202,7 +243,9 @@ export default function Home({ total }: { total: number }) {
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
md={6}
|
||||
sm={6}
|
||||
mt={{ xs: 2, sm: 0 }}
|
||||
pl={{ xs: 10, sm: 0 }}
|
||||
sx={{ display: "flex", justifyContent: "center" }}
|
||||
>
|
||||
<Link
|
||||
@@ -212,10 +255,10 @@ export default function Home({ total }: { total: number }) {
|
||||
<Stack direction="row" justifyContent="center">
|
||||
<Stack spacing={2} alignItems="center">
|
||||
<Stack direction="row" sx={{ ...totalSx }}>
|
||||
<Typography variant="h1" ref={startRef}>
|
||||
<Typography variant="h1" ref={startRef} fontSize="70px">
|
||||
-
|
||||
</Typography>
|
||||
<Typography variant="h1">k</Typography>
|
||||
<Typography variant="h1" fontSize="70px">k</Typography>
|
||||
</Stack>
|
||||
<Typography variant="h5">GitHub Star</Typography>
|
||||
</Stack>
|
||||
@@ -236,96 +279,51 @@ export default function Home({ total }: { total: number }) {
|
||||
<Box mt={7}>
|
||||
<Grid container spacing={2}>
|
||||
{ARTICLES.map((article, index) => (
|
||||
<Grid key={article} item xs={12} sm={4}>
|
||||
<Grid
|
||||
key={article.title}
|
||||
item
|
||||
xs={6}
|
||||
sm={4}
|
||||
display="flex"
|
||||
justifyContent={{ xs: 'flex-start', sm: justifyContents[index % 3] }}
|
||||
>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
color: "#86909C",
|
||||
textAlign: { xs: "center", md: textAligns[index % 3] },
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
fontSize: "20px",
|
||||
width: article.width + "px",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
{article}
|
||||
<Link
|
||||
sx={{ color: "#86909C", fontFamily: "AlimamaShuHeiTi-Bold", fontSize: { xs: "14px", sm: "20px" }}}
|
||||
target="_blank"
|
||||
href={article.href}
|
||||
>
|
||||
{article.title}
|
||||
</Link>
|
||||
</Typography>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
</Container>
|
||||
<Container sx={{ pb: 3, mb: 3, mt: 18 }}>
|
||||
<Container sx={{ pb: 3, mb: 3, mt: { xs: 10, md: 18 }}}>
|
||||
<Features />
|
||||
</Container>
|
||||
<Abilities />
|
||||
<Box
|
||||
sx={{
|
||||
backgroundImage: "url(/images/partner-bg.png)",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
sx={{ position: "relative" }}
|
||||
>
|
||||
<Image
|
||||
src="/images/partner-bg.png"
|
||||
alt="partner bg"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
objectPosition="center"
|
||||
/>
|
||||
<Partner />
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: { xs: "243px", md: "343px" },
|
||||
mt: 19,
|
||||
backgroundImage: "url(/images/enterprise-bg.svg)",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
>
|
||||
<Container className="relative h-full">
|
||||
<Stack justifyContent="center" className="h-full">
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
fontWeight: 400,
|
||||
color: "common.white",
|
||||
fontSize: { xs: "20px", md: "28px" },
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
letterSpacing: "3px",
|
||||
}}
|
||||
>
|
||||
欢迎使用雷池其他版本
|
||||
</Typography>
|
||||
<Button
|
||||
variant="outlined"
|
||||
sx={{
|
||||
width: { xs: "146px" },
|
||||
height: "50px",
|
||||
mt: 4,
|
||||
backgroundColor: "common.white",
|
||||
fontSize: "16px",
|
||||
"&:hover": {
|
||||
color: "#0A8A87",
|
||||
backgroundColor: "common.white",
|
||||
},
|
||||
}}
|
||||
href="/version"
|
||||
>
|
||||
版本对比
|
||||
</Button>
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
right: -96,
|
||||
top: -65,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/images/shield.png"
|
||||
alt="雷池"
|
||||
width={417}
|
||||
height={359}
|
||||
/>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
<Version />
|
||||
</Box>
|
||||
</Box>
|
||||
</main>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import { Box, Container, Stack, Typography } from "@mui/material";
|
||||
import Version from "@/components/Version";
|
||||
|
||||
@@ -12,26 +13,33 @@ export default function VersionView() {
|
||||
<meta name="keywords" content="WAF,雷池,社区版,免费,版本对比,企业版,智能语义分析检测"></meta>
|
||||
<meta name="description" content="雷池 WAF 社区版,大小网站皆宜,免费开启使用"></meta>
|
||||
</Head>
|
||||
<Box mb={26}>
|
||||
<Box mb={{ xs: 10, md: 26 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "380px",
|
||||
backgroundImage: "url(/images/version-banner.png)",
|
||||
height: { xs: "866px", md: "380px" },
|
||||
position: "relative",
|
||||
backgroundImage: { xs: "url(/images/version-banner-mobile.png)", md: "url(/images/version-banner.png)" },
|
||||
backgroundSize: "cover",
|
||||
position: 'relative',
|
||||
backgroundPosition: 'center center',
|
||||
backgroundRepeat: 'no-repeat'
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<Box pt={23}>
|
||||
<Typography variant="h2" sx={{ fontFamily: "AlimamaShuHeiTi-Bold" }}>大小网站皆宜,免费即可开始</Typography>
|
||||
<Container className="relative">
|
||||
<Box pt={{ xs: 21, md: 23 }}>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
fontFamily: "AlimamaShuHeiTi-Bold",
|
||||
textAlign: { xs: "center", md: "left" },
|
||||
fontSize: { xs: "32px", md: "48px" },
|
||||
}}
|
||||
>大小网站皆宜,免费即可开始</Typography>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
<Container>
|
||||
<Stack sx={{ pt: 20 }} spacing={3} alignItems="center">
|
||||
<Container sx={{ position: "relative", bottom: { xs: 124, md: 0 }, mb: { xs: "-124px", md: 0 } }}>
|
||||
<Stack pt={{ xs: 0, md: 20 }} spacing={3} alignItems="center">
|
||||
<Version />
|
||||
</Stack>
|
||||
</Container>
|
||||
|
||||
@@ -62,6 +62,11 @@
|
||||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@corex/deepmerge@^4.0.43":
|
||||
version "4.0.43"
|
||||
resolved "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-4.0.43.tgz"
|
||||
integrity sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==
|
||||
|
||||
"@emotion/babel-plugin@^11.11.0":
|
||||
version "11.11.0"
|
||||
resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz"
|
||||
@@ -358,6 +363,11 @@
|
||||
prop-types "^15.8.1"
|
||||
react-is "^18.2.0"
|
||||
|
||||
"@next/env@^13.4.3":
|
||||
version "13.5.6"
|
||||
resolved "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz"
|
||||
integrity sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==
|
||||
|
||||
"@next/env@14.0.1":
|
||||
version "14.0.1"
|
||||
resolved "https://registry.npmmirror.com/@next/env/-/env-14.0.1.tgz"
|
||||
@@ -1320,7 +1330,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1:
|
||||
fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.1.tgz"
|
||||
integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==
|
||||
@@ -1988,7 +1998,7 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.6:
|
||||
minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
@@ -2017,7 +2027,17 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
next@14.0.1:
|
||||
next-sitemap@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.2.3.tgz"
|
||||
integrity sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==
|
||||
dependencies:
|
||||
"@corex/deepmerge" "^4.0.43"
|
||||
"@next/env" "^13.4.3"
|
||||
fast-glob "^3.2.12"
|
||||
minimist "^1.2.8"
|
||||
|
||||
next@*, next@14.0.1:
|
||||
version "14.0.1"
|
||||
resolved "https://registry.npmmirror.com/next/-/next-14.0.1.tgz"
|
||||
integrity sha512-s4YaLpE4b0gmb3ggtmpmV+wt+lPRuGtANzojMQ2+gmBpgX9w5fTbjsy6dXByBuENsdCX5pukZH/GxdFgO62+pA==
|
||||
|
||||