|
| 1 | +// Package config はアプリケーション設定の管理機能を提供します |
| 2 | +package config |
| 3 | + |
| 4 | +import ( |
| 5 | + "fmt" |
| 6 | + "os" |
| 7 | + "os/exec" |
| 8 | + "strconv" |
| 9 | + "strings" |
| 10 | + "time" |
| 11 | +) |
| 12 | + |
| 13 | +// Config はアプリケーションの設定を保持する構造体です |
| 14 | +type Config struct { |
| 15 | + // 環境 |
| 16 | + Env string |
| 17 | + |
| 18 | + // データベース |
| 19 | + DatabaseURL string |
| 20 | + |
| 21 | + // サーバー |
| 22 | + Port string |
| 23 | + Domain string |
| 24 | + |
| 25 | + // Cookie設定 |
| 26 | + CookieDomain string |
| 27 | + |
| 28 | + // セッション |
| 29 | + SessionSecure bool |
| 30 | + SessionHTTPOnly bool |
| 31 | + |
| 32 | + // Rate Limiting設定 |
| 33 | + DisableRateLimit bool |
| 34 | + |
| 35 | + // Rails版アプリのURL(リバースプロキシ用) |
| 36 | + RailsAppURL string |
| 37 | + |
| 38 | + // Cloudflare Turnstile(Bot対策) |
| 39 | + TurnstileSiteKey string |
| 40 | + TurnstileSecretKey string |
| 41 | + |
| 42 | + // メンテナンスモード |
| 43 | + MaintenanceMode bool |
| 44 | + AdminIPs []string |
| 45 | + |
| 46 | + // アセットバージョン(CDNキャッシュ対策用) |
| 47 | + AssetVersion string |
| 48 | +} |
| 49 | + |
| 50 | +// Load は環境変数から設定を読み込みます |
| 51 | +func Load() (*Config, error) { |
| 52 | + // APP_ENVの値を取得(デフォルト: dev) |
| 53 | + // dev: 開発環境、test: テスト環境、prod: 本番環境 |
| 54 | + // |
| 55 | + // すべての環境でGoプロセス起動時には既に環境変数がセット済みです: |
| 56 | + // - ローカル開発/テスト: op run --env-file=".env" が処理済み |
| 57 | + // - CI環境: GitHub Actionsが設定済み |
| 58 | + // - 本番環境: Dokkuが設定済み |
| 59 | + env := os.Getenv("APP_ENV") |
| 60 | + if env == "" { |
| 61 | + env = "dev" |
| 62 | + } |
| 63 | + |
| 64 | + cfg := &Config{ |
| 65 | + Env: env, |
| 66 | + } |
| 67 | + |
| 68 | + // 必須の環境変数をチェック |
| 69 | + cfg.DatabaseURL = os.Getenv("DATABASE_URL") |
| 70 | + if cfg.DatabaseURL == "" { |
| 71 | + return nil, fmt.Errorf("必須の環境変数 DATABASE_URL が設定されていません") |
| 72 | + } |
| 73 | + |
| 74 | + cfg.Port = os.Getenv("MEWST_PORT") |
| 75 | + if cfg.Port == "" { |
| 76 | + return nil, fmt.Errorf("必須の環境変数 MEWST_PORT が設定されていません") |
| 77 | + } |
| 78 | + |
| 79 | + cfg.Domain = os.Getenv("MEWST_DOMAIN") |
| 80 | + if cfg.Domain == "" { |
| 81 | + return nil, fmt.Errorf("必須の環境変数 MEWST_DOMAIN が設定されていません") |
| 82 | + } |
| 83 | + |
| 84 | + cfg.CookieDomain = os.Getenv("MEWST_COOKIE_DOMAIN") |
| 85 | + if cfg.CookieDomain == "" { |
| 86 | + return nil, fmt.Errorf("必須の環境変数 MEWST_COOKIE_DOMAIN が設定されていません") |
| 87 | + } |
| 88 | + |
| 89 | + sessionSecureStr := os.Getenv("MEWST_SESSION_SECURE") |
| 90 | + if sessionSecureStr == "" { |
| 91 | + return nil, fmt.Errorf("必須の環境変数 MEWST_SESSION_SECURE が設定されていません") |
| 92 | + } |
| 93 | + cfg.SessionSecure = sessionSecureStr == "true" |
| 94 | + |
| 95 | + sessionHTTPOnlyStr := os.Getenv("MEWST_SESSION_HTTPONLY") |
| 96 | + if sessionHTTPOnlyStr == "" { |
| 97 | + return nil, fmt.Errorf("必須の環境変数 MEWST_SESSION_HTTPONLY が設定されていません") |
| 98 | + } |
| 99 | + cfg.SessionHTTPOnly = sessionHTTPOnlyStr == "true" |
| 100 | + |
| 101 | + // Rate Limiting設定(オプショナル - 開発環境でRate Limitingを無効化) |
| 102 | + cfg.DisableRateLimit = os.Getenv("MEWST_DISABLE_RATE_LIMIT") == "true" |
| 103 | + |
| 104 | + // Rails版アプリのURL(オプショナル - リバースプロキシ機能で使用) |
| 105 | + cfg.RailsAppURL = os.Getenv("MEWST_RAILS_APP_URL") |
| 106 | + |
| 107 | + // Cloudflare Turnstile(オプショナル - ログイン・サインアップフォームで使用) |
| 108 | + // テスト環境では空文字列でも動作する(モック設定として使用) |
| 109 | + cfg.TurnstileSiteKey = os.Getenv("MEWST_TURNSTILE_SITE_KEY") |
| 110 | + cfg.TurnstileSecretKey = os.Getenv("MEWST_TURNSTILE_SECRET_KEY") |
| 111 | + |
| 112 | + // メンテナンスモード(オプショナル - "on"のときメンテナンスモードを有効化) |
| 113 | + cfg.MaintenanceMode = os.Getenv("MEWST_MAINTENANCE_MODE") == "on" |
| 114 | + |
| 115 | + // 管理者IP(オプショナル - カンマ区切りで複数指定可能) |
| 116 | + adminIPStr := os.Getenv("MEWST_ADMIN_IP") |
| 117 | + if adminIPStr != "" { |
| 118 | + cfg.AdminIPs = parseAdminIPs(adminIPStr) |
| 119 | + } |
| 120 | + |
| 121 | + // アセットバージョン(Gitコミットハッシュ)を設定 |
| 122 | + cfg.AssetVersion = getGitCommitHash() |
| 123 | + |
| 124 | + return cfg, nil |
| 125 | +} |
| 126 | + |
| 127 | +// DatabaseDSN は PostgreSQL 接続文字列を返します |
| 128 | +func (c *Config) DatabaseDSN() string { |
| 129 | + return c.DatabaseURL |
| 130 | +} |
| 131 | + |
| 132 | +// IsDev は開発環境かどうかを返します |
| 133 | +func (c *Config) IsDev() bool { |
| 134 | + return c.Env == "dev" |
| 135 | +} |
| 136 | + |
| 137 | +// IsTest はテスト環境かどうかを返します |
| 138 | +func (c *Config) IsTest() bool { |
| 139 | + return c.Env == "test" |
| 140 | +} |
| 141 | + |
| 142 | +// IsProduction は本番環境かどうかを返します |
| 143 | +func (c *Config) IsProduction() bool { |
| 144 | + return c.Env == "prod" |
| 145 | +} |
| 146 | + |
| 147 | +// AppURL はアプリケーションのベースURLを返します |
| 148 | +func (c *Config) AppURL() string { |
| 149 | + return "https://" + c.Domain |
| 150 | +} |
| 151 | + |
| 152 | +// getGitCommitHash はGitのコミットハッシュ(短縮版)を取得します |
| 153 | +// CDNキャッシュ対策として、CSS/JSファイルのクエリパラメータに使用します |
| 154 | +func getGitCommitHash() string { |
| 155 | + cmd := exec.Command("git", "rev-parse", "--short", "HEAD") |
| 156 | + out, err := cmd.Output() |
| 157 | + if err != nil { |
| 158 | + // Gitが利用できない場合は "dev" を返す(開発環境用のフォールバック) |
| 159 | + return "dev" |
| 160 | + } |
| 161 | + return strings.TrimSpace(string(out)) |
| 162 | +} |
| 163 | + |
| 164 | +// GetAssetVersion はアセットのバージョン文字列を返します |
| 165 | +// 開発環境: 現在時刻のUnixタイムスタンプ(ミリ秒)を返す(キャッシュを無効化) |
| 166 | +// 本番/テスト環境: Gitコミットハッシュを返す(起動時に設定された値) |
| 167 | +func (c *Config) GetAssetVersion() string { |
| 168 | + if c.IsDev() { |
| 169 | + // 開発環境では毎回異なる値を返す(現在時刻のUnixタイムスタンプ、ミリ秒) |
| 170 | + return strconv.FormatInt(time.Now().UnixMilli(), 10) |
| 171 | + } |
| 172 | + // 本番/テスト環境では起動時に設定されたGitコミットハッシュを返す |
| 173 | + return c.AssetVersion |
| 174 | +} |
| 175 | + |
| 176 | +// parseAdminIPs はカンマ区切りのIP文字列をスライスに変換します |
| 177 | +// 各IPアドレスの前後の空白は除去されます |
| 178 | +func parseAdminIPs(s string) []string { |
| 179 | + parts := strings.Split(s, ",") |
| 180 | + ips := make([]string, 0, len(parts)) |
| 181 | + for _, p := range parts { |
| 182 | + ip := strings.TrimSpace(p) |
| 183 | + if ip != "" { |
| 184 | + ips = append(ips, ip) |
| 185 | + } |
| 186 | + } |
| 187 | + return ips |
| 188 | +} |
0 commit comments