Merge pull request #628 from rocboss/beta

v0.6.0-alpha.3
pull/632/head v0.6.0-alpha.3
北野 - Michael 5 months ago committed by GitHub
commit 335d6e0e7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -47,7 +47,7 @@ jobs:
name: Test
strategy:
matrix:
go-version: [ 1.22.x, 1.23.x ]
go-version: [ 1.23.x, 1.24.x ]
platform: [ ubuntu-latest, macos-latest ]
runs-on: ${{ matrix.platform }}
steps:
@ -66,7 +66,7 @@ jobs:
name: TestOnWindows
strategy:
matrix:
go-version: [ 1.21.x, 1.22.x ]
go-version: [ 1.23.x, 1.24.x ]
platform: [ windows-latest ]
runs-on: ${{ matrix.platform }}
steps:

@ -0,0 +1,574 @@
### 安装说明
### 方式一. 手动安装(推荐)
克隆代码库
```sh
git clone https://github.com/rocboss/paopao-ce.git
```
#### 后端
1. 导入项目根目录下的 `scripts/paopao.sql` 文件至MySQL数据库
2. 拷贝项目根目录下 `config.yaml.sample` 文件至 `config.yaml`,按照注释完成配置编辑
3. 编译后端
编译api服务:
```sh
make build
```
编译api服务、内嵌web前端ui:
```sh
make build
```
也可以使用精简模式编译不内嵌web前端ui:
```sh
make build TAGS='slim embed'
```
编译后在`release`目录可以找到对应可执行文件。
```sh
release/paopao
```
4. 直接运行后端
运行api服务:
```sh
make run
```
运行api服务、web前端ui服务:
```sh
make run TAGS='embed'
```
提示: 如果需要内嵌web前端ui请先构建web前端(建议设置web/.env为VITE_HOST="")。
5. 使用内置的Migrate机制自动升级维护SQL DDL:
```sh
# 添加 Migration 功能到 Features 中 开启migrate功能
vim config.yaml
# file: config.yaml
# Features:
# Default: ["Base", "MySQL", "Zinc", "MinIO", "LoggerZinc", "Migration"]
# 编译时加入migration tag编译出支持migrate功能的可执行文件
make build TAGS='migration'
release/paopao
# 或者 带上migration tag直接运行
make run TAGS='migration'
```
> 注意默认编译出来的可执行文件是不内置migrate功能需要编译时带上migration tag才能内置支持migrage功能。
#### 前端
1. 进入前端目录 `web`,拷贝`.env` 到 `.env.local`,编辑 `.env.local ` 文件中后端服务地址及其他配置项,下载依赖包
```sh
cd ./web && cp .env .env.local
vim .env.local
yarn
```
2. 编译前端
```sh
yarn build
```
build完成后可以在dist目录获取编译产出配置nginx指向至该目录即可
#### 桌面端
1. 进入前端目录 `web`,拷贝`.env` 到 `.env.local`,编辑 `.env.local ` 文件中后端服务地址及其他配置项,下载依赖包
```sh
cd ./web && cp .env .env.local
vim .env.local
yarn
```
2. 编译前端
```sh
yarn build
```
3. 构建桌面端
```sh
yarn tauri build
```
桌面端是使用[Rust](https://www.rust-lang.org/) + [tauri](https://github.com/tauri-apps/tauri)编写
需要安装tauri的依赖具体参考[https://tauri.studio/v1/guides/getting-started/prerequisites](https://tauri.studio/v1/guides/getting-started/prerequisites).
### 方式二. 使用Docker构建、运行
* 后端:
```sh
# 默认参数构建, 默认内嵌web ui并设置api host为空
docker build -t your/paopao-ce:tag .
# 内嵌web ui并且自定义API host参数
docker build -t your/paopao-ce:tag --build-arg API_HOST=http://api.paopao.info .
# 内嵌web ui并且使用本地web/.env中的API host
docker build -t your/paopao-ce:tag --build-arg USE_API_HOST=no .
# 内嵌web ui并且使用本地编译的web/dist构建
docker build -t your/paopao-ce:tag --build-arg USE_DIST=yes .
# 只编译api server
docker build -t your/paopao-ce:tag --build-arg EMBED_UI=no .
# 运行
mkdir custom && docker run -d -p 8008:8008 -v ${PWD}/custom:/app/paopao-ce/custom -v ${PWD}/config.yaml.sample:/app/paopao-ce/config.yaml your/paopao-ce:tag
# 或者直接运行构建好的docker image
mkdir custom && docker run -d -p 8008:8008 -v ${PWD}/custom:/app/paopao-ce/custom -v ${PWD}/config.yaml.sample:/app/paopao-ce/config.yaml bitbus/paopao-ce:latest
```
* 前端:
```sh
cd web
# 默认参数构建
docker build -t your/paopao-ce:web .
# 自定义API host 参数构建
docker build -t your/paopao-ce:web --build-arg API_HOST=http://api.paopao.info .
# 使用本地编译的dist构建
docker build -t your/paopao-ce:web --build-arg USE_DIST=yes .
# 运行
docker run -d -p 8010:80 your/paopao-ce:web
```
* All-In-One:
```sh
# 构建Image
docker buildx build --build-arg USE_DIST="yes" -t your/paopao-ce:all-in-one-latest -f Dockerfile.allinone .
# 运行
docker run --name paopao-ce-allinone -d -p 8000:8008 -p 7700:7700 -v ./data/custom:/app/custom -v ./data/meili_data:/app/meili_data your/paopao-ce:all-in-one-latest
# 或者使用官方Image运行
docker run --name paopao-ce-allinone -d -p 8000:8008 -p 7700:7700 -v ./data/custom:/app/custom -v ./data/meili_data:/app/meili_data bitbus/paopao-ce:all-in-one-latest
# 或者使用官方Image运行 + 自定义config.yaml
docker run --name paopao-ce-allinone -d -p 8000:8008 -p 7700:7700 -v ./config.yaml:/app/config.yaml -v ./data/custom:/app/custom -v ./data/meili_data:/app/meili_data bitbus/paopao-ce:all-in-one-latest
```
> 注意在`config.yaml` 中`Meili.ApiKey`的值必须与容器中meili启动时设定的`MEILI_MASTER_KEY`环境变量值相同,默认为`paopao-meilisearch`. 可以在docker启动容器时通过`-e MEILI_MASTER_KEY=<custom-key>`设置该值。
### 方式三. 使用 docker-compose 运行
```sh
git clone https://github.com/rocboss/paopao-ce.git
cd paopao-ce && docker compose up -d
# visit http://localhost:8008 👀 paopao-ce
# visit http://localhost:8001 👀 RedisInsight
# visit http://localhost:8080 👀 phpMyAdmin
```
默认是使用config.yaml.sample的配置如果需要自定义配置请拷贝默认配置文件(比如config.yaml)修改后再同步配置到docker-compose.yaml如下
```
# file: docker-compose.yaml
...
backend:
image: bitbus/paopao-ce:latest
restart: always
depends_on:
- db
- redis
- zinc
# modify below to reflect your custom configure
volumes:
- ./config.yaml:/app/paopao-ce/config.yaml
ports:
- 8008:8008
networks:
- paopao-network
....
```
> 注意:默认提供的 docker-compose.yaml 初衷是搭建本机开发调试环境,如果需要产品部署供外网访问,请自行调优配置参数或使用其他方式部署。
### 开发文档
#### Docs文档说明
`docs`目录提供了各种开发文档,包括:
* [deploy](docs/deploy/) - paopao-ce部署文档
* [discuss](docs/discuss/) - 开发相关的问题交流论述文档
* [openapi](docs/openapi/) - paopao-ce后端导出API文档
* [proposal](docs/proposal/) - paopao-ce功能特性提按文档
> 比如关于paopao-ce的设计定位可以参考[docs/proposal/22110411-关于paopao-ce的设计定位](docs/proposal/22110411-关于paopao-ce的设计定位.md)简要阐述了paopao-ce是如何定位自身的。
#### API文档
开发者可以在本地开启`Docs`服务浏览后端导出的API服务接口文档。
* `config.yaml` 添加 `Docs` 功能项:
```yaml
...
Features:
Default: ["Base", "MySQL", "Option", "LocalOSS", "LoggerFile", "Docs"]
Docs: ["Docs:OpenAPI"]
...
```
* 构建时将 `docs` 添加到TAGS中:
```sh
make run TAGS='docs'
# visit http://127.0.0.1:8011/docs/openapi
```
### 配置说明
`config.yaml.sample` 是一份完整的配置文件模版paopao-ce启动时会读取`./custom/config.yaml`、`./config.yaml`任意一份配置文件(优先读取最先找到的文件)。
```sh
cp config.yaml.sample config.yaml
vim config.yaml # 修改参数
paopao serve
```
配置文件中的 `Features` 小节是声明paopao-ce运行时开启哪些功能项:
```yaml
...
Features:
Default: ["Base", "MySQL", "Option", "LocalOSS", "LoggerFile"]
Develop: ["Base", "MySQL", "Option", "Sms", "AliOSS", "LoggerOtlp"]
Demo: ["Base", "MySQL", "Option", "Sms", "MinIO", "LoggerOtlp"]
Slim: ["Base", "Sqlite3", "LocalOSS", "LoggerFile"]
Base: ["Zinc", "Redis", "Alipay",]
Option: ["SimpleCacheIndex"]
Sms: "SmsJuhe"
...
```
如上:
Default/Develop/Demo/Slim 是不同 功能集套件(Features Suite) Base/Option 是子功能套件, Sms是关于短信验证码功能的参数选项。
这里 `Default`套件 代表的意思是: 使用`Base/Option` 中的功能,外加 `MySQL/LocalOSS/LoggerFile`功能,也就是说开启了`Zinc/Redis/Alipay/SimpleCacheIndex/MySQL/LocalOSS/LoggerFile` 7项功能
`Develop`套件依例类推。
使用Feautures:
```sh
release/paopao serve --help
Usage of release/paopao:
-features value
use special features
-no-default-features
whether use default features
# 默认使用 Default 功能套件
release/paopao serve
# 不包含 default 中的功能集,仅仅使用 develop 中声明的功能集
release/paopao serve --no-default-features --features develop
# 使用 default 中的功能集,外加 sms 功能
release/paopao serve --features sms
# 手动指定需要开启的功能集
release/paopao serve --no-default-features --features sqlite3,localoss,loggerfile,redis
```
目前支持的功能集合:
| 功能项 | 类别 | 状态 | 备注 |
| ----- | ----- | ----- | ----- |
|`Web` | 子服务 | 内测 | 开启Web服务|
|`Admin` | 子服务 | WIP | 开启Admin后台运维服务|
|`SpaceX` | 子服务 | WIP | 开启SpaceX服务|
|`Bot` | 子服务 | WIP | 开启Bot服务|
|`NativeOBS` | 子服务 | WIP | 开启NativeOBS服务|
|`Docs` | 子服务 | WIP | 开启开发者文档服务|
|`Frontend:Web` | 子服务 | 稳定 | 开启独立前端服务|
|`Frontend:EmbedWeb` | 子服务 | 稳定 | 开启内嵌于后端Web API服务中的前端服务|
|`Gorm` | 数据库 | 稳定(默认) | 使用[gorm](https://github.com/go-gorm/gorm)作为数据库的ORM默认使用 `Gorm` + `MySQL`组合|
|`Sqlx`| 数据库 | WIP | 使用[sqlx](https://github.com/jmoiron/sqlx)作为数据库的ORM|
|`Sqlc`| 数据库 | WIP | 使用[sqlc](https://github.com/kyleconroy/sqlc)自动生成ORM代码|
|`MySQL`| 数据库 | 稳定(默认) | 使用MySQL作为数据库|
|`Postgres`| 数据库 | 稳定 | 使用PostgreSQL作为数据库|
|`Sqlite3`| 数据库 | 稳定 | 使用Sqlite3作为数据库|
|`AliOSS` | 对象存储 | 稳定(推荐) |阿里云对象存储服务|
|`COS` | 对象存储 | 内测 |腾讯云对象存储服务|
|`HuaweiOBS` | 对象存储 | 内测 |华为云对象存储服务|
|`MinIO` | 对象存储 | 稳定 |[MinIO](https://github.com/minio/minio)对象存储服务|
|`S3` | 对象存储 | 内测 |AWS S3兼容的对象存储服务|
|`LocalOSS` | 对象存储 | 内测 |提供使用本地目录文件作为对象存储的功能,仅用于开发调试环境|
|`OSS:Retention` | 对象存储 | 内测 |基于对象存储系统的对象过期自动删除特性实现 先创建临时对象再持久化的功能|
|`OSS:TempDir` | 对象存储 | 内测 |基于对象存储系统的对象拷贝/移动特性实现 先创建临时对象再持久化的功能|
|`Redis` | 缓存 | 稳定 | Redis缓存功能 |
|`SimpleCacheIndex` | 缓存 | Deprecated | 提供简单的 广场推文列表 的缓存功能 |
|`BigCacheIndex` | 缓存 | Deprecated | 使用[BigCache](https://github.com/allegro/bigcache)缓存 广场推文列表,缓存每个用户每一页,简单做到千人千面 |
|`RedisCacheIndex` | 缓存 | Deprecated | 使用Redis缓存 广场推文列表,缓存每个用户每一页,简单做到千人千面 |
|`Zinc` | 搜索 | Deprecated | 基于[Zinc](https://github.com/zinclabs/zinc)搜索引擎提供推文搜索服务 |
|`Meili` | 搜索 | 稳定(推荐) | 基于[Meilisearch](https://github.com/meilisearch/meilisearch)搜索引擎提供推文搜索服务 |
|`Bleve` | 搜索 | WIP | 基于[Bleve](https://github.com/blevesearch/bleve)搜索引擎提供推文搜索服务 |
|[`Sentry`](docs/proposal/23040412-关于使用sentry用于错误追踪与性能检测的设计.md) | 监控 | 内测 | 使用Sentry进行错误跟踪与性能监控 |
|`LoggerFile` | 日志 | 稳定 | 使用文件写日志 |
|`LoggerZinc` | 日志 | Deprecated | 使用[Zinc](https://github.com/zinclabs/zinc)写日志 |
|`LoggerMeili` | 日志 | Deprecated | 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志 |
|`LoggerOpenObserve` | 日志 | Deprecated | 使用[OpenObserve](https://github.com/openobserve/openobserve)写日志 |
|`LoggerOtlp` | 日志 | 内测 | 使用[OpenTelemetry](https://github.com/open-telemetry/opentelemetry-go)写日志 |
|[`Friendship`](docs/proposal/22110410-关于Friendship功能项的设计.md) | 关系模式 | 内置 Builtin | 弱关系好友模式,类似微信朋友圈 |
|[`Followship`](docs/proposal/22110409-关于Followship功能项的设计.md) | 关系模式 | 内置 Builtin | 关注者模式类似Twitter的Follow模式 |
|[`Lightship`](docs/proposal/22121409-关于Lightship功能项的设计.md) | 关系模式 | 弃用 Deprecated | 开放模式,所有推文都公开可见 |
|`Alipay` | 支付 | 稳定 | 开启基于[支付宝开放平台](https://open.alipay.com/)的钱包功能 |
|`Sms` | 短信验证 | 稳定 | 开启短信验证码功能,用于手机绑定验证手机是否注册者的;功能如果没有开启,手机绑定时任意短信验证码都可以绑定手机 |
|`Docs:OpenAPI` | 开发文档 | 稳定 | 开启openapi文档功能提供web api文档说明(visit http://127.0.0.1:8008/docs/openapi) |
|[`Pyroscope`](docs/proposal/23021510-关于使用pyroscope用于性能调试的设计.md)| 性能优化 | 内测 | 开启Pyroscope功能用于性能调试 |
|[`Pprof`](docs/proposal/23062905-添加Pprof功能特性用于获取Profile.md)| 性能优化 | 内测 | 开启Pprof功能收集Profile信息 |
|`PhoneBind` | 其他 | 稳定 | 手机绑定功能 |
|`UseAuditHook` | 其他 | 内测 | 使用审核hook功能 |
|`DisableJobManager` | 其他 | 内测 | 禁止使用JobManager功能 |
|`Web:DisallowUserRegister` | 功能特性 | 稳定 | 不允许用户注册 |
> 功能项状态详情参考 [features-status](features-status.md).
### 搭建依赖环境
#### [Zinc](https://github.com/zinclabs/zinc) 搜索引擎:
* Zinc运行
```sh
# 创建用于存放zinc数据的目录
mkdir -p data/zinc/data
# 使用Docker运行zinc
docker run -d --name zinc --user root -v ${PWD}/data/zinc/data:/data -p 4080:4080 -e ZINC_FIRST_ADMIN_USER=admin -e ZINC_FIRST_ADMIN_PASSWORD=admin -e DATA_PATH=/data public.ecr.aws/zinclabs/zinc:latest
# 查看zinc运行状态
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41465feea2ff getmeili/meilisearch:v0.27.0 "tini -- /bin/sh -c …" 20 hours ago Up 20 hours 0.0.0.0:7700->7700/tcp paopao-ce-meili-1
7daf982ca062 public.ecr.aws/prabhat/zinc:latest "/go/bin/zinc" 3 weeks ago Up 6 days 0.0.0.0:4080->4080/tcp zinc
# 使用docker compose运行
docker compose up -d zinc
# visit http://localhost:4080 打开自带的ui管理界面
```
* 修改Zinc配置
```yaml
# features中加上 Zinc 和 LoggerZinc
Features:
Default: ["Zinc", "LoggerZinc", "Base", "Sqlite3", "BigCacheIndex","MinIO"]
...
LoggerZinc: # 使用Zinc写日志
Host: 127.0.0.1:4080 # 这里的host就是paopao-ce能访问到的zinc主机
Index: paopao-log
User: admin
Password: admin
Secure: False # 如果使用https访问zinc就设置为True
...
Zinc: # Zinc搜索配置
Host: 127.0.0.1:4080
Index: paopao-data
User: admin
Password: admin
Secure: False
```
#### [Meilisearch](https://github.com/meilisearch/meilisearch) 搜索引擎:
* Meili运行
```sh
mkdir -p data/meili/data
# 使用Docker运行
docker run -d --name meili -v ${PWD}/data/meili/data:/meili_data -p 7700:7700 -e MEILI_MASTER_KEY=paopao-meilisearch getmeili/meilisearch:v0.29.0
# visit http://localhost:7700 打开自带的搜索前端ui
# 使用docker compose运行需要删除docker-compose.yaml中关于meili的注释
docker compose up -d meili
# 查看meili运行状态
docker compose ps
NAME COMMAND SERVICE STATUS PORTS
paopao-ce-meili-1 "tini -- /bin/sh -c …" meili running 0.0.0.0:7700->7700/tcp
```
* 修改Meili配置
```yaml
# features中加上 Meili 和 LoggerMeili
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex","MinIO"]
...
LoggerMeili: # 使用Meili写日志
Host: 127.0.0.1:7700
Index: paopao-log
ApiKey: paopao-meilisearch
Secure: False
MinWorker: 5 # 最小后台工作者, 设置范围[5, 100], 默认5
MaxLogBuffer: 100 # 最大log缓存条数, 设置范围[10, 10000], 默认100
...
Meili: # Meili搜索配置
Host: 127.0.0.1:7700 # 这里的host就是paopao-ce能访问到的meili主机
Index: paopao-data
ApiKey: paopao-meilisearch
Secure: False # 如果使用https访问meili就设置为True
```
#### [MinIO](https://github.com/minio/minio) 对象存储服务
* MinIO运行
```sh
mkdir -p data/minio/data
# 使用Docker运行
docker run -d --name minio -v ${PWD}/data/minio/data:/data -p 9000:9000 -p 9001:9001 -e MINIO_ROOT_USER=minio-root-user -e MINIO_ROOT_PASSWORD=minio-root-password -e MINIO_DEFAULT_BUCKETS=paopao:public bitnami/minio:latest
# 使用docker compose运行 需要删除docker-compose.yaml中关于minio的注释
docker compose up -d minio
```
* 修改Minio配置
```yaml
# features中加上 MinIO
Features:
Default: ["MinIO", "Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex"]
...
MinIO: # MinIO 存储配置
AccessKey: Q3AM3UQ867SPQQA43P2F # AccessKey/SecretKey 需要登入minio管理界面手动创建管理界面地址: http://127.0.0.1:9001
SecretKey: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
Secure: False
Endpoint: 127.0.0.1:9000 # 根据部署的minio主机修改对应地址
Bucket: paopao # 如上需要在管理界面创建bucket并赋予外部可读写权限
Domain: 127.0.0.1:9000 # minio外网访问的地址(如果想让外网访问这里需要设置为外网可访问到的minio主机地址)
...
```
#### [OpenObserve](https://github.com/openobserve/openobserve) 日志收集、指标度量、轨迹跟踪
* OpenObserve运行
```sh
# 使用Docker运行
mkdir data && docker run -v $PWD/data:/data -e ZO_DATA_DIR="/data" -p 5080:5080 \
-e ZO_ROOT_USER_EMAIL="root@paopao.info" -e ZO_ROOT_USER_PASSWORD="paopao-ce" \
public.ecr.aws/zinclabs/openobserve:latest
# 使用docker compose运行 需要删除docker-compose.yaml中关于openobserve的注释
docker compose up -d openobserve
# visit http://loclahost:5080
```
* 修改LoggerOpenObserve配置
```yaml
# features中加上 LoggerOpenObserve
Features:
Default: ["Meili", "LoggerOpenObserve", "Base", "Sqlite3", "BigCacheIndex"]
...
LoggerOpenObserve: # 使用OpenObserve写日志
Host: 127.0.0.1:5080
Organization: paopao-ce
Stream: default
User: root@paopao.info
Password: tiFEI8UeJWuYA7kN
Secure: False
...
```
#### [Pyroscope](https://github.com/pyroscope-io/pyroscope) 性能剖析
* Pyroscope运行
```sh
mkdir -p data/minio/data
# 使用Docker运行
docker run -it -p 4040:4040 pyroscope/pyroscope:latest server
# 使用docker compose运行 需要删除docker-compose.yaml中关于pyroscope的注释
docker compose up -d pyroscope
# visit http://loclahost:4040
```
* 修改Pyroscope配置
```yaml
# features中加上 Pyroscope
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex", "Pyroscope"]
...
Pyroscope: # Pyroscope配置
AppName: "paopao-ce"
Endpoint: "http://localhost:4040" # Pyroscope server address
AuthToken: # Pyroscope authentication token
Logger: none # Pyroscope logger (standard | logrus | none)
...
```
### 源代码分支管理
**主代码库`github.com/rocboss/paopao-ce`**
```bash
git branch
main
beta
dev
feature/bleve
feature/followship
feature/mir
feature/localoss
jc/alimy
r/paopao-plus
r/paopao-pro
x/sqlc
x/sqlx
```
**分支说明**
| 名称 | 说明 | 备注|
| ----- | ----- | ----- |
| [`main`](https://github.com/rocboss/paopao-ce) | 主分支 |分支`main`是主分支也是paopao-ce的稳定版本发布分支只有经过内部测试没有重大bug出现的稳定代码才会推进到这个分支该分支主要由`beta`分支代码演进而来,原则上**只接受bug修复PR**。`rc版本/稳定版本` 发布都应该在`main`主分支中进行。|
| [`beta`](https://github.com/rocboss/paopao-ce/tree/beta) | 公测分支 |分支`beta`是公测分支,代码推进到`main`主分支的候选分支;该分支主要由`alpha`分支代码演进而来,**接受bug修复以及新功能优化的PR**原则上不接受新功能PR。`beta版本` 发布都应该在`beta`公测分支下进行。|
| [`alpha`](https://github.com/rocboss/paopao-ce/tree/alpha) | 内测分支 |分支`alpha`是内测分支,代码推进到`beta`分支的候选分支;该分支主要由`dev`分支代码演进而来,**接受bug修复以及新功能相关的PR**接受新功能PR。分支代码演进到一个里程碑式的阶段后**冻结所有新功能**,合并代码到`beta`公测分支进行下一阶段的持续演进。`alpha版本` 发布都应该在`alpha`内测分支下进行。|
| [`dev`](https://github.com/rocboss/paopao-ce/tree/dev) | 开发分支 | 分支`dev`是开发分支,**不定期频繁更新**,接受 *新功能PR、代码优化PR、bug修复PR***新功能PR** 都应该首先提交给`dev`分支进行合并bug修复/新功能开发/代码优化 **阶段性冻结** 后将代码演进合并到`alpha`分支。|
| `feature/*` | 子功能分支 |`feature/*`是新功能子分支,一般新功能子分支都是 *从`dev`开发分支fork出来的*;子功能分支 **只专注于该新功能** 代码的开发/优化,待开发接近内测阶段 *提交新功能PR给`dev`分支进行review/merge*,待新功能代码演进到`beta`分支后,原则上是可以删除该分支,但也可以保留到稳定版本发布。**该分支专注于新功能的开发只接受新功能的bug修复/优化PR**。|
| `jc/*` |维护者的开发分支|`jc/*`是代码库维护者的开发分支一般包含一些局部优化或者bug修复代码有时可以直接将代码merge到`dev/beta`分支原则上不允许直接merge代码到`main`主分支。|
| `x/*` |实验分支|`x/*`是技术实验分支某些技术的引入需要经过具体的代码实现与真实场景的测评考量评估后如果某项技术适合引入到paopao-ce就fork出一个`feature/*`分支作为新功能引入到paopao-ce。一般一些比较激进的技术从`dev`分支fork出一个新的`x/*`分支各种尝试、考量、评估后或丢弃、或引入到paopao-ce。|
| `t/*` | 临时分支 |`t/*`是临时发版本分支,一般 `beta` 分支演进到正式版本发布前的最后某个beta版本比如v0.2.0-beta)就从beta分支fork出一个 `t/*` 分支用于向 `main` 分支提交 PR 用于Review待 PR Reviewed 合并到 `main` 分支后,可以删除这个临时创建的分支。这样设计主要是考虑到有时合并到 `main` 分支时需要Review的时间可能会长一些而dev分支的代码又急需推进到beta分支以发布下一个alpha版本用于内测相当于为下一个测试版本发布腾地方。|
| `r/*` |发行版本分支|`r/*`是不同发行版本分支,不同发行版本各有不同的侧重点,可以根据需要选择适合的发行版本。|
**发行版本分支说明**
| 名称 | 说明 | 维护者 | 备注 |
| ----- | ----- | ----- | ----- |
|[`paopao-ce`](https://github.com/rocboss/paopao-ce/tree/dev)|paopao-ce 主发行版本|[ROC](https://github.com/rocboss 'ROC')|该分支 [数据逻辑层](https://github.com/rocboss/paopao-ce/tree/dev/internal/dao/jinzhu) 使用[gorm](https://github.com/go-gorm/gorm)作为数据逻辑层的ORM框架适配MySQL/PostgreSQL/Sqlite3数据库。|
|[`r/paopao-ce`](https://github.com/rocboss/paopao-ce/tree/r/paopao-ce)|paopao-ce 主分支预览版本|[ROC](https://github.com/rocboss 'ROC')<br/>[北野](https://github.com/alimy 'Michael Li')|该分支 [数据逻辑层](https://github.com/rocboss/paopao-ce/tree/dev/internal/dao/jinzhu) 使用[gorm](https://github.com/go-gorm/gorm)作为数据逻辑层的ORM框架适配MySQL/PostgreSQL/Sqlite3数据库。代码较`main`分支新,是主发行版本的前瞻预览版本。|
|[`r/paopao-ce-plus`](https://github.com/rocboss/paopao-ce/tree/r/paopao-ce-plus)|paopao-ce-plus 发行版本|[北野](https://github.com/alimy 'Michael Li')|该分支 [数据逻辑层](https://github.com/rocboss/paopao-ce/tree/r/paopao-ce-plus/internal/dao/sakila) 使用[sqlx](https://github.com/jmoiron/sqlx)作为数据逻辑层的ORM框架专注于为MySQL/PostgreSQL/Sqlite3使用更优化的查询语句以提升数据检索效率。建议熟悉[sqlx](https://github.com/jmoiron/sqlx)的开发人员可以基于此版本来做 二次开发。|
|[`r/paopao-ce-pro`](https://github.com/rocboss/paopao-ce/tree/r/paopao-ce-pro)|paopao-ce-pro 发行版本|[北野](https://github.com/alimy 'Michael Li')|该分支 [数据逻辑层](https://github.com/rocboss/paopao-ce/tree/r/paopao-ce-pro/internal/dao/slonik) 使用[sqlc](https://github.com/kyleconroy/sqlc)作为sql语句生成器自动生成ORM代码专门针对特定数据库MySQL/PostgreSQL进行查询优化熟悉[sqlc](https://github.com/kyleconroy/sqlc)的开发人员可以基于此版本来做 二次开发。(另:分支目前只使用[pgx-v5](https://github.com/jackc/pgx)适配了PostgreSQL数据库后续或许会适配MySQL/TiDB数据库。)|
|[`r/paopao-ce-xtra`](https://github.com/rocboss/paopao-ce/tree/r/paopao-ce-xtra)|paopao-ce-xtra 发行版本|[北野](https://github.com/alimy 'Michael Li')|该分支 是r/paopao-ce、r/paopao-ce-plus、r/paopao-ce-pro的合集|
|[`r/paopao-ce-mini`](https://github.com/rocboss/paopao-ce/tree/r/paopao-ce-mini)|paopao-ce-mini 发行版本|[北野](https://github.com/alimy 'Michael Li')|该分支是paopao-ce最小可用版本专注于个人部署、一键傻瓜式最简部署|
**代码分支演进图**
![](docs/proposal/.assets/000-01.png)
### 部署站点信息
* [官方 paopao.info](https://www.paopao.info)
> 具体部署站点信息请查阅 [deployed-sites](./deployed-sites.md 'deployed sites'). 欢迎站长将已部署PaoPao实例的站点信息添加到 [deployed-sites](./deployed-sites.md 'deployed sites') 列表中。
#### Collaborator's paopao account
| 昵称 | [@GitHub](https://github.com 'github.com') | [@PaoPao](https://www.paopao.info 'paopao.info') |
| ----- | ----- | ----- |
| ROC | [ROC](https://github.com/rocboss 'ROC')|[ROC](https://www.paopao.info/#/user?username=roc 'ROC @roc')|
| [北野](https://alimy.me '糊涂小栈') | [Michael Li](https://github.com/alimy 'Michael Li') | [alimy](https://www.paopao.info/#/user?username=alimy '北野 @alimy')|
| orzi!| [orzi!](https://github.com/orziz 'orzi!')||
### 其他说明
建议后端服务使用 `supervisor` 守护进程,并通过 `nginx` 反向代理后提供API给前端服务调用。
短信通道使用的[聚合数据](https://www.juhe.cn/),如果申请不下来,可以考虑替换其他服务商。
代码结构比较简单,很方便扩展,开发文档请参阅[docs](docs '开发文档').
<!-- MARKDOWN LINKS & IMAGES -->
[contributors-shield]: https://img.shields.io/github/contributors/rocboss/paopao-ce?style=flat
[contributors-url]: https://github.com/rocboss/paopao-ce/graphs/contributors
[goreport-shield]: https://goreportcard.com/badge/github.com/rocboss/paopao-ce
[goreport-url]: https://goreportcard.com/report/github.com/rocboss/paopao-ce
[forks-shield]: https://img.shields.io/github/forks/rocboss/paopao-ce?style=flat
[forks-url]: https://github.com/rocboss/paopao-ce/network/members
[stars-shield]: https://img.shields.io/github/stars/rocboss/paopao-ce.svg?style=flat
[stars-url]: https://github.com/rocboss/paopao-ce/stargazers
[issues-shield]: https://img.shields.io/github/issues/rocboss/paopao-ce.svg?style=flat
[issues-url]: https://github.com/rocboss/paopao-ce/issues
[license-shield]: https://img.shields.io/github/license/rocboss/paopao-ce.svg?style=flat
[license-url]: https://github.com/rocboss/paopao-ce/blob/master/LICENSE.txt
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat&logo=linkedin&colorB=555
[product-light-screenshot]: https://cdn.rocs.me/static/paopao-light.jpeg
[product-dark-screenshot]: https://cdn.rocs.me/static/paopao-dark.jpeg

@ -88,9 +88,9 @@ gen-mir:
.PHONY: gen-rpc
gen-rpc:
@rm -rf auto/rpc auto/connect
@rm -rf auto/rpc
@buf generate proto
@go fmt ./auto/rpc/... ./auto/connect/...
@go fmt ./auto/rpc/...
.PHONY: proto-mod
proto-mod:
@ -117,12 +117,15 @@ test:
pre-commit: fmt
@go mod tidy
.PHONY: install-tools
install-tools: install-protobuf-plugins
@go install github.com/abice/go-enum@latest
.PHONY: install-protobuf-plugins
install-protobuf-plugins:
@go install github.com/bufbuild/buf/cmd/buf@v1.28.1
@go install github.com/bufbuild/buf/cmd/protoc-gen-buf-breaking@v1.28.1
@go install github.com/bufbuild/buf/cmd/protoc-gen-buf-lint@v1.28.1
@go install connectrpc.com/connect/cmd/protoc-gen-connect-go@latest
@go install github.com/bufbuild/buf/cmd/buf@latest
@go install github.com/bufbuild/buf/cmd/protoc-gen-buf-breaking@latest
@go install github.com/bufbuild/buf/cmd/protoc-gen-buf-lint@latest
@go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
@go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

@ -50,9 +50,9 @@ PaoPao主要由以下优秀的开源项目/工具构建
* [Go](https://go.dev/ 'go')
* [Gin](https://gin-gonic.com/ 'gin')
* [Mir](https://github.com/alimy/mir 'go-mir')
* [Buf](https://github.com/bufbuild/buf 'buf')
* [gRPC](https://github.com/grpc/grpc-go 'grpc-go')
* [Meilisearch](https://www.meilisearch.com/ 'meilisearch')
* [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-go 'OpenTelemetry')
* [OpenObserve](https://github.com/openobserve/openobserve 'OpenObserve')
#### 前端:
* [Naive UI](https://www.naiveui.com/)
@ -65,7 +65,7 @@ PaoPao主要由以下优秀的开源项目/工具构建
### 环境要求
* Go (1.20+)
* Go (1.22+)
* Node.js (14+)
* MySQL (5.7+)
* Redis
@ -74,227 +74,15 @@ PaoPao主要由以下优秀的开源项目/工具构建
以上环境版本为PaoPao官方的开发版本仅供参考其他版本的环境未进行充分测试
### 安装说明
### 方式一. 手动安装(推荐)
克隆代码库
```sh
git clone https://github.com/rocboss/paopao-ce.git
```
#### 后端
1. 导入项目根目录下的 `scripts/paopao.sql` 文件至MySQL数据库
2. 拷贝项目根目录下 `config.yaml.sample` 文件至 `config.yaml`,按照注释完成配置编辑
3. 编译后端
编译api服务:
```sh
make build
```
编译api服务、内嵌web前端ui:
```sh
make build
```
也可以使用精简模式编译不内嵌web前端ui:
```sh
make build TAGS='slim embed'
```
编译后在`release`目录可以找到对应可执行文件。
```sh
release/paopao
```
4. 直接运行后端
运行api服务:
```sh
make run
```
运行api服务、web前端ui服务:
```sh
make run TAGS='embed'
```
提示: 如果需要内嵌web前端ui请先构建web前端(建议设置web/.env为VITE_HOST="")。
5. 使用内置的Migrate机制自动升级维护SQL DDL:
```sh
# 添加 Migration 功能到 Features 中 开启migrate功能
vim config.yaml
# file: config.yaml
# Features:
# Default: ["Base", "MySQL", "Zinc", "MinIO", "LoggerZinc", "Migration"]
# 编译时加入migration tag编译出支持migrate功能的可执行文件
make build TAGS='migration'
release/paopao
# 或者 带上migration tag直接运行
make run TAGS='migration'
```
> 注意默认编译出来的可执行文件是不内置migrate功能需要编译时带上migration tag才能内置支持migrage功能。
#### 前端
1. 进入前端目录 `web`,拷贝`.env` 到 `.env.local`,编辑 `.env.local ` 文件中后端服务地址及其他配置项,下载依赖包
```sh
cd ./web && cp .env .env.local
vim .env.local
yarn
```
2. 编译前端
```sh
yarn build
```
build完成后可以在dist目录获取编译产出配置nginx指向至该目录即可
#### 桌面端
1. 进入前端目录 `web`,拷贝`.env` 到 `.env.local`,编辑 `.env.local ` 文件中后端服务地址及其他配置项,下载依赖包
```sh
cd ./web && cp .env .env.local
vim .env.local
yarn
```
2. 编译前端
```sh
yarn build
```
3. 构建桌面端
```sh
yarn tauri build
```
桌面端是使用[Rust](https://www.rust-lang.org/) + [tauri](https://github.com/tauri-apps/tauri)编写
需要安装tauri的依赖具体参考[https://tauri.studio/v1/guides/getting-started/prerequisites](https://tauri.studio/v1/guides/getting-started/prerequisites).
### 方式二. 使用Docker构建、运行
* 后端:
```sh
# 默认参数构建, 默认内嵌web ui并设置api host为空
docker build -t your/paopao-ce:tag .
# 内嵌web ui并且自定义API host参数
docker build -t your/paopao-ce:tag --build-arg API_HOST=http://api.paopao.info .
# 内嵌web ui并且使用本地web/.env中的API host
docker build -t your/paopao-ce:tag --build-arg USE_API_HOST=no .
# 内嵌web ui并且使用本地编译的web/dist构建
docker build -t your/paopao-ce:tag --build-arg USE_DIST=yes .
# 只编译api server
docker build -t your/paopao-ce:tag --build-arg EMBED_UI=no .
# 运行
mkdir custom && docker run -d -p 8008:8008 -v ${PWD}/custom:/app/paopao-ce/custom -v ${PWD}/config.yaml.sample:/app/paopao-ce/config.yaml your/paopao-ce:tag
# 或者直接运行构建好的docker image
mkdir custom && docker run -d -p 8008:8008 -v ${PWD}/custom:/app/paopao-ce/custom -v ${PWD}/config.yaml.sample:/app/paopao-ce/config.yaml bitbus/paopao-ce:latest
```
* 前端:
```sh
cd web
# 默认参数构建
docker build -t your/paopao-ce:web .
# 自定义API host 参数构建
docker build -t your/paopao-ce:web --build-arg API_HOST=http://api.paopao.info .
# 使用本地编译的dist构建
docker build -t your/paopao-ce:web --build-arg USE_DIST=yes .
# 运行
docker run -d -p 8010:80 your/paopao-ce:web
```
* All-In-One:
```sh
# 构建Image
docker buildx build --build-arg USE_DIST="yes" -t your/paopao-ce:all-in-one-latest -f Dockerfile.allinone .
# 运行
docker run --name paopao-ce-allinone -d -p 8000:8008 -p 7700:7700 -v ./data/custom:/app/custom -v ./data/meili_data:/app/meili_data your/paopao-ce:all-in-one-latest
# 或者使用官方Image运行
docker run --name paopao-ce-allinone -d -p 8000:8008 -p 7700:7700 -v ./data/custom:/app/custom -v ./data/meili_data:/app/meili_data bitbus/paopao-ce:all-in-one-latest
# 或者使用官方Image运行 + 自定义config.yaml
docker run --name paopao-ce-allinone -d -p 8000:8008 -p 7700:7700 -v ./config.yaml:/app/config.yaml -v ./data/custom:/app/custom -v ./data/meili_data:/app/meili_data bitbus/paopao-ce:all-in-one-latest
```
> 注意在`config.yaml` 中`Meili.ApiKey`的值必须与容器中meili启动时设定的`MEILI_MASTER_KEY`环境变量值相同,默认为`paopao-meilisearch`. 可以在docker启动容器时通过`-e MEILI_MASTER_KEY=<custom-key>`设置该值。
### 方式三. 使用 docker-compose 运行
```sh
git clone https://github.com/rocboss/paopao-ce.git
cd paopao-ce && docker compose up -d
# visit http://localhost:8008 👀 paopao-ce
# visit http://localhost:8001 👀 RedisInsight
# visit http://localhost:8080 👀 phpMyAdmin
```
默认是使用config.yaml.sample的配置如果需要自定义配置请拷贝默认配置文件(比如config.yaml)修改后再同步配置到docker-compose.yaml如下
```
# file: docker-compose.yaml
...
backend:
image: bitbus/paopao-ce:latest
restart: always
depends_on:
- db
- redis
- zinc
# modify below to reflect your custom configure
volumes:
- ./config.yaml:/app/paopao-ce/config.yaml
ports:
- 8008:8008
networks:
- paopao-network
....
```
> 注意:默认提供的 docker-compose.yaml 初衷是搭建本机开发调试环境,如果需要产品部署供外网访问,请自行调优配置参数或使用其他方式部署。
参考 [安装说明 (INSTALL.md);](INSTALL.md '参考 安装说明')
### 开发文档
#### Docs文档说明
`docs`目录提供了各种开发文档,包括:
* [deploy](docs/deploy/) - paopao-ce部署文档
* [discuss](docs/discuss/) - 开发相关的问题交流论述文档
* [openapi](docs/openapi/) - paopao-ce后端导出API文档
* [proposal](docs/proposal/) - paopao-ce功能特性提按文档
> 比如关于paopao-ce的设计定位可以参考[docs/proposal/001-关于paopao-ce的设计定位](docs/proposal/001-关于paopao-ce的设计定位.md)简要阐述了paopao-ce是如何定位自身的。
#### API文档
开发者可以在本地开启`Docs`服务浏览后端导出的API服务接口文档。
* `config.yaml` 添加 `Docs` 功能项:
```yaml
...
Features:
Default: ["Base", "MySQL", "Option", "LocalOSS", "LoggerFile", "Docs"]
Docs: ["Docs:OpenAPI"]
...
```
* 构建时将 `docs` 添加到TAGS中:
```sh
make run TAGS='docs'
# visit http://127.0.0.1:8011/docs/openapi
```
> 比如关于paopao-ce的设计定位可以参考[docs/proposal/22110411-关于paopao-ce的设计定位](docs/proposal/22110411-关于paopao-ce的设计定位.md)简要阐述了paopao-ce是如何定位自身的。
### 配置说明
@ -313,8 +101,8 @@ paopao serve
Features:
Default: ["Base", "MySQL", "Option", "LocalOSS", "LoggerFile"]
Develop: ["Base", "MySQL", "Option", "Sms", "AliOSS", "LoggerZinc"]
Demo: ["Base", "MySQL", "Option", "Sms", "MinIO", "LoggerZinc"]
Develop: ["Base", "MySQL", "Option", "Sms", "AliOSS", "LoggerOtlp"]
Demo: ["Base", "MySQL", "Option", "Sms", "MinIO", "LoggerOtlp"]
Slim: ["Base", "Sqlite3", "LocalOSS", "LoggerFile"]
Base: ["Zinc", "Redis", "Alipay",]
Option: ["SimpleCacheIndex"]
@ -381,14 +169,15 @@ release/paopao serve --no-default-features --features sqlite3,localoss,loggerfil
|`SimpleCacheIndex` | 缓存 | Deprecated | 提供简单的 广场推文列表 的缓存功能 |
|`BigCacheIndex` | 缓存 | Deprecated | 使用[BigCache](https://github.com/allegro/bigcache)缓存 广场推文列表,缓存每个用户每一页,简单做到千人千面 |
|`RedisCacheIndex` | 缓存 | Deprecated | 使用Redis缓存 广场推文列表,缓存每个用户每一页,简单做到千人千面 |
|`Zinc` | 搜索 | 稳定(推荐) | 基于[Zinc](https://github.com/zinclabs/zinc)搜索引擎提供推文搜索服务 |
|`Zinc` | 搜索 | Deprecated | 基于[Zinc](https://github.com/zinclabs/zinc)搜索引擎提供推文搜索服务 |
|`Meili` | 搜索 | 稳定(推荐) | 基于[Meilisearch](https://github.com/meilisearch/meilisearch)搜索引擎提供推文搜索服务 |
|`Bleve` | 搜索 | WIP | 基于[Bleve](https://github.com/blevesearch/bleve)搜索引擎提供推文搜索服务 |
|[`Sentry`](docs/proposal/23040412-关于使用sentry用于错误追踪与性能检测的设计.md) | 监控 | 内测 | 使用Sentry进行错误跟踪与性能监控 |
|`LoggerFile` | 日志 | 稳定 | 使用文件写日志 |
|`LoggerZinc` | 日志 | 稳定(推荐) | 使用[Zinc](https://github.com/zinclabs/zinc)写日志 |
|`LoggerMeili` | 日志 | 内测 | 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志 |
|`LoggerOpenObserve` | 日志 | 内测 | 使用[OpenObserve](https://github.com/openobserve/openobserve)写日志 |
|`LoggerZinc` | 日志 | Deprecated | 使用[Zinc](https://github.com/zinclabs/zinc)写日志 |
|`LoggerMeili` | 日志 | Deprecated | 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志 |
|`LoggerOpenObserve` | 日志 | Deprecated | 使用[OpenObserve](https://github.com/openobserve/openobserve)写日志 |
|`LoggerOtlp` | 日志 | 内测 | 使用[OpenTelemetry](https://github.com/open-telemetry/opentelemetry-go)写日志 |
|[`Friendship`](docs/proposal/22110410-关于Friendship功能项的设计.md) | 关系模式 | 内置 Builtin | 弱关系好友模式,类似微信朋友圈 |
|[`Followship`](docs/proposal/22110409-关于Followship功能项的设计.md) | 关系模式 | 内置 Builtin | 关注者模式类似Twitter的Follow模式 |
|[`Lightship`](docs/proposal/22121409-关于Lightship功能项的设计.md) | 关系模式 | 弃用 Deprecated | 开放模式,所有推文都公开可见 |
@ -403,170 +192,6 @@ release/paopao serve --no-default-features --features sqlite3,localoss,loggerfil
|`Web:DisallowUserRegister` | 功能特性 | 稳定 | 不允许用户注册 |
> 功能项状态详情参考 [features-status](features-status.md).
### 搭建依赖环境
#### [Zinc](https://github.com/zinclabs/zinc) 搜索引擎:
* Zinc运行
```sh
# 创建用于存放zinc数据的目录
mkdir -p data/zinc/data
# 使用Docker运行zinc
docker run -d --name zinc --user root -v ${PWD}/data/zinc/data:/data -p 4080:4080 -e ZINC_FIRST_ADMIN_USER=admin -e ZINC_FIRST_ADMIN_PASSWORD=admin -e DATA_PATH=/data public.ecr.aws/zinclabs/zinc:latest
# 查看zinc运行状态
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41465feea2ff getmeili/meilisearch:v0.27.0 "tini -- /bin/sh -c …" 20 hours ago Up 20 hours 0.0.0.0:7700->7700/tcp paopao-ce-meili-1
7daf982ca062 public.ecr.aws/prabhat/zinc:latest "/go/bin/zinc" 3 weeks ago Up 6 days 0.0.0.0:4080->4080/tcp zinc
# 使用docker compose运行
docker compose up -d zinc
# visit http://localhost:4080 打开自带的ui管理界面
```
* 修改Zinc配置
```yaml
# features中加上 Zinc 和 LoggerZinc
Features:
Default: ["Zinc", "LoggerZinc", "Base", "Sqlite3", "BigCacheIndex","MinIO"]
...
LoggerZinc: # 使用Zinc写日志
Host: 127.0.0.1:4080 # 这里的host就是paopao-ce能访问到的zinc主机
Index: paopao-log
User: admin
Password: admin
Secure: False # 如果使用https访问zinc就设置为True
...
Zinc: # Zinc搜索配置
Host: 127.0.0.1:4080
Index: paopao-data
User: admin
Password: admin
Secure: False
```
#### [Meilisearch](https://github.com/meilisearch/meilisearch) 搜索引擎:
* Meili运行
```sh
mkdir -p data/meili/data
# 使用Docker运行
docker run -d --name meili -v ${PWD}/data/meili/data:/meili_data -p 7700:7700 -e MEILI_MASTER_KEY=paopao-meilisearch getmeili/meilisearch:v0.29.0
# visit http://localhost:7700 打开自带的搜索前端ui
# 使用docker compose运行需要删除docker-compose.yaml中关于meili的注释
docker compose up -d meili
# 查看meili运行状态
docker compose ps
NAME COMMAND SERVICE STATUS PORTS
paopao-ce-meili-1 "tini -- /bin/sh -c …" meili running 0.0.0.0:7700->7700/tcp
```
* 修改Meili配置
```yaml
# features中加上 Meili 和 LoggerMeili
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex","MinIO"]
...
LoggerMeili: # 使用Meili写日志
Host: 127.0.0.1:7700
Index: paopao-log
ApiKey: paopao-meilisearch
Secure: False
MinWorker: 5 # 最小后台工作者, 设置范围[5, 100], 默认5
MaxLogBuffer: 100 # 最大log缓存条数, 设置范围[10, 10000], 默认100
...
Meili: # Meili搜索配置
Host: 127.0.0.1:7700 # 这里的host就是paopao-ce能访问到的meili主机
Index: paopao-data
ApiKey: paopao-meilisearch
Secure: False # 如果使用https访问meili就设置为True
```
#### [MinIO](https://github.com/minio/minio) 对象存储服务
* MinIO运行
```sh
mkdir -p data/minio/data
# 使用Docker运行
docker run -d --name minio -v ${PWD}/data/minio/data:/data -p 9000:9000 -p 9001:9001 -e MINIO_ROOT_USER=minio-root-user -e MINIO_ROOT_PASSWORD=minio-root-password -e MINIO_DEFAULT_BUCKETS=paopao:public bitnami/minio:latest
# 使用docker compose运行 需要删除docker-compose.yaml中关于minio的注释
docker compose up -d minio
```
* 修改Minio配置
```yaml
# features中加上 MinIO
Features:
Default: ["MinIO", "Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex"]
...
MinIO: # MinIO 存储配置
AccessKey: Q3AM3UQ867SPQQA43P2F # AccessKey/SecretKey 需要登入minio管理界面手动创建管理界面地址: http://127.0.0.1:9001
SecretKey: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
Secure: False
Endpoint: 127.0.0.1:9000 # 根据部署的minio主机修改对应地址
Bucket: paopao # 如上需要在管理界面创建bucket并赋予外部可读写权限
Domain: 127.0.0.1:9000 # minio外网访问的地址(如果想让外网访问这里需要设置为外网可访问到的minio主机地址)
...
```
#### [OpenObserve](https://github.com/openobserve/openobserve) 日志收集、指标度量、轨迹跟踪
* OpenObserve运行
```sh
# 使用Docker运行
mkdir data && docker run -v $PWD/data:/data -e ZO_DATA_DIR="/data" -p 5080:5080 \
-e ZO_ROOT_USER_EMAIL="root@paopao.info" -e ZO_ROOT_USER_PASSWORD="paopao-ce" \
public.ecr.aws/zinclabs/openobserve:latest
# 使用docker compose运行 需要删除docker-compose.yaml中关于openobserve的注释
docker compose up -d openobserve
# visit http://loclahost:5080
```
* 修改LoggerOpenObserve配置
```yaml
# features中加上 LoggerOpenObserve
Features:
Default: ["Meili", "LoggerOpenObserve", "Base", "Sqlite3", "BigCacheIndex"]
...
LoggerOpenObserve: # 使用OpenObserve写日志
Host: 127.0.0.1:5080
Organization: paopao-ce
Stream: default
User: root@paopao.info
Password: tiFEI8UeJWuYA7kN
Secure: False
...
```
#### [Pyroscope](https://github.com/pyroscope-io/pyroscope) 性能剖析
* Pyroscope运行
```sh
mkdir -p data/minio/data
# 使用Docker运行
docker run -it -p 4040:4040 pyroscope/pyroscope:latest server
# 使用docker compose运行 需要删除docker-compose.yaml中关于pyroscope的注释
docker compose up -d pyroscope
# visit http://loclahost:4040
```
* 修改Pyroscope配置
```yaml
# features中加上 Pyroscope
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex", "Pyroscope"]
...
Pyroscope: # Pyroscope配置
AppName: "paopao-ce"
Endpoint: "http://localhost:4040" # Pyroscope server address
AuthToken: # Pyroscope authentication token
Logger: none # Pyroscope logger (standard | logrus | none)
...
```
### 源代码分支管理
**主代码库`github.com/rocboss/paopao-ce`**
@ -575,10 +200,7 @@ git branch
main
beta
dev
feature/bleve
feature/followship
feature/mir
feature/localoss
jc/alimy
r/paopao-plus
r/paopao-pro
@ -615,21 +237,6 @@ x/sqlx
* [官方 paopao.info](https://www.paopao.info)
> 具体部署站点信息请查阅 [deployed-sites](./deployed-sites.md 'deployed sites'). 欢迎站长将已部署PaoPao实例的站点信息添加到 [deployed-sites](./deployed-sites.md 'deployed sites') 列表中。
#### Collaborator's paopao account
| 昵称 | [@GitHub](https://github.com 'github.com') | [@PaoPao](https://www.paopao.info 'paopao.info') |
| ----- | ----- | ----- |
| ROC | [ROC](https://github.com/rocboss 'ROC')|[ROC](https://www.paopao.info/#/user?username=roc 'ROC @roc')|
| [北野](https://alimy.me '糊涂小栈') | [Michael Li](https://github.com/alimy 'Michael Li') | [alimy](https://www.paopao.info/#/user?username=alimy '北野 @alimy')|
| orzi!| [orzi!](https://github.com/orziz 'orzi!')||
### 其他说明
建议后端服务使用 `supervisor` 守护进程,并通过 `nginx` 反向代理后提供API给前端服务调用。
短信通道使用的[聚合数据](https://www.juhe.cn/),如果申请不下来,可以考虑替换其他服务商。
代码结构比较简单,很方便扩展,开发文档请参阅[docs](docs '开发文档').
## 👯‍♀️ 贡献
paopao-ce 是一个利用 *业余时间* 本着 **"Just for fun just do it."** 的心态 *持续有序* **开发/优化/维护**的开源项目没有KPI考核、没有Roadmap进度压力、没有技术支持日程安排或许有些许不足之处但是重在精神可嘉。 借用网络中的话 **"F\*k talk, f\*k of tech innovation, Shut up and show me your code."** 一切都因更好的体验,一切都是为了爱好,一切都在代码里;期待老铁们加入,一起开发、一起折腾、一起快乐。

@ -1,173 +0,0 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: core/v1/auth.proto
package corev1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/rocboss/paopao-ce/auto/rpc/core/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// AuthenticateServiceName is the fully-qualified name of the AuthenticateService service.
AuthenticateServiceName = "core.v1.AuthenticateService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// AuthenticateServicePreLoginProcedure is the fully-qualified name of the AuthenticateService's
// preLogin RPC.
AuthenticateServicePreLoginProcedure = "/core.v1.AuthenticateService/preLogin"
// AuthenticateServiceLoginProcedure is the fully-qualified name of the AuthenticateService's login
// RPC.
AuthenticateServiceLoginProcedure = "/core.v1.AuthenticateService/login"
// AuthenticateServiceLogoutProcedure is the fully-qualified name of the AuthenticateService's
// logout RPC.
AuthenticateServiceLogoutProcedure = "/core.v1.AuthenticateService/logout"
)
// These variables are the protoreflect.Descriptor objects for the RPCs defined in this package.
var (
authenticateServiceServiceDescriptor = v1.File_core_v1_auth_proto.Services().ByName("AuthenticateService")
authenticateServicePreLoginMethodDescriptor = authenticateServiceServiceDescriptor.Methods().ByName("preLogin")
authenticateServiceLoginMethodDescriptor = authenticateServiceServiceDescriptor.Methods().ByName("login")
authenticateServiceLogoutMethodDescriptor = authenticateServiceServiceDescriptor.Methods().ByName("logout")
)
// AuthenticateServiceClient is a client for the core.v1.AuthenticateService service.
type AuthenticateServiceClient interface {
PreLogin(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error)
Login(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.LoginReply], error)
Logout(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error)
}
// NewAuthenticateServiceClient constructs a client for the core.v1.AuthenticateService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewAuthenticateServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) AuthenticateServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
return &authenticateServiceClient{
preLogin: connect.NewClient[v1.User, v1.ActionReply](
httpClient,
baseURL+AuthenticateServicePreLoginProcedure,
connect.WithSchema(authenticateServicePreLoginMethodDescriptor),
connect.WithClientOptions(opts...),
),
login: connect.NewClient[v1.User, v1.LoginReply](
httpClient,
baseURL+AuthenticateServiceLoginProcedure,
connect.WithSchema(authenticateServiceLoginMethodDescriptor),
connect.WithClientOptions(opts...),
),
logout: connect.NewClient[v1.User, v1.ActionReply](
httpClient,
baseURL+AuthenticateServiceLogoutProcedure,
connect.WithSchema(authenticateServiceLogoutMethodDescriptor),
connect.WithClientOptions(opts...),
),
}
}
// authenticateServiceClient implements AuthenticateServiceClient.
type authenticateServiceClient struct {
preLogin *connect.Client[v1.User, v1.ActionReply]
login *connect.Client[v1.User, v1.LoginReply]
logout *connect.Client[v1.User, v1.ActionReply]
}
// PreLogin calls core.v1.AuthenticateService.preLogin.
func (c *authenticateServiceClient) PreLogin(ctx context.Context, req *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error) {
return c.preLogin.CallUnary(ctx, req)
}
// Login calls core.v1.AuthenticateService.login.
func (c *authenticateServiceClient) Login(ctx context.Context, req *connect.Request[v1.User]) (*connect.Response[v1.LoginReply], error) {
return c.login.CallUnary(ctx, req)
}
// Logout calls core.v1.AuthenticateService.logout.
func (c *authenticateServiceClient) Logout(ctx context.Context, req *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error) {
return c.logout.CallUnary(ctx, req)
}
// AuthenticateServiceHandler is an implementation of the core.v1.AuthenticateService service.
type AuthenticateServiceHandler interface {
PreLogin(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error)
Login(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.LoginReply], error)
Logout(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error)
}
// NewAuthenticateServiceHandler builds an HTTP handler from the service implementation. It returns
// the path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewAuthenticateServiceHandler(svc AuthenticateServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
authenticateServicePreLoginHandler := connect.NewUnaryHandler(
AuthenticateServicePreLoginProcedure,
svc.PreLogin,
connect.WithSchema(authenticateServicePreLoginMethodDescriptor),
connect.WithHandlerOptions(opts...),
)
authenticateServiceLoginHandler := connect.NewUnaryHandler(
AuthenticateServiceLoginProcedure,
svc.Login,
connect.WithSchema(authenticateServiceLoginMethodDescriptor),
connect.WithHandlerOptions(opts...),
)
authenticateServiceLogoutHandler := connect.NewUnaryHandler(
AuthenticateServiceLogoutProcedure,
svc.Logout,
connect.WithSchema(authenticateServiceLogoutMethodDescriptor),
connect.WithHandlerOptions(opts...),
)
return "/core.v1.AuthenticateService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case AuthenticateServicePreLoginProcedure:
authenticateServicePreLoginHandler.ServeHTTP(w, r)
case AuthenticateServiceLoginProcedure:
authenticateServiceLoginHandler.ServeHTTP(w, r)
case AuthenticateServiceLogoutProcedure:
authenticateServiceLogoutHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedAuthenticateServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedAuthenticateServiceHandler struct{}
func (UnimplementedAuthenticateServiceHandler) PreLogin(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("core.v1.AuthenticateService.preLogin is not implemented"))
}
func (UnimplementedAuthenticateServiceHandler) Login(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.LoginReply], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("core.v1.AuthenticateService.login is not implemented"))
}
func (UnimplementedAuthenticateServiceHandler) Logout(context.Context, *connect.Request[v1.User]) (*connect.Response[v1.ActionReply], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("core.v1.AuthenticateService.logout is not implemented"))
}

@ -1,112 +0,0 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: greet/v1/greet.proto
package greetv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/rocboss/paopao-ce/auto/rpc/greet/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// GreetServiceName is the fully-qualified name of the GreetService service.
GreetServiceName = "greet.v1.GreetService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// GreetServiceGreetProcedure is the fully-qualified name of the GreetService's Greet RPC.
GreetServiceGreetProcedure = "/greet.v1.GreetService/Greet"
)
// These variables are the protoreflect.Descriptor objects for the RPCs defined in this package.
var (
greetServiceServiceDescriptor = v1.File_greet_v1_greet_proto.Services().ByName("GreetService")
greetServiceGreetMethodDescriptor = greetServiceServiceDescriptor.Methods().ByName("Greet")
)
// GreetServiceClient is a client for the greet.v1.GreetService service.
type GreetServiceClient interface {
Greet(context.Context, *connect.Request[v1.GreetRequest]) (*connect.Response[v1.GreetResponse], error)
}
// NewGreetServiceClient constructs a client for the greet.v1.GreetService service. By default, it
// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewGreetServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) GreetServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
return &greetServiceClient{
greet: connect.NewClient[v1.GreetRequest, v1.GreetResponse](
httpClient,
baseURL+GreetServiceGreetProcedure,
connect.WithSchema(greetServiceGreetMethodDescriptor),
connect.WithClientOptions(opts...),
),
}
}
// greetServiceClient implements GreetServiceClient.
type greetServiceClient struct {
greet *connect.Client[v1.GreetRequest, v1.GreetResponse]
}
// Greet calls greet.v1.GreetService.Greet.
func (c *greetServiceClient) Greet(ctx context.Context, req *connect.Request[v1.GreetRequest]) (*connect.Response[v1.GreetResponse], error) {
return c.greet.CallUnary(ctx, req)
}
// GreetServiceHandler is an implementation of the greet.v1.GreetService service.
type GreetServiceHandler interface {
Greet(context.Context, *connect.Request[v1.GreetRequest]) (*connect.Response[v1.GreetResponse], error)
}
// NewGreetServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewGreetServiceHandler(svc GreetServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
greetServiceGreetHandler := connect.NewUnaryHandler(
GreetServiceGreetProcedure,
svc.Greet,
connect.WithSchema(greetServiceGreetMethodDescriptor),
connect.WithHandlerOptions(opts...),
)
return "/greet.v1.GreetService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case GreetServiceGreetProcedure:
greetServiceGreetHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedGreetServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedGreetServiceHandler struct{}
func (UnimplementedGreetServiceHandler) Greet(context.Context, *connect.Request[v1.GreetRequest]) (*connect.Response[v1.GreetResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("greet.v1.GreetService.Greet is not implemented"))
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.32.0
// protoc-gen-go v1.36.6
// protoc (unknown)
// source: core/v1/auth.proto
@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@ -21,20 +22,17 @@ const (
)
type User struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
PhoneNum string `protobuf:"bytes,1,opt,name=phone_num,json=phoneNum,proto3" json:"phone_num,omitempty"`
unknownFields protoimpl.UnknownFields
PhoneNum string `protobuf:"bytes,1,opt,name=phone_num,json=phoneNum,proto3" json:"phone_num,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *User) Reset() {
*x = User{}
if protoimpl.UnsafeEnabled {
mi := &file_core_v1_auth_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_core_v1_auth_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *User) String() string {
@ -45,7 +43,7 @@ func (*User) ProtoMessage() {}
func (x *User) ProtoReflect() protoreflect.Message {
mi := &file_core_v1_auth_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@ -68,21 +66,18 @@ func (x *User) GetPhoneNum() string {
}
type UserVerify struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PhoneNum string `protobuf:"bytes,1,opt,name=phone_num,json=phoneNum,proto3" json:"phone_num,omitempty"`
VerificationCode string `protobuf:"bytes,2,opt,name=verification_code,json=verificationCode,proto3" json:"verification_code,omitempty"`
state protoimpl.MessageState `protogen:"open.v1"`
PhoneNum string `protobuf:"bytes,1,opt,name=phone_num,json=phoneNum,proto3" json:"phone_num,omitempty"`
VerificationCode string `protobuf:"bytes,2,opt,name=verification_code,json=verificationCode,proto3" json:"verification_code,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UserVerify) Reset() {
*x = UserVerify{}
if protoimpl.UnsafeEnabled {
mi := &file_core_v1_auth_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_core_v1_auth_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UserVerify) String() string {
@ -93,7 +88,7 @@ func (*UserVerify) ProtoMessage() {}
func (x *UserVerify) ProtoReflect() protoreflect.Message {
mi := &file_core_v1_auth_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@ -123,21 +118,18 @@ func (x *UserVerify) GetVerificationCode() string {
}
type LoginReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
unknownFields protoimpl.UnknownFields
StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *LoginReply) Reset() {
*x = LoginReply{}
if protoimpl.UnsafeEnabled {
mi := &file_core_v1_auth_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_core_v1_auth_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LoginReply) String() string {
@ -148,7 +140,7 @@ func (*LoginReply) ProtoMessage() {}
func (x *LoginReply) ProtoReflect() protoreflect.Message {
mi := &file_core_v1_auth_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@ -178,20 +170,17 @@ func (x *LoginReply) GetToken() string {
}
type ActionReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
unknownFields protoimpl.UnknownFields
StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *ActionReply) Reset() {
*x = ActionReply{}
if protoimpl.UnsafeEnabled {
mi := &file_core_v1_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_core_v1_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ActionReply) String() string {
@ -202,7 +191,7 @@ func (*ActionReply) ProtoMessage() {}
func (x *ActionReply) ProtoReflect() protoreflect.Message {
mi := &file_core_v1_auth_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@ -226,60 +215,43 @@ func (x *ActionReply) GetStatusCode() int32 {
var File_core_v1_auth_proto protoreflect.FileDescriptor
var file_core_v1_auth_proto_rawDesc = []byte{
0x0a, 0x12, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x22, 0x23, 0x0a,
0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x5f, 0x6e,
0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e,
0x75, 0x6d, 0x22, 0x56, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79,
0x12, 0x1b, 0x0a, 0x09, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x12, 0x2b, 0x0a,
0x11, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f,
0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x43, 0x0a, 0x0a, 0x4c, 0x6f,
0x67, 0x69, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74,
0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b,
0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22,
0x2e, 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1f,
0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x32,
0xa2, 0x01, 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x4c, 0x6f,
0x67, 0x69, 0x6e, 0x12, 0x0d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73,
0x65, 0x72, 0x1a, 0x14, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x69,
0x6e, 0x12, 0x0d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72,
0x1a, 0x13, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x2d, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12,
0x0d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x14,
0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x65, 0x70, 0x6c, 0x79, 0x42, 0x8b, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x72,
0x65, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50,
0x01, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f,
0x63, 0x62, 0x6f, 0x73, 0x73, 0x2f, 0x70, 0x61, 0x6f, 0x70, 0x61, 0x6f, 0x2d, 0x63, 0x65, 0x2f,
0x61, 0x75, 0x74, 0x6f, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31,
0x3b, 0x63, 0x6f, 0x72, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x58, 0x58, 0xaa, 0x02, 0x07,
0x43, 0x6f, 0x72, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x43, 0x6f, 0x72, 0x65, 0x5c, 0x56,
0x31, 0xe2, 0x02, 0x13, 0x43, 0x6f, 0x72, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x43, 0x6f, 0x72, 0x65, 0x3a, 0x3a,
0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_core_v1_auth_proto_rawDesc = "" +
"\n" +
"\x12core/v1/auth.proto\x12\acore.v1\"#\n" +
"\x04User\x12\x1b\n" +
"\tphone_num\x18\x01 \x01(\tR\bphoneNum\"V\n" +
"\n" +
"UserVerify\x12\x1b\n" +
"\tphone_num\x18\x01 \x01(\tR\bphoneNum\x12+\n" +
"\x11verification_code\x18\x02 \x01(\tR\x10verificationCode\"C\n" +
"\n" +
"LoginReply\x12\x1f\n" +
"\vstatus_code\x18\x01 \x01(\x05R\n" +
"statusCode\x12\x14\n" +
"\x05token\x18\x02 \x01(\tR\x05token\".\n" +
"\vActionReply\x12\x1f\n" +
"\vstatus_code\x18\x01 \x01(\x05R\n" +
"statusCode2\xa2\x01\n" +
"\x13AuthenticateService\x12/\n" +
"\bpreLogin\x12\r.core.v1.User\x1a\x14.core.v1.ActionReply\x12+\n" +
"\x05login\x12\r.core.v1.User\x1a\x13.core.v1.LoginReply\x12-\n" +
"\x06logout\x12\r.core.v1.User\x1a\x14.core.v1.ActionReplyB\x8b\x01\n" +
"\vcom.core.v1B\tAuthProtoP\x01Z4github.com/rocboss/paopao-ce/auto/rpc/core/v1;corev1\xa2\x02\x03CXX\xaa\x02\aCore.V1\xca\x02\aCore\\V1\xe2\x02\x13Core\\V1\\GPBMetadata\xea\x02\bCore::V1b\x06proto3"
var (
file_core_v1_auth_proto_rawDescOnce sync.Once
file_core_v1_auth_proto_rawDescData = file_core_v1_auth_proto_rawDesc
file_core_v1_auth_proto_rawDescData []byte
)
func file_core_v1_auth_proto_rawDescGZIP() []byte {
file_core_v1_auth_proto_rawDescOnce.Do(func() {
file_core_v1_auth_proto_rawDescData = protoimpl.X.CompressGZIP(file_core_v1_auth_proto_rawDescData)
file_core_v1_auth_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_core_v1_auth_proto_rawDesc), len(file_core_v1_auth_proto_rawDesc)))
})
return file_core_v1_auth_proto_rawDescData
}
var file_core_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_core_v1_auth_proto_goTypes = []interface{}{
var file_core_v1_auth_proto_goTypes = []any{
(*User)(nil), // 0: core.v1.User
(*UserVerify)(nil), // 1: core.v1.UserVerify
(*LoginReply)(nil), // 2: core.v1.LoginReply
@ -304,61 +276,11 @@ func file_core_v1_auth_proto_init() {
if File_core_v1_auth_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_core_v1_auth_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*User); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_core_v1_auth_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UserVerify); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_core_v1_auth_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*LoginReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_core_v1_auth_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ActionReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_core_v1_auth_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_core_v1_auth_proto_rawDesc), len(file_core_v1_auth_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
@ -369,7 +291,6 @@ func file_core_v1_auth_proto_init() {
MessageInfos: file_core_v1_auth_proto_msgTypes,
}.Build()
File_core_v1_auth_proto = out.File
file_core_v1_auth_proto_rawDesc = nil
file_core_v1_auth_proto_goTypes = nil
file_core_v1_auth_proto_depIdxs = nil
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: core/v1/auth.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
AuthenticateService_PreLogin_FullMethodName = "/core.v1.AuthenticateService/preLogin"
@ -42,8 +42,9 @@ func NewAuthenticateServiceClient(cc grpc.ClientConnInterface) AuthenticateServi
}
func (c *authenticateServiceClient) PreLogin(ctx context.Context, in *User, opts ...grpc.CallOption) (*ActionReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ActionReply)
err := c.cc.Invoke(ctx, AuthenticateService_PreLogin_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, AuthenticateService_PreLogin_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -51,8 +52,9 @@ func (c *authenticateServiceClient) PreLogin(ctx context.Context, in *User, opts
}
func (c *authenticateServiceClient) Login(ctx context.Context, in *User, opts ...grpc.CallOption) (*LoginReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LoginReply)
err := c.cc.Invoke(ctx, AuthenticateService_Login_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, AuthenticateService_Login_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -60,8 +62,9 @@ func (c *authenticateServiceClient) Login(ctx context.Context, in *User, opts ..
}
func (c *authenticateServiceClient) Logout(ctx context.Context, in *User, opts ...grpc.CallOption) (*ActionReply, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ActionReply)
err := c.cc.Invoke(ctx, AuthenticateService_Logout_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, AuthenticateService_Logout_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -70,7 +73,7 @@ func (c *authenticateServiceClient) Logout(ctx context.Context, in *User, opts .
// AuthenticateServiceServer is the server API for AuthenticateService service.
// All implementations must embed UnimplementedAuthenticateServiceServer
// for forward compatibility
// for forward compatibility.
type AuthenticateServiceServer interface {
PreLogin(context.Context, *User) (*ActionReply, error)
Login(context.Context, *User) (*LoginReply, error)
@ -78,9 +81,12 @@ type AuthenticateServiceServer interface {
mustEmbedUnimplementedAuthenticateServiceServer()
}
// UnimplementedAuthenticateServiceServer must be embedded to have forward compatible implementations.
type UnimplementedAuthenticateServiceServer struct {
}
// UnimplementedAuthenticateServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedAuthenticateServiceServer struct{}
func (UnimplementedAuthenticateServiceServer) PreLogin(context.Context, *User) (*ActionReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method PreLogin not implemented")
@ -92,6 +98,7 @@ func (UnimplementedAuthenticateServiceServer) Logout(context.Context, *User) (*A
return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented")
}
func (UnimplementedAuthenticateServiceServer) mustEmbedUnimplementedAuthenticateServiceServer() {}
func (UnimplementedAuthenticateServiceServer) testEmbeddedByValue() {}
// UnsafeAuthenticateServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AuthenticateServiceServer will
@ -101,6 +108,13 @@ type UnsafeAuthenticateServiceServer interface {
}
func RegisterAuthenticateServiceServer(s grpc.ServiceRegistrar, srv AuthenticateServiceServer) {
// If the following call pancis, it indicates UnimplementedAuthenticateServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&AuthenticateService_ServiceDesc, srv)
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.32.0
// protoc-gen-go v1.36.6
// protoc (unknown)
// source: greet/v1/greet.proto
@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@ -21,20 +22,17 @@ const (
)
type GreetRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GreetRequest) Reset() {
*x = GreetRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_greet_v1_greet_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_greet_v1_greet_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GreetRequest) String() string {
@ -45,7 +43,7 @@ func (*GreetRequest) ProtoMessage() {}
func (x *GreetRequest) ProtoReflect() protoreflect.Message {
mi := &file_greet_v1_greet_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@ -68,20 +66,17 @@ func (x *GreetRequest) GetName() string {
}
type GreetResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
state protoimpl.MessageState `protogen:"open.v1"`
Greeting string `protobuf:"bytes,1,opt,name=greeting,proto3" json:"greeting,omitempty"`
unknownFields protoimpl.UnknownFields
Greeting string `protobuf:"bytes,1,opt,name=greeting,proto3" json:"greeting,omitempty"`
sizeCache protoimpl.SizeCache
}
func (x *GreetResponse) Reset() {
*x = GreetResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_greet_v1_greet_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
mi := &file_greet_v1_greet_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GreetResponse) String() string {
@ -92,7 +87,7 @@ func (*GreetResponse) ProtoMessage() {}
func (x *GreetResponse) ProtoReflect() protoreflect.Message {
mi := &file_greet_v1_greet_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@ -116,45 +111,32 @@ func (x *GreetResponse) GetGreeting() string {
var File_greet_v1_greet_proto protoreflect.FileDescriptor
var file_greet_v1_greet_proto_rawDesc = []byte{
0x0a, 0x14, 0x67, 0x72, 0x65, 0x65, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x67, 0x72, 0x65, 0x65, 0x74, 0x2e, 0x76, 0x31,
0x22, 0x22, 0x0a, 0x0c, 0x47, 0x72, 0x65, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x22, 0x2b, 0x0a, 0x0d, 0x47, 0x72, 0x65, 0x65, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e,
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e,
0x67, 0x32, 0x4a, 0x0a, 0x0c, 0x47, 0x72, 0x65, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x3a, 0x0a, 0x05, 0x47, 0x72, 0x65, 0x65, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x72, 0x65,
0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x65, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x72, 0x65, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72,
0x65, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x93, 0x01,
0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x72, 0x65, 0x65, 0x74, 0x2e, 0x76, 0x31, 0x42, 0x0a,
0x47, 0x72, 0x65, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x63, 0x62, 0x6f, 0x73, 0x73,
0x2f, 0x70, 0x61, 0x6f, 0x70, 0x61, 0x6f, 0x2d, 0x63, 0x65, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x2f,
0x72, 0x70, 0x63, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x2f, 0x76, 0x31, 0x3b, 0x67, 0x72, 0x65,
0x65, 0x74, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x47, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x47, 0x72, 0x65,
0x65, 0x74, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x08, 0x47, 0x72, 0x65, 0x65, 0x74, 0x5c, 0x56, 0x31,
0xe2, 0x02, 0x14, 0x47, 0x72, 0x65, 0x65, 0x74, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x47, 0x72, 0x65, 0x65, 0x74, 0x3a,
0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
const file_greet_v1_greet_proto_rawDesc = "" +
"\n" +
"\x14greet/v1/greet.proto\x12\bgreet.v1\"\"\n" +
"\fGreetRequest\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\"+\n" +
"\rGreetResponse\x12\x1a\n" +
"\bgreeting\x18\x01 \x01(\tR\bgreeting2J\n" +
"\fGreetService\x12:\n" +
"\x05Greet\x12\x16.greet.v1.GreetRequest\x1a\x17.greet.v1.GreetResponse\"\x00B\x93\x01\n" +
"\fcom.greet.v1B\n" +
"GreetProtoP\x01Z6github.com/rocboss/paopao-ce/auto/rpc/greet/v1;greetv1\xa2\x02\x03GXX\xaa\x02\bGreet.V1\xca\x02\bGreet\\V1\xe2\x02\x14Greet\\V1\\GPBMetadata\xea\x02\tGreet::V1b\x06proto3"
var (
file_greet_v1_greet_proto_rawDescOnce sync.Once
file_greet_v1_greet_proto_rawDescData = file_greet_v1_greet_proto_rawDesc
file_greet_v1_greet_proto_rawDescData []byte
)
func file_greet_v1_greet_proto_rawDescGZIP() []byte {
file_greet_v1_greet_proto_rawDescOnce.Do(func() {
file_greet_v1_greet_proto_rawDescData = protoimpl.X.CompressGZIP(file_greet_v1_greet_proto_rawDescData)
file_greet_v1_greet_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_greet_v1_greet_proto_rawDesc), len(file_greet_v1_greet_proto_rawDesc)))
})
return file_greet_v1_greet_proto_rawDescData
}
var file_greet_v1_greet_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_greet_v1_greet_proto_goTypes = []interface{}{
var file_greet_v1_greet_proto_goTypes = []any{
(*GreetRequest)(nil), // 0: greet.v1.GreetRequest
(*GreetResponse)(nil), // 1: greet.v1.GreetResponse
}
@ -173,37 +155,11 @@ func file_greet_v1_greet_proto_init() {
if File_greet_v1_greet_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_greet_v1_greet_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GreetRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_greet_v1_greet_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GreetResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_greet_v1_greet_proto_rawDesc,
RawDescriptor: unsafe.Slice(unsafe.StringData(file_greet_v1_greet_proto_rawDesc), len(file_greet_v1_greet_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
@ -214,7 +170,6 @@ func file_greet_v1_greet_proto_init() {
MessageInfos: file_greet_v1_greet_proto_msgTypes,
}.Build()
File_greet_v1_greet_proto = out.File
file_greet_v1_greet_proto_rawDesc = nil
file_greet_v1_greet_proto_goTypes = nil
file_greet_v1_greet_proto_depIdxs = nil
}

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: greet/v1/greet.proto
@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
GreetService_Greet_FullMethodName = "/greet.v1.GreetService/Greet"
@ -38,8 +38,9 @@ func NewGreetServiceClient(cc grpc.ClientConnInterface) GreetServiceClient {
}
func (c *greetServiceClient) Greet(ctx context.Context, in *GreetRequest, opts ...grpc.CallOption) (*GreetResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GreetResponse)
err := c.cc.Invoke(ctx, GreetService_Greet_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, GreetService_Greet_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@ -48,20 +49,24 @@ func (c *greetServiceClient) Greet(ctx context.Context, in *GreetRequest, opts .
// GreetServiceServer is the server API for GreetService service.
// All implementations must embed UnimplementedGreetServiceServer
// for forward compatibility
// for forward compatibility.
type GreetServiceServer interface {
Greet(context.Context, *GreetRequest) (*GreetResponse, error)
mustEmbedUnimplementedGreetServiceServer()
}
// UnimplementedGreetServiceServer must be embedded to have forward compatible implementations.
type UnimplementedGreetServiceServer struct {
}
// UnimplementedGreetServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedGreetServiceServer struct{}
func (UnimplementedGreetServiceServer) Greet(context.Context, *GreetRequest) (*GreetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Greet not implemented")
}
func (UnimplementedGreetServiceServer) mustEmbedUnimplementedGreetServiceServer() {}
func (UnimplementedGreetServiceServer) testEmbeddedByValue() {}
// UnsafeGreetServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to GreetServiceServer will
@ -71,6 +76,13 @@ type UnsafeGreetServiceServer interface {
}
func RegisterGreetServiceServer(s grpc.ServiceRegistrar, srv GreetServiceServer) {
// If the following call pancis, it indicates UnimplementedGreetServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&GreetService_ServiceDesc, srv)
}

@ -14,6 +14,3 @@ plugins:
opt:
- paths=source_relative
- require_unimplemented_servers=true
- plugin: connect-go
out: auto/connect
opt: paths=source_relative

@ -62,6 +62,10 @@ func serveRun(_cmd *cobra.Command, _args []string) {
// initial configure
conf.Initial(features, noDefaultFeatures)
if cfg.If("loggerOtlp") {
shutdownFn, _ := conf.InitTelemetry()
defer shutdownFn()
}
internal.Initial()
ss := service.MustInitService()
if len(ss) < 1 {

@ -87,19 +87,14 @@ LoggerFile: # 使用File写日志
SavePath: custom/data/paopao-ce/logs
FileName: app
FileExt: .log
LoggerZinc: # 使用Zinc写日志
Host: zinc:4080
Index: paopao-log
User: admin
Password: admin
Secure: False
LoggerMeili: # 使用Meili写日志
Host: meili:7700
Index: paopao-log
ApiKey: paopao-meilisearch
Secure: False
MinWorker: 5 # 最小后台工作者, 设置范围[5, 100], 默认5
MaxLogBuffer: 100 # 最大log缓存条数, 设置范围[10, 10000], 默认100
LoggerOtlp: # 使用OpenTelemetry写日志
Endpoint: openobserve:5081
Authorization: Basic ls8icEBvcGVub2JzFXJ2ZS6haCpZTU4ybGdBUFlXcjA0UdNk
Organization: paopao-ce
TraceStream: paopao-trace
MetricStream: paopao-metric
LogStream: paopao-log
Insecure: true
JWT: # 鉴权加密
Secret: 18a6413dc4fe394c66345ebe501b2f26
Issuer: paopao-api
@ -185,7 +180,7 @@ Redis:
- redis:6379
WebProfile:
UseFriendship: true # 前端是否使用好友体系
EnableTrendsBar: true # 广场页面是否开启动态条栏功能
EnableTrendsBar: false # 广场页面是否开启动态条栏功能
EnableWallet: false # 是否开启钱包功能
AllowTweetAttachment: true # 是否允许推文附件
AllowTweetAttachmentPrice: true # 是否允许推文付费附件

@ -111,7 +111,7 @@
* [x] 业务逻辑实现
#### 搜索:
* `Zinc` 基于[Zinc](https://github.com/zinclabs/zinc)搜索引擎提供推文搜索服务(目前状态: 稳定,推荐使用)
* `Zinc` 基于[Zinc](https://github.com/zinclabs/zinc)搜索引擎提供推文搜索服务(目前状态: Deprecated)
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
@ -129,18 +129,23 @@
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
* `LoggerZinc` 使用[Zinc](https://github.com/zinclabs/zinc)写日志(目前状态: 稳定,推荐使用);
* `LoggerZinc` 使用[Zinc](https://github.com/zinclabs/zinc)写日志(目前状态: Deprecated);
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
* `LoggerMeili` 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志(目前状态: 内测阶段);
* `LoggerMeili` 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志(目前状态: Deprecated);
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
* `LoggerOpenObserve` 使用[OpenObserve](https://github.com/openobserve/openobserve)写日志(目前状态: 内测阶段);
* `LoggerOpenObserve` 使用[OpenObserve](https://github.com/openobserve/openobserve)写日志(目前状态: Deprecated);
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
* `LoggerOtlp` 使用[OpenTelemetry](https://github.com/open-telemetry/opentelemetry-go)写日志(目前状态: 内测阶段);
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
#### 监控:
* `Sentry` 使用Sentry进行错误跟踪与性能监控(目前状态: 内测);

@ -1,27 +1,26 @@
module github.com/rocboss/paopao-ce
go 1.22.0
go 1.23.0
require (
connectrpc.com/connect v1.16.2
github.com/Masterminds/semver/v3 v3.2.1
github.com/RoaringBitmap/roaring v1.9.4
github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868
github.com/alimy/mir/v4 v4.2.0-alpha.5
github.com/alimy/tryst v0.22.0
github.com/alimy/tryst v1.1.0
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/allegro/bigcache/v3 v3.1.0
github.com/bytedance/sonic v1.12.6
github.com/cockroachdb/errors v1.11.3
github.com/disintegration/imaging v1.6.2
github.com/fatih/color v1.17.0
github.com/fatih/color v1.18.0
github.com/getsentry/sentry-go v0.31.1
github.com/getsentry/sentry-go/gin v0.31.1
github.com/getsentry/sentry-go/logrus v0.31.1
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0
github.com/go-resty/resty/v2 v2.12.0
github.com/goccy/go-json v0.10.3
github.com/goccy/go-json v0.10.4
github.com/gofrs/uuid/v5 v5.3.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang-migrate/migrate/v4 v4.18.1
@ -29,32 +28,42 @@ require (
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible
github.com/json-iterator/go v1.1.12
github.com/meilisearch/meilisearch-go v0.27.2
github.com/minio/minio-go/v7 v7.0.66
github.com/minio/minio-go/v7 v7.0.84
github.com/onsi/ginkgo/v2 v2.19.0
github.com/onsi/gomega v1.34.1
github.com/prometheus/client_golang v1.19.1
github.com/redis/rueidis v1.0.45
github.com/redis/rueidis v1.0.53
github.com/robfig/cron/v3 v3.0.1
github.com/sirupsen/logrus v1.9.3
github.com/smartwalle/alipay/v3 v3.2.20
github.com/sourcegraph/conc v0.3.0
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.55
github.com/tencentyun/cos-go-sdk-v5 v0.7.65
github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc
go.opentelemetry.io/contrib/bridges/otellogrus v0.9.0
go.opentelemetry.io/contrib/instrumentation/runtime v0.59.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.10.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0
go.opentelemetry.io/otel/log v0.10.0
go.opentelemetry.io/otel/sdk v1.34.0
go.opentelemetry.io/otel/sdk/log v0.10.0
go.opentelemetry.io/otel/sdk/metric v1.34.0
go.uber.org/automaxprocs v1.5.3
golang.org/x/crypto v0.31.0
google.golang.org/grpc v1.64.1
google.golang.org/protobuf v1.34.2
golang.org/x/crypto v0.36.0
google.golang.org/grpc v1.71.1
google.golang.org/protobuf v1.36.4
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/resty.v1 v1.12.0
gorm.io/driver/mysql v1.5.7
gorm.io/driver/postgres v1.5.9
gorm.io/driver/sqlite v1.5.6
gorm.io/driver/sqlite v1.5.7
gorm.io/gorm v1.25.10
gorm.io/plugin/dbresolver v1.5.2
gorm.io/plugin/soft_delete v1.2.1
modernc.org/sqlite v1.29.6
modernc.org/sqlite v1.34.5
)
require (
@ -62,7 +71,8 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.12.0 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
@ -72,7 +82,9 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
@ -86,6 +98,7 @@ require (
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@ -98,8 +111,8 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
@ -110,7 +123,6 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@ -123,8 +135,8 @@ require (
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/smartwalle/ncrypto v1.0.4 // indirect
@ -138,24 +150,27 @@ require (
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.52.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/image v0.0.0-20210216034530-4410531fe030 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.24.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.41.0 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
modernc.org/memory v1.8.0 // indirect
)

158
go.sum

@ -1,5 +1,3 @@
connectrpc.com/connect v1.16.2 h1:ybd6y+ls7GOlb7Bh5C8+ghA6SvCBajHwxssO2CGFjqE=
connectrpc.com/connect v1.16.2/go.mod h1:n2kgwskMHXC+lVqb18wngEpF95ldBHXjZYJussz5FRc=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
@ -13,8 +11,8 @@ github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868 h1:uFrPOl1VBt/Abfl2
github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868/go.mod h1:srphKZ1i+yGXxl/LpBS7ZIECTjCTPzZzAMtJWoG3sLo=
github.com/alimy/mir/v4 v4.2.0-alpha.5 h1:ExSJpbFzKX3Avk1CoTOU3OLyvo4PTB2SnTSQXfeJNIc=
github.com/alimy/mir/v4 v4.2.0-alpha.5/go.mod h1:d58dBvw2KImcVbAUANrciEV/of0arMNsI9c/5UNCMMc=
github.com/alimy/tryst v0.22.0 h1:tjZFvHliMDkymEZuuhH/e6Tg41B72LJt8/5TCiJztGw=
github.com/alimy/tryst v0.22.0/go.mod h1:HPOlTam3dT+of3slvIxpzf1pUQEUAfBJp1zgIuk/uLY=
github.com/alimy/tryst v1.1.0 h1:EiGiTLLw9n6bfg1WwnqoZTb1gnDHDHxozjfE/cuVjlI=
github.com/alimy/tryst v1.1.0/go.mod h1:t/Ocn3Qd2P8Sg5VIL11s1H7cBCYZcDy+VNwiFHix+lI=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
@ -31,8 +29,10 @@ github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKz
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
@ -65,8 +65,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@ -89,6 +89,9 @@ github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@ -107,8 +110,8 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@ -121,6 +124,8 @@ github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4C
github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@ -137,6 +142,8 @@ github.com/grafana/pyroscope-go v1.2.0 h1:aILLKjTj8CS8f/24OPMGPewQSYlhmdQMBmol1d
github.com/grafana/pyroscope-go v1.2.0/go.mod h1:2GHr28Nr05bg2pElS+dDsc98f3JTUh2f6Fz1hWXrqwk=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -172,12 +179,12 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@ -203,10 +210,8 @@ github.com/meilisearch/meilisearch-go v0.27.2 h1:3G21dJ5i208shnLPDsIEZ0L0Geg/5oe
github.com/meilisearch/meilisearch-go v0.27.2/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw=
github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E=
github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@ -255,17 +260,17 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/redis/rueidis v1.0.45 h1:j7hfcqfLLIqgTK3IkxBhXdeJcP34t3XLXvorDLqXfgM=
github.com/redis/rueidis v1.0.45/go.mod h1:by+34b0cFXndxtYmPAHpoTHO5NkosDlBvhexoTURIxM=
github.com/redis/rueidis v1.0.53 h1:r3eT4bp7Nyt+kSldT2po/EO9YeawHfZDY9TJBrHRLD4=
github.com/redis/rueidis v1.0.53/go.mod h1:by+34b0cFXndxtYmPAHpoTHO5NkosDlBvhexoTURIxM=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
@ -305,14 +310,15 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.55 h1:9DfH3umWUd0I2jdqcUxrU1kLfUPOydULNy4T9qN5PF8=
github.com/tencentyun/cos-go-sdk-v5 v0.7.55/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.65 h1:+WBbfwThfZSbxpf1Dw6fyMwyzVtWBBExqfDJ5giiR2s=
github.com/tencentyun/cos-go-sdk-v5 v0.7.65/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
@ -328,14 +334,38 @@ github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc/go.mod h1:Pcc297eV
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/bridges/otellogrus v0.9.0 h1:8WlIPjiMRU8Pg86+JoBjdzXOKp9bWt2M9qiVbjjFUsg=
go.opentelemetry.io/contrib/bridges/otellogrus v0.9.0/go.mod h1:bkQ7eZh+3ruXMBGR2UfReI0EjxtkcsjsmQKJTh1N2Is=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/contrib/instrumentation/runtime v0.59.0 h1:rfi2MMujBc4yowE0iHckZX4o4jg6SA67EnFVL8ldVvU=
go.opentelemetry.io/contrib/instrumentation/runtime v0.59.0/go.mod h1:IO/gfPEcQYpOpPxn1OXFp1DvRY0viP8ONMedXLjjHIU=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.10.0 h1:5dTKu4I5Dn4P2hxyW3l3jTaZx9ACgg0ECos1eAVrheY=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.10.0/go.mod h1:P5HcUI8obLrCCmM3sbVBohZFH34iszk/+CPWuakZWL8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 h1:ajl4QczuJVA2TU9W9AGw++86Xga/RKt//16z/yxPgdk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0/go.mod h1:Vn3/rlOJ3ntf/Q3zAI0V5lDnTbHGaUsNUeF6nZmm7pA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/log v0.10.0 h1:1CXmspaRITvFcjA4kyVszuG4HjA61fPDxMb7q3BuyF0=
go.opentelemetry.io/otel/log v0.10.0/go.mod h1:PbVdm9bXKku/gL0oFfUF4wwsQsOPlpo4VEqjvxih+FM=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/log v0.10.0 h1:lR4teQGWfeDVGoute6l0Ou+RpFqQ9vaPdrNJlST0bvw=
go.opentelemetry.io/otel/sdk/log v0.10.0/go.mod h1:A+V1UTWREhWAittaQEG4bYm4gAZa6xnvVu+xKrIRkzo=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
@ -353,8 +383,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -379,15 +409,15 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -405,8 +435,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -420,8 +450,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -436,12 +466,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA=
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@ -460,8 +492,8 @@ gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkD
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
@ -471,18 +503,26 @@ gorm.io/plugin/dbresolver v1.5.2 h1:Iut7lW4TXNoVs++I+ra3zxjSxTRj4ocIeFEVp4lLhII=
gorm.io/plugin/dbresolver v1.5.2/go.mod h1:jPh59GOQbO7v7v28ZKZPd45tr+u3vyT+8tHdfdfOWcU=
gorm.io/plugin/soft_delete v1.2.1 h1:qx9D/c4Xu6w5KT8LviX8DgLcB9hkKl6JC9f44Tj7cGU=
gorm.io/plugin/soft_delete v1.2.1/go.mod h1:Zv7vQctOJTGOsJ/bWgrN1n3od0GBAZgnLjEx+cApLGk=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4=
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

@ -17,6 +17,7 @@ var (
loggerZincSetting *loggerZincConf
loggerMeiliSetting *loggerMeiliConf
loggerOpenObserveSetting *loggerOpenObserveConf
loggerOtlpSetting *loggerOtlponf
sentrySetting *sentryConf
redisSetting *redisConf
@ -102,6 +103,7 @@ func setupSetting(suite []string, noDefault bool) error {
"LoggerZinc": &loggerZincSetting,
"LoggerMeili": &loggerMeiliSetting,
"LoggerOpenObserve": &loggerOpenObserveSetting,
"LoggerOtlp": &loggerOtlpSetting,
"Database": &DatabaseSetting,
"MySQL": &MysqlSetting,
"Postgres": &PostgresSetting,

@ -152,7 +152,7 @@ LoggerMeili: # 使用Meili写日志
MinWorker: 5 # 最小后台工作者, 设置范围[5, 100], 默认5
MaxLogBuffer: 100 # 最大log缓存条数, 设置范围[10, 10000], 默认100
LoggerOpenObserve: # 使用OpenObserve写日志
Host: 127.0.0.1:5080
Host: openobserve:5080
Organization: paopao-ce
Stream: default
User: root@paopao.info
@ -160,6 +160,14 @@ LoggerOpenObserve: # 使用OpenObserve写日志
Secure: False
MinWorker: 5 # 最小后台工作者, 设置范围[5, 100], 默认5
MaxLogBuffer: 100 # 最大log缓存条数, 设置范围[10, 10000], 默认100
LoggerOtlp: # 使用OpenTelemetry写日志
Endpoint: openobserve:5081
Authorization: Basic ls8icEBvcGVub2JzFXJ2ZS6haCpZTU4ybGdBUFlXcjA0UdNk
Organization: paopao-ce
TraceStream: paopao-trace
MetricStream: paopao-metric
LogStream: paopao-log
Insecure: true
JWT: # 鉴权加密
Secret: 18a6413dc4fe394c66345ebe501b2f26
Issuer: paopao-api

@ -12,6 +12,7 @@ import (
"github.com/getsentry/sentry-go"
sentrylogrus "github.com/getsentry/sentry-go/logrus"
"github.com/sirupsen/logrus"
"go.opentelemetry.io/contrib/bridges/otellogrus"
"gopkg.in/natefinch/lumberjack.v2"
)
@ -48,6 +49,10 @@ func setupLogger() {
logrus.SetOutput(io.Discard)
logrus.AddHook(hook)
},
"LoggerOtlp": func() {
hook := otellogrus.NewHook("main")
logrus.AddHook(hook)
},
})
}

@ -74,6 +74,16 @@ type loggerOpenObserveConf struct {
MinWorker int
}
type loggerOtlponf struct {
Endpoint string
Authorization string
Organization string
TraceStream string
MetricStream string
LogStream string
Insecure bool
}
type httpServerConf struct {
RunMode string
HttpIp string

@ -0,0 +1,149 @@
// Copyright 2025 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package conf
import (
"context"
"fmt"
"sync"
"go.opentelemetry.io/contrib/instrumentation/runtime"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/propagation"
sdklog "go.opentelemetry.io/otel/sdk/log"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
sdkresource "go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
)
var (
resource *sdkresource.Resource
initResourcesOnce sync.Once
)
func InitTelemetry() (shutdownFn func(), err error) {
s := loggerOtlpSetting
var (
tp *sdktrace.TracerProvider
mp *sdkmetric.MeterProvider
lp *sdklog.LoggerProvider
)
if tp, err = initTracerProvider(s); err != nil {
return
}
if mp, err = initMeterProvider(s); err != nil {
return
}
if lp, err = initLoggerProvider(s); err != nil {
return
}
// Starting runtime instrumentation
if err = runtime.Start(); err != nil {
shutdownFn = func() {
fmt.Printf("skip shutdown by step of init telemetry occurs error:%s", err)
}
return
}
shutdownFn = func() {
ctx := context.Background()
tp.Shutdown(ctx)
mp.Shutdown(ctx)
lp.Shutdown(ctx)
}
return
}
func initResource() *sdkresource.Resource {
initResourcesOnce.Do(func() {
extraResources, _ := sdkresource.New(
context.Background(),
sdkresource.WithContainer(),
sdkresource.WithHost(),
sdkresource.WithSchemaURL(semconv.SchemaURL),
sdkresource.WithAttributes(semconv.ServiceNameKey.String("jswhatx")),
)
resource, _ = sdkresource.Merge(
sdkresource.Default(),
extraResources,
)
})
return resource
}
func initTracerProvider(s *loggerOtlponf) (*sdktrace.TracerProvider, error) {
opts := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(s.Endpoint),
otlptracegrpc.WithHeaders(newOtlpHeaders(s.Authorization, s.Organization, s.TraceStream)),
}
if s.Insecure {
opts = append(opts, otlptracegrpc.WithInsecure())
}
exporter, err := otlptracegrpc.New(context.Background(), opts...)
if err != nil {
return nil, fmt.Errorf("otlp trace grpc exporter failed: %w", err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(initResource()),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
return tp, nil
}
func initMeterProvider(s *loggerOtlponf) (*sdkmetric.MeterProvider, error) {
opts := []otlpmetricgrpc.Option{
otlpmetricgrpc.WithEndpoint(s.Endpoint),
otlpmetricgrpc.WithHeaders(newOtlpHeaders(s.Authorization, s.Organization, s.MetricStream)),
}
if s.Insecure {
opts = append(opts, otlpmetricgrpc.WithInsecure())
}
exporter, err := otlpmetricgrpc.New(context.Background(), opts...)
if err != nil {
return nil, fmt.Errorf("otlp metric grpc exporter failed: %w", err)
}
mp := sdkmetric.NewMeterProvider(
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(exporter)),
sdkmetric.WithResource(initResource()),
)
otel.SetMeterProvider(mp)
return mp, nil
}
func initLoggerProvider(s *loggerOtlponf) (*sdklog.LoggerProvider, error) {
opts := []otlploggrpc.Option{
otlploggrpc.WithEndpoint(s.Endpoint),
otlploggrpc.WithHeaders(newOtlpHeaders(s.Authorization, s.Organization, s.LogStream)),
}
if s.Insecure {
opts = append(opts, otlploggrpc.WithInsecure())
}
exporter, err := otlploggrpc.New(context.Background(), opts...)
if err != nil {
return nil, fmt.Errorf("otlp log grpc exporter failed: %w", err)
}
processor := sdklog.NewBatchProcessor(exporter)
provider := sdklog.NewLoggerProvider(
sdklog.WithProcessor(processor),
sdklog.WithResource(initResource()),
)
global.SetLoggerProvider(provider)
return provider, nil
}
func newOtlpHeaders(auth, org, stream string) map[string]string {
return map[string]string{
"Authorization": auth,
"organization": org,
"stream-name": stream,
}
}

@ -14,7 +14,7 @@ import (
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/sirupsen/logrus"
)

@ -11,7 +11,7 @@ import (
"github.com/alimy/tryst/pool"
"github.com/robfig/cron/v3"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/metrics/statistics"
"github.com/rocboss/paopao-ce/internal/infra/metrics/statistics"
"github.com/sirupsen/logrus"
)

@ -0,0 +1,7 @@
// Copyright 2025 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// Package infra infrastructure components.
package infra

@ -13,7 +13,7 @@ import (
"github.com/robfig/cron/v3"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/sirupsen/logrus"
)

@ -5,9 +5,9 @@
package internal
import (
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/metrics"
"github.com/rocboss/paopao-ce/internal/migration"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/rocboss/paopao-ce/internal/infra/metrics"
"github.com/rocboss/paopao-ce/internal/infra/migration"
)
func Initial() {

@ -0,0 +1,14 @@
package enum
//go:generate go-enum --names --values --nocase --marshal --mustparse --sqlnullint
/*
Boolean
ENUM(
False, // false
True, // true
)
*/
type Boolean int

@ -0,0 +1,258 @@
// Code generated by go-enum DO NOT EDIT.
// Version:
// Revision:
// Build Date:
// Built By:
package enum
import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
)
const (
// BooleanFalse is a Boolean of type False.
// false
BooleanFalse Boolean = iota
// BooleanTrue is a Boolean of type True.
// true
BooleanTrue
)
var ErrInvalidBoolean = fmt.Errorf("not a valid Boolean, try [%s]", strings.Join(_BooleanNames, ", "))
const _BooleanName = "FalseTrue"
var _BooleanNames = []string{
_BooleanName[0:5],
_BooleanName[5:9],
}
// BooleanNames returns a list of possible string values of Boolean.
func BooleanNames() []string {
tmp := make([]string, len(_BooleanNames))
copy(tmp, _BooleanNames)
return tmp
}
// BooleanValues returns a list of the values for Boolean
func BooleanValues() []Boolean {
return []Boolean{
BooleanFalse,
BooleanTrue,
}
}
var _BooleanMap = map[Boolean]string{
BooleanFalse: _BooleanName[0:5],
BooleanTrue: _BooleanName[5:9],
}
// String implements the Stringer interface.
func (x Boolean) String() string {
if str, ok := _BooleanMap[x]; ok {
return str
}
return fmt.Sprintf("Boolean(%d)", x)
}
// IsValid provides a quick way to determine if the typed value is
// part of the allowed enumerated values
func (x Boolean) IsValid() bool {
_, ok := _BooleanMap[x]
return ok
}
var _BooleanValue = map[string]Boolean{
_BooleanName[0:5]: BooleanFalse,
strings.ToLower(_BooleanName[0:5]): BooleanFalse,
_BooleanName[5:9]: BooleanTrue,
strings.ToLower(_BooleanName[5:9]): BooleanTrue,
}
// ParseBoolean attempts to convert a string to a Boolean.
func ParseBoolean(name string) (Boolean, error) {
if x, ok := _BooleanValue[name]; ok {
return x, nil
}
// Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to.
if x, ok := _BooleanValue[strings.ToLower(name)]; ok {
return x, nil
}
return Boolean(0), fmt.Errorf("%s is %w", name, ErrInvalidBoolean)
}
// MustParseBoolean converts a string to a Boolean, and panics if is not valid.
func MustParseBoolean(name string) Boolean {
val, err := ParseBoolean(name)
if err != nil {
panic(err)
}
return val
}
// MarshalText implements the text marshaller method.
func (x Boolean) MarshalText() ([]byte, error) {
return []byte(x.String()), nil
}
// UnmarshalText implements the text unmarshaller method.
func (x *Boolean) UnmarshalText(text []byte) error {
name := string(text)
tmp, err := ParseBoolean(name)
if err != nil {
return err
}
*x = tmp
return nil
}
var errBooleanNilPtr = errors.New("value pointer is nil") // one per type for package clashes
// Scan implements the Scanner interface.
func (x *Boolean) Scan(value interface{}) (err error) {
if value == nil {
*x = Boolean(0)
return
}
// A wider range of scannable types.
// driver.Value values at the top of the list for expediency
switch v := value.(type) {
case int64:
*x = Boolean(v)
case string:
*x, err = ParseBoolean(v)
if err != nil {
// try parsing the integer value as a string
if val, verr := strconv.Atoi(v); verr == nil {
*x, err = Boolean(val), nil
}
}
case []byte:
*x, err = ParseBoolean(string(v))
if err != nil {
// try parsing the integer value as a string
if val, verr := strconv.Atoi(string(v)); verr == nil {
*x, err = Boolean(val), nil
}
}
case Boolean:
*x = v
case int:
*x = Boolean(v)
case *Boolean:
if v == nil {
return errBooleanNilPtr
}
*x = *v
case uint:
*x = Boolean(v)
case uint64:
*x = Boolean(v)
case *int:
if v == nil {
return errBooleanNilPtr
}
*x = Boolean(*v)
case *int64:
if v == nil {
return errBooleanNilPtr
}
*x = Boolean(*v)
case float64: // json marshals everything as a float64 if it's a number
*x = Boolean(v)
case *float64: // json marshals everything as a float64 if it's a number
if v == nil {
return errBooleanNilPtr
}
*x = Boolean(*v)
case *uint:
if v == nil {
return errBooleanNilPtr
}
*x = Boolean(*v)
case *uint64:
if v == nil {
return errBooleanNilPtr
}
*x = Boolean(*v)
case *string:
if v == nil {
return errBooleanNilPtr
}
*x, err = ParseBoolean(*v)
if err != nil {
// try parsing the integer value as a string
if val, verr := strconv.Atoi(*v); verr == nil {
*x, err = Boolean(val), nil
}
}
}
return
}
// Value implements the driver Valuer interface.
func (x Boolean) Value() (driver.Value, error) {
return int64(x), nil
}
type NullBoolean struct {
Boolean Boolean
Valid bool
Set bool
}
func NewNullBoolean(val interface{}) (x NullBoolean) {
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
return
}
// Scan implements the Scanner interface.
func (x *NullBoolean) Scan(value interface{}) (err error) {
x.Set = true
if value == nil {
x.Boolean, x.Valid = Boolean(0), false
return
}
err = x.Boolean.Scan(value)
x.Valid = (err == nil)
return
}
// Value implements the driver Valuer interface.
func (x NullBoolean) Value() (driver.Value, error) {
if !x.Valid {
return nil, nil
}
// driver.Value accepts int64 for int values.
return int64(x.Boolean), nil
}
// MarshalJSON correctly serializes a NullBoolean to JSON.
func (n NullBoolean) MarshalJSON() ([]byte, error) {
const nullStr = "null"
if n.Valid {
return json.Marshal(n.Boolean)
}
return []byte(nullStr), nil
}
// UnmarshalJSON correctly deserializes a NullBoolean from JSON.
func (n *NullBoolean) UnmarshalJSON(b []byte) error {
n.Set = true
var x interface{}
err := json.Unmarshal(b, &x)
if err != nil {
return err
}
err = n.Scan(x)
return err
}

@ -0,0 +1,7 @@
// Copyright 2025 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// Package enum all const enum variables define.
package enum

@ -21,7 +21,7 @@ import (
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/types"

@ -10,7 +10,7 @@ import (
"github.com/alimy/tryst/event"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/pkg/json"
)

@ -6,7 +6,7 @@ package chain
import (
"github.com/alimy/tryst/event"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/sirupsen/logrus"
)

@ -7,7 +7,7 @@ package chain
import (
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/metrics"
"github.com/rocboss/paopao-ce/internal/infra/metrics"
)
type OnlineUserMetric struct {

@ -5,9 +5,6 @@
package servants
import (
"net/http"
"connectrpc.com/connect"
"github.com/alimy/tryst/cfg"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/servants/admin"
@ -17,7 +14,6 @@ import (
"github.com/rocboss/paopao-ce/internal/servants/mobile"
"github.com/rocboss/paopao-ce/internal/servants/space"
"github.com/rocboss/paopao-ce/internal/servants/statick"
"github.com/rocboss/paopao-ce/internal/servants/triplet"
"github.com/rocboss/paopao-ce/internal/servants/web"
"google.golang.org/grpc"
)
@ -67,8 +63,3 @@ func RegisterFrontendWebServants(e *gin.Engine) {
func RegisterMobileServants(s *grpc.Server) {
mobile.RegisterServants(s)
}
// RegisterTripletServants register all the servants to Connect
func RegisterTripletServants(h func(string, http.Handler), opts ...connect.HandlerOption) {
triplet.RegisterAuthenticateHandler(h, opts...)
}

@ -1,17 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package triplet
import (
api "github.com/rocboss/paopao-ce/auto/connect/core/v1/corev1connect"
)
var (
_ api.AuthenticateServiceHandler = (*authenticateSrv)(nil)
)
type authenticateSrv struct {
api.UnimplementedAuthenticateServiceHandler
}

@ -1,16 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package triplet
import (
"net/http"
"connectrpc.com/connect"
api "github.com/rocboss/paopao-ce/auto/connect/core/v1/corev1connect"
)
func RegisterAuthenticateHandler(h func(string, http.Handler), opts ...connect.HandlerOption) {
h(api.NewAuthenticateServiceHandler(&authenticateSrv{}, opts...))
}

@ -14,7 +14,7 @@ import (
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/sirupsen/logrus"

@ -8,7 +8,7 @@ import (
"github.com/alimy/tryst/cfg"
"github.com/robfig/cron/v3"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/sirupsen/logrus"
)

@ -1,67 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package service
import (
"context"
"net/http"
"connectrpc.com/connect"
hx "github.com/rocboss/paopao-ce/pkg/http"
)
var (
_ server = (*connectServer)(nil)
)
// connectServer wraper for connect
type connectServer struct {
*baseServer
isTLS bool
certFile string
keyFile string
handlerOpts []connect.HandlerOption
server *http.Server
mux connectMux
}
type connectMux interface {
http.Handler
Handle(string, http.Handler)
}
func (s *connectServer) start() error {
s.server.Handler = s.mux
if s.isTLS {
return s.server.ListenAndServeTLS(s.certFile, s.keyFile)
} else {
return s.server.ListenAndServe()
}
}
func (s *connectServer) stop() error {
return s.server.Shutdown(context.Background())
}
func (s *connectServer) register(path string, handler http.Handler) {
s.mux.Handle(path, handler)
}
func defaultConnectServer(addr string) (s *connectServer) {
s = &connectServer{
baseServer: newBaseServe(),
server: &http.Server{
Addr: addr,
},
mux: &http.ServeMux{},
}
// TODO: custom value from config
var useConnectMux bool
if useConnectMux {
s.mux = hx.NewConnectMux()
}
return
}

@ -1,32 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package service
import (
"net/http"
"connectrpc.com/connect"
)
type baseConnectService struct {
baseService
server *connectServer
}
func (s *baseConnectService) registerServer(srv Service, h func(func(string, http.Handler), ...connect.HandlerOption), opts ...connect.HandlerOption) {
h(s.server.register, append(opts, s.server.handlerOpts...)...)
s.server.addService(srv)
}
func (s *baseConnectService) OnStart() error {
// do nothing default
return nil
}
func (s *baseConnectService) OnStop() error {
// do nothing default
return nil
}

@ -13,8 +13,8 @@ import (
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/dao"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/metrics/prometheus"
"github.com/rocboss/paopao-ce/internal/metrics/statistics"
"github.com/rocboss/paopao-ce/internal/infra/metrics/prometheus"
"github.com/rocboss/paopao-ce/internal/infra/metrics/statistics"
)
var (

@ -15,9 +15,8 @@ import (
)
var (
httpServers = newServerPool[*httpServer]()
grpcServers = newServerPool[*grpcServer]()
connectServers = newServerPool[*connectServer]()
httpServers = newServerPool[*httpServer]()
grpcServers = newServerPool[*grpcServer]()
)
const (
@ -117,7 +116,6 @@ func checkServices() (int, int) {
var ss []Service
ss = append(ss, httpServers.allServices()...)
ss = append(ss, grpcServers.allServices()...)
ss = append(ss, connectServers.allServices()...)
return len(ss), maxSidSize(ss)
}
@ -176,7 +174,6 @@ func Start(wg *conc.WaitGroup) {
// start servers
httpServers.startServer(wg, maxSidSize)
grpcServers.startServer(wg, maxSidSize)
connectServers.startServer(wg, maxSidSize)
}
// Stop stop all servers
@ -188,5 +185,4 @@ func Stop() {
// stop servers
httpServers.stopServer(maxSidSize)
grpcServers.stopServer(maxSidSize)
connectServers.stopServer(maxSidSize)
}

@ -1,51 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package service
import (
"fmt"
"github.com/Masterminds/semver/v3"
"github.com/fatih/color"
"github.com/rocboss/paopao-ce/internal/servants"
)
var (
_ Service = (*tripletService)(nil)
)
// tripletService just a demo service for Connect
type tripletService struct {
*baseConnectService
}
func (s *tripletService) Name() string {
return "TripletService"
}
func (s *tripletService) Version() *semver.Version {
return semver.MustParse("v0.1.0")
}
func (s *tripletService) OnInit() error {
s.registerServer(s, servants.RegisterTripletServants)
return nil
}
func (s *tripletService) String() string {
return fmt.Sprintf("listen on %s\n", color.GreenString("http://localhost:8080"))
}
func newTripletService() Service {
server := connectServers.from(":8080", func() *connectServer {
// TODO: do something for conenct server initialize
return defaultConnectServer(":8080")
})
return &tripletService{
baseConnectService: &baseConnectService{
server: server,
},
}
}

@ -0,0 +1,34 @@
// Copyright 2025 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package version
import (
"debug/buildinfo"
"os"
"time"
)
var (
BuildTime = time.Now()
)
func init() {
exe, err := os.Executable()
if err != nil {
return
}
info, err := buildinfo.ReadFile(exe)
if err != nil {
return
}
for _, s := range info.Settings {
if s.Key == "vcs.time" && s.Value != "" {
if t, err := time.Parse(time.RFC3339, s.Value); err == nil {
BuildTime = t
}
break
}
}
}

@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
FROM getmeili/meilisearch:v1.5 as meilisearch
FROM redis:7.2-alpine3.18
FROM redis:7.4-alpine3.21
ENV TZ=Asia/Shanghai
RUN apk update && apk add --no-cache ca-certificates && update-ca-certificates
RUN apk update --quiet \

@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
FROM golang:1.21-alpine3.18
FROM golang:1.24-alpine3.21
RUN apk --no-cache --no-progress add --virtual \
build-deps \
build-base \

@ -1,4 +1,4 @@
# syntax=docker/dockerfile:1
FROM alpine:3.18
FROM alpine:3.21
ENV TZ=Asia/Shanghai
RUN apk update && apk add --no-cache ca-certificates && update-ca-certificates

@ -47,6 +47,7 @@ CREATE TABLE "p_comment" (
"ip" text(64) NOT NULL,
"ip_loc" text(64) NOT NULL,
"is_essence" integer NOT NULL DEFAULT 0,
"reply_count" int NOT NULL DEFAULT 0, -- 回复数
"thumbs_up_count" integer NOT NULL DEFAULT 0, -- 点赞数
"thumbs_down_count" integer NOT NULL DEFAULT 0, -- 点踩数
"created_on" integer NOT NULL,

1
web/.gitignore vendored

@ -23,6 +23,7 @@ dist-ssr
*.sln
*.sw?
bun.lock
yarn.lock
package-lock.json
components.d.ts

@ -0,0 +1,30 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": {
"enabled": true
},
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"formatter": {
"indentStyle": "space"
},
"javascript": {
"formatter": {
"quoteStyle": "single"
}
},
"css": {
"parser": {
"cssModules": true
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}

@ -0,0 +1 @@
import{_}from"./main-nav.vue_vue_type_style_index_0_lang-D7-FAbTY.js";import{u as a}from"./vue-router-yrkFRUM9.js";import{G as c,a2 as u,B as l}from"./naive-ui-BJojRuLw.js";import{d,f,q as t,w as o,G as x,j as w}from"./@vue-9sINKCPW.js";import{_ as B}from"./index-DxHQoSDp.js";import"./vuex--ttreJMD.js";import"./vooks-CfQnrjIt.js";import"./evtd-CI_DDEu_.js";import"./@vicons-PCg97L0F.js";import"./seemly-D-teBmey.js";import"./vueuc-DrMWnH2h.js";import"./@css-render-DN2R7sM6.js";import"./vdirs-DRH9Xvnd.js";import"./@juggle-C8OzoCMD.js";import"./css-render-BDrvWz3H.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-TJvrUncL.js";import"./treemate-HRdUPn5m.js";import"./async-validator-9PlIezaS.js";import"./date-fns-Db9XENWt.js";import"./axios-t--hEgTQ.js";import"./moment-P60zs0je.js";/* empty css */const g=d({__name:"404",setup(k){const n=a(),p=()=>{n.push({path:"/"})};return(v,r)=>{const e=_,m=l,i=u,s=c;return w(),f("div",null,[t(e,{title:"404"}),t(s,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(i,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(m,{onClick:p},{default:o(()=>r[0]||(r[0]=[x("回主页")])),_:1})]),_:1})]),_:1})])}}}),O=B(g,[["__scopeId","data-v-e62daa85"]]);export{O as default};

@ -1 +0,0 @@
import{_ as i}from"./main-nav.vue_vue_type_style_index_0_lang-DmAlkpQJ.js";import{u as s}from"./vue-router-zwGLnBy5.js";import{G as a,e as c,a2 as u}from"./naive-ui-DNcWoFGl.js";import{d as l,f as d,k as t,w as o,e as f,A as x}from"./@vue-CQsYufSu.js";import{_ as g}from"./index-v3l9hw1O.js";import"./vuex-DNAxYlmG.js";import"./vooks-BQzJqMzq.js";import"./evtd-CI_DDEu_.js";import"./@vicons-C3A8jsfr.js";import"./seemly-B7f2tHrf.js";import"./vueuc-CbQ6ZCvR.js";import"./@css-render-CQdyXCYJ.js";import"./vdirs-DL8EOfHr.js";import"./@juggle-C8OzoCMD.js";import"./css-render-Ct37b3-v.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-i05dkx59.js";import"./treemate-HRdUPn5m.js";import"./async-validator-DKvM95Vc.js";import"./date-fns-x7VUUoCw.js";import"./axios-Bo0ATomq.js";import"./moment-BqTRGcJI.js";/* empty css */const h=l({__name:"404",setup(k){const n=s(),e=()=>{n.push({path:"/"})};return(w,v)=>{const r=i,p=c,_=u,m=a;return f(),d("div",null,[t(r,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(_,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:e},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}}),O=g(h,[["__scopeId","data-v-e62daa85"]]);export{O as default};

@ -1 +0,0 @@
var o=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function l(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}export{o as c,l as g};

@ -1,3 +0,0 @@
import{i as d}from"./@vue-CQsYufSu.js";function C(i){let r=".",f="__",o="--",s;if(i){let e=i.blockPrefix;e&&(r=e),e=i.elementPrefix,e&&(f=e),e=i.modifierPrefix,e&&(o=e)}const b={install(e){s=e.c;const l=e.context;l.bem={},l.bem.b=null,l.bem.els=null}};function v(e){let l,n;return{before(t){l=t.bem.b,n=t.bem.els,t.bem.els=null},after(t){t.bem.b=l,t.bem.els=n},$({context:t,props:u}){return e=typeof e=="string"?e:e({context:t,props:u}),t.bem.b=e,`${(u==null?void 0:u.bPrefix)||r}${t.bem.b}`}}}function y(e){let l;return{before(n){l=n.bem.els},after(n){n.bem.els=l},$({context:n,props:t}){return e=typeof e=="string"?e:e({context:n,props:t}),n.bem.els=e.split(",").map(u=>u.trim()),n.bem.els.map(u=>`${(t==null?void 0:t.bPrefix)||r}${n.bem.b}${f}${u}`).join(", ")}}}function P(e){return{$({context:l,props:n}){e=typeof e=="string"?e:e({context:l,props:n});const t=e.split(",").map(m=>m.trim());function u(m){return t.map(x=>`&${(n==null?void 0:n.bPrefix)||r}${l.bem.b}${m!==void 0?`${f}${m}`:""}${o}${x}`).join(", ")}const c=l.bem.els;return c!==null?u(c[0]):u()}}}function _(e){return{$({context:l,props:n}){e=typeof e=="string"?e:e({context:l,props:n});const t=l.bem.els;return`&:not(${(n==null?void 0:n.bPrefix)||r}${l.bem.b}${t!==null&&t.length>0?`${f}${t[0]}`:""}${o}${e})`}}}return Object.assign(b,{cB:(...e)=>s(v(e[0]),e[1],e[2]),cE:(...e)=>s(y(e[0]),e[1],e[2]),cM:(...e)=>s(P(e[0]),e[1],e[2]),cNotM:(...e)=>s(_(e[0]),e[1],e[2])}),b}const $=Symbol("@css-render/vue3-ssr");function M(i,r){return`<style cssr-id="${i}">
${r}
</style>`}function S(i,r){const f=d($,null);if(f===null){console.error("[css-render/vue3-ssr]: no ssr context found.");return}const{styles:o,ids:s}=f;s.has(i)||o!==null&&(s.add(i),o.push(M(i,r)))}const j=typeof document<"u";function N(){if(j)return;const i=d($,null);if(i!==null)return{adapter:S,context:i}}export{C as p,N as u};

@ -0,0 +1,3 @@
import{i as _}from"./@vue-9sINKCPW.js";function N(l){let u=".",m="__",b="--",r;if(l){let e=l.blockPrefix;e&&(u=e),e=l.elementPrefix,e&&(m=e),e=l.modifierPrefix,e&&(b=e)}const o={install(e){r=e.c;const i=e.context;i.bem={},i.bem.b=null,i.bem.els=null}};function d(e){let i,n;return{before(t){i=t.bem.b,n=t.bem.els,t.bem.els=null},after(t){t.bem.b=i,t.bem.els=n},$({context:t,props:f}){return e=typeof e=="string"?e:e({context:t,props:f}),t.bem.b=e,`${(f==null?void 0:f.bPrefix)||u}${t.bem.b}`}}}function $(e){let i;return{before(n){i=n.bem.els},after(n){n.bem.els=i},$({context:n,props:t}){return e=typeof e=="string"?e:e({context:n,props:t}),n.bem.els=e.split(",").map(f=>f.trim()),n.bem.els.map(f=>`${(t==null?void 0:t.bPrefix)||u}${n.bem.b}${m}${f}`).join(", ")}}}function v(e){return{$({context:i,props:n}){e=typeof e=="string"?e:e({context:i,props:n});const t=e.split(",").map(s=>s.trim());function f(s){return t.map(P=>`&${(n==null?void 0:n.bPrefix)||u}${i.bem.b}${s!==void 0?`${m}${s}`:""}${b}${P}`).join(", ")}const c=i.bem.els;return c!==null?f(c[0]):f()}}}function y(e){return{$({context:i,props:n}){e=typeof e=="string"?e:e({context:i,props:n});const t=i.bem.els;return`&:not(${(n==null?void 0:n.bPrefix)||u}${i.bem.b}${t!==null&&t.length>0?`${m}${t[0]}`:""}${b}${e})`}}}return Object.assign(o,{cB:(...e)=>r(d(e[0]),e[1],e[2]),cE:(...e)=>r($(e[0]),e[1],e[2]),cM:(...e)=>r(v(e[0]),e[1],e[2]),cNotM:(...e)=>r(y(e[0]),e[1],e[2])}),o}const M="@css-render/vue3-ssr";function j(l,u){return`<style cssr-id="${l}">
${u}
</style>`}function B(l,u,m){const{styles:b,ids:r}=m;r.has(l)||b!==null&&(r.add(l),b.push(j(l,u)))}const E=typeof document<"u";function k(){if(E)return;const l=_(M,null);if(l!==null)return{adapter:(u,m)=>B(u,m,l),context:l}}export{N as p,k as u};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
import{_ as N}from"./post-skeleton-YfJeD-2o.js";import{_ as R}from"./main-nav.vue_vue_type_style_index_0_lang-DmAlkpQJ.js";import{u as z}from"./vuex-DNAxYlmG.js";import{b as F}from"./vue-router-zwGLnBy5.js";import{K as S,_ as V}from"./index-v3l9hw1O.js";import{G as q,R as A,J as H,H as P}from"./naive-ui-DNcWoFGl.js";import{d as j,H as n,b as D,f as e,k as a,w as p,e as o,bk as u,Z as l,F as E,u as G,q as I,j as s,x as _,l as J}from"./@vue-CQsYufSu.js";import"./vooks-BQzJqMzq.js";import"./evtd-CI_DDEu_.js";import"./@vicons-C3A8jsfr.js";import"./axios-Bo0ATomq.js";import"./moment-BqTRGcJI.js";/* empty css */import"./seemly-B7f2tHrf.js";import"./vueuc-CbQ6ZCvR.js";import"./@css-render-CQdyXCYJ.js";import"./vdirs-DL8EOfHr.js";import"./@juggle-C8OzoCMD.js";import"./css-render-Ct37b3-v.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-i05dkx59.js";import"./treemate-HRdUPn5m.js";import"./async-validator-DKvM95Vc.js";import"./date-fns-x7VUUoCw.js";const K={key:0,class:"pagination-wrap"},L={key:0,class:"skeleton-wrap"},M={key:1},O={key:0,class:"empty-wrap"},T={class:"bill-line"},U=j({__name:"Anouncement",setup(Z){const d=z(),g=F(),v=n(!1),r=n([]),i=n(+g.query.p||1),f=n(20),m=n(0),h=c=>{i.value=c};return D(()=>{}),(c,$)=>{const k=R,y=A,w=N,x=H,B=P,C=q;return o(),e("div",null,[a(k,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[m.value>1?(o(),e("div",K,[a(y,{page:i.value,"onUpdate:page":h,"page-slot":u(d).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):l("",!0)]),default:p(()=>[v.value?(o(),e("div",L,[a(w,{num:f.value},null,8,["num"])])):(o(),e("div",M,[r.value.length===0?(o(),e("div",O,[a(x,{size:"large",description:"暂无数据"})])):l("",!0),(o(!0),e(E,null,G(r.value,t=>(o(),I(B,{key:t.id},{default:p(()=>[s("div",T,[s("div",null,"NO."+_(t.id),1),s("div",null,_(t.reason),1),s("div",{class:J({income:t.change_amount>=0,out:t.change_amount<0})},_((t.change_amount>0?"+":"")+(t.change_amount/100).toFixed(2)),3),s("div",null,_(u(S)(t.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}}),kt=V(U,[["__scopeId","data-v-d4d04859"]]);export{kt as default};

@ -0,0 +1 @@
import{_ as C}from"./post-skeleton-B6KFVL2X.js";import{_ as N}from"./main-nav.vue_vue_type_style_index_0_lang-D7-FAbTY.js";import{u as R}from"./vuex--ttreJMD.js";import{b as q}from"./vue-router-yrkFRUM9.js";import{K as F,_ as S}from"./index-DxHQoSDp.js";import{G as V,J as A,R as P,H as j}from"./naive-ui-BJojRuLw.js";import{d as D,r as n,b as E,f as e,q as a,w as p,Y as u,F as G,x as H,bq as l,j as o,t as I,k as s,z as _,s as J}from"./@vue-9sINKCPW.js";import"./vooks-CfQnrjIt.js";import"./evtd-CI_DDEu_.js";import"./@vicons-PCg97L0F.js";import"./axios-t--hEgTQ.js";import"./moment-P60zs0je.js";/* empty css */import"./seemly-D-teBmey.js";import"./vueuc-DrMWnH2h.js";import"./@css-render-DN2R7sM6.js";import"./vdirs-DRH9Xvnd.js";import"./@juggle-C8OzoCMD.js";import"./css-render-BDrvWz3H.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-TJvrUncL.js";import"./treemate-HRdUPn5m.js";import"./async-validator-9PlIezaS.js";import"./date-fns-Db9XENWt.js";const K={key:0,class:"pagination-wrap"},L={key:0,class:"skeleton-wrap"},M={key:1},O={key:0,class:"empty-wrap"},T={class:"bill-line"},U=D({__name:"Anouncement",setup(Y){const d=R(),g=q(),v=n(!1),r=n([]),i=n(+g.query.p||1),f=n(20),m=n(0),h=c=>{i.value=c};return E(()=>{}),(c,$)=>{const k=N,y=P,w=C,x=A,z=j,B=V;return o(),e("div",null,[a(k,{title:"公告"}),a(B,{class:"main-content-wrap",bordered:""},{footer:p(()=>[m.value>1?(o(),e("div",K,[a(y,{page:i.value,"onUpdate:page":h,"page-slot":l(d).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):u("",!0)]),default:p(()=>[v.value?(o(),e("div",L,[a(w,{num:f.value},null,8,["num"])])):(o(),e("div",M,[r.value.length===0?(o(),e("div",O,[a(x,{size:"large",description:"暂无数据"})])):u("",!0),(o(!0),e(G,null,H(r.value,t=>(o(),I(z,{key:t.id},{default:p(()=>[s("div",T,[s("div",null,"NO."+_(t.id),1),s("div",null,_(t.reason),1),s("div",{class:J({income:t.change_amount>=0,out:t.change_amount<0})},_((t.change_amount>0?"+":"")+(t.change_amount/100).toFixed(2)),3),s("div",null,_(l(F)(t.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}}),kt=S(U,[["__scopeId","data-v-d4d04859"]]);export{kt as default};

@ -0,0 +1 @@
import{_ as D}from"./whisper-CD0RDnNY.js";import{_ as H,a as R}from"./post-item.vue_vue_type_style_index_0_lang-3gNUBBWd.js";import{_ as W}from"./post-skeleton-B6KFVL2X.js";import{_ as Y}from"./main-nav.vue_vue_type_style_index_0_lang-D7-FAbTY.js";import{u as E}from"./vuex--ttreJMD.js";import{b as G}from"./vue-router-yrkFRUM9.js";import{Y as J}from"./v3-infinite-loading-D3303HHP.js";import{U as L,u as K,f as Q,_ as X}from"./index-DxHQoSDp.js";import{d as Z,r as t,b as ee,f as n,q as a,t as d,Y as h,w as u,bq as f,F as b,x as $,k as z,z as oe,j as o}from"./@vue-9sINKCPW.js";import{F as se,G as te,a as ne,J as ae,j as ie,H as le}from"./naive-ui-BJojRuLw.js";import"./content-x8CCof-x.js";import"./@vicons-PCg97L0F.js";import"./paopao-video-player-FrzfkELx.js";import"./copy-to-clipboard-DgsYVcxl.js";import"./toggle-selection-DGa8lynz.js";import"./vooks-CfQnrjIt.js";import"./evtd-CI_DDEu_.js";import"./axios-t--hEgTQ.js";import"./moment-P60zs0je.js";/* empty css */import"./seemly-D-teBmey.js";import"./vueuc-DrMWnH2h.js";import"./@css-render-DN2R7sM6.js";import"./vdirs-DRH9Xvnd.js";import"./@juggle-C8OzoCMD.js";import"./css-render-BDrvWz3H.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-TJvrUncL.js";import"./treemate-HRdUPn5m.js";import"./async-validator-9PlIezaS.js";import"./date-fns-Db9XENWt.js";const re={key:0,class:"skeleton-wrap"},_e={key:1},ue={key:0,class:"empty-wrap"},ce={key:1},me={key:2},pe={class:"load-more-wrap"},de={class:"load-more-spinner"},fe=Z({__name:"Collection",setup(ve){const v=E(),A=G(),B=se(),c=t(!1),_=t(!1),s=t([]),l=t(+A.query.p||1),w=t(20),m=t(0),g=t(!1),k=t({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),y=e=>{k.value=e,g.value=!0},I=()=>{g.value=!1},x=e=>{B.success({title:"提示",content:"确定"+(e.user.is_following?"取消关注":"关注")+"该用户吗?",positiveText:"确定",negativeText:"取消",onPositiveClick:()=>{e.user.is_following?K({user_id:e.user.id}).then(r=>{window.$message.success("操作成功"),C(e.user_id,!1)}).catch(r=>{}):Q({user_id:e.user.id}).then(r=>{window.$message.success("关注成功"),C(e.user_id,!0)}).catch(r=>{})}})};function C(e,r){for(let p in s.value)s.value[p].user_id==e&&(s.value[p].user.is_following=r)}const F=()=>{c.value=!0,L({page:l.value,page_size:w.value}).then(e=>{c.value=!1,e.list.length===0&&(_.value=!0),l.value>1?s.value=s.value.concat(e.list):(s.value=e.list,window.scrollTo(0,0)),m.value=Math.ceil(e.pager.total_rows/w.value)}).catch(e=>{c.value=!1,l.value>1&&l.value--})},M=()=>{l.value<m.value||m.value==0?(_.value=!1,l.value++,F()):_.value=!0};return ee(()=>{F()}),(e,r)=>{const p=Y,O=W,P=ae,j=H,S=le,q=R,N=D,T=te,U=ie,V=ne;return o(),n("div",null,[a(p,{title:"收藏"}),a(T,{class:"main-content-wrap",bordered:""},{default:u(()=>[c.value&&s.value.length===0?(o(),n("div",re,[a(O,{num:w.value},null,8,["num"])])):(o(),n("div",_e,[s.value.length===0?(o(),n("div",ue,[a(P,{size:"large",description:"暂无数据"})])):h("",!0),f(v).state.desktopModelShow?(o(),n("div",ce,[(o(!0),n(b,null,$(s.value,i=>(o(),d(S,{key:i.id},{default:u(()=>[a(j,{post:i,isOwner:f(v).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:y,onHandleFollowAction:x},null,8,["post","isOwner"])]),_:2},1024))),128))])):(o(),n("div",me,[(o(!0),n(b,null,$(s.value,i=>(o(),d(S,{key:i.id},{default:u(()=>[a(q,{post:i,isOwner:f(v).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:y,onHandleFollowAction:x},null,8,["post","isOwner"])]),_:2},1024))),128))]))])),a(N,{show:g.value,user:k.value,onSuccess:I},null,8,["show","user"])]),_:1}),m.value>0?(o(),d(V,{key:0,justify:"center"},{default:u(()=>[a(f(J),{class:"load-more",slots:{complete:"没有更多收藏了",error:"加载出错"},onInfinite:M},{spinner:u(()=>[z("div",pe,[_.value?h("",!0):(o(),d(U,{key:0,size:14})),z("span",de,oe(_.value?"没有更多收藏了":"加载更多"),1)])]),_:1})]),_:1})):h("",!0)])}}}),Je=X(fe,[["__scopeId","data-v-735372fb"]]);export{Je as default};

@ -1 +0,0 @@
import{_ as j}from"./whisper-DFhobfFF.js";import{_ as q,a as D}from"./post-item.vue_vue_type_style_index_0_lang-Dfn2xguO.js";import{_ as R}from"./post-skeleton-YfJeD-2o.js";import{_ as E}from"./main-nav.vue_vue_type_style_index_0_lang-DmAlkpQJ.js";import{u as G}from"./vuex-DNAxYlmG.js";import{b as J}from"./vue-router-zwGLnBy5.js";import{W as L}from"./v3-infinite-loading-DK6TzcZc.js";import{U as Z,u as K,f as Q,_ as X}from"./index-v3l9hw1O.js";import{d as Y,H as t,b as ee,f as n,k as a,w as u,q as d,Z as h,e as o,bk as f,F as b,u as $,j as z,x as oe}from"./@vue-CQsYufSu.js";import{F as se,G as te,a as ne,J as ae,k as ie,H as le}from"./naive-ui-DNcWoFGl.js";import"./content-BMjusBPS.js";import"./@vicons-C3A8jsfr.js";import"./paopao-video-player-DAi68TC5.js";import"./copy-to-clipboard-CA7crPat.js";import"./@babel-Cpj98o6Y.js";import"./toggle-selection-BHUZwh74.js";import"./vooks-BQzJqMzq.js";import"./evtd-CI_DDEu_.js";import"./axios-Bo0ATomq.js";import"./moment-BqTRGcJI.js";/* empty css */import"./seemly-B7f2tHrf.js";import"./vueuc-CbQ6ZCvR.js";import"./@css-render-CQdyXCYJ.js";import"./vdirs-DL8EOfHr.js";import"./@juggle-C8OzoCMD.js";import"./css-render-Ct37b3-v.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-i05dkx59.js";import"./treemate-HRdUPn5m.js";import"./async-validator-DKvM95Vc.js";import"./date-fns-x7VUUoCw.js";const re={key:0,class:"skeleton-wrap"},_e={key:1},ue={key:0,class:"empty-wrap"},ce={key:1},me={key:2},pe={class:"load-more-wrap"},de={class:"load-more-spinner"},fe=Y({__name:"Collection",setup(ve){const v=G(),A=J(),B=se(),c=t(!1),_=t(!1),s=t([]),l=t(+A.query.p||1),w=t(20),m=t(0),g=t(!1),k=t({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),y=e=>{k.value=e,g.value=!0},I=()=>{g.value=!1},x=e=>{B.success({title:"提示",content:"确定"+(e.user.is_following?"取消关注":"关注")+"该用户吗?",positiveText:"确定",negativeText:"取消",onPositiveClick:()=>{e.user.is_following?K({user_id:e.user.id}).then(r=>{window.$message.success("操作成功"),C(e.user_id,!1)}).catch(r=>{}):Q({user_id:e.user.id}).then(r=>{window.$message.success("关注成功"),C(e.user_id,!0)}).catch(r=>{})}})};function C(e,r){for(let p in s.value)s.value[p].user_id==e&&(s.value[p].user.is_following=r)}const F=()=>{c.value=!0,Z({page:l.value,page_size:w.value}).then(e=>{c.value=!1,e.list.length===0&&(_.value=!0),l.value>1?s.value=s.value.concat(e.list):(s.value=e.list,window.scrollTo(0,0)),m.value=Math.ceil(e.pager.total_rows/w.value)}).catch(e=>{c.value=!1,l.value>1&&l.value--})},M=()=>{l.value<m.value||m.value==0?(_.value=!1,l.value++,F()):_.value=!0};return ee(()=>{F()}),(e,r)=>{const p=E,O=R,P=ae,H=q,S=le,N=D,T=j,U=te,V=ie,W=ne;return o(),n("div",null,[a(p,{title:"收藏"}),a(U,{class:"main-content-wrap",bordered:""},{default:u(()=>[c.value&&s.value.length===0?(o(),n("div",re,[a(O,{num:w.value},null,8,["num"])])):(o(),n("div",_e,[s.value.length===0?(o(),n("div",ue,[a(P,{size:"large",description:"暂无数据"})])):h("",!0),f(v).state.desktopModelShow?(o(),n("div",ce,[(o(!0),n(b,null,$(s.value,i=>(o(),d(S,{key:i.id},{default:u(()=>[a(H,{post:i,isOwner:f(v).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:y,onHandleFollowAction:x},null,8,["post","isOwner"])]),_:2},1024))),128))])):(o(),n("div",me,[(o(!0),n(b,null,$(s.value,i=>(o(),d(S,{key:i.id},{default:u(()=>[a(N,{post:i,isOwner:f(v).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:y,onHandleFollowAction:x},null,8,["post","isOwner"])]),_:2},1024))),128))]))])),a(T,{show:g.value,user:k.value,onSuccess:I},null,8,["show","user"])]),_:1}),m.value>0?(o(),d(W,{key:0,justify:"center"},{default:u(()=>[a(f(L),{class:"load-more",slots:{complete:"没有更多收藏了",error:"加载出错"},onInfinite:M},{spinner:u(()=>[z("div",pe,[_.value?h("",!0):(o(),d(V,{key:0,size:14})),z("span",de,oe(_.value?"没有更多收藏了":"加载更多"),1)])]),_:1})]),_:1})):h("",!0)])}}}),Ze=X(fe,[["__scopeId","data-v-735372fb"]]);export{Ze as default};

@ -0,0 +1 @@
import{_ as G}from"./whisper-CD0RDnNY.js";import{d as S,c as H,l as L,f as p,j as s,q as t,w as o,k as a,bq as g,A as O,G as R,z as d,h as B,r,b as W,t as C,Y as b,F as I,x as E}from"./@vue-9sINKCPW.js";import{L as J,_ as N,Y as Q}from"./index-DxHQoSDp.js";import{M as U,Q as K,B as X,i as M,m as Z,G as ee,a as te,J as ne,j as oe,H as se}from"./naive-ui-BJojRuLw.js";import{q as ae,r as ce}from"./@vicons-PCg97L0F.js";import{_ as ie}from"./post-skeleton-B6KFVL2X.js";import{_ as re}from"./main-nav.vue_vue_type_style_index_0_lang-D7-FAbTY.js";import{Y as le}from"./v3-infinite-loading-D3303HHP.js";import{b as _e}from"./vue-router-yrkFRUM9.js";import"./vuex--ttreJMD.js";import"./axios-t--hEgTQ.js";import"./moment-P60zs0je.js";/* empty css */import"./seemly-D-teBmey.js";import"./vueuc-DrMWnH2h.js";import"./evtd-CI_DDEu_.js";import"./@css-render-DN2R7sM6.js";import"./vooks-CfQnrjIt.js";import"./vdirs-DRH9Xvnd.js";import"./@juggle-C8OzoCMD.js";import"./css-render-BDrvWz3H.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-TJvrUncL.js";import"./treemate-HRdUPn5m.js";import"./async-validator-9PlIezaS.js";import"./date-fns-Db9XENWt.js";const ue={class:"contact-item"},pe={class:"nickname-wrap"},me={class:"username-wrap"},de={class:"user-info"},fe={class:"info-item"},ve={class:"info-item"},he={class:"item-header-extra"},ge=S({__name:"contact-item",props:{contact:{}},emits:["send-whisper"],setup(z,{emit:w}){const _=w,l=e=>()=>B(M,null,{default:()=>B(e)}),n=z,c=H(()=>[{label:"私信 @"+n.contact.username,key:"whisper",icon:l(ce)}]),m=e=>{switch(e){case"whisper":const i={id:n.contact.user_id,avatar:n.contact.avatar,username:n.contact.username,nickname:n.contact.nickname,is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1};_("send-whisper",i);break}};return(e,i)=>{const f=Z,k=L("router-link"),y=X,$=K,v=U;return s(),p("div",ue,[t(v,{"content-indented":""},{avatar:o(()=>[t(f,{size:54,src:e.contact.avatar},null,8,["src"])]),header:o(()=>[a("span",pe,[t(k,{onClick:i[0]||(i[0]=O(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{s:e.contact.username}}},{default:o(()=>[R(d(e.contact.nickname),1)]),_:1},8,["to"])]),a("span",me," @"+d(e.contact.username),1),a("div",de,[a("span",fe," UID. "+d(e.contact.user_id),1),a("span",ve,d(g(J)(e.contact.created_on))+" 加入 ",1)])]),"header-extra":o(()=>[a("div",he,[t($,{placement:"bottom-end",trigger:"click",size:"small",options:c.value,onSelect:m},{default:o(()=>[t(y,{quaternary:"",circle:""},{icon:o(()=>[t(g(M),null,{default:o(()=>[t(g(ae))]),_:1})]),_:1})]),_:1},8,["options"])])]),_:1})])}}}),we=N(ge,[["__scopeId","data-v-42e975ce"]]),ke={key:0,class:"skeleton-wrap"},ye={key:1},$e={key:0,class:"empty-wrap"},Ce={class:"load-more-wrap"},be={class:"load-more-spinner"},ze=S({__name:"Contacts",setup(z){const w=_e(),_=r(!1),l=r(!1),n=r([]),c=r(+w.query.p||1),m=r(20),e=r(0),i=r(!1),f=r({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),k=h=>{f.value=h,i.value=!0},y=()=>{i.value=!1},$=()=>{c.value<e.value||e.value==0?(l.value=!1,c.value++,v()):l.value=!0};W(()=>{v()});const v=(h=!1)=>{n.value.length===0&&(_.value=!0),Q({page:c.value,page_size:m.value}).then(u=>{_.value=!1,u.list.length===0&&(l.value=!0),c.value>1?n.value=n.value.concat(u.list):(n.value=u.list,h&&setTimeout(()=>{window.scrollTo(0,99999)},50)),e.value=Math.ceil(u.pager.total_rows/m.value)}).catch(u=>{_.value=!1,c.value>1&&c.value--})};return(h,u)=>{const x=re,P=ie,V=ne,j=we,D=se,F=G,T=ee,Y=oe,A=te;return s(),p(I,null,[a("div",null,[t(x,{title:"好友"}),t(T,{class:"main-content-wrap",bordered:""},{default:o(()=>[_.value&&n.value.length===0?(s(),p("div",ke,[t(P,{num:m.value},null,8,["num"])])):(s(),p("div",ye,[n.value.length===0?(s(),p("div",$e,[t(V,{size:"large",description:"暂无数据"})])):b("",!0),(s(!0),p(I,null,E(n.value,q=>(s(),C(D,{class:"list-item",key:q.user_id},{default:o(()=>[t(j,{contact:q,onSendWhisper:k},null,8,["contact"])]),_:2},1024))),128))])),t(F,{show:i.value,user:f.value,onSuccess:y},null,8,["show","user"])]),_:1})]),e.value>0?(s(),C(A,{key:0,justify:"center"},{default:o(()=>[t(g(le),{class:"load-more",slots:{complete:"没有更多好友了",error:"加载出错"},onInfinite:$},{spinner:o(()=>[a("div",Ce,[l.value?b("",!0):(s(),C(Y,{key:0,size:14})),a("span",be,d(l.value?"没有更多好友了":"加载更多"),1)])]),_:1})]),_:1})):b("",!0)],64)}}}),Xe=N(ze,[["__scopeId","data-v-69277f0c"]]);export{Xe as default};

@ -1 +0,0 @@
import{_ as W}from"./whisper-DFhobfFF.js";import{d as P,c as A,r as L,e as s,f as p,k as t,w as o,j as a,y as R,A as E,x as d,bk as g,h as S,H as r,b as G,q as C,Z as b,F as M,u as J}from"./@vue-CQsYufSu.js";import{L as U,_ as q,Y}from"./index-v3l9hw1O.js";import{k as Z,r as K}from"./@vicons-C3A8jsfr.js";import{j as N,o as Q,e as X,P as ee,O as te,G as ne,a as oe,J as se,k as ae,H as ce}from"./naive-ui-DNcWoFGl.js";import{_ as ie}from"./post-skeleton-YfJeD-2o.js";import{_ as re}from"./main-nav.vue_vue_type_style_index_0_lang-DmAlkpQJ.js";import{W as le}from"./v3-infinite-loading-DK6TzcZc.js";import{b as _e}from"./vue-router-zwGLnBy5.js";import"./vuex-DNAxYlmG.js";import"./axios-Bo0ATomq.js";import"./moment-BqTRGcJI.js";/* empty css */import"./seemly-B7f2tHrf.js";import"./vueuc-CbQ6ZCvR.js";import"./evtd-CI_DDEu_.js";import"./@css-render-CQdyXCYJ.js";import"./vooks-BQzJqMzq.js";import"./vdirs-DL8EOfHr.js";import"./@juggle-C8OzoCMD.js";import"./css-render-Ct37b3-v.js";import"./@emotion-WldOFDRm.js";import"./lodash-es-i05dkx59.js";import"./treemate-HRdUPn5m.js";import"./async-validator-DKvM95Vc.js";import"./date-fns-x7VUUoCw.js";const ue={class:"contact-item"},pe={class:"nickname-wrap"},me={class:"username-wrap"},de={class:"user-info"},fe={class:"info-item"},ve={class:"info-item"},he={class:"item-header-extra"},ge=P({__name:"contact-item",props:{contact:{}},emits:["send-whisper"],setup(z,{emit:w}){const _=w,l=e=>()=>S(N,null,{default:()=>S(e)}),n=z,c=A(()=>[{label:"私信 @"+n.contact.username,key:"whisper",icon:l(K)}]),m=e=>{switch(e){case"whisper":const i={id:n.contact.user_id,avatar:n.contact.avatar,username:n.contact.username,nickname:n.contact.nickname,is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1};_("send-whisper",i);break}};return(e,i)=>{const f=Q,k=L("router-link"),y=X,$=ee,v=te;return s(),p("div",ue,[t(v,{"content-indented":""},{avatar:o(()=>[t(f,{size:54,src:e.contact.avatar},null,8,["src"])]),header:o(()=>[a("span",pe,[t(k,{onClick:i[0]||(i[0]=R(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{s:e.contact.username}}},{default:o(()=>[E(d(e.contact.nickname),1)]),_:1},8,["to"])]),a("span",me," @"+d(e.contact.username),1),a("div",de,[a("span",fe," UID. "+d(e.contact.user_id),1),a("span",ve,d(g(U)(e.contact.created_on))+" 加入 ",1)])]),"header-extra":o(()=>[a("div",he,[t($,{placement:"bottom-end",trigger:"click",size:"small",options:c.value,onSelect:m},{default:o(()=>[t(y,{quaternary:"",circle:""},{icon:o(()=>[t(g(N),null,{default:o(()=>[t(g(Z))]),_:1})]),_:1})]),_:1},8,["options"])])]),_:1})])}}}),we=q(ge,[["__scopeId","data-v-42e975ce"]]),ke={key:0,class:"skeleton-wrap"},ye={key:1},$e={key:0,class:"empty-wrap"},Ce={class:"load-more-wrap"},be={class:"load-more-spinner"},ze=P({__name:"Contacts",setup(z){const w=_e(),_=r(!1),l=r(!1),n=r([]),c=r(+w.query.p||1),m=r(20),e=r(0),i=r(!1),f=r({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),k=h=>{f.value=h,i.value=!0},y=()=>{i.value=!1},$=()=>{c.value<e.value||e.value==0?(l.value=!1,c.value++,v()):l.value=!0};G(()=>{v()});const v=(h=!1)=>{n.value.length===0&&(_.value=!0),Y({page:c.value,page_size:m.value}).then(u=>{_.value=!1,u.list.length===0&&(l.value=!0),c.value>1?n.value=n.value.concat(u.list):(n.value=u.list,h&&setTimeout(()=>{window.scrollTo(0,99999)},50)),e.value=Math.ceil(u.pager.total_rows/m.value)}).catch(u=>{_.value=!1,c.value>1&&c.value--})};return(h,u)=>{const x=re,B=ie,V=se,j=we,D=ce,F=W,H=ne,O=ae,T=oe;return s(),p(M,null,[a("div",null,[t(x,{title:"好友"}),t(H,{class:"main-content-wrap",bordered:""},{default:o(()=>[_.value&&n.value.length===0?(s(),p("div",ke,[t(B,{num:m.value},null,8,["num"])])):(s(),p("div",ye,[n.value.length===0?(s(),p("div",$e,[t(V,{size:"large",description:"暂无数据"})])):b("",!0),(s(!0),p(M,null,J(n.value,I=>(s(),C(D,{class:"list-item",key:I.user_id},{default:o(()=>[t(j,{contact:I,onSendWhisper:k},null,8,["contact"])]),_:2},1024))),128))])),t(F,{show:i.value,user:f.value,onSuccess:y},null,8,["show","user"])]),_:1})]),e.value>0?(s(),C(T,{key:0,justify:"center"},{default:o(()=>[t(g(le),{class:"load-more",slots:{complete:"没有更多好友了",error:"加载出错"},onInfinite:$},{spinner:o(()=>[a("div",Ce,[l.value?b("",!0):(s(),C(O,{key:0,size:14})),a("span",be,d(l.value?"没有更多好友了":"加载更多"),1)])]),_:1})]),_:1})):b("",!0)],64)}}}),Qe=q(ze,[["__scopeId","data-v-69277f0c"]]);export{Qe as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save