Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ import (
"os"
"strings"

"github.com/julwrites/BotPlatform/pkg/secrets"
"github.com/julwrites/ScriptureBot/pkg/bot"
"github.com/julwrites/ScriptureBot/pkg/secrets"
)

func bothandler(res http.ResponseWriter, req *http.Request) {
secretsPath := "/go/bin/secrets.yaml"
secretsData, err := secrets.LoadSecrets(secretsPath)
secretsData, err := secrets.LoadSecrets()
if err != nil {
panic(err)
log.Fatalf("Failed to load secrets: %v", err)
}

switch strings.Trim(req.URL.EscapedPath(), "\n") {
Expand All @@ -32,10 +31,9 @@ func bothandler(res http.ResponseWriter, req *http.Request) {
}

func subscriptionhandler() {
secretsPath := "/go/bin/secrets.yaml"
secretsData, err := secrets.LoadSecrets(secretsPath)
secretsData, err := secrets.LoadSecrets()
if err != nil {
panic(err)
log.Fatalf("Failed to load secrets: %v", err)
}

bot.SubscriptionHandler(&secretsData)
Expand Down
13 changes: 9 additions & 4 deletions pkg/bot/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
"log"
"net/http"

"github.com/julwrites/BotPlatform/pkg/secrets"
"github.com/julwrites/ScriptureBot/pkg/utils"

"github.com/julwrites/BotPlatform/pkg/def"
"github.com/julwrites/BotPlatform/pkg/platform"
bpsecrets "github.com/julwrites/BotPlatform/pkg/secrets"
"github.com/julwrites/ScriptureBot/pkg/secrets"
"github.com/julwrites/ScriptureBot/pkg/utils"
)

func TelegramHandler(res http.ResponseWriter, req *http.Request, secrets *secrets.SecretsData) {
Expand All @@ -22,7 +22,12 @@ func TelegramHandler(res http.ResponseWriter, req *http.Request, secrets *secret
return
}

env.Secrets = *secrets
// Create a BotPlatform-compatible SecretsData struct using an import alias
platformSecrets := bpsecrets.SecretsData{
TELEGRAM_ID: secrets.TELEGRAM_ID,
PROJECT_ID: secrets.PROJECT_ID,
}
env.Secrets = platformSecrets
// log.Printf("Loaded secrets...")

env.ResourcePath = "/go/bin/"
Expand Down
86 changes: 64 additions & 22 deletions pkg/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,104 @@ import (
"fmt"
"log"
"os"
"sync"

secretmanager "cloud.google.com/go/secretmanager/apiv1"
"cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
"github.com/joho/godotenv"
)

// SecretsData holds all the secrets for the application.
type SecretsData struct {
TELEGRAM_ID string
PROJECT_ID string
// Add other secrets here as needed
}

func init() {
LoadAndLog()
}

// LoadAndLog loads environment variables from a .env file (if present) and logs
// the status of the GCLOUD_PROJECT_ID. This function is called automatically on package initialization.
// It is also exported to allow for re-loading in test environments.
// the status of the GCLOUD_PROJECT_ID.
func LoadAndLog() {
// godotenv.Overload will read your .env file and set the environment variables.
// It will OVERWRITE any existing environment variables.
err := godotenv.Overload()
if err != nil {
log.Println("No .env file found, continuing with environment variables")
log.Println("No .env file found, using environment variables.")
}

// Log the status of the GCLOUD_PROJECT_ID for debugging purposes.
if projectID, ok := os.LookupEnv("GCLOUD_PROJECT_ID"); ok {
log.Printf("GCLOUD_PROJECT_ID is set: %s", projectID)
} else {
log.Println("GCLOUD_PROJECT_ID is not set. Google Secret Manager will not be used.")
log.Println("GCLOUD_PROJECT_ID is not set. Assuming local development.")
}
}

// LoadSecrets populates the SecretsData struct by fetching secrets.
func LoadSecrets() (SecretsData, error) {
projectID := os.Getenv("GCLOUD_PROJECT_ID")

var secrets SecretsData
secrets.PROJECT_ID = projectID

var wg sync.WaitGroup
var errs = make(chan error, 1) // Buffer to hold the first error

// List of secret names to fetch
secretNames := []string{"TELEGRAM_ID"} // Add other secret names here

for _, secretName := range secretNames {
wg.Add(1)
go func(name string) {
defer wg.Done()
value, err := Get(name)
if err != nil {
select {
case errs <- fmt.Errorf("failed to load secret '%s': %v", name, err):
default:
}
return
}
switch name {
case "TELEGRAM_ID":
secrets.TELEGRAM_ID = value
}
}(secretName)
}

wg.Wait()
close(errs)

if err := <-errs; err != nil {
return SecretsData{}, err
}

return secrets, nil
}

// Get retrieves a secret. It follows a specific order of precedence:
// 1. Google Secret Manager (if GCLOUD_PROJECT_ID is set)
// 2. Environment variables (which includes those loaded from a .env file)
//
// If the secret is not found in any of these locations, it returns an error.
// Get retrieves a secret.
// If GCLOUD_PROJECT_ID is set, it exclusively fetches from Google Secret Manager.
// Otherwise, it falls back to environment variables for local development.
func Get(secretName string) (string, error) {
// Attempt to get the secret from Google Secret Manager first.
projectID, isCloudRun := os.LookupEnv("GCLOUD_PROJECT_ID")
if isCloudRun && projectID != "" {
// Cloud environment: Use Secret Manager exclusively.
secretValue, err := getFromSecretManager(projectID, secretName)
if err == nil {
log.Printf("Loaded '%s' from Secret Manager", secretName)
return secretValue, nil
if err != nil {
return "", fmt.Errorf("failed to get secret '%s' from Secret Manager: %v", secretName, err)
}
log.Printf("Could not fetch '%s' from Secret Manager, falling back to environment variables: %v", secretName, err)
log.Printf("Loaded '%s' from Secret Manager", secretName)
return secretValue, nil
}

// Fallback to environment variables.
// Local environment: Use environment variables.
if value, ok := os.LookupEnv(secretName); ok {
log.Printf("Loaded '%s' from .env file or environment", secretName)
log.Printf("Loaded '%s' from environment", secretName)
return value, nil
}

return "", fmt.Errorf("secret '%s' not found in Secret Manager, .env file, or environment variables", secretName)
return "", fmt.Errorf("secret '%s' not found in environment variables", secretName)
}

// getFromSecretManager fetches a secret from Google Secret Manager.
func getFromSecretManager(projectID, secretName string) (string, error) {
ctx := context.Background()
client, err := secretmanager.NewClient(ctx)
Expand Down
Loading