Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3831950780 | ||
|
|
9220cf82aa | ||
|
|
4a61527952 |
@@ -1,9 +1,14 @@
|
||||
# goodBaby v2
|
||||
|
||||
摇篮系统 —— 为独居青年准备的「死人开关」(Dead Man's Switch)。
|
||||

|
||||

|
||||

|
||||
|
||||
一个人在大城市生活,最怕的是出了事都没人知道。goodBaby 让你定期回来报一声平安;一旦超过设定的期限没有签到,它就会替你把预先托付的留言送出去:给家人的邮件、给朋友的 QQ / 钉钉消息、一条 B 站动态,或是公开你的 GitHub 仓库,把作品留给世界。
|
||||
根据《中国心血管健康与疾病报告2023》,中国 每年 猝死总人数(含心源性和非心源性)或达80万-100万,18-35岁人群占比为38%,也就是说,每天18-35岁人群有近1000人猝死
|
||||
|
||||
伴随着中国人口老龄化加剧,年轻人的牵挂越来越多,面对着可能的猝死,我们或许真的需要做好准备
|
||||
|
||||
所以我写了goodBaby来解决这个问题,他和apple app store里的“死了么”形式上有些接近,不过goodBaby支持了更多好玩的通知方式,比如bilibili动态,github仓库,邮件,OneBot,还可以方便的自托管
|
||||
[](https://app.rainyun.com/apps/rca/store/7125/cat_)
|
||||
|
||||
## 功能
|
||||
@@ -39,12 +44,10 @@ docker compose up -d
|
||||
docker run -d --name goodbaby -p 8088:8088 -v ./data:/app/data ghcr.io/ssdomei232/goodbaby:latest
|
||||
```
|
||||
|
||||
可用标签:`latest`、`v2.2.0`(完整版本)、`2.2`(次版本)、`2`(主版本)。
|
||||
|
||||
### 下载预编译二进制
|
||||
|
||||
[Releases](https://github.com/ssdomei232/goodBaby/releases) 提供 Linux / Windows / macOS 的 amd64 与 arm64 产物,
|
||||
解压后直接运行即可(前端已内嵌,无需额外部署)。压缩包旁的 `checksums.txt` 可校验完整性:
|
||||
解压后直接运行即可。压缩包旁的 `checksums.txt` 可校验完整性:
|
||||
|
||||
```bash
|
||||
sha256sum -c checksums.txt --ignore-missing
|
||||
@@ -55,7 +58,7 @@ sha256sum -c checksums.txt --ignore-missing
|
||||
需要 Go 1.25+ 与 Node.js 20+:
|
||||
|
||||
```bash
|
||||
# 1. 构建前端(产物输出到 web/dist,会被 go:embed 打进二进制)
|
||||
# 1. 构建前端
|
||||
cd web/frontend && npm install && npm run build && cd ../..
|
||||
|
||||
# 2. 构建后端
|
||||
@@ -97,16 +100,7 @@ cd web/frontend && npm run dev
|
||||
|
||||
### 管理员后台
|
||||
|
||||
**第一个注册的用户自动成为管理员**(老版本升级上来时,注册最早的用户会被自动提升)。
|
||||
|
||||
管理员在「设置」页面会多出一个「系统配置」区块,可以直接修改并立即生效,无需重启:
|
||||
|
||||
* 开放注册
|
||||
* 检查间隔(保存后会自动重排定时任务)
|
||||
* 规则重试时长
|
||||
* 日志保留条数
|
||||
|
||||
监听地址、数据库、会话密钥这类启动项不在后台开放,只能改配置文件。
|
||||
**第一个注册的用户自动成为管理员**。
|
||||
|
||||
## 数据库
|
||||
|
||||
@@ -126,6 +120,7 @@ cd web/frontend && npm run dev
|
||||
* [OneBot (QQ)](docs/onebot-config.md)
|
||||
* [钉钉机器人](docs/dingtalk-config.md)
|
||||
* [GitHub](docs/github-config.md)
|
||||
* [饭碗警告](docs/fwalert-config.md)
|
||||
|
||||
## 开发:新增一种规则类型
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# 饭碗警告配置
|
||||
|
||||
规则类型: `fwalert`。
|
||||
|
||||
定时器到期后,请求饭碗警告的 Webhook 地址
|
||||
|
||||
饭碗警告: 比较简单的(不用实名认证)支持电话和短信通知的第三方平台: `https://fwalert.com/`,价格不算最低,但比较方便
|
||||
|
||||
## 规则配置 (Rule Config)
|
||||
|
||||
```json
|
||||
{
|
||||
"webhook_url": "https://fwalert.com/xxx-xxx-xxx-xxx-xxx",
|
||||
"msg": "消息内容"
|
||||
}
|
||||
```
|
||||
|
||||
## 饭碗警告平台配置
|
||||
|
||||
goodbaby 在请求饭碗警告时将`msg`放在请求体中,参照下面和官方教程设置:
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| `变量名` | `message` |
|
||||
| `变量来源` | `请求体(body)` |
|
||||
| `提取规则` | `序列化数据` |
|
||||
| `键` | `message` |
|
||||
| `通知简述` | 随便填 |
|
||||
| `通知正文` | `{{message}}` |
|
||||
| `联系方式类型` | 参照官方文档添加并选择 |
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
)
|
||||
|
||||
// DingTalkExecutor 钉钉机器人执行器
|
||||
//
|
||||
// 之前只注册了校验器却没有执行器,导致钉钉规则可以创建但永远不会被执行。
|
||||
type DingTalkExecutor struct{}
|
||||
|
||||
func (e *DingTalkExecutor) GetType() string {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package fwalert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
// send 发送消息
|
||||
func send(config *FwAlertRuleConfig) error {
|
||||
restyClient := resty.New()
|
||||
req := restyClient.R()
|
||||
|
||||
req.SetBody(FwAlertRequest{
|
||||
Message: config.Msg,
|
||||
})
|
||||
|
||||
resp, err := req.Execute(resty.MethodPost, config.WebhookURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return fmt.Errorf("发送饭碗警告失败: %s", resp.Status())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package fwalert
|
||||
|
||||
const RuleTypeFwalert = "fwalert"
|
||||
|
||||
type FwAlertRuleConfig struct {
|
||||
WebhookURL string `json:"webhook_url"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type FwAlertRequest struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package fwalert
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/ssdomei232/goodBaby/internal/meta"
|
||||
)
|
||||
|
||||
type FwalertRuleValidator struct{}
|
||||
|
||||
func (v *FwalertRuleValidator) GetType() string {
|
||||
return RuleTypeFwalert
|
||||
}
|
||||
|
||||
func (v *FwalertRuleValidator) Validate(configJSON string) error {
|
||||
var config FwAlertRuleConfig
|
||||
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
|
||||
return fmt.Errorf("解析饭碗警告规则配置失败: %v", err)
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(config.WebhookURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("饭碗警告规则配置中 webhook_url 格式不正确: %v", err)
|
||||
}
|
||||
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
||||
return fmt.Errorf("饭碗警告规则配置中 webhook_url 必须是 HTTP 或 HTTPS URL")
|
||||
}
|
||||
|
||||
if config.Msg == "" {
|
||||
return fmt.Errorf("饭碗警告规则配置中 msg 不能为空")
|
||||
}
|
||||
if len([]rune(config.Msg)) > 1000 {
|
||||
return fmt.Errorf("饭碗警告内容过长")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *FwalertRuleValidator) Meta() meta.RuleMeta {
|
||||
return meta.RuleMeta{
|
||||
Type: RuleTypeFwalert,
|
||||
Label: "发送饭碗警告",
|
||||
Description: "触发时发送饭碗警告消息。",
|
||||
Docs: "docs/fwalert-config.md",
|
||||
AccountType: "",
|
||||
Fields: []meta.Field{
|
||||
{Key: "webhook_url", Label: "Webhook URL", Type: meta.FieldString, Required: true, Help: "饭碗警告的触发地址", Placeholder: "https://fwalert.com/xxx-xxx-xxx-xxx-xxx"},
|
||||
{Key: "msg", Label: "消息内容", Type: meta.FieldTextarea, Required: true, Help: "最大长度 1000 个字符"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ParseRuleConfig 解析饭碗警告规则配置
|
||||
func ParseRuleConfig(configJSON string) (*FwAlertRuleConfig, error) {
|
||||
var config FwAlertRuleConfig
|
||||
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
|
||||
return nil, fmt.Errorf("解析饭碗警告规则配置失败: %v", err)
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package fwalert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/ssdomei232/goodBaby/internal/retry"
|
||||
"github.com/ssdomei232/goodBaby/model"
|
||||
)
|
||||
|
||||
// FwalertExecutor 饭碗警告执行器
|
||||
type FwalertExecutor struct{}
|
||||
|
||||
func (e *FwalertExecutor) GetType() string {
|
||||
return RuleTypeFwalert
|
||||
}
|
||||
|
||||
func (e *FwalertExecutor) Execute(ctx context.Context, rule *model.Rule) error {
|
||||
log.Printf("执行饭碗警告规则: %s (ID: %d)", rule.Name, rule.ID)
|
||||
|
||||
config, err := ParseRuleConfig(rule.ConfigJson)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return retry.Do(ctx, func() error {
|
||||
return send(config)
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/ssdomei232/goodBaby/drivers/bilibili"
|
||||
"github.com/ssdomei232/goodBaby/drivers/dingtalk"
|
||||
"github.com/ssdomei232/goodBaby/drivers/email"
|
||||
"github.com/ssdomei232/goodBaby/drivers/fwalert"
|
||||
"github.com/ssdomei232/goodBaby/drivers/github"
|
||||
"github.com/ssdomei232/goodBaby/drivers/onebot"
|
||||
"github.com/ssdomei232/goodBaby/model"
|
||||
@@ -76,6 +77,7 @@ func InitExecutorRegistry() *ExecutorRegistry {
|
||||
registry.Register(&github.GithubMakeRepoPublicExecutor{})
|
||||
registry.Register(&onebot.OneBotExecutor{})
|
||||
registry.Register(&dingtalk.DingTalkExecutor{})
|
||||
registry.Register(&fwalert.FwalertExecutor{})
|
||||
// 未来添加新规则类型时,在这里注册即可
|
||||
|
||||
globalExecutorRegistry = registry
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/ssdomei232/goodBaby/drivers/bilibili"
|
||||
"github.com/ssdomei232/goodBaby/drivers/dingtalk"
|
||||
"github.com/ssdomei232/goodBaby/drivers/email"
|
||||
"github.com/ssdomei232/goodBaby/drivers/fwalert"
|
||||
"github.com/ssdomei232/goodBaby/drivers/github"
|
||||
"github.com/ssdomei232/goodBaby/drivers/onebot"
|
||||
)
|
||||
@@ -27,6 +28,7 @@ func InitValidatorRegistry() *ValidatorRegistry {
|
||||
r.Register(&onebot.OneBotRuleValidator{})
|
||||
r.Register(&dingtalk.DingTalkRuleValidator{})
|
||||
r.Register(&github.GithubMakeRepositoryPublicRuleValidator{})
|
||||
r.Register(&fwalert.FwalertRuleValidator{})
|
||||
|
||||
registry = r
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user