Compare commits

...

115 Commits

Author SHA1 Message Date
dundunHa
1b31d3229a feat: release v4.1.1 2024-01-11 18:58:48 +08:00
dundunHa
591e3598e6 feat: release v4.1.0 2024-01-11 17:40:37 +08:00
delong.wang
0fbc727e0c feat: update rec version 2024-01-11 11:30:01 +08:00
yrluke
e91f2d40d4 Merge pull request #598 from chaitin/update_doc_compose
Update doc compose
2024-01-11 11:18:38 +08:00
yrluke
097c8f7676 feat: use the huawei cloud 2024-01-11 11:18:06 +08:00
yrluke
150eec4585 doc: update the container name in doc 2024-01-11 11:16:56 +08:00
dundunHa
ead80a58d7 chore: changelog 2024-01-09 19:05:55 +08:00
yrluke
a960361348 Merge pull request #587 from xbingW/main
修改文档图片
2024-01-09 17:11:05 +08:00
张华杰
c80aff05bc 修改图 2024-01-09 17:08:46 +08:00
张华杰
694c5e35bd 与官网统一 2024-01-09 17:08:46 +08:00
delong.wang
c236378f01 feat: update version 4.0.2 2024-01-06 15:34:57 +08:00
yrluke
3e75e7c6b6 Update upgrade.sh
fix: use the right command
2024-01-05 22:03:13 +08:00
delong.wang
2bff3ecf9d fix: qps missing in dashboard 2024-01-05 21:57:08 +08:00
yrluke
6651db33a7 fix: rollback the SUBNET_PREFIX 2024-01-05 20:13:35 +08:00
yrluke
e7bccbaf6e fix: add the image prefix to offline 2024-01-05 20:10:39 +08:00
yrluke
bc91a9834f feat: use the right compose.yml 2024-01-05 19:52:32 +08:00
yrluke
81edced29d fix: when change the cdn is 0 is fail 2024-01-05 19:32:02 +08:00
dundunHa
1ef873dcc3 chore: changelog 2024-01-05 19:30:48 +08:00
yrluke
8d49f7045d feat: fix the wrong condition 2024-01-05 19:16:47 +08:00
yrluke
dad3deb482 doc: update the install and upgrade 2024-01-05 19:04:47 +08:00
dundunHa
cd13c08a2f feat: release 4.0.0 2024-01-05 19:02:37 +08:00
yrluke
045ec5f44e feat: add the huawei image repo 2024-01-05 18:56:38 +08:00
yrluke
094211f28f feat: use the right image tag 2024-01-05 18:47:29 +08:00
yrluke
93c9739292 feat: merge the stream and latest and upload the huawei compose 2024-01-05 18:43:39 +08:00
delong.wang
3a468d6af5 feat(doc): remove docs related with beta version 2024-01-05 18:39:57 +08:00
delong.wang
77adc02bd9 feat: add mixed crawler ip pool 2024-01-04 18:07:10 +08:00
delong.wang
7ccea91046 Merge pull request #561 from nmgliangwei/main
Update upgrade.sh
2024-01-04 16:23:09 +08:00
delong.wang
6f264ce8d3 Merge pull request #569 from safe1ine/patch-1
Update README.md
2024-01-04 16:19:31 +08:00
safe1ine
b108b6feff Update README.md 2024-01-04 16:17:48 +08:00
delong.wang
1692fff007 fix: missing npm depens in lock file 2024-01-04 15:12:03 +08:00
delong.wang
4f503d358a feat: add crawler ip group data 2024-01-04 14:51:49 +08:00
delong.wang
6c44959c49 feat: change feature description about basic version 2024-01-02 18:27:02 +08:00
梁伟
e0fe48bebf Update upgrade.sh
beta支持升级
2023-12-30 12:05:46 +08:00
delong.wang
f23da8a9b9 feat: upgrade recommanded version 2023-12-28 16:35:16 +08:00
delong.wang
fcdcf124d5 feat: change release log 2023-12-28 15:36:15 +08:00
dundunHa
fea6a0efa9 feat: release 4.0.0-beta.3, update compose.yml 2023-12-28 14:58:38 +08:00
dundunHa
9f22ad048c feat: release 4.0.0-beta.3 2023-12-28 14:30:07 +08:00
delong.wang
ac7c858520 feat: update recommand version 2023-12-28 11:29:15 +08:00
delong.wang
5d0a7f7a90 Merge pull request #545 from xbingW/main
添加 bing 支持
2023-12-27 17:59:19 +08:00
delong.wang
d4eeee14da feat: replace env name SNSERVER_ADDR with TCD_SNSERVER 2023-12-27 15:15:56 +08:00
dundunHa
09019fe38b feat: release 4.0.0-beta.2 2023-12-22 15:01:30 +08:00
dundunHa
bec7c51e9e feat: release 4.0.0-beta.2 2023-12-22 14:21:48 +08:00
delong.wang
9aceca264c fix: postgres alpine image has time error 2023-12-21 18:27:01 +08:00
xiaobing.wang
8ef0770db2 feat: add bing verify 2023-12-21 16:40:10 +08:00
dundunHa
ef8d0eefc4 feat: release 4.0.0-beta.1 2023-12-21 16:37:36 +08:00
xiaobing.wang
1c67f1acc8 feat: add bing verify 2023-12-21 16:36:49 +08:00
delong.wang
8e5d3a11a9 feat: use "-stream" mode in beta version 2023-12-21 16:28:47 +08:00
delong.wang
55fd344735 Merge pull request #544 from xbingW/main
添加谷歌验证
2023-12-21 16:11:52 +08:00
xbingW
5a9a24d01f Merge branch 'chaitin:main' into main 2023-12-21 16:09:35 +08:00
xiaobing.wang
e59fe81244 feat: add google verify 2023-12-21 16:08:10 +08:00
delong.wang
ed6795b4cc feat: add beta version upgrade tips 2023-12-21 15:55:25 +08:00
delong.wang
d65df2a57f feat: remove useless mounted dir 2023-12-21 14:39:15 +08:00
delong.wang
574e600974 fix: compose.yaml config error 2023-12-21 12:28:58 +08:00
delong.wang
e1d0b4058b feat: remove redis password gen funcs 2023-12-21 12:06:45 +08:00
delong.wang
397091015b fix: disable postgres ssl link mode 2023-12-21 12:06:08 +08:00
delong.wang
341806a3bd fix: change safline-postgres container name in compose.yaml 2023-12-21 11:56:33 +08:00
delong.wang
d25144fe9d fix: latest upgrade process should't stop mgt/fvm containers 2023-12-20 17:56:54 +08:00
delong.wang
7a980b4b34 feat: remove fvm image mounted dir 2023-12-20 17:56:09 +08:00
dundunHa
2c75cf70d5 feat: change upgrade.sh remove mgt-api,fvm-manager 2023-12-20 17:47:16 +08:00
delong.wang
24e55faeb8 feat: add beta version install shell 2023-12-20 17:00:59 +08:00
delong.wang
78e73497b0 feat: update 502/504 page styles 2023-12-20 11:02:29 +08:00
delong.wang
12e6ede7be feat: use github as default assets resource 2023-12-19 12:23:34 +08:00
delong.wang
d91b651273 Merge pull request #523 from xbingW/main
更新官网
2023-12-15 15:59:45 +08:00
xbingW
1fb34fc49a Merge branch 'chaitin:main' into main 2023-12-15 15:51:41 +08:00
dundunHa
39e93fad1c feat: change release v3.16.1 log 2023-12-15 14:41:40 +08:00
dundunHa
413453a1e7 feat: change release v3.16.1 date 2023-12-15 14:32:23 +08:00
dundunHa
9312ef2f48 feat: change release v3.16.1 log 2023-12-15 14:32:00 +08:00
dundunHa
f8d5861e5a feat: change release v3.16.1 log 2023-12-15 14:30:44 +08:00
dundunHa
99b09d8597 feat: change release v3.16.1 log 2023-12-15 14:30:22 +08:00
dundunHa
c9c9544d22 feat: release v3.16.1 2023-12-15 14:23:07 +08:00
xiaobing.wang
e7a7976774 feat: add sitemap 2023-12-14 10:59:29 +08:00
xbingW
a215268a10 Merge branch 'chaitin:main' into main 2023-12-14 10:57:11 +08:00
lanlan
88bfec45cd feat(websit): mobile page style optimization 2023-12-14 10:56:49 +08:00
lanlan
6d1c328402 feat(website): add robots.txt and sitemap.xml 2023-12-14 10:56:49 +08:00
lanlan
2259c9984e feat(website): update issue api 2023-12-14 10:56:49 +08:00
xiaobing.wang
50373094ad feat: update issue api 2023-12-14 10:56:49 +08:00
delong.wang
5930692edc feat: update 5xx page 2023-12-14 10:43:58 +08:00
xbingW
58d57ee33a Merge pull request #6 from ct-jaryn/main
少量修改
2023-12-14 10:42:44 +08:00
张华杰
9c987a4bc7 少量修改 2023-12-14 10:28:49 +08:00
yrluke
ef5269f634 Merge pull request #505 from xbingW/main
增加 /api/exist 接口
2023-12-08 18:28:13 +08:00
xiaobing.wang
9166f87178 feat: add /api/exist 2023-12-08 17:49:44 +08:00
xiaobing.wang
8963fdd7bf feat: add /api/exist 2023-12-08 16:57:14 +08:00
dundunHa
77f94765c6 chore: modify changelog 2023-12-08 14:18:54 +08:00
dundunHa
34e08e7918 feat: add 502 page image 2023-12-08 11:45:16 +08:00
dundunHa
dc4fb861ef feat: release v3.15.3 2023-12-08 11:23:26 +08:00
dundunHa
5e86861510 feat: release v3.15.2 2023-12-07 18:07:26 +08:00
dundunHa
088d502d4b feat: release v3.15.1 2023-12-07 17:27:30 +08:00
delong.wang
93ccb1e1f6 feat: update 502/504 page 2023-12-07 16:10:41 +08:00
dundunHa
4abcbc03ae feat: release v3.15.0 2023-12-07 15:33:30 +08:00
delong.wang
44487b3a8b feat: add default 504 page, fix few translations 2023-12-07 14:42:41 +08:00
delong.wang
f4dbf3bde5 feat: new 502 page 2023-12-07 11:38:00 +08:00
delong.wang
bcdd0be188 feat: support english language in block page 2023-12-07 10:56:46 +08:00
delong.wang
c0b0bc6547 feat: rename index.html to denied.html 2023-12-07 10:47:17 +08:00
delong.wang
09505941db Merge pull request #490 from xbingW/main
官网更新
2023-12-04 10:29:13 +08:00
xbingW
162f76a737 Merge pull request #4 from xbingW/website
Website
2023-12-01 19:22:02 +08:00
dundunHa
e2bcabff20 feat: release v3.14.1 2023-12-01 19:19:02 +08:00
delong.wang
eaac017f82 fix: redis is cursed when upgrade to 3.14.0 2023-12-01 19:19:02 +08:00
delong.wang
ed838e6052 feat: update v3.14.0 compose.yaml 2023-12-01 19:19:02 +08:00
delong.wang
8814a0bb6f feat: update to v3.14.0 2023-12-01 19:19:02 +08:00
delong.wang
f8c63f7d86 feat: format compose.yaml 2023-12-01 19:19:02 +08:00
xbingW
124ec73684 Merge pull request #3 from ct-jaryn/main
调整
2023-12-01 19:18:49 +08:00
张华杰
2dbe8cd35e 调整 2023-12-01 19:09:57 +08:00
xbingW
f896a21a9d Merge pull request #2 from ct-jaryn/main
增加视频暂停功能
2023-12-01 18:41:03 +08:00
张华杰
e59e1da238 增加视频暂停功能 2023-12-01 18:29:39 +08:00
lanlan
34c830d7cd feat: 移动端页面适配 2023-12-01 17:19:19 +08:00
xiaobing.wang
3df9012906 fix: refresh cache 2023-12-01 16:59:56 +08:00
xbingW
6850295f78 Merge pull request #1 from ct-jaryn/main
调整和优化
2023-12-01 16:59:18 +08:00
张华杰
ced4a0f4a0 优化更新 2023-12-01 13:25:55 +08:00
张华杰
045577348f 文档优化,突出了云监测的福利 2023-12-01 13:25:06 +08:00
lanlan
b6297f289e feat(website): add star count api and update text 2023-11-30 19:39:52 +08:00
dundunHa
2becd43537 feat: release v3.14.1 2023-11-30 18:51:38 +08:00
delong.wang
1b58308ba6 fix: redis is cursed when upgrade to 3.14.0 2023-11-30 18:03:36 +08:00
张华杰
37cbd0d4dd 调整和优化 2023-11-30 15:13:17 +08:00
delong.wang
9d51bc9ea3 feat: update v3.14.0 compose.yaml 2023-11-30 14:39:53 +08:00
xiaobing.wang
6504339a03 feat: add star count 2023-11-29 15:05:14 +08:00
83 changed files with 12442 additions and 797 deletions

1
.gitignore vendored
View File

@@ -2,5 +2,6 @@
.DS_Store
*.zip
*.tar
*.tar.gz
build.sh
compose.yml

View File

@@ -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))

View File

@@ -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
View File

@@ -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 到最新版本尝试一下。

View File

@@ -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 的认证软件** 扫描二维码,然后输入动态口令登录:
![login.gif](https://waf-ce.chaitin.cn/images/gif/login.gif)
![login.gif](https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/gif/login.gif)
### 配置防护站点
雷池以反向代理方式接入,优先于网站服务器接收流量,对流量中的攻击行为进行检测和清洗,将清洗过后的流量转发给网站服务器。
![config.gif](https://waf-ce.chaitin.cn/images/gif/config_site.gif)
![config.gif](https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/gif/config_site.gif)
<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>`
![log.gif](https://waf-ce.chaitin.cn/images/gif/detect_log.gif)
![log.gif](https://raw.githubusercontent.com/chaitin/SafeLine/main/documents/static/images/gif/detect_log.gif)
> 如果你需要进行深度测试,请参考 <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>

View File

@@ -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%" />

View File

@@ -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": {

View File

@@ -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": {

View File

@@ -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"

View File

@@ -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)
}

View 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})
}

View File

@@ -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
}

View 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
}

View File

@@ -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

File diff suppressed because one or more lines are too long

162
blockpage/504.html Normal file

File diff suppressed because one or more lines are too long

View 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>

View File

@@ -9,7 +9,7 @@ slug: /
雷池SafeLine是长亭科技耗时近 10 年倾情打造的 WAF核心检测能力由智能语义分析算法驱动。
Slogan: 不让黑客越雷池步。
Slogan: 不让黑客越雷池步。
## 什么是 WAF

View File

@@ -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>

View File

@@ -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.完成首次登录后,**无法回退查看二维码**,使用页面提供的方法重置
## 常见登录问题

View File

@@ -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.禁止网站服务器上,除雷池之外的访问。例如配置防火墙
```
![Alt text](/images/docs/guide_config/config_site2.png)
### 配置完成
浏览器访问`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
上游nginxIPC端口C
下游nginxIPD
环境信息:
网站服务器:IPA
雷池服务器:IPB
上游服务器:IPC端口C
下游服务器:IPD域名example.com
目的使用雷池的80端口接受请求进行防护
步骤:
1. 将下游nginx的流量指向雷池的IPB访问端口指向80。
2. 具体配置参考下图
步骤:
1.将下游nginx的流量指向雷池的IPC访问端口指向80。
2.具体配置参考下图
```
![Alt text](/images/docs/guide_config/config_site3.png)
### 配置完成
浏览器访问`example.com:80`,若能获取到业务网站的响应,并且站点上 “今日访问量” 增加,代表配置成功。
如果浏览器访问`example.com:80`能获取到业务网站的响应,并且数据统计页的 “今日请求数” 增加,代表配置成功。
效果大致如图:
![Alt text](/images/docs/guide_config/deploy_with_other_server.svg)
![Alt text](/images/docs/guide_config/deploy_with_other_server.png)
## 常见配置问题

View File

@@ -24,6 +24,8 @@ title: "配置其他"
### 人机验证
人机验证的有效时间默认是一个小时,未来可能会支持配置,敬请期待
详情查看 [人机验证 2.0](/about/challenge)
### 语义分析
@@ -40,7 +42,7 @@ title: "配置其他"
### 证书管理
管理需要使用的正式,点击添加证书添加
管理需要使用的证书,点击添加证书添加
### 其他

View File

@@ -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) 并传输到需要安装雷池的服务器上,执行以下命令加载镜像

View File

@@ -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`
## 问题无法解决

View File

@@ -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. 将 “绑定二维码” 分享给其他人进行绑定(“绑定二维码” 无绑定次数限制,无时效限制)

View File

@@ -20,23 +20,21 @@ title: "配置问题"
4. 同时存在其他错误的配置可能会导致新的配置一直不生效,检查有没有存在其他错误的配置
## 排查步骤
## 排查步骤
1. 明确 “网站无法访问” 的具体表现:
- 如果 `502 Bad Gateway tengine`
![Alt text](/images/docs/guide_config/tengine_502.png)
大概率是是雷池的上游服务器配置不正确,或者雷池无法访问到上游服务器,请继续按下面步骤排查。
大概率是是雷池的上游服务器配置不正确,或者雷池无法访问到上游服务器。请继续按下面步骤排查,重点排查步骤 6、7
![Alt text](/images/docs/guide_config/tengine_502.png)
- 如果请求能够返回但是十分缓慢
- 首先确认服务器负载是否正常
- 确认服务器负载是否正常,检查服务器的 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说明雷池配置正确网络正常
![Alt text](/images/docs/guide_config/check_the_site1.png)
- 如果浏览器无法访问,但这一步正常获取到响应,大概率是因为:
如果浏览器无法访问,但这一步正常获取到响应,大概率是因为:
- 网站域名还没有切到雷池,浏览器测试时访问的是 `http(s)://<雷池 IP>`,恰好业务服务上有 Host 验证,所以拒绝了该请求。这种情况需要修改本机 host把域名解析到雷池 IP再访问 `http(s)://<域名>`,才能准确测试
- 网站业务做了其他一些特殊处理。例如访问后 301 跳转到了其他地址,需要具体排查网站业务的响应内容
- 如果不能获取到响应,继续下一步
- 如果不能获取到响应,继续下一步
3. 在雷池设备上执行 `curl -v -H "Host: <域名或者IP>" http://<雷池 IP>:<雷池监听端口>`。如能获取到业务网站的响应,并且站点上 “今日访问量” +1说明雷池配置正确

View File

@@ -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";
```
## 问题无法解决

View File

@@ -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 证书合法性、网站敏感内容等信息。
交互界面简洁直观、操作上手轻松,实时监测每个页面的状态和详细信息,让你对网站运行状态了如指掌。
![website.png](/images/docs/practice_monitor/website.png)
### 雷池用户福利
### 领取方式
凡是安装雷池社区版的用户,可凭借雷池设备码领取长亭网站监控产品 100 元体验金一份
1. 安装雷池社区版,查看雷池设备码
2. 登录网站监测,在网站监测页输入雷池设备码,地址:[长亭百川网站监测](https://rivers.chaitin.cn/landing/radar)
![machineid.png](/images/docs/practice_monitor/machineid.png)
![gift.png](/images/docs/practice_monitor/gift.png)

View File

@@ -1,8 +1,8 @@
---
title: "检测效果对比"
title: "WAF检测效果对比"
---
# 检测效果对比
# WAF检测效果对比
雷池社区版与其他 WAF 的检测能力对比

View File

@@ -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)
- ![](/images/docs/about_changelog/ip_group_url.png)
- 新增 “搜索引擎爬虫 IP”包含 Google、Bing、百度、360 的爬虫 IP[#374](https://github.com/chaitin/SafeLine/issues/374)、[#399](https://github.com/chaitin/SafeLine/issues/399)
![](/images/docs/about_changelog/ip_group_builtin.png)
- 出厂预置 “搜索引擎爬虫白名单” 和 “长亭社区恶意 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
#### 新增
- 右上角增加 “更多工具”,方便快速访问牧云主机助手、百川网站监测等常用运维管理工具
- ![](/images/docs/about_changelog/moretools.png)
#### 优化
- 登录时若验证码错误,不再自动清空内容,方便修改([#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 页面。网站服务器异常、配置有误时,能给网站用户提供更清晰友好的说明
- ![](/images/docs/about_changelog/502_page.png)
- 拦截页面支持英文,根据客户端语言自动切换
#### 优化
- 单个 IP 组内的 IP 数量,增加 1w 行的上限。避免更新配置时系统异常
- 修复创建或修改站点时,端口占用检查没有生效的问题
- 略微提高流量检测和配置修改时的执行效率
### [3.14.1] - 2023-11-30
#### 修复
- 修复日志服务 CPU 占用过高问题
### [3.14.0] - 2023-11-30
#### 新增

View File

@@ -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

View File

@@ -18,7 +18,7 @@ apisixhttps://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 端口映射到宿主机即可。
进入雷池的安装目录

View File

@@ -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",
},
],

View File

@@ -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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

9025
documents/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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/"

View File

@@ -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

View 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

View 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

View File

@@ -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

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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/"

View File

@@ -1,4 +1,4 @@
{
"latest_version": "v3.14.0",
"rec_version": "v3.8.2"
"latest_version": "v4.1.1",
"rec_version": "v3.11.1"
}

View 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

View File

@@ -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*',
},
],
}

View File

@@ -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"
},

View File

@@ -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"
},

View File

@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<users>
<user>3EF7E30C5A378E54709FA57D67499571</user>
</users>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
google-site-verification: googlef97f8402f9139518.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

10
website/public/robots.txt Normal file
View 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

View 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>

View 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>

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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 }}

View File

@@ -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>
);
};

View File

@@ -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>
))}

View File

@@ -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>

View File

@@ -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"
>

View File

@@ -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>
))}

View File

@@ -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>

View File

@@ -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}

View File

@@ -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>
);

View 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;

View File

@@ -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;

View File

@@ -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 XSSSQL </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 }}> XSSSQL </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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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==