Ent

快速开始 创建第一个模式 1 go run -mod=mod entgo.io/ent/cmd/ent init User 执行后会引入Ent并创建User的schema 1 2 3 4 5 6 7 8 9 10 11 12 13 14 // User在User实体中组合了ent默认的数据库模式定义 type User struct { ent.Schema } // User的字段 func (User) Fields() []ent.Field { return nil } // User的边 func (User) Edges() []ent.Edge { return nil } 添加字段 1 2 3 4 5 6 7 8 9 // User的字段 func (User) Fields() []ent.Field { return []ent.Field{ field.Int("age"). Positive(), field.String("name"). Default("unknown"), } } 生成代码 1 go generate ./ent 创建连接 postgres 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package main import ( "context" "log" "<project>/ent" _ "github.com/lib/pq" ) func main() { client, err := ent.Open("postgres", "host=<host> port=<port> user=<user> dbname=<database> password=<pass>") if err != nil { log.Fatalf("failed opening connection to postgres: %v", err) } defer client.Close() // 运行自动迁移工具。 if err := client.Schema.Create(context.Background()); err != nil { log.Fatalf("failed creating schema resources: %v", err) } } sqlite3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package main import ( "context" "log" "<project>/ent" _ "github.com/mattn/go-sqlite3" ) func main() { client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") if err != nil { log.Fatalf("failed opening connection to sqlite: %v", err) } defer client.Close() // 运行自动迁移工具。 if err := client.Schema.Create(context.Background()); err != nil { log.Fatalf("failed creating schema resources: %v", err) } } mysql 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package main import ( "context" "log" "<project>/ent" _ "github.com/go-sql-driver/mysql" ) func main() { client, err := ent.Open("mysql", "<user>:<pass>@tcp(<host>:<port>)/<database>?parseTime=True") if err != nil { log.Fatalf("failed opening connection to mysql: %v", err) } defer client.Close() // 运行自动迁移工具。 if err := client.Schema.Create(context.Background()); err != nil { log.Fatalf("failed creating schema resources: %v", err) } } 创建实体 1 2 3 4 5 6 7 8 9 10 11 12 func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) { u, err := client.User. Create(). SetAge(30). SetName("a8m"). Save(ctx) if err != nil { return nil, fmt.Errorf("failed creating user: %w", err) } log.Println("user was created: ", u) return u, nil } 查询实体 1 2 3 4 5 6 7 8 9 10 11 12 13 func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) { u, err := client.User. Query(). Where(user.Name("a8m")). // `Only` 如果没有发现用户则报错, // 否则正常返回。 Only(ctx) if err != nil { return nil, fmt.Errorf("failed querying user: %w", err) } log.Println("user returned: ", u) return u, nil }

2024-11-13 · 2 分钟 · Nebula

NATS

NATS-Server 基于docker compose部署 1 2 3 4 5 6 7 8 services: nats-main: image: nats:latest ports: - "4222:4222" - "6222:6222" - "8222:8222" restart: unless-stopped Go-NATS 引入依赖 1 go get github.com/nats-io/nats.go 发布订阅模式 在此模式下,发送者发送消息后,所有在线订阅者都能接收到消息 sub.go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package main import "github.com/nats-io/nats.go" func main() { conn, err := nats.Connect("nats://ip:4222") if err != nil { return } for i := 1; i <= 2; i++ { dummy := i conn.Subscribe("hello", func(msg *nats.Msg) { fmt.Printf("消费者[%d]收到:%s\n", dummy, string(msg.Data)) }) } select {} } pub.go ...

2024-09-26 · 4 分钟 · Nebula

Air

引入 1 go install github.com/air-verse/air@latest 启动 1 2 3 air init # 初始化配置文件 air -c .air.toml # 启动服务并指定配置文件 air # 使用默认配置启动服务 运行时参数 可以在命令后面添加参数来构建二进制文件 1 2 3 4 5 # Will run ./tmp/main bench air bench # Will run ./tmp/main server --port 8080 air server --port 8080 也可用通过--来分割参数 1 2 3 4 5 # Will run ./tmp/main -h air -- -h # Will run air with custom config and pass -h argument to the built binary air -c .air.toml -- -h 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 # [Air](https://github.com/air-verse/air) 的配置文件,格式为 TOML # 工作目录 # 当前目录 . 或绝对路径,请注意后续的目录必须在根目录下。 root = "." tmp_dir = "tmp" # 临时文件目录 [build] # 在每次构建之前运行的命令数组 pre_cmd = ["echo 'hello air' > pre_cmd.txt"] # 构建前执行的命令 # 普通的 shell 命令。你也可以使用 `make`。 cmd = "go build -o ./tmp/main ." # 构建 Go 项目生成二进制文件的命令 # 在 ^C (中断) 之后运行的命令数组 post_cmd = ["echo 'hello air' > post_cmd.txt"] # 构建后执行的命令 # 由 `cmd` 命令生成的二进制文件。 bin = "tmp/main" # 二进制文件路径 # 自定义二进制文件,可以在运行应用程序时设置环境变量。 full_bin = "APP_ENV=dev APP_USER=air ./tmp/main" # 完整运行二进制文件的命令,包含环境变量 # 在运行二进制文件(bin/full_bin)时添加额外的参数,将会运行 './tmp/main hello world'。 args_bin = ["hello", "world"] # 传递给二进制文件的参数 # 监听这些文件扩展名的变化。 include_ext = ["go", "tpl", "tmpl", "html"] # 监听的文件扩展名 # 忽略这些扩展名的文件或目录。 exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"] # 忽略的目录 # 如果你指定了,监听这些目录。 include_dir = [] # 监听的目录 # 监听这些文件。 include_file = [] # 监听的文件 # 忽略这些文件。 exclude_file = [] # 忽略的文件 # 排除特定的正则表达式。 exclude_regex = ["_test\\.go"] # 忽略匹配正则表达式的文件 # 排除未更改的文件。 exclude_unchanged = true # 忽略未更改的文件 # 是否跟随符号链接 follow_symlink = true # 跟随符号链接 # 这个日志文件会放在你的 tmp_dir 中。 log = "air.log" # 日志文件 # 使用轮询文件变化而不是 fsnotify。 poll = false # 是否轮询文件 # 轮询间隔(默认为 500ms)。 poll_interval = 500 # 轮询间隔(毫秒) # 如果文件变更频繁,不需要每次都触发构建。 delay = 0 # 构建延迟(毫秒) # 当构建错误时停止运行旧的二进制文件。 stop_on_error = true # 构建出错时是否停止旧二进制文件 # 在终止进程前发送中断信号(Windows 不支持此功能) send_interrupt = false # 是否发送中断信号 # 发送中断信号后的延迟 kill_delay = 500 # 中断延迟(纳秒) # 是否重新运行二进制文件 rerun = false # 是否重新运行二进制文件 # 每次执行后的延迟 rerun_delay = 500 # 执行后的延迟(毫秒) [log] # 显示日志时间 time = false # 是否显示日志时间 # 只显示主日志(静默 watcher、构建、运行器的日志) main_only = false # 是否只显示主日志 [color] # 自定义每个部分的颜色。如果没有找到颜色,则使用应用程序的原始日志颜色。 main = "magenta" # 主日志颜色 watcher = "cyan" # 文件监听器的日志颜色 build = "yellow" # 构建器的日志颜色 runner = "green" # 运行器的日志颜色 [misc] # 退出时删除 tmp 目录 clean_on_exit = true # 退出时是否删除临时目录 [screen] clear_on_rebuild = true # 构建后是否清屏 keep_scroll = true # 保持滚动 # 启用浏览器的实时重载。 [proxy] enabled = true # 是否启用代理 proxy_port = 8090 # 代理端口 app_port = 8080 # 应用程序端口 基于Docker构建镜像 1 2 3 4 5 6 7 8 9 10 11 12 13 services: my-project-with-air: image: cosmtrek/air # working_dir value has to be the same of mapped volume working_dir: /project-package ports: - <any>:<any> environment: - ENV_A=${ENV_A} - ENV_B=${ENV_B} - ENV_C=${ENV_C} volumes: - ./project-relative-path/:/project-package/

2024-09-14 · 3 分钟 · Nebula

Go-JWT

基本结构 Header(头部) Hedaer 部分用于描述该 JWT 的基本信息,比如其类型(通常是 JWT)以及所使用的签名算法(如 HMAC SHA256 或 RSA)。 Payload(负载) Payload 部分包含所传递的声明。声明是关于实体(通常是用户)和其他数据的语句。声明可以分为三种类型:注册声明、公共声明 和 私有声明。 注册声明 这些声明是预定义的,非必须使用的但被推荐使用。官方标准定义的注册声明有 7 个 Claim(声明) 含义 iss(Issuer) 发行者,标识 JWT 的发行者。 sub(Subject) 主题,标识 JWT 的主题,通常指用户的唯一标识 aud(Audience) 观众,标识 JWT 的接收者 exp(Expiration Time) 过期时间。标识 JWT 的过期时间,这个时间必须是将来的 nbf(Not Before) 不可用时间。在此时间之前,JWT 不应被接受处理 iat(Issued At) 发行时间,标识 JWT 的发行时间 jti(JWT ID) JWT 的唯一标识符,用于防止 JWT 被重放(即重复使用) 公共声明:可以由使用 JWT 的人自定义,但为了避免冲突,任何新定义的声明都应已在 IANA JSON Web Token Registry 中注册或者是一个公共名称,其中包含了碰撞防抗性名称(Collision-Resistant Name)。 私有声明:发行和使用 JWT 的双方共同商定的声明,区别于 注册声明 和 公共声明。 Signature(签名) 为了防止数据篡改,将头部和负载的信息进行一定算法处理,加上一个密钥,最后生成签名。如果使用的是 HMAC SHA256 算法,那么签名就是将编码后的头部、编码后的负载拼接起来,通过密钥进行 HMAC SHA256 运算后的结果。 ...

2024-09-14 · 3 分钟 · Nebula

Casbin

引入

2024-09-12 · 1 分钟 · Nebula