mcpField
E
encore-go-infrastructure

encore-go-infrastructure

github.com/encoredev云与 DevOps30211 次安装2 天前更新
代码仓库

关于 encore-go-infrastructure

用于在 Encore Go 应用中通过包级资源声明基础设施的技能,涵盖数据库、Pub/Sub、定时任务、对象存储、密钥和配置。

主要功能

  1. 01包级基础设施声明
  2. 02PostgreSQL 数据库与迁移
  3. 03Pub/Sub 主题与订阅
  4. 04定时任务调度
  5. 05对象存储与签名 URL
  6. 06密钥与配置值

适用场景

  1. 01声明服务数据库和迁移
  2. 02发布和接收事件
  3. 03安排定期任务
  4. 04管理文件存储与签名访问

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/encoredev/skills --skill encore-go-infrastructure

安装后在 Claude Code 中输入/encore-go-infrastructure确认已加载。

详细介绍

Encore Go 基础设施声明

说明

Encore Go 使用声明式基础设施——将资源定义为包级变量,Encore 会负责配置:

  • 本地(encore run)——Encore 在 Docker 中运行基础设施(Postgres、Redis 等)
  • 生产环境——通过 Encore Cloud 部署到你的 AWS/GCP,或使用生成的基础设施配置自行托管
重要规则

所有基础设施都必须在包级别声明,不能在函数内部声明。

数据库(PostgreSQL)

package user

import "encore.dev/storage/sqldb"

// CORRECT: Package level
var db = sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{
    Migrations: "./migrations",
})

// WRONG: Inside function
func setup() {
    db := sqldb.NewDatabase("userdb", sqldb.DatabaseConfig{...})
}
迁移

在 migrations/ 目录中创建迁移:

user/
├── user.go
├── db.go
└── migrations/
    ├── 1_create_users.up.sql
    └── 2_add_email_index.up.sql

迁移命名格式:{number}_{description}.up.sql

Pub/Sub

主题
package events

import "encore.dev/pubsub"

type OrderCreatedEvent struct {
    OrderID string `json:"order_id"`
    UserID  string `json:"user_id"`
    Total   int    `json:"total"`
}

// Package level declaration
var OrderCreated = pubsub.NewTopic[*OrderCreatedEvent]("order-created", pubsub.TopicConfig{
    DeliveryGuarantee: pubsub.AtLeastOnce,
})
发布
msgID, err := events.OrderCreated.Publish(ctx, &events.OrderCreatedEvent{
    OrderID: "123",
    UserID:  "user-456",
    Total:   9999,
})
订阅
package notifications

import (
    "context"
    "myapp/events"
    "encore.dev/pubsub"
)

var _ = pubsub.NewSubscription(events.OrderCreated, "send-confirmation-email",
    pubsub.SubscriptionConfig[*events.OrderCreatedEvent]{
        Handler: sendConfirmationEmail,
    },
)

func sendConfirmationEmail(ctx context.Context, event *events.OrderCreatedEvent) error {
    // Send email...
    return nil
}
主题引用

向库代码传递主题访问权限,同时保留静态分析能力:

// Create a reference with publish permission
ref := pubsub.TopicRef[pubsub.Publisher[*OrderCreatedEvent]](OrderCreated)

// Use the reference in library code
func publishEvent(ref pubsub.Publisher[*OrderCreatedEvent], event *OrderCreatedEvent) error {
    _, err := ref.Publish(ctx, event)
    return err
}

定时任务

package cleanup

import (
    "context"
    "encore.dev/cron"
)

// The cron job declaration
var _ = cron.NewJob("cleanup-sessions", cron.JobConfig{
    Title:    "Clean up expired sessions",
    Schedule: "0 * * * *",  // Every hour
    Endpoint: CleanupExpiredSessions,
})

//encore:api private
func CleanupExpiredSessions(ctx context.Context) error {
    // Cleanup logic
    return nil
}
计划格式
格式示例说明
Cron 表达式"0 9 * * 1"每周一上午 9 点
固定间隔"every 1h"每小时
固定间隔"every 30m"每 30 分钟

对象存储

package uploads

import "encore.dev/storage/objects"

// Package level
var Uploads = objects.NewBucket("user-uploads", objects.BucketConfig{})

// Public bucket
var PublicAssets = objects.NewBucket("public-assets", objects.BucketConfig{
    Public: true,
})
操作
// Upload (streaming pattern)
writer := Uploads.Upload(ctx, "path/to/file.jpg")
_, err := io.Copy(writer, dataReader)
if err != nil {
    writer.Abort()
    return err
}
err = writer.Close()

// Download
reader := Uploads.Download(ctx, "path/to/file.jpg")
if err := reader.Err(); err != nil {
    return err
}
defer reader.Close()
data, _ := io.ReadAll(reader)

// Check existence
exists, err := Uploads.Exists(ctx, "path/to/file.jpg")

// Get attributes (size, content type, ETag)
attrs, err := Uploads.Attrs(ctx, "path/to/file.jpg")

// List objects
for err, entry := range Uploads.List(ctx, &objects.Query{}) {
    if err != nil {
        return err
    }
    fmt.Println(entry.Key, entry.Size)
}

// Delete
err := Uploads.Remove(ctx, "path/to/file.jpg")

// Public URL (only for public buckets)
url := PublicAssets.PublicURL("image.jpg")
签名 URL

生成临时 URL,以便上传/下载,而无需暴露你的存储桶:

import "time"

// Signed upload URL (expires in 2 hours)
url, err := Uploads.SignedUploadURL(ctx, "user-uploads/avatar.jpg",
    objects.WithTTL(time.Duration(7200)*time.Second))

// Signed download URL
url, err := Uploads.SignedDownloadURL(ctx, "documents/report.pdf",
    objects.WithTTL(time.Duration(7200)*time.Second))
存储桶引用

向库代码传递具有特定权限的存储桶访问权限:

// Create a reference with download permission only
ref := objects.BucketRef[objects.Downloader](Uploads)

// Create a reference with multiple permissions
type myPerms interface {
    objects.Downloader
    objects.Uploader
}
ref := objects.BucketRef[myPerms](Uploads)

// Permission types: Downloader, Uploader, Lister, Attrser, Remover,
// SignedDownloader, SignedUploader, ReadWriter

密钥

package email

var secrets struct {
    SendGridAPIKey string
    SMTPPassword   string
}

func sendEmail() error {
    apiKey := secrets.SendGridAPIKey
    // Use the secret...
}

通过 CLI 设置密钥:

encore secret set --type prod SendGridAPIKey

配置值

package myservice

import "encore.dev/config"

var cfg struct {
    MaxRetries config.Int
    BaseURL    config.String
    Debug      config.Bool
}

func doSomething() {
    if cfg.Debug() {
        log.Println("Debug mode enabled")
    }
}

指南

  • 基础设施声明必须位于包级别
  • 为资源使用描述性名称
  • 按顺序编号并保持迁移连续
  • 订阅处理程序必须具备幂等性(至少一次投递)
  • 通过将密钥作为函数调用来访问
  • 定时任务端点应为 private(仅限内部访问)
  • 每个服务通常都有自己的数据库

评论

支持 Markdown