gitea, notion webhook
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

162 lines
5.1 KiB

package config
import (
"os"
"strconv"
"strings"
"github.com/joho/godotenv"
)
// Config holds all runtime settings, loaded from environment (.env optional).
type Config struct {
// Slack
SlackBotToken string
DefaultSlackChannel string // 모든 이벤트 브로드캐스트 채널
MonitorChannel string // 에러(slog.Error) 발생 시 알림을 보낼 채널 (예: monitor-dev)
// Gitea
GiteaWebhookSecret string
GiteaBroadcast bool // true면 모든 Gitea 이벤트를 DefaultSlackChannel로 브로드캐스트(기본 off, DM만)
// Notion
NotionVerificationToken string
NotionAPIToken string
NotionAssigneeProperties []string // 알림 대상이 되는 People 속성들 (담당자/처리자/참조인원 등)
NotionSnapshotFile string // 페이지 스냅샷 저장 파일 (이전→현재 diff용). 비우면 메모리만
// App
MappingFile string
Addr string
// Logging
Env string // development | production
LogLevel string // debug | info | warn | error
LogFormat string // text | json
LogFile string // optional file path; empty = stdout only (날짜별 회전의 기준 경로)
LogRetentionDays int // 날짜별 로그 파일 보존 일수 (기본 7)
}
// Load reads configuration for the given environment and loads its .env files.
//
// The environment is decided by the run command (the -env flag passed as
// envOverride). If empty, it falls back to the APP_ENV variable, then
// "development". This keeps env selection command-driven, not ambient.
//
// Precedence: an earlier-loaded file wins (godotenv.Load never overrides an
// already-set variable), and missing files are ignored:
//
// .env.{env}.local # 개인 비밀값 (git 제외, 선택)
// .env.{env} # 환경별 설정 (.env.dev / .env.prod)
// .env # 공통 기본값 (fallback)
func Load(envOverride string) Config {
env := envOverride
if env == "" {
env = getenv("APP_ENV", "dev")
}
env = normalizeEnv(env)
_ = godotenv.Load(".env." + env + ".local")
_ = godotenv.Load(".env." + env)
_ = godotenv.Load(".env")
return Config{
SlackBotToken: os.Getenv("SLACK_BOT_TOKEN"),
DefaultSlackChannel: os.Getenv("DEFAULT_SLACK_CHANNEL"),
MonitorChannel: os.Getenv("MONITOR_SLACK_CHANNEL"),
GiteaWebhookSecret: os.Getenv("GITEA_WEBHOOK_SECRET"),
GiteaBroadcast: getenvBool("GITEA_BROADCAST", false),
NotionVerificationToken: os.Getenv("NOTION_VERIFICATION_TOKEN"),
NotionAPIToken: os.Getenv("NOTION_API_TOKEN"),
NotionAssigneeProperties: getenvList("NOTION_ASSIGNEE_PROPERTY", "담당자,처리자,참조인원"),
NotionSnapshotFile: getenv("NOTION_SNAPSHOT_FILE", "data/notion-snapshots.json"),
MappingFile: getenv("MAPPING_FILE", "data/mappings.json"),
Addr: getenv("ADDR", ":8000"),
Env: env,
LogLevel: getenv("LOG_LEVEL", defaultLogLevel(env)),
LogFormat: getenv("LOG_FORMAT", defaultLogFormat(env)),
LogFile: getenv("LOG_FILE", defaultLogFile(env)),
LogRetentionDays: getenvInt("LOG_RETENTION_DAYS", 7),
}
}
// normalizeEnv canonicalizes the env name to the short form "dev" or "prod".
// Long forms (development/production) and unknown values map sensibly.
func normalizeEnv(s string) string {
switch strings.ToLower(strings.TrimSpace(s)) {
case "prod", "production":
return "prod"
default:
return "dev"
}
}
// IsProduction reports whether the app runs in the production environment.
func (c Config) IsProduction() bool { return c.Env == "prod" }
func defaultLogLevel(env string) string {
if env == "prod" {
return "info"
}
return "debug"
}
func defaultLogFormat(env string) string {
if env == "prod" {
return "json"
}
return "text"
}
// defaultLogFile decides where logs are written when LOG_FILE is unset:
// - dev → "" (콘솔/IDE 출력만)
// - prod → 파일로 추출 (logs/app.log)
func defaultLogFile(env string) string {
if env == "prod" {
return "logs/app.log"
}
return ""
}
func getenv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
// getenvList는 콤마로 구분된 값을 트림해 슬라이스로 돌려준다(빈 항목 제거).
// 미설정 시 fallback(역시 콤마 구분)을 파싱한다.
func getenvList(key, fallback string) []string {
v := getenv(key, fallback)
parts := strings.Split(v, ",")
out := make([]string, 0, len(parts))
for _, p := range parts {
if s := strings.TrimSpace(p); s != "" {
out = append(out, s)
}
}
return out
}
func getenvInt(key string, fallback int) int {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return fallback
}
// getenvBool은 on/off, true/false, 1/0, yes/no를 모두 받는다. 미설정/인식불가 시 fallback.
func getenvBool(key string, fallback bool) bool {
switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) {
case "1", "true", "t", "yes", "y", "on":
return true
case "0", "false", "f", "no", "n", "off":
return false
default:
return fallback
}
}