Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
100.00% |
141 / 141 |
|
100.00% |
17 / 17 |
CRAP | |
100.00% |
1 / 1 |
| Defaults | |
100.00% |
141 / 141 |
|
100.00% |
17 / 17 |
19 | |
100.00% |
1 / 1 |
| values | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
1 | |||
| app | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
1 | |||
| auth | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| paths | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
| panel | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
| api | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
| error | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
| icons | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
| database | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
1 | |||
| session | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
1 | |||
| media | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
| richtext | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
| upload | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
1 | |||
| uid | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
| password | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| queue | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
| |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | ||||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Cosray\Config; |
| 6 | |
| 7 | use Cosray\Uid as UidGenerator; |
| 8 | |
| 9 | use function Cosray\env; |
| 10 | |
| 11 | final class Defaults |
| 12 | { |
| 13 | /** @return array<string, mixed> */ |
| 14 | public static function values(string $root, Env $env): array |
| 15 | { |
| 16 | return array_merge( |
| 17 | self::app($env), |
| 18 | self::auth($env), |
| 19 | self::paths($root), |
| 20 | self::panel($root), |
| 21 | self::api(), |
| 22 | self::error(), |
| 23 | self::icons(), |
| 24 | self::database(), |
| 25 | self::session($env), |
| 26 | self::media(), |
| 27 | self::richtext(), |
| 28 | self::upload(), |
| 29 | self::uid(), |
| 30 | self::password($env), |
| 31 | self::queue($env), |
| 32 | self::mail($env), |
| 33 | ); |
| 34 | } |
| 35 | |
| 36 | /** @return array<string, mixed> */ |
| 37 | private static function app(Env $env): array |
| 38 | { |
| 39 | return [ |
| 40 | 'app.name' => env('APP_NAME', 'cosray'), |
| 41 | 'app.title' => env('APP_TITLE', null), |
| 42 | 'app.debug' => $env->bool('APP_DEBUG', false), |
| 43 | 'app.env' => env('APP_ENV', ''), |
| 44 | 'app.secret' => env('APP_SECRET', null), |
| 45 | 'app.timezone' => env('APP_TIMEZONE', 'UTC'), |
| 46 | 'app.url_prefix' => '', |
| 47 | // The absolute URL for links in mails; never derived from a request. |
| 48 | 'app.url' => env('APP_URL', null), |
| 49 | // Reverse proxies whose X-Forwarded-For names the client, comma-separated in TRUSTED_PROXIES. |
| 50 | 'app.trusted_proxies' => $env->string('TRUSTED_PROXIES') ?? [], |
| 51 | 'plugins' => [], |
| 52 | 'webhooks' => [], |
| 53 | ]; |
| 54 | } |
| 55 | |
| 56 | /** @return array<string, mixed> */ |
| 57 | private static function auth(Env $env): array |
| 58 | { |
| 59 | return [ |
| 60 | 'auth.remember_lifetime' => $env->int('AUTH_REMEMBER_LIFETIME', 60 * 60 * 24 * 30), |
| 61 | ]; |
| 62 | } |
| 63 | |
| 64 | /** @return array<string, mixed> */ |
| 65 | private static function paths(string $root): array |
| 66 | { |
| 67 | return [ |
| 68 | 'path.root' => $root, |
| 69 | 'path.public' => $root . '/public', |
| 70 | 'path.assets' => '/assets', |
| 71 | 'path.cache' => '/cache', |
| 72 | 'path.views' => '/views', |
| 73 | ]; |
| 74 | } |
| 75 | |
| 76 | /** @return array<string, mixed> */ |
| 77 | private static function panel(string $root): array |
| 78 | { |
| 79 | return [ |
| 80 | 'panel.path' => '/panel', |
| 81 | // Keep the client outside the public directory so a physical |
| 82 | // directory cannot shadow the panel route in the web server. |
| 83 | 'panel.theme' => [], |
| 84 | 'panel.logo' => '/images/logo.png', |
| 85 | 'panel.dashboard' => true, |
| 86 | // Default panel UI language; NULL negotiates from the browser. |
| 87 | 'panel.locale' => null, |
| 88 | // Origins that may frame the panel's embedded editor. |
| 89 | 'panel.embed' => [], |
| 90 | ]; |
| 91 | } |
| 92 | |
| 93 | /** @return array<string, mixed> */ |
| 94 | private static function api(): array |
| 95 | { |
| 96 | return [ |
| 97 | 'api.path' => null, |
| 98 | // Origins whose browser scripts may call the API; '*' allows any. |
| 99 | 'api.origins' => [], |
| 100 | // Seconds shared caches may keep anonymous reads; 0 asks them to revalidate. |
| 101 | 'api.max_age' => 0, |
| 102 | // A headless frontend's preview URL with {uid}, {token} and {locale}. |
| 103 | 'api.preview' => null, |
| 104 | ]; |
| 105 | } |
| 106 | |
| 107 | /** @return array<string, mixed> */ |
| 108 | private static function error(): array |
| 109 | { |
| 110 | return [ |
| 111 | 'error.enabled' => true, |
| 112 | 'error.renderer' => null, |
| 113 | 'error.trusted' => [], |
| 114 | 'error.views' => null, |
| 115 | 'error.whoops' => true, |
| 116 | ]; |
| 117 | } |
| 118 | |
| 119 | /** @return array<string, mixed> */ |
| 120 | private static function icons(): array |
| 121 | { |
| 122 | return [ |
| 123 | 'icons.local.paths' => [], |
| 124 | 'icons.iconify.base_url' => 'https://api.iconify.design', |
| 125 | 'icons.iconify.timeout' => 5, |
| 126 | 'icons.iconify.user_agent' => 'cosray/cms', |
| 127 | ]; |
| 128 | } |
| 129 | |
| 130 | /** @return array<string, mixed> */ |
| 131 | private static function database(): array |
| 132 | { |
| 133 | return [ |
| 134 | 'db.dsn' => env('DATABASE_URL', null), |
| 135 | 'db.sql' => [], |
| 136 | 'db.migrations' => [], |
| 137 | 'db.placeholders' => [ |
| 138 | 'all' => [ |
| 139 | 'cms.prefix' => 'cms_', |
| 140 | ], |
| 141 | 'pgsql' => [ |
| 142 | 'cms.prefix' => 'cms.', |
| 143 | ], |
| 144 | ], |
| 145 | 'db.print' => false, |
| 146 | 'db.options' => [], |
| 147 | // Keep the connection between requests of a worker, rolled back to |
| 148 | // a clean state; false disconnects after every request. |
| 149 | 'db.reuse' => true, |
| 150 | ]; |
| 151 | } |
| 152 | |
| 153 | /** @return array<string, mixed> */ |
| 154 | private static function session(Env $env): array |
| 155 | { |
| 156 | return [ |
| 157 | 'session.enabled' => $env->bool('SITE_SESSION_ENABLED', false), |
| 158 | 'session.options' => [ |
| 159 | 'cookie_httponly' => true, |
| 160 | 'cookie_samesite' => 'Lax', |
| 161 | 'cookie_secure' => $env->bool('SESSION_COOKIE_SECURE', true), |
| 162 | 'cookie_lifetime' => $env->int('SESSION_COOKIE_LIFETIME', 0), |
| 163 | 'gc_maxlifetime' => $env->int('SESSION_IDLE_TIMEOUT', 60 * 60 * 8), |
| 164 | 'cache_expire' => 3600, |
| 165 | ], |
| 166 | 'session.handler' => null, |
| 167 | ]; |
| 168 | } |
| 169 | |
| 170 | /** @return array<string, mixed> */ |
| 171 | private static function media(): array |
| 172 | { |
| 173 | return [ |
| 174 | 'media.fileserver' => null, |
| 175 | 'media.sizes' => [], |
| 176 | ]; |
| 177 | } |
| 178 | |
| 179 | /** @return array<string, mixed> */ |
| 180 | private static function richtext(): array |
| 181 | { |
| 182 | return [ |
| 183 | 'richtext.classes' => [], |
| 184 | 'richtext.styles' => [], |
| 185 | 'richtext.tools' => [], |
| 186 | ]; |
| 187 | } |
| 188 | |
| 189 | /** @return array<string, mixed> */ |
| 190 | private static function upload(): array |
| 191 | { |
| 192 | return [ |
| 193 | 'upload.mimetypes.file' => [ |
| 194 | 'application/pdf' => ['pdf'], |
| 195 | ], |
| 196 | 'upload.mimetypes.image' => [ |
| 197 | 'image/avif' => ['avif'], |
| 198 | 'image/gif' => ['gif'], |
| 199 | 'image/jpeg' => ['jpeg', 'jpg', 'jpe', 'jfif'], |
| 200 | 'image/png' => ['png'], |
| 201 | 'image/svg+xml' => ['svg'], |
| 202 | 'image/webp' => ['webp'], |
| 203 | ], |
| 204 | 'upload.mimetypes.video' => [ |
| 205 | 'video/mp4' => ['mp4', 'm4v'], |
| 206 | 'video/ogg' => ['ogv', 'ogg'], |
| 207 | 'video/webm' => ['webm'], |
| 208 | ], |
| 209 | 'upload.maxsize' => 10 * 1024 * 1024, |
| 210 | ]; |
| 211 | } |
| 212 | |
| 213 | /** @return array<string, mixed> */ |
| 214 | private static function uid(): array |
| 215 | { |
| 216 | return [ |
| 217 | 'uid.alphabet' => UidGenerator::ALPHABET_LOWERCASE_WORD_SAFE, |
| 218 | 'uid.length' => 13, |
| 219 | ]; |
| 220 | } |
| 221 | |
| 222 | /** @return array<string, mixed> */ |
| 223 | private static function password(Env $env): array |
| 224 | { |
| 225 | return [ |
| 226 | // Off by default: it sends the start of each new password's SHA-1 to a third party. |
| 227 | 'password.breach_check' => $env->bool('PASSWORD_BREACH_CHECK', false), |
| 228 | ]; |
| 229 | } |
| 230 | |
| 231 | /** @return array<string, mixed> */ |
| 232 | private static function queue(Env $env): array |
| 233 | { |
| 234 | return [ |
| 235 | // Run due jobs after each response; turn off where cron or a daemon runs them. |
| 236 | 'queue.after_response' => $env->bool('QUEUE_AFTER_RESPONSE', true), |
| 237 | ]; |
| 238 | } |
| 239 | |
| 240 | /** @return array<string, mixed> */ |
| 241 | private static function mail(Env $env): array |
| 242 | { |
| 243 | $dsn = $env->string('MAILER_DSN'); |
| 244 | |
| 245 | return [ |
| 246 | // Symfony Mailer DSNs by name, the first is the default; MAILER_DSN names `default`. |
| 247 | 'mail.transports' => $dsn !== null && $dsn !== '' ? ['default' => $dsn] : [], |
| 248 | 'mail.from' => $env->string('MAILER_FROM'), |
| 249 | // Addresses that get every mail instead of its recipients, comma-separated in MAILER_REDIRECT. |
| 250 | 'mail.redirect' => $env->string('MAILER_REDIRECT') ?? [], |
| 251 | // Template directories; empty means `mail` in the views directory. |
| 252 | 'mail.views' => [], |
| 253 | ]; |
| 254 | } |
| 255 | } |