Skip to content

Repository files navigation

jyatesdotdev-api

Go backend for jyates.dev — four HTTP Lambda functions behind API Gateway plus an S3-triggered content notification function.

Architecture

  • Language: Go 1.26 (CI reads the version from backend/go.mod)
  • Runtime: AWS Lambda (ARM64, provided.al2023)
  • Database: DynamoDB (KMS-encrypted, on-demand)
  • Email: SES v2 (sends from blog@jyates.dev)
  • Router: chi (via aws-lambda-go-api-proxy)
  • Structure: Handlers → Services → Repositories

Functions

FunctionPurpose
interactionsLikes and comments (GET/POST)
contactContact form → SES email
adminComment moderation (approve/reject/delete)
authorizerBasic Auth for admin endpoints
notificationsNew-content manifest → confirmed SES topic subscribers

API Routes (via CloudFront /api/*)

GET /api/v1/likes?slug=...
POST /api/v1/likes {slug}
GET /api/v1/comments?slug=...
POST /api/v1/comments {slug, content, authorName, authorEmail, website}
POST /api/v1/comments/:id/like {slug}
POST /api/v1/contact {name, email, message, website}
POST /api/v1/subscriptions {email, topics, website}
POST /api/v1/subscriptions/confirm {token}
GET /api/v1/geo
GET /api/v1/visits
POST /api/v1/visits
GET /api/v1/admin/comments?status=...
PUT /api/v1/admin/comments/:id {slug, status}
DELETE /api/v1/admin/comments/:id {slug}

Spam Protection & Identity

  • Honeypot: website is a hidden form field that must be empty; submissions that fill it are rejected (contact returns a fake 200 so bots aren't tipped off). There is no reCAPTCHA.
  • Likes are deduplicated by the X-Visitor-Id request header (required — requests without it get a 400), not by IP.
  • Comments require a valid email address and are approved unless AUTO_APPROVE=false; the admin dashboard can approve or reject them. The CloudFront-generated viewer address is stored for moderation context, and comment creation is limited per IP/day.
  • Subscriptions use a 48-hour, single-use email confirmation token. Confirmed addresses and blog/projects preferences live in an SES contact list, which also supplies one-click unsubscribe links. Pending token hashes live in DynamoDB.

DynamoDB Schema

Single table jyatesdotdev-state with single-table design:

PKSKPurpose
POST#<slug>METADATAPost metadata (likeCount)
POST#<slug>LIKE#<visitorID>Like record (existence = liked)
POST#<slug>COMMENT#<uuid>Comment (content, author, status, likeCount)
COMMENT#<uuid>LIKE#<visitorID>Comment like tracking
POST#<slug>#USER#<visitorID>LIKE#COMMENT#<uuid>User-comment like cross-reference
SUBSCRIPTION_REQUEST#<tokenHash>METADATAPending double-opt-in request (TTL; atomically consumed)
NOTIFICATION#<commitSHA>MANIFESTCompleted-manifest marker (TTL)
NOTIFICATION#<commitSHA>RECIPIENT#<event>#<emailHash>Resumable per-recipient delivery checkpoint (TTL)

GSI1 (GSI1PK/GSI1SK) indexes comments by status for admin queries (e.g., STATUS#approved, STATUS#pending).

Testing

cd backend
go test -short ./...

Deployment

Pushes to main (under backend/**) or manual workflow_dispatch trigger the pipeline:

  1. Build five Lambda binaries (cross-compiled for linux/arm64)
  2. Zip and upload to the artifacts S3 bucket under lambdas/<git-sha>/
  3. Dispatch deploy_api to jyatesdotdev-infra with the artifact locations, and run_e2e to jyatesdotdev-integration

Manual Trigger

gh workflow run deploy.yml --repo <owner>/jyatesdotdev-api --ref main

This builds, uploads, and dispatches to infra automatically.

Required Secrets & Variables

TypeNameDescription
SecretAWS_ROLE_ARNGitHub OIDC deploy role ARN
SecretINFRA_REPO_PATPAT to trigger jyatesdotdev-infra / jyatesdotdev-integration dispatches
VariableARTIFACTS_BUCKETS3 bucket for Lambda zips
VariableAWS_REGIONus-west-2

About

Go Lambda backend for jyates.dev

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages