mirror of
https://github.com/aykhans/bsky-feedgen.git
synced 2025-07-17 13:24:01 +00:00
🦋
This commit is contained in:
17
cmd/feedgen/az/Dockerfile
Normal file
17
cmd/feedgen/az/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM golang:1.24-alpine AS builder
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY ../../pkg ./pkg
|
||||
COPY ../../cmd/feedgen/az ./cmd/feedgen/az
|
||||
|
||||
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o feedgen ./cmd/feedgen/az/main.go
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /src/feedgen .
|
||||
|
||||
ENTRYPOINT ["/app/feedgen"]
|
38
cmd/feedgen/az/README.md
Normal file
38
cmd/feedgen/az/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# AzPulse Feed Generator Service
|
||||
|
||||
## Overview
|
||||
|
||||
The AzPulse Feed Generator service processes posts stored by the Consumer service and generates feed content that will be served by the API service. It implements the logic for the "AzPulse" feed, which showcases selected content from the Bluesky network.
|
||||
|
||||
**Pre-Built Docker Image**: `git.aykhans.me/bsky/feedgen-generator-az:latest`
|
||||
|
||||
## Features
|
||||
|
||||
- Processes posts from MongoDB
|
||||
- Applies custom feed generation logic for the AzPulse feed
|
||||
- Stores feed results in MongoDB for API service to access
|
||||
- Manages feed data lifecycle with automatic pruning
|
||||
- Runs as a background service with cron jobs
|
||||
|
||||
## Command Line Options
|
||||
|
||||
- `-cursor`: Specify the starting point for feed data generation
|
||||
- `last-generated`: Resume from the last generated data (default)
|
||||
- `first-post`: Start from the beginning of the posts collection
|
||||
|
||||
## Running the Service
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker build -f cmd/feedgen/az/Dockerfile -t bsky-feedgen-az .
|
||||
docker --env-file config/app/feedgen/.az.env --enf-file config/app/.mongodb.env run bsky-feedgen-az
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
task run-feedgen-az
|
||||
# or
|
||||
make run-feedgen-az
|
||||
```
|
140
cmd/feedgen/az/main.go
Normal file
140
cmd/feedgen/az/main.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/aykhans/bsky-feedgen/pkg/generator"
|
||||
"github.com/aykhans/bsky-feedgen/pkg/types"
|
||||
|
||||
"github.com/aykhans/bsky-feedgen/pkg/config"
|
||||
"github.com/aykhans/bsky-feedgen/pkg/logger"
|
||||
"github.com/aykhans/bsky-feedgen/pkg/storage/mongodb"
|
||||
"github.com/aykhans/bsky-feedgen/pkg/storage/mongodb/collections"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go listenForTermination(func() { cancel() })
|
||||
|
||||
flag.Usage = func() {
|
||||
fmt.Println(
|
||||
`Usage:
|
||||
|
||||
feedgen-az [flags]
|
||||
|
||||
Flags:
|
||||
-h, -help Display this help message
|
||||
-cursor string Specify the starting point for feed data generation (default: last-generated)
|
||||
Options:
|
||||
last-generated: Resume from the last generated data in storage
|
||||
first-post: Start from the beginning of the posts`)
|
||||
}
|
||||
|
||||
var cursorOption types.GeneratorCursor
|
||||
flag.Var(&cursorOption, "cursor", "")
|
||||
flag.Parse()
|
||||
|
||||
if args := flag.Args(); len(args) > 0 {
|
||||
if len(args) == 1 {
|
||||
fmt.Printf("unexpected argument: %s\n\n", args[0])
|
||||
} else {
|
||||
fmt.Printf("unexpected arguments: %v\n\n", strings.Join(args, ", "))
|
||||
}
|
||||
flag.CommandLine.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if cursorOption == "" {
|
||||
_ = cursorOption.Set("")
|
||||
}
|
||||
|
||||
feedGenAzConfig, errMap := config.NewFeedGenAzConfig()
|
||||
if errMap != nil {
|
||||
logger.Log.Error("feedGenAzConfig ENV error", "error", errMap.ToStringMap())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mongoDBConfig, errMap := config.NewMongoDBConfig()
|
||||
if errMap != nil {
|
||||
logger.Log.Error("mongodb ENV error", "error", errMap.ToStringMap())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
client, err := mongodb.NewDB(ctx, mongoDBConfig)
|
||||
if err != nil {
|
||||
logger.Log.Error("mongodb connection error", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
postCollection, err := collections.NewPostCollection(client)
|
||||
if err != nil {
|
||||
logger.Log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
feedAzCollection, err := collections.NewFeedAzCollection(client)
|
||||
if err != nil {
|
||||
logger.Log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
feedGeneratorAz := generator.NewFeedGeneratorAz(postCollection, feedAzCollection)
|
||||
|
||||
startCrons(ctx, feedGenAzConfig, feedGeneratorAz, feedAzCollection, cursorOption)
|
||||
logger.Log.Info("Cron jobs started")
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func startCrons(
|
||||
ctx context.Context,
|
||||
feedGenAzConfig *config.FeedGenAzConfig,
|
||||
feedGeneratorAz *generator.FeedGeneratorAz,
|
||||
feedAzCollection *collections.FeedAzCollection,
|
||||
cursorOption types.GeneratorCursor,
|
||||
) {
|
||||
// Feed az generator
|
||||
go func() {
|
||||
for {
|
||||
startTime := time.Now()
|
||||
err := feedGeneratorAz.Start(ctx, cursorOption, 1)
|
||||
if err != nil {
|
||||
logger.Log.Error("Feed az generator cron error", "error", err)
|
||||
}
|
||||
elapsedTime := time.Since(startTime)
|
||||
logger.Log.Info("Feed az generator cron completed", "time", elapsedTime)
|
||||
|
||||
time.Sleep(feedGenAzConfig.GeneratorCronDelay)
|
||||
}
|
||||
}()
|
||||
|
||||
// feed_az collection cutoff
|
||||
go func() {
|
||||
for {
|
||||
startTime := time.Now()
|
||||
deleteCount, err := feedAzCollection.CutoffByCount(ctx, feedGenAzConfig.CollectionMaxDocument)
|
||||
if err != nil {
|
||||
logger.Log.Error("feed_az collection cutoff cron error", "error", err)
|
||||
}
|
||||
elapsedTime := time.Since(startTime)
|
||||
logger.Log.Info("feed_az collection cutoff cron completed", "count", deleteCount, "time", elapsedTime)
|
||||
|
||||
time.Sleep(feedGenAzConfig.CutoffCronDelay)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func listenForTermination(do func()) {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
do()
|
||||
}
|
Reference in New Issue
Block a user