mirror of
https://github.com/aykhans/bsky-feedgen.git
synced 2025-07-17 21:34:00 +00:00
Compare commits
20 Commits
feat/add-v
...
b9633e84da
Author | SHA1 | Date | |
---|---|---|---|
b9633e84da | |||
802ff21a42 | |||
bc29dabd8a | |||
7b89230cae | |||
dbf892535b | |||
1f12a3d090 | |||
19be0f09d2 | |||
919fec0aa9 | |||
3097aba9c3 | |||
6799ad241e | |||
3378baf0bc | |||
94df1dd259 | |||
ab3baf76b1 | |||
3c6fb06282 | |||
ea9d7bb67e | |||
56581c7332 | |||
8ea4602141 | |||
12e51b5a22 | |||
50af73f26a | |||
35907380fb |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
*.env
|
||||
prod/**/*.env
|
||||
.claude
|
21
Taskfile.yml
21
Taskfile.yml
@@ -21,33 +21,24 @@ tasks:
|
||||
run-consumer:
|
||||
cmd: go run ./cmd/consumer {{.CLI_ARGS}}
|
||||
dotenv:
|
||||
- config/app/.consumer.env
|
||||
- config/app/.mongodb.env
|
||||
- config/app/consumer.env
|
||||
- config/app/mongodb.env
|
||||
|
||||
run-feedgen-az:
|
||||
cmd: go run ./cmd/feedgen/az {{.CLI_ARGS}}
|
||||
dotenv:
|
||||
- config/app/feedgen/.az.env
|
||||
- config/app/.mongodb.env
|
||||
- config/app/feedgen/az.env
|
||||
- config/app/mongodb.env
|
||||
|
||||
run-api:
|
||||
cmd: go run ./cmd/api {{.CLI_ARGS}}
|
||||
dotenv:
|
||||
- config/app/.api.env
|
||||
- config/app/.mongodb.env
|
||||
- config/app/api.env
|
||||
- config/app/mongodb.env
|
||||
|
||||
run-manager:
|
||||
cmd: go run cmd/manager/main.go {{.CLI_ARGS}}
|
||||
|
||||
generate-env:
|
||||
desc: Generate env files from templates
|
||||
cmds:
|
||||
- cp config/app/consumer.env.example config/app/.consumer.env
|
||||
- cp config/app/api.env.example config/app/.api.env
|
||||
- cp config/app/mongodb.env.example config/app/.mongodb.env
|
||||
- cp config/app/feedgen/az.env.example config/app/feedgen/.az.env
|
||||
- cp config/mongodb/env.example config/mongodb/.env
|
||||
|
||||
docker-publish-all:
|
||||
desc: Publish docker images for all services
|
||||
cmds:
|
||||
|
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
const version = "0.1.0"
|
||||
const version = "0.2.201"
|
||||
|
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
const version = "0.1.0"
|
||||
const version = "0.1.106"
|
||||
|
3
config/app/consumer.env
Normal file
3
config/app/consumer.env
Normal file
@@ -0,0 +1,3 @@
|
||||
POST_MAX_DATE=720h # Save only posts created in the last month
|
||||
POST_COLLECTION_CUTOFF_CRON_DELAY=30m # 30 minutes
|
||||
POST_COLLECTION_CUTOFF_CRON_MAX_DOCUMENT=1000000 # Delete post documents after 1 million
|
@@ -23,6 +23,7 @@ func Run(
|
||||
return err
|
||||
}
|
||||
feedHandler := handler.NewFeedHandler(feeds, apiConfig.FeedgenPublisherDID)
|
||||
generatorHandler := handler.NewGeneratorHandler()
|
||||
|
||||
authMiddleware := middleware.NewAuth(apiConfig.ServiceDID)
|
||||
|
||||
@@ -34,6 +35,9 @@ func Run(
|
||||
"GET /xrpc/app.bsky.feed.getFeedSkeleton",
|
||||
authMiddleware.JWTAuthMiddleware(http.HandlerFunc(feedHandler.GetFeedSkeleton)),
|
||||
)
|
||||
mux.HandleFunc("GET /{feed}/users", generatorHandler.GetAllUsers)
|
||||
mux.HandleFunc("GET /{feed}/users/valid/", generatorHandler.GetValidUsers)
|
||||
mux.HandleFunc("GET /{feed}/users/invalid/", generatorHandler.GetInvalidUsers)
|
||||
|
||||
httpServer := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", apiConfig.APIPort),
|
||||
|
57
pkg/api/handler/generator.go
Normal file
57
pkg/api/handler/generator.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/aykhans/bsky-feedgen/pkg/api/response"
|
||||
generatorAz "github.com/aykhans/bsky-feedgen/pkg/generator/az"
|
||||
)
|
||||
|
||||
type GeneratorHandler struct{}
|
||||
|
||||
func NewGeneratorHandler() *GeneratorHandler {
|
||||
return &GeneratorHandler{}
|
||||
}
|
||||
|
||||
func (handler *GeneratorHandler) GetValidUsers(w http.ResponseWriter, r *http.Request) {
|
||||
feed := r.PathValue("feed")
|
||||
|
||||
validUsers := make([]string, 0)
|
||||
switch feed {
|
||||
case "AzPulse":
|
||||
validUsers = generatorAz.Users.GetValidUsers()
|
||||
}
|
||||
|
||||
response.JSON(w, 200, response.M{
|
||||
"feed": feed,
|
||||
"users": validUsers,
|
||||
})
|
||||
}
|
||||
|
||||
func (handler *GeneratorHandler) GetInvalidUsers(w http.ResponseWriter, r *http.Request) {
|
||||
feed := r.PathValue("feed")
|
||||
|
||||
invalidUsers := make([]string, 0)
|
||||
switch feed {
|
||||
case "AzPulse":
|
||||
invalidUsers = generatorAz.Users.GetInvalidUsers()
|
||||
}
|
||||
|
||||
response.JSON(w, 200, response.M{
|
||||
"feed": feed,
|
||||
"users": invalidUsers,
|
||||
})
|
||||
}
|
||||
|
||||
func (handler *GeneratorHandler) GetAllUsers(w http.ResponseWriter, r *http.Request) {
|
||||
feed := r.PathValue("feed")
|
||||
|
||||
responseData := response.M{"feed": feed}
|
||||
switch feed {
|
||||
case "AzPulse":
|
||||
responseData["valid_users"] = generatorAz.Users.GetValidUsers()
|
||||
responseData["invalid_users"] = generatorAz.Users.GetInvalidUsers()
|
||||
}
|
||||
|
||||
response.JSON(w, 200, responseData)
|
||||
}
|
@@ -39,7 +39,7 @@ func (f *FeedAz) Describe(_ context.Context) bsky.FeedDescribeFeedGenerator_Feed
|
||||
|
||||
func (f *FeedAz) GetPage(
|
||||
ctx context.Context,
|
||||
_ string,
|
||||
_ string, // user did
|
||||
limit int64,
|
||||
cursor string,
|
||||
) ([]*bsky.FeedDefs_SkeletonFeedPost, *string, error) {
|
||||
|
@@ -110,16 +110,24 @@ func (generator *Generator) Start(ctx context.Context, cursorOption types.Genera
|
||||
}
|
||||
|
||||
func (generator *Generator) IsValid(post *collections.Post) bool {
|
||||
// Skip posts that are deep replies (not direct replies to original posts)
|
||||
if post.Reply != nil && post.Reply.RootURI != post.Reply.ParentURI {
|
||||
return false
|
||||
}
|
||||
|
||||
if isValidUser := users.IsValid(post.DID); isValidUser != nil {
|
||||
// Check if the user who created this post is in our pre-defined list
|
||||
// This allows for explicit inclusion/exclusion of specific users
|
||||
if isValidUser := Users.IsValid(post.DID); isValidUser != nil {
|
||||
return *isValidUser
|
||||
}
|
||||
|
||||
if (slices.Contains(post.Langs, "az") && len(post.Langs) < 3) || // Posts in Azerbaijani language with fewer than 3 languages
|
||||
generator.textRegex.MatchString(post.Text) { // Posts containing Azerbaijan-related keywords
|
||||
// A post is considered valid if it meets either of the following criteria:
|
||||
// 1. It's primarily in Azerbaijani (language code "az") with less than 3 detected languages
|
||||
// (to filter out multi-language spam)
|
||||
// 2. It contains Azerbaijan-related keywords in the text AND has at least one valid language
|
||||
// from our approved language list
|
||||
if (slices.Contains(post.Langs, "az") && len(post.Langs) < 3) ||
|
||||
(generator.textRegex.MatchString(post.Text) && Langs.IsExistsAny(post.Langs)) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@ package az
|
||||
|
||||
import "github.com/aykhans/bsky-feedgen/pkg/generator"
|
||||
|
||||
var users = generator.Users{
|
||||
var Users = generator.Users{
|
||||
// Invalid
|
||||
"did:plc:5zww7zorx2ajw7hqrhuix3ba": false,
|
||||
"did:plc:c4vhz47h566t2ntgd7gtawen": false,
|
||||
@@ -14,6 +14,23 @@ var users = generator.Users{
|
||||
"did:plc:5vwjnzaibnwscbbcvkzhy57v": false,
|
||||
"did:plc:6mfp3coadoobuvlg6w2avw6x": false,
|
||||
"did:plc:lm2uhaoqoe6yo76oeihndfyi": false,
|
||||
"did:plc:vizwdor43adw3277u2kkrssd": false,
|
||||
"did:plc:oqatvbgbhvqbjl2w2o63ehgi": false,
|
||||
"did:plc:gy7yilnydusx5hy2z3dltynp": false,
|
||||
"did:plc:xk7cs24wk6njv42azm2yd7dv": false,
|
||||
"did:plc:ijmt7f4p3dcfqtg3j3zshimn": false,
|
||||
"did:plc:2q5dx6whenn7pnsrfn3jpd6h": false,
|
||||
"did:plc:s2waw3gkmn7h2nn6od44apng": false,
|
||||
"did:plc:4hm6gb7dzobynqrpypif3dck": false,
|
||||
"did:plc:odvarii7w7soygxet3xvzop7": false,
|
||||
"did:plc:5cbkdchsxjvz5fog2oo7m4le": false,
|
||||
"did:plc:ooeuisen5rtr4rojmz7gkbrh": false,
|
||||
"did:plc:6bvhdvgeqkj7nol2zodtqmww": false,
|
||||
"did:plc:k6sxlkd5ssq2uaylzisap2tw": false,
|
||||
"did:plc:uxljnh22mmfzmr4i3oien6mx": false,
|
||||
"did:plc:w5gg2zgwcyfevphehdcmavev": false,
|
||||
"did:plc:ckawbibgmrwg3lbskfppwtlw": false,
|
||||
"did:plc:43fdk46qa5gsokzygzildsaq": false,
|
||||
|
||||
// Valid
|
||||
"did:plc:jbt4qi6psd7rutwzedtecsq7": true,
|
||||
@@ -22,6 +39,16 @@ var users = generator.Users{
|
||||
"did:plc:phtq2rhgbwipyx5ie3apw44j": true,
|
||||
"did:plc:jfdvklrs5n5qv7f25v6swc5h": true,
|
||||
"did:plc:u5ez5w6qslh6advti4wyddba": true,
|
||||
"did:plc:cs2cbzojm6hmx5lfxiuft3mq": true,
|
||||
"did:plc:x7alwnnjygt2aqcwblhazko7": true,
|
||||
"did:plc:mgciyhgfn65z7iazxuar6o6a": true,
|
||||
"did:plc:ay2f5go4lxq2hspiaqohegac": true,
|
||||
"did:plc:ftoopigdpuzqt2kpeyqxsofx": true,
|
||||
"did:plc:cs2cbzojm6hmx5lfxiuft3mq": true,
|
||||
}
|
||||
|
||||
var Langs = generator.Langs{
|
||||
"az": true,
|
||||
"en": true,
|
||||
"tr": true,
|
||||
"ru": true,
|
||||
}
|
||||
|
@@ -4,6 +4,15 @@ import "github.com/aykhans/bsky-feedgen/pkg/utils"
|
||||
|
||||
type Users map[string]bool
|
||||
|
||||
// IsValid checks if a given DID exists in the Users map and returns its validity status.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// did: The Decentralized Identifier string to check
|
||||
//
|
||||
// Returns:
|
||||
// - *bool: A pointer to the validity status if the DID exists in the map
|
||||
// - nil: If the DID does not exist in the map
|
||||
func (u Users) IsValid(did string) *bool {
|
||||
isValid, ok := u[did]
|
||||
if ok == false {
|
||||
@@ -12,3 +21,69 @@ func (u Users) IsValid(did string) *bool {
|
||||
|
||||
return utils.ToPtr(isValid)
|
||||
}
|
||||
|
||||
// GetValidUsers returns a slice of DIDs that are marked as valid in the Users map.
|
||||
//
|
||||
// Returns:
|
||||
// - []string: A slice of valid DIDs, limited by the specified parameters
|
||||
func (u Users) GetValidUsers() []string {
|
||||
validUsers := make([]string, 0)
|
||||
|
||||
for did, isValid := range u {
|
||||
if isValid {
|
||||
validUsers = append(validUsers, did)
|
||||
}
|
||||
}
|
||||
|
||||
return validUsers
|
||||
}
|
||||
|
||||
// GetInvalidUsers returns a slice of DIDs that are marked as invalid in the Users map.
|
||||
//
|
||||
// Returns:
|
||||
// - []string: A slice of invalid DIDs, limited by the specified parameters
|
||||
func (u Users) GetInvalidUsers() []string {
|
||||
invalidUsers := make([]string, 0)
|
||||
|
||||
for did, isValid := range u {
|
||||
if !isValid {
|
||||
invalidUsers = append(invalidUsers, did)
|
||||
}
|
||||
}
|
||||
|
||||
return invalidUsers
|
||||
}
|
||||
|
||||
// GetAll returns a slice of all DIDs in the Users map, regardless of validity status.
|
||||
//
|
||||
// Returns:
|
||||
// - []string: A slice containing all DIDs in the map
|
||||
func (u Users) GetAll() []string {
|
||||
allUsers := make([]string, 0, len(u))
|
||||
|
||||
for did := range u {
|
||||
allUsers = append(allUsers, did)
|
||||
}
|
||||
|
||||
return allUsers
|
||||
}
|
||||
|
||||
type Langs map[string]bool
|
||||
|
||||
// IsExistsAny checks if any of the given language codes exist in the Langs map.
|
||||
//
|
||||
// Parameters:
|
||||
// - langs: A slice of language code strings to check for existence
|
||||
//
|
||||
// Returns:
|
||||
// - bool: true if at least one language code from the input slice exists in the map,
|
||||
// false if none of the provided language codes exist
|
||||
func (l Langs) IsExistsAny(langs []string) bool {
|
||||
for _, lang := range langs {
|
||||
if _, ok := l[lang]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
26
prod/Makefile
Normal file
26
prod/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
.PHONY: configure
|
||||
|
||||
configure:
|
||||
@cp config/app/api.env.example config/app/.api.env
|
||||
@cp config/app/consumer.env.example config/app/.consumer.env
|
||||
@cp config/app/mongodb.env.example config/app/.mongodb.env
|
||||
@cp config/app/feedgen/az.env.example config/app/feedgen/.az.env
|
||||
@cp config/caddy/env.example config/caddy/.env
|
||||
@cp config/mongodb/env.example config/mongodb/.env
|
||||
|
||||
@read -p "Enter MongoDB username: " mongodb_user; \
|
||||
printf "Enter MongoDB password: "; \
|
||||
read mongodb_pass; \
|
||||
sed -i "s/MONGO_INITDB_ROOT_USERNAME=.*/MONGO_INITDB_ROOT_USERNAME=$$mongodb_user/" config/mongodb/.env; \
|
||||
sed -i "s/MONGO_INITDB_ROOT_PASSWORD=.*/MONGO_INITDB_ROOT_PASSWORD=$$mongodb_pass/" config/mongodb/.env; \
|
||||
sed -i "s/MONGODB_USERNAME=.*/MONGODB_USERNAME=$$mongodb_user/" config/app/.mongodb.env; \
|
||||
sed -i "s/MONGODB_PASSWORD=.*/MONGODB_PASSWORD=$$mongodb_pass/" config/app/.mongodb.env
|
||||
|
||||
@read -p "Enter domain name (e.g., feeds.bsky.example.com): " domain; \
|
||||
read -p "Enter your AT Protocol DID: " publisher_did; \
|
||||
sed -i "s/DOMAIN=.*/DOMAIN=$$domain/" config/caddy/.env; \
|
||||
sed -i "s|FEEDGEN_HOSTNAME=.*|FEEDGEN_HOSTNAME=https://$$domain|" config/app/.api.env; \
|
||||
sed -i "s/FEEDGEN_PUBLISHER_DID=.*/FEEDGEN_PUBLISHER_DID=$$publisher_did/" config/app/.api.env
|
||||
|
||||
@echo
|
||||
@echo "Configuration complete! You can now run 'docker compose up -d'"
|
76
prod/README.md
Normal file
76
prod/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Example Production Deployment
|
||||
|
||||
This is an example of a production deployment for the Feed Generator.
|
||||
|
||||
## Architecture
|
||||
|
||||
The production setup includes the following services:
|
||||
|
||||
- **MongoDB**: Database for storing posts and feed data
|
||||
- **Consumer**: Service that consumes AT Protocol firehose data
|
||||
- **Feed Generator (AZ)**: Generates feeds for Azerbaijan-related content
|
||||
- **API**: REST API service for serving feeds
|
||||
- **Caddy**: Reverse proxy
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Configure the environment**:
|
||||
```bash
|
||||
make configure
|
||||
```
|
||||
This will:
|
||||
- Copy all example configuration files
|
||||
- Prompt for MongoDB credentials
|
||||
- Prompt for domain name and AT Protocol DID
|
||||
- Update configuration files with your values
|
||||
|
||||
2. **Start the services**:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
3. **Check service status**:
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### Application Configuration
|
||||
- `config/app/.api.env` - API service configuration
|
||||
- `config/app/.consumer.env` - Consumer service configuration
|
||||
- `config/app/.mongodb.env` - MongoDB connection settings
|
||||
- `config/app/feedgen/.az.env` - Azerbaijan feed generator settings
|
||||
|
||||
### Infrastructure Configuration
|
||||
- `config/caddy/.env` - Caddy reverse proxy settings
|
||||
- `config/caddy/Caddyfile` - Caddy server configuration
|
||||
- `config/mongodb/.env` - MongoDB initialization settings
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### API Service
|
||||
- `FEEDGEN_HOSTNAME` - Public hostname for the feed generator
|
||||
- `FEEDGEN_PUBLISHER_DID` - Your AT Protocol DID
|
||||
- `API_PORT` - Port for the API service (default: 8421)
|
||||
|
||||
### Consumer Service
|
||||
- `POST_MAX_DATE` - Maximum age of posts to store (default: 720h/30 days)
|
||||
- `POST_COLLECTION_CUTOFF_CRON_DELAY` - Cleanup interval (default: 30m)
|
||||
- `POST_COLLECTION_CUTOFF_CRON_MAX_DOCUMENT` - Max documents before cleanup (default: 1M)
|
||||
|
||||
### AZ Feed Generator
|
||||
- `FEED_AZ_GENERATER_CRON_DELAY` - Feed generation interval (default: 1m)
|
||||
- `FEED_AZ_COLLECTION_CUTOFF_CRON_DELAY` - Cleanup interval (default: 30m)
|
||||
- `FEED_AZ_COLLECTION_CUTOFF_CRON_MAX_DOCUMENT` - Max documents before cleanup (default: 500K)
|
||||
|
||||
### MongoDB
|
||||
- `MONGODB_HOST` - MongoDB hostname (default: mongodb)
|
||||
- `MONGODB_PORT` - MongoDB port (default: 27017)
|
||||
- `MONGODB_USERNAME` - Database username
|
||||
- `MONGODB_PASSWORD` - Database password
|
||||
|
||||
### Caddy
|
||||
- `DOMAIN` - Your domain name
|
||||
- `API_HOST` - Internal API service URL (default: http://api:8421)
|
3
prod/config/app/api.env.example
Normal file
3
prod/config/app/api.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
FEEDGEN_HOSTNAME=https://feeds.bsky.example.com
|
||||
FEEDGEN_PUBLISHER_DID=did:plc:qwertyuiopp
|
||||
API_PORT=8421
|
@@ -1,3 +1,3 @@
|
||||
POST_MAX_DATE=720h # Save only posts created in the last month
|
||||
POST_COLLECTION_CUTOFF_CRON_DELAY=30m # 30 minutes
|
||||
POST_COLLECTION_CUTOFF_CRON_MAX_DOCUMENT=10000000 # Delete post documents after 10 million
|
||||
POST_COLLECTION_CUTOFF_CRON_MAX_DOCUMENT=1000000 # Delete post documents after 1 million
|
3
prod/config/app/feedgen/az.env.example
Normal file
3
prod/config/app/feedgen/az.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
FEED_AZ_GENERATER_CRON_DELAY=1m # 1 minute
|
||||
FEED_AZ_COLLECTION_CUTOFF_CRON_DELAY=30m # 30 minutes
|
||||
FEED_AZ_COLLECTION_CUTOFF_CRON_MAX_DOCUMENT=500000 # Delete post documents after 500 thousand
|
4
prod/config/app/mongodb.env.example
Normal file
4
prod/config/app/mongodb.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
MONGODB_HOST=mongodb
|
||||
MONGODB_PORT=27017
|
||||
MONGODB_USERNAME=root
|
||||
MONGODB_PASSWORD=toor
|
11
prod/config/caddy/Caddyfile
Normal file
11
prod/config/caddy/Caddyfile
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
admin off
|
||||
}
|
||||
|
||||
{$DOMAIN} {
|
||||
request_body {
|
||||
max_size 8MB
|
||||
}
|
||||
|
||||
reverse_proxy {$API_HOST}
|
||||
}
|
2
prod/config/caddy/env.example
Normal file
2
prod/config/caddy/env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
DOMAIN=feeds.bsky.example.com
|
||||
API_HOST=http://api:8421
|
2
prod/config/mongodb/env.example
Normal file
2
prod/config/mongodb/env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
MONGO_INITDB_ROOT_USERNAME=root
|
||||
MONGO_INITDB_ROOT_PASSWORD=toor
|
63
prod/docker-compose.yml
Normal file
63
prod/docker-compose.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:8.0.9-noble
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - 27017:27017
|
||||
env_file: ./config/mongodb/.env
|
||||
volumes:
|
||||
- mongodb_data:/data/db
|
||||
healthcheck:
|
||||
test: echo 'db.runCommand("ping").ok' | mongosh --quiet
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
|
||||
consumer:
|
||||
image: git.aykhans.me/bsky/feedgen-consumer:latest
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./config/app/.mongodb.env
|
||||
- ./config/app/.consumer.env
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
|
||||
feedgen_az:
|
||||
image: git.aykhans.me/bsky/feedgen-generator-az:latest
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./config/app/.mongodb.env
|
||||
- ./config/app/feedgen/.az.env
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
|
||||
api:
|
||||
image: git.aykhans.me/bsky/feedgen-api:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8421:8421
|
||||
env_file:
|
||||
- ./config/app/.mongodb.env
|
||||
- ./config/app/.api.env
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
|
||||
caddy:
|
||||
image: caddy:2.10.0-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
- 443:443/udp
|
||||
env_file: ./config/caddy/.env
|
||||
volumes:
|
||||
- ./config/caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
|
||||
volumes:
|
||||
mongodb_data:
|
||||
caddy_data:
|
Reference in New Issue
Block a user