Greenfield Go multi-tenant IPFS Pinning Service wire-compatible with the
IPFS Pinning Services API spec. Paired 1:1 with Kubo over localhost RPC,
clustered via embedded NATS JetStream, Postgres source-of-truth with
RLS-enforced tenancy, Fiber + huma v2 for the HTTP surface, Authentik
OIDC for session login with kid-rotated HS256 JWT API tokens.
Feature-complete against the 22-milestone build plan, including the
ship-it v1.0 gap items:
* admin CLIs: drain/uncordon, maintenance, mint-token, rotate-key,
prune-denylist, rebalance --dry-run, cache-stats, cluster-presences
* TTL leader election via NATS KV, fence tokens, JetStream dedup
* rebalancer (plan/apply split), reconciler, requeue sweeper
* ristretto caches with NATS-backed cross-node invalidation
(placements live-nodes + token denylist)
* maintenance watchdog for stuck cluster-pause flag
* Prometheus /metrics with CIDR ACL, HTTP/pin/scheduler/cache gauges
* rate limiting: session (10/min) + anonymous global (120/min)
* integration tests: rebalance, refcount multi-org, RLS belt
* goreleaser (tar + deb/rpm/apk + Alpine Docker) targeting Gitea
Stack: Cobra/Viper, Fiber v2 + huma v2, embedded NATS JetStream,
pgx/sqlc/golang-migrate, ristretto, TypeID, prometheus/client_golang,
testcontainers-go.
123 lines
4.0 KiB
SQL
123 lines
4.0 KiB
SQL
-- name: CreatePin :one
|
|
INSERT INTO pins (request_id, org_id, cid, name, meta, origins, status)
|
|
VALUES ($1, $2, $3, $4, $5, $6, 'queued')
|
|
RETURNING *;
|
|
|
|
-- name: GetPin :one
|
|
SELECT * FROM pins WHERE request_id = $1 AND org_id = $2;
|
|
|
|
-- name: GetExistingLivePinByCID :one
|
|
SELECT * FROM pins
|
|
WHERE org_id = $1 AND cid = $2 AND status <> 'failed'
|
|
LIMIT 1;
|
|
|
|
-- name: UpdatePinStatus :exec
|
|
UPDATE pins SET status = $2, failure_reason = $3 WHERE request_id = $1;
|
|
|
|
-- name: DeletePin :exec
|
|
DELETE FROM pins WHERE request_id = $1 AND org_id = $2;
|
|
|
|
-- name: ListPins :many
|
|
SELECT * FROM pins
|
|
WHERE org_id = $1
|
|
ORDER BY created DESC
|
|
LIMIT $2 OFFSET $3;
|
|
|
|
-- FilterPins honors the full IPFS Pinning API spec filter surface.
|
|
--
|
|
-- All filters are optional; empty arrays / NULL values short-circuit to
|
|
-- "no restriction" via the IS NULL / array-length guards. Name matching
|
|
-- supports four modes through a single `match` discriminator:
|
|
--
|
|
-- 'exact' — LIKE (case-sensitive, no wildcards)
|
|
-- 'iexact' — ILIKE anchored both ends
|
|
-- 'partial' — LIKE %name%
|
|
-- 'ipartial' — ILIKE %name% (uses pg_trgm GIN)
|
|
--
|
|
-- `meta` is treated as a jsonb "contains" check — the pin's meta must
|
|
-- be a superset of the requested map.
|
|
--
|
|
-- name: FilterPins :many
|
|
SELECT *
|
|
FROM pins
|
|
WHERE org_id = $1
|
|
AND (COALESCE(array_length(sqlc.arg(cids)::text[], 1), 0) = 0
|
|
OR cid = ANY(sqlc.arg(cids)::text[]))
|
|
AND (COALESCE(array_length(sqlc.arg(statuses)::text[], 1), 0) = 0
|
|
OR status = ANY(sqlc.arg(statuses)::text[]))
|
|
AND (sqlc.narg(name)::text IS NULL
|
|
OR (sqlc.arg(match_mode)::text = 'exact' AND name = sqlc.narg(name)::text)
|
|
OR (sqlc.arg(match_mode)::text = 'iexact' AND lower(name) = lower(sqlc.narg(name)::text))
|
|
OR (sqlc.arg(match_mode)::text = 'partial' AND name LIKE '%' || sqlc.narg(name)::text || '%')
|
|
OR (sqlc.arg(match_mode)::text = 'ipartial' AND name ILIKE '%' || sqlc.narg(name)::text || '%'))
|
|
AND (sqlc.narg(before)::timestamptz IS NULL OR created < sqlc.narg(before)::timestamptz)
|
|
AND (sqlc.narg(after)::timestamptz IS NULL OR created >= sqlc.narg(after)::timestamptz)
|
|
AND (sqlc.arg(meta_filter)::jsonb = '{}'::jsonb
|
|
OR meta @> sqlc.arg(meta_filter)::jsonb)
|
|
ORDER BY created DESC
|
|
LIMIT sqlc.arg(pin_limit)
|
|
OFFSET sqlc.arg(pin_offset);
|
|
|
|
-- ReplacePin swaps a pin's CID atomically. Used by POST /v1/pins/{rid}
|
|
-- (pin replace per spec). Placements + refcount are left to the caller
|
|
-- to rearrange in the same transaction.
|
|
--
|
|
-- name: ReplacePin :one
|
|
UPDATE pins
|
|
SET cid = $3,
|
|
name = $4,
|
|
meta = $5,
|
|
origins = $6,
|
|
status = 'queued',
|
|
failure_reason = NULL
|
|
WHERE request_id = $1 AND org_id = $2
|
|
RETURNING *;
|
|
|
|
-- name: InsertPlacement :one
|
|
INSERT INTO pin_placements (request_id, node_id, status, fence)
|
|
VALUES ($1, $2, 'queued', $3)
|
|
RETURNING *;
|
|
|
|
-- name: GetPlacement :one
|
|
SELECT * FROM pin_placements WHERE request_id = $1 AND node_id = $2;
|
|
|
|
-- name: ListPlacementsForPin :many
|
|
SELECT pp.*, n.multiaddrs
|
|
FROM pin_placements pp JOIN nodes n ON n.id = pp.node_id
|
|
WHERE pp.request_id = $1;
|
|
|
|
-- name: ListPlacementsForNode :many
|
|
SELECT * FROM pin_placements
|
|
WHERE node_id = $1 AND status = $2
|
|
ORDER BY created_at;
|
|
|
|
-- name: UpdatePlacementStatusFenced :execrows
|
|
UPDATE pin_placements
|
|
SET status = $3, failure_reason = $4, attempts = attempts + 1
|
|
WHERE request_id = $1 AND node_id = $2 AND fence = $5;
|
|
|
|
-- name: ReplacePlacementFence :one
|
|
UPDATE pin_placements
|
|
SET node_id = $3, fence = fence + 1, status = 'queued', failure_reason = NULL
|
|
WHERE request_id = $1 AND node_id = $2
|
|
RETURNING *;
|
|
|
|
-- name: StuckPlacements :many
|
|
SELECT request_id, node_id, fence
|
|
FROM pin_placements
|
|
WHERE status = 'queued' AND created_at < (now() - make_interval(secs => $1));
|
|
|
|
-- name: IncRefcount :exec
|
|
INSERT INTO pin_refcount (node_id, cid, count)
|
|
VALUES ($1, $2, 1)
|
|
ON CONFLICT (node_id, cid) DO UPDATE SET count = pin_refcount.count + 1;
|
|
|
|
-- name: DecRefcount :one
|
|
UPDATE pin_refcount
|
|
SET count = count - 1
|
|
WHERE node_id = $1 AND cid = $2
|
|
RETURNING count;
|
|
|
|
-- name: DeleteRefcount :exec
|
|
DELETE FROM pin_refcount WHERE node_id = $1 AND cid = $2 AND count <= 0;
|