Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
141 / 141
100.00% covered (success)
100.00%
17 / 17
CRAP
100.00% covered (success)
100.00%
1 / 1
Defaults
100.00% covered (success)
100.00%
141 / 141
100.00% covered (success)
100.00%
17 / 17
19
100.00% covered (success)
100.00%
1 / 1
 values
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
1
 app
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 auth
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 paths
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 panel
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 api
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 error
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 icons
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 database
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
 session
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 media
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 richtext
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 upload
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
1
 uid
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 password
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 queue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 mail
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Config;
6
7use Cosray\Uid as UidGenerator;
8
9use function Cosray\env;
10
11final 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}