mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
refactor: content-addressed assets, cross-channel multimodal, infra simplification (#63)
* refactor(attachment): multimodal attachment refactor with snapshot schema and storage layer - Add snapshot schema migration (0008) and update init/versions/snapshots - Add internal/attachment and internal/channel normalize for unified attachment handling - Move containerfs provider from internal/media to internal/storage - Update agent types, channel adapters (Telegram/Feishu), inbound and handlers - Add containerd snapshot lineage and local_channel tests - Regenerate sqlc, swagger and SDK * refactor(media): content-addressed asset system with unified naming - Replace asset_id foreign key with content_hash as sole identifier for bot_history_message_assets (pure soft-link model) - Remove mime, size_bytes, storage_key from DB; derive at read time via media.Resolve from actual storage - Merge migrations 0008/0009 into single 0008; keep 0001 as canonical schema - Add Docker initdb script for deterministic migration execution order - Fix cross-channel real-time image display (Telegram → WebUI SSE) - Fix message disappearing on refresh (null assets fallback) - Fix file icon instead of image preview (mime derivation from storage) - Unify AssetID → ContentHash naming across Go, Agent, and Frontend - Change storage key prefix from 4-char to 2-char for directory sharding - Add server-entrypoint.sh for Docker deployment migration handling * refactor(infra): embedded migrations, Docker simplification, and config consolidation - Embed SQL migrations into Go binary, removing shell-based migration scripts - Consolidate config files into conf/ directory (app.example.toml, app.docker.toml, app.dev.toml) - Simplify Docker setup: remove initdb.d scripts, streamline nginx config and entrypoint - Remove legacy CLI, feishu-echo commands, and obsolete incremental migration files - Update install script and docs to require sudo for one-click install - Add mise tasks for dev environment orchestration * chore: recover migrations --------- Co-authored-by: Acbox <acbox0328@gmail.com>
This commit is contained in:
+32
-275
@@ -11,110 +11,41 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createMediaAsset = `-- name: CreateMediaAsset :one
|
||||
INSERT INTO media_assets (
|
||||
bot_id, storage_provider_id, content_hash, media_type, mime,
|
||||
size_bytes, storage_key, original_name, width, height, duration_ms, metadata
|
||||
)
|
||||
const createMessageAsset = `-- name: CreateMessageAsset :one
|
||||
INSERT INTO bot_history_message_assets (message_id, role, ordinal, content_hash)
|
||||
VALUES (
|
||||
$1,
|
||||
$2::uuid,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8::text,
|
||||
$9::integer,
|
||||
$10::integer,
|
||||
$11::bigint,
|
||||
$12
|
||||
$4
|
||||
)
|
||||
ON CONFLICT (bot_id, content_hash) DO UPDATE SET
|
||||
bot_id = media_assets.bot_id
|
||||
RETURNING id, bot_id, storage_provider_id, content_hash, media_type, mime, size_bytes, storage_key, original_name, width, height, duration_ms, metadata, created_at
|
||||
`
|
||||
|
||||
type CreateMediaAssetParams struct {
|
||||
BotID pgtype.UUID `json:"bot_id"`
|
||||
StorageProviderID pgtype.UUID `json:"storage_provider_id"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
MediaType string `json:"media_type"`
|
||||
Mime string `json:"mime"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
StorageKey string `json:"storage_key"`
|
||||
OriginalName pgtype.Text `json:"original_name"`
|
||||
Width pgtype.Int4 `json:"width"`
|
||||
Height pgtype.Int4 `json:"height"`
|
||||
DurationMs pgtype.Int8 `json:"duration_ms"`
|
||||
Metadata []byte `json:"metadata"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateMediaAsset(ctx context.Context, arg CreateMediaAssetParams) (MediaAsset, error) {
|
||||
row := q.db.QueryRow(ctx, createMediaAsset,
|
||||
arg.BotID,
|
||||
arg.StorageProviderID,
|
||||
arg.ContentHash,
|
||||
arg.MediaType,
|
||||
arg.Mime,
|
||||
arg.SizeBytes,
|
||||
arg.StorageKey,
|
||||
arg.OriginalName,
|
||||
arg.Width,
|
||||
arg.Height,
|
||||
arg.DurationMs,
|
||||
arg.Metadata,
|
||||
)
|
||||
var i MediaAsset
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.StorageProviderID,
|
||||
&i.ContentHash,
|
||||
&i.MediaType,
|
||||
&i.Mime,
|
||||
&i.SizeBytes,
|
||||
&i.StorageKey,
|
||||
&i.OriginalName,
|
||||
&i.Width,
|
||||
&i.Height,
|
||||
&i.DurationMs,
|
||||
&i.Metadata,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createMessageAsset = `-- name: CreateMessageAsset :one
|
||||
INSERT INTO bot_history_message_assets (message_id, asset_id, role, ordinal)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (message_id, asset_id) DO UPDATE SET
|
||||
ON CONFLICT (message_id, content_hash) DO UPDATE SET
|
||||
role = EXCLUDED.role,
|
||||
ordinal = EXCLUDED.ordinal
|
||||
RETURNING id, message_id, asset_id, role, ordinal, created_at
|
||||
RETURNING id, message_id, role, ordinal, content_hash, created_at
|
||||
`
|
||||
|
||||
type CreateMessageAssetParams struct {
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
AssetID pgtype.UUID `json:"asset_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateMessageAsset(ctx context.Context, arg CreateMessageAssetParams) (BotHistoryMessageAsset, error) {
|
||||
row := q.db.QueryRow(ctx, createMessageAsset,
|
||||
arg.MessageID,
|
||||
arg.AssetID,
|
||||
arg.Role,
|
||||
arg.Ordinal,
|
||||
arg.ContentHash,
|
||||
)
|
||||
var i BotHistoryMessageAsset
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.MessageID,
|
||||
&i.AssetID,
|
||||
&i.Role,
|
||||
&i.Ordinal,
|
||||
&i.ContentHash,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
@@ -146,15 +77,6 @@ func (q *Queries) CreateStorageProvider(ctx context.Context, arg CreateStoragePr
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteMediaAsset = `-- name: DeleteMediaAsset :exec
|
||||
DELETE FROM media_assets WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteMediaAsset(ctx context.Context, id pgtype.UUID) error {
|
||||
_, err := q.db.Exec(ctx, deleteMediaAsset, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteMessageAssets = `-- name: DeleteMessageAssets :exec
|
||||
DELETE FROM bot_history_message_assets WHERE message_id = $1
|
||||
`
|
||||
@@ -182,64 +104,6 @@ func (q *Queries) GetBotStorageBinding(ctx context.Context, botID pgtype.UUID) (
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getMediaAssetByHash = `-- name: GetMediaAssetByHash :one
|
||||
SELECT id, bot_id, storage_provider_id, content_hash, media_type, mime, size_bytes, storage_key, original_name, width, height, duration_ms, metadata, created_at FROM media_assets
|
||||
WHERE bot_id = $1 AND content_hash = $2
|
||||
`
|
||||
|
||||
type GetMediaAssetByHashParams struct {
|
||||
BotID pgtype.UUID `json:"bot_id"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetMediaAssetByHash(ctx context.Context, arg GetMediaAssetByHashParams) (MediaAsset, error) {
|
||||
row := q.db.QueryRow(ctx, getMediaAssetByHash, arg.BotID, arg.ContentHash)
|
||||
var i MediaAsset
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.StorageProviderID,
|
||||
&i.ContentHash,
|
||||
&i.MediaType,
|
||||
&i.Mime,
|
||||
&i.SizeBytes,
|
||||
&i.StorageKey,
|
||||
&i.OriginalName,
|
||||
&i.Width,
|
||||
&i.Height,
|
||||
&i.DurationMs,
|
||||
&i.Metadata,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getMediaAssetByID = `-- name: GetMediaAssetByID :one
|
||||
SELECT id, bot_id, storage_provider_id, content_hash, media_type, mime, size_bytes, storage_key, original_name, width, height, duration_ms, metadata, created_at FROM media_assets WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetMediaAssetByID(ctx context.Context, id pgtype.UUID) (MediaAsset, error) {
|
||||
row := q.db.QueryRow(ctx, getMediaAssetByID, id)
|
||||
var i MediaAsset
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.StorageProviderID,
|
||||
&i.ContentHash,
|
||||
&i.MediaType,
|
||||
&i.Mime,
|
||||
&i.SizeBytes,
|
||||
&i.StorageKey,
|
||||
&i.OriginalName,
|
||||
&i.Width,
|
||||
&i.Height,
|
||||
&i.DurationMs,
|
||||
&i.Metadata,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getStorageProviderByID = `-- name: GetStorageProviderByID :one
|
||||
SELECT id, name, provider, config, created_at, updated_at FROM storage_providers WHERE id = $1
|
||||
`
|
||||
@@ -276,84 +140,19 @@ func (q *Queries) GetStorageProviderByName(ctx context.Context, name string) (St
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listMediaAssetsByBotID = `-- name: ListMediaAssetsByBotID :many
|
||||
SELECT id, bot_id, storage_provider_id, content_hash, media_type, mime, size_bytes, storage_key, original_name, width, height, duration_ms, metadata, created_at FROM media_assets
|
||||
WHERE bot_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListMediaAssetsByBotID(ctx context.Context, botID pgtype.UUID) ([]MediaAsset, error) {
|
||||
rows, err := q.db.Query(ctx, listMediaAssetsByBotID, botID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []MediaAsset
|
||||
for rows.Next() {
|
||||
var i MediaAsset
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.StorageProviderID,
|
||||
&i.ContentHash,
|
||||
&i.MediaType,
|
||||
&i.Mime,
|
||||
&i.SizeBytes,
|
||||
&i.StorageKey,
|
||||
&i.OriginalName,
|
||||
&i.Width,
|
||||
&i.Height,
|
||||
&i.DurationMs,
|
||||
&i.Metadata,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listMessageAssets = `-- name: ListMessageAssets :many
|
||||
SELECT
|
||||
ma.id AS rel_id,
|
||||
ma.message_id,
|
||||
ma.asset_id,
|
||||
ma.role,
|
||||
ma.ordinal,
|
||||
a.media_type,
|
||||
a.mime,
|
||||
a.size_bytes,
|
||||
a.storage_key,
|
||||
a.original_name,
|
||||
a.width,
|
||||
a.height,
|
||||
a.duration_ms,
|
||||
a.metadata AS asset_metadata
|
||||
FROM bot_history_message_assets ma
|
||||
JOIN media_assets a ON a.id = ma.asset_id
|
||||
WHERE ma.message_id = $1
|
||||
ORDER BY ma.ordinal ASC
|
||||
SELECT id AS rel_id, message_id, role, ordinal, content_hash
|
||||
FROM bot_history_message_assets
|
||||
WHERE message_id = $1
|
||||
ORDER BY ordinal ASC
|
||||
`
|
||||
|
||||
type ListMessageAssetsRow struct {
|
||||
RelID pgtype.UUID `json:"rel_id"`
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
AssetID pgtype.UUID `json:"asset_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
MediaType string `json:"media_type"`
|
||||
Mime string `json:"mime"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
StorageKey string `json:"storage_key"`
|
||||
OriginalName pgtype.Text `json:"original_name"`
|
||||
Width pgtype.Int4 `json:"width"`
|
||||
Height pgtype.Int4 `json:"height"`
|
||||
DurationMs pgtype.Int8 `json:"duration_ms"`
|
||||
AssetMetadata []byte `json:"asset_metadata"`
|
||||
RelID pgtype.UUID `json:"rel_id"`
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListMessageAssets(ctx context.Context, messageID pgtype.UUID) ([]ListMessageAssetsRow, error) {
|
||||
@@ -368,18 +167,9 @@ func (q *Queries) ListMessageAssets(ctx context.Context, messageID pgtype.UUID)
|
||||
if err := rows.Scan(
|
||||
&i.RelID,
|
||||
&i.MessageID,
|
||||
&i.AssetID,
|
||||
&i.Role,
|
||||
&i.Ordinal,
|
||||
&i.MediaType,
|
||||
&i.Mime,
|
||||
&i.SizeBytes,
|
||||
&i.StorageKey,
|
||||
&i.OriginalName,
|
||||
&i.Width,
|
||||
&i.Height,
|
||||
&i.DurationMs,
|
||||
&i.AssetMetadata,
|
||||
&i.ContentHash,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -392,42 +182,18 @@ func (q *Queries) ListMessageAssets(ctx context.Context, messageID pgtype.UUID)
|
||||
}
|
||||
|
||||
const listMessageAssetsBatch = `-- name: ListMessageAssetsBatch :many
|
||||
SELECT
|
||||
ma.id AS rel_id,
|
||||
ma.message_id,
|
||||
ma.asset_id,
|
||||
ma.role,
|
||||
ma.ordinal,
|
||||
a.media_type,
|
||||
a.mime,
|
||||
a.size_bytes,
|
||||
a.storage_key,
|
||||
a.original_name,
|
||||
a.width,
|
||||
a.height,
|
||||
a.duration_ms,
|
||||
a.metadata AS asset_metadata
|
||||
FROM bot_history_message_assets ma
|
||||
JOIN media_assets a ON a.id = ma.asset_id
|
||||
WHERE ma.message_id = ANY($1::uuid[])
|
||||
ORDER BY ma.message_id, ma.ordinal ASC
|
||||
SELECT id AS rel_id, message_id, role, ordinal, content_hash
|
||||
FROM bot_history_message_assets
|
||||
WHERE message_id = ANY($1::uuid[])
|
||||
ORDER BY message_id, ordinal ASC
|
||||
`
|
||||
|
||||
type ListMessageAssetsBatchRow struct {
|
||||
RelID pgtype.UUID `json:"rel_id"`
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
AssetID pgtype.UUID `json:"asset_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
MediaType string `json:"media_type"`
|
||||
Mime string `json:"mime"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
StorageKey string `json:"storage_key"`
|
||||
OriginalName pgtype.Text `json:"original_name"`
|
||||
Width pgtype.Int4 `json:"width"`
|
||||
Height pgtype.Int4 `json:"height"`
|
||||
DurationMs pgtype.Int8 `json:"duration_ms"`
|
||||
AssetMetadata []byte `json:"asset_metadata"`
|
||||
RelID pgtype.UUID `json:"rel_id"`
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListMessageAssetsBatch(ctx context.Context, messageIds []pgtype.UUID) ([]ListMessageAssetsBatchRow, error) {
|
||||
@@ -442,18 +208,9 @@ func (q *Queries) ListMessageAssetsBatch(ctx context.Context, messageIds []pgtyp
|
||||
if err := rows.Scan(
|
||||
&i.RelID,
|
||||
&i.MessageID,
|
||||
&i.AssetID,
|
||||
&i.Role,
|
||||
&i.Ordinal,
|
||||
&i.MediaType,
|
||||
&i.Mime,
|
||||
&i.SizeBytes,
|
||||
&i.StorageKey,
|
||||
&i.OriginalName,
|
||||
&i.Width,
|
||||
&i.Height,
|
||||
&i.DurationMs,
|
||||
&i.AssetMetadata,
|
||||
&i.ContentHash,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ SELECT
|
||||
m.bot_id,
|
||||
m.route_id,
|
||||
m.sender_channel_identity_id,
|
||||
m.sender_account_user_id AS sender_user_id,
|
||||
COALESCE(ci.user_id, m.sender_account_user_id) AS sender_user_id,
|
||||
m.channel_type AS platform,
|
||||
m.source_message_id AS external_message_id,
|
||||
m.source_reply_to_message_id,
|
||||
@@ -211,7 +211,7 @@ SELECT
|
||||
m.bot_id,
|
||||
m.route_id,
|
||||
m.sender_channel_identity_id,
|
||||
m.sender_account_user_id AS sender_user_id,
|
||||
COALESCE(ci.user_id, m.sender_account_user_id) AS sender_user_id,
|
||||
m.channel_type AS platform,
|
||||
m.source_message_id AS external_message_id,
|
||||
m.source_reply_to_message_id,
|
||||
@@ -296,7 +296,7 @@ SELECT
|
||||
m.bot_id,
|
||||
m.route_id,
|
||||
m.sender_channel_identity_id,
|
||||
m.sender_account_user_id AS sender_user_id,
|
||||
COALESCE(ci.user_id, m.sender_account_user_id) AS sender_user_id,
|
||||
m.channel_type AS platform,
|
||||
m.source_message_id AS external_message_id,
|
||||
m.source_reply_to_message_id,
|
||||
@@ -379,7 +379,7 @@ SELECT
|
||||
m.bot_id,
|
||||
m.route_id,
|
||||
m.sender_channel_identity_id,
|
||||
m.sender_account_user_id AS sender_user_id,
|
||||
COALESCE(ci.user_id, m.sender_account_user_id) AS sender_user_id,
|
||||
m.channel_type AS platform,
|
||||
m.source_message_id AS external_message_id,
|
||||
m.source_reply_to_message_id,
|
||||
|
||||
+15
-31
@@ -75,12 +75,12 @@ type BotHistoryMessage struct {
|
||||
}
|
||||
|
||||
type BotHistoryMessageAsset struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
AssetID pgtype.UUID `json:"asset_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
MessageID pgtype.UUID `json:"message_id"`
|
||||
Role string `json:"role"`
|
||||
Ordinal int32 `json:"ordinal"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type BotMember struct {
|
||||
@@ -150,9 +150,9 @@ type Container struct {
|
||||
}
|
||||
|
||||
type ContainerVersion struct {
|
||||
ID string `json:"id"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ContainerID string `json:"container_id"`
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
SnapshotID pgtype.UUID `json:"snapshot_id"`
|
||||
Version int32 `json:"version"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
@@ -186,23 +186,6 @@ type McpConnection struct {
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
type MediaAsset struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
BotID pgtype.UUID `json:"bot_id"`
|
||||
StorageProviderID pgtype.UUID `json:"storage_provider_id"`
|
||||
ContentHash string `json:"content_hash"`
|
||||
MediaType string `json:"media_type"`
|
||||
Mime string `json:"mime"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
StorageKey string `json:"storage_key"`
|
||||
OriginalName pgtype.Text `json:"original_name"`
|
||||
Width pgtype.Int4 `json:"width"`
|
||||
Height pgtype.Int4 `json:"height"`
|
||||
DurationMs pgtype.Int8 `json:"duration_ms"`
|
||||
Metadata []byte `json:"metadata"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ModelID string `json:"model_id"`
|
||||
@@ -250,12 +233,13 @@ type SearchProvider struct {
|
||||
}
|
||||
|
||||
type Snapshot struct {
|
||||
ID string `json:"id"`
|
||||
ContainerID string `json:"container_id"`
|
||||
ParentSnapshotID pgtype.Text `json:"parent_snapshot_id"`
|
||||
Snapshotter string `json:"snapshotter"`
|
||||
Digest pgtype.Text `json:"digest"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ContainerID string `json:"container_id"`
|
||||
RuntimeSnapshotName string `json:"runtime_snapshot_name"`
|
||||
ParentRuntimeSnapshotName pgtype.Text `json:"parent_runtime_snapshot_name"`
|
||||
Snapshotter string `json:"snapshotter"`
|
||||
Source string `json:"source"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type StorageProvider struct {
|
||||
|
||||
@@ -11,8 +11,147 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const insertSnapshot = `-- name: InsertSnapshot :exec
|
||||
INSERT INTO snapshots (id, container_id, parent_snapshot_id, snapshotter, digest)
|
||||
const getSnapshotByContainerAndRuntimeName = `-- name: GetSnapshotByContainerAndRuntimeName :one
|
||||
SELECT
|
||||
id,
|
||||
container_id,
|
||||
runtime_snapshot_name,
|
||||
parent_runtime_snapshot_name,
|
||||
snapshotter,
|
||||
source,
|
||||
created_at
|
||||
FROM snapshots
|
||||
WHERE container_id = $1
|
||||
AND runtime_snapshot_name = $2
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetSnapshotByContainerAndRuntimeNameParams struct {
|
||||
ContainerID string `json:"container_id"`
|
||||
RuntimeSnapshotName string `json:"runtime_snapshot_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetSnapshotByContainerAndRuntimeName(ctx context.Context, arg GetSnapshotByContainerAndRuntimeNameParams) (Snapshot, error) {
|
||||
row := q.db.QueryRow(ctx, getSnapshotByContainerAndRuntimeName, arg.ContainerID, arg.RuntimeSnapshotName)
|
||||
var i Snapshot
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ContainerID,
|
||||
&i.RuntimeSnapshotName,
|
||||
&i.ParentRuntimeSnapshotName,
|
||||
&i.Snapshotter,
|
||||
&i.Source,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listSnapshotsByContainerID = `-- name: ListSnapshotsByContainerID :many
|
||||
SELECT
|
||||
id,
|
||||
container_id,
|
||||
runtime_snapshot_name,
|
||||
parent_runtime_snapshot_name,
|
||||
snapshotter,
|
||||
source,
|
||||
created_at
|
||||
FROM snapshots
|
||||
WHERE container_id = $1
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListSnapshotsByContainerID(ctx context.Context, containerID string) ([]Snapshot, error) {
|
||||
rows, err := q.db.Query(ctx, listSnapshotsByContainerID, containerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Snapshot
|
||||
for rows.Next() {
|
||||
var i Snapshot
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ContainerID,
|
||||
&i.RuntimeSnapshotName,
|
||||
&i.ParentRuntimeSnapshotName,
|
||||
&i.Snapshotter,
|
||||
&i.Source,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listSnapshotsWithVersionByContainerID = `-- name: ListSnapshotsWithVersionByContainerID :many
|
||||
SELECT
|
||||
s.id,
|
||||
s.container_id,
|
||||
s.runtime_snapshot_name,
|
||||
s.parent_runtime_snapshot_name,
|
||||
s.snapshotter,
|
||||
s.source,
|
||||
s.created_at,
|
||||
cv.version
|
||||
FROM snapshots s
|
||||
LEFT JOIN container_versions cv ON cv.snapshot_id = s.id
|
||||
WHERE s.container_id = $1
|
||||
ORDER BY s.created_at DESC
|
||||
`
|
||||
|
||||
type ListSnapshotsWithVersionByContainerIDRow struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ContainerID string `json:"container_id"`
|
||||
RuntimeSnapshotName string `json:"runtime_snapshot_name"`
|
||||
ParentRuntimeSnapshotName pgtype.Text `json:"parent_runtime_snapshot_name"`
|
||||
Snapshotter string `json:"snapshotter"`
|
||||
Source string `json:"source"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
Version pgtype.Int4 `json:"version"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListSnapshotsWithVersionByContainerID(ctx context.Context, containerID string) ([]ListSnapshotsWithVersionByContainerIDRow, error) {
|
||||
rows, err := q.db.Query(ctx, listSnapshotsWithVersionByContainerID, containerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ListSnapshotsWithVersionByContainerIDRow
|
||||
for rows.Next() {
|
||||
var i ListSnapshotsWithVersionByContainerIDRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ContainerID,
|
||||
&i.RuntimeSnapshotName,
|
||||
&i.ParentRuntimeSnapshotName,
|
||||
&i.Snapshotter,
|
||||
&i.Source,
|
||||
&i.CreatedAt,
|
||||
&i.Version,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const upsertSnapshot = `-- name: UpsertSnapshot :one
|
||||
INSERT INTO snapshots (
|
||||
container_id,
|
||||
runtime_snapshot_name,
|
||||
parent_runtime_snapshot_name,
|
||||
snapshotter,
|
||||
source
|
||||
)
|
||||
VALUES (
|
||||
$1,
|
||||
$2,
|
||||
@@ -20,24 +159,39 @@ VALUES (
|
||||
$4,
|
||||
$5
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
ON CONFLICT (container_id, runtime_snapshot_name) DO UPDATE
|
||||
SET
|
||||
parent_runtime_snapshot_name = EXCLUDED.parent_runtime_snapshot_name,
|
||||
snapshotter = EXCLUDED.snapshotter,
|
||||
source = EXCLUDED.source
|
||||
RETURNING id, container_id, runtime_snapshot_name, parent_runtime_snapshot_name, snapshotter, source, created_at
|
||||
`
|
||||
|
||||
type InsertSnapshotParams struct {
|
||||
ID string `json:"id"`
|
||||
ContainerID string `json:"container_id"`
|
||||
ParentSnapshotID pgtype.Text `json:"parent_snapshot_id"`
|
||||
Snapshotter string `json:"snapshotter"`
|
||||
Digest pgtype.Text `json:"digest"`
|
||||
type UpsertSnapshotParams struct {
|
||||
ContainerID string `json:"container_id"`
|
||||
RuntimeSnapshotName string `json:"runtime_snapshot_name"`
|
||||
ParentRuntimeSnapshotName pgtype.Text `json:"parent_runtime_snapshot_name"`
|
||||
Snapshotter string `json:"snapshotter"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
func (q *Queries) InsertSnapshot(ctx context.Context, arg InsertSnapshotParams) error {
|
||||
_, err := q.db.Exec(ctx, insertSnapshot,
|
||||
arg.ID,
|
||||
func (q *Queries) UpsertSnapshot(ctx context.Context, arg UpsertSnapshotParams) (Snapshot, error) {
|
||||
row := q.db.QueryRow(ctx, upsertSnapshot,
|
||||
arg.ContainerID,
|
||||
arg.ParentSnapshotID,
|
||||
arg.RuntimeSnapshotName,
|
||||
arg.ParentRuntimeSnapshotName,
|
||||
arg.Snapshotter,
|
||||
arg.Digest,
|
||||
arg.Source,
|
||||
)
|
||||
return err
|
||||
var i Snapshot
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ContainerID,
|
||||
&i.RuntimeSnapshotName,
|
||||
&i.ParentRuntimeSnapshotName,
|
||||
&i.Snapshotter,
|
||||
&i.Source,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -7,49 +7,48 @@ package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const getVersionSnapshotID = `-- name: GetVersionSnapshotID :one
|
||||
SELECT snapshot_id FROM container_versions WHERE container_id = $1 AND version = $2
|
||||
const getVersionSnapshotRuntimeName = `-- name: GetVersionSnapshotRuntimeName :one
|
||||
SELECT s.runtime_snapshot_name
|
||||
FROM container_versions cv
|
||||
JOIN snapshots s ON s.id = cv.snapshot_id
|
||||
WHERE cv.container_id = $1
|
||||
AND cv.version = $2
|
||||
`
|
||||
|
||||
type GetVersionSnapshotIDParams struct {
|
||||
type GetVersionSnapshotRuntimeNameParams struct {
|
||||
ContainerID string `json:"container_id"`
|
||||
Version int32 `json:"version"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetVersionSnapshotID(ctx context.Context, arg GetVersionSnapshotIDParams) (string, error) {
|
||||
row := q.db.QueryRow(ctx, getVersionSnapshotID, arg.ContainerID, arg.Version)
|
||||
var snapshot_id string
|
||||
err := row.Scan(&snapshot_id)
|
||||
return snapshot_id, err
|
||||
func (q *Queries) GetVersionSnapshotRuntimeName(ctx context.Context, arg GetVersionSnapshotRuntimeNameParams) (string, error) {
|
||||
row := q.db.QueryRow(ctx, getVersionSnapshotRuntimeName, arg.ContainerID, arg.Version)
|
||||
var runtime_snapshot_name string
|
||||
err := row.Scan(&runtime_snapshot_name)
|
||||
return runtime_snapshot_name, err
|
||||
}
|
||||
|
||||
const insertVersion = `-- name: InsertVersion :one
|
||||
INSERT INTO container_versions (id, container_id, snapshot_id, version)
|
||||
INSERT INTO container_versions (container_id, snapshot_id, version)
|
||||
VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4
|
||||
$3
|
||||
)
|
||||
RETURNING id, container_id, snapshot_id, version, created_at
|
||||
`
|
||||
|
||||
type InsertVersionParams struct {
|
||||
ID string `json:"id"`
|
||||
ContainerID string `json:"container_id"`
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
Version int32 `json:"version"`
|
||||
ContainerID string `json:"container_id"`
|
||||
SnapshotID pgtype.UUID `json:"snapshot_id"`
|
||||
Version int32 `json:"version"`
|
||||
}
|
||||
|
||||
func (q *Queries) InsertVersion(ctx context.Context, arg InsertVersionParams) (ContainerVersion, error) {
|
||||
row := q.db.QueryRow(ctx, insertVersion,
|
||||
arg.ID,
|
||||
arg.ContainerID,
|
||||
arg.SnapshotID,
|
||||
arg.Version,
|
||||
)
|
||||
row := q.db.QueryRow(ctx, insertVersion, arg.ContainerID, arg.SnapshotID, arg.Version)
|
||||
var i ContainerVersion
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
@@ -62,24 +61,44 @@ func (q *Queries) InsertVersion(ctx context.Context, arg InsertVersionParams) (C
|
||||
}
|
||||
|
||||
const listVersionsByContainerID = `-- name: ListVersionsByContainerID :many
|
||||
SELECT id, container_id, snapshot_id, version, created_at FROM container_versions WHERE container_id = $1 ORDER BY version ASC
|
||||
SELECT
|
||||
cv.id,
|
||||
cv.container_id,
|
||||
cv.snapshot_id,
|
||||
cv.version,
|
||||
cv.created_at,
|
||||
s.runtime_snapshot_name
|
||||
FROM container_versions cv
|
||||
JOIN snapshots s ON s.id = cv.snapshot_id
|
||||
WHERE cv.container_id = $1
|
||||
ORDER BY cv.version ASC
|
||||
`
|
||||
|
||||
func (q *Queries) ListVersionsByContainerID(ctx context.Context, containerID string) ([]ContainerVersion, error) {
|
||||
type ListVersionsByContainerIDRow struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ContainerID string `json:"container_id"`
|
||||
SnapshotID pgtype.UUID `json:"snapshot_id"`
|
||||
Version int32 `json:"version"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
RuntimeSnapshotName string `json:"runtime_snapshot_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListVersionsByContainerID(ctx context.Context, containerID string) ([]ListVersionsByContainerIDRow, error) {
|
||||
rows, err := q.db.Query(ctx, listVersionsByContainerID, containerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ContainerVersion
|
||||
var items []ListVersionsByContainerIDRow
|
||||
for rows.Next() {
|
||||
var i ContainerVersion
|
||||
var i ListVersionsByContainerIDRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.ContainerID,
|
||||
&i.SnapshotID,
|
||||
&i.Version,
|
||||
&i.CreatedAt,
|
||||
&i.RuntimeSnapshotName,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user