2026-09-11 02:46:56 +02:00
|
|
|
import type { Core } from '@strapi/strapi';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Media types the Media Library accepts.
|
|
|
|
|
*
|
|
|
|
|
* The site publishes reports, guidance notes and event material, so office
|
|
|
|
|
* documents and PDFs are allowed next to the usual image/video/audio types.
|
|
|
|
|
*/
|
|
|
|
|
const allowedMediaTypes = [
|
|
|
|
|
'image/*',
|
|
|
|
|
'video/*',
|
|
|
|
|
'audio/*',
|
|
|
|
|
'application/pdf',
|
|
|
|
|
'application/msword',
|
|
|
|
|
'application/vnd.openxmlformats-officedocument.*',
|
|
|
|
|
'text/plain',
|
|
|
|
|
'text/csv',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Types that are never accepted, even if they slip past `allowedMediaTypes`.
|
|
|
|
|
* SVG is denied on purpose: it can carry script and is served from the same
|
|
|
|
|
* origin as the API.
|
|
|
|
|
*/
|
|
|
|
|
const deniedTypes = [
|
|
|
|
|
'image/svg+xml',
|
|
|
|
|
'application/vnd.microsoft.portable-executable',
|
|
|
|
|
'application/x-msdownload',
|
|
|
|
|
'application/x-msdos-program',
|
|
|
|
|
'application/x-executable',
|
|
|
|
|
'application/x-dosexec',
|
|
|
|
|
'application/x-sh',
|
|
|
|
|
'text/x-shellscript',
|
|
|
|
|
'application/x-mach-binary',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The locales the site ships with.
|
|
|
|
|
*
|
|
|
|
|
* `en` is the default (LTR) and `ar` is the right-to-left translation. The web
|
|
|
|
|
* app mirrors these codes in its URL prefixes (`/en/...`, `/ar/...`), so the
|
|
|
|
|
* codes here are part of the public contract - do not rename them.
|
|
|
|
|
*
|
|
|
|
|
* The i18n plugin itself does not read this key; it is our own declaration and
|
|
|
|
|
* is read back at boot by `src/bootstrap/locales.ts`, which creates any missing
|
|
|
|
|
* locale and pins the default one. Adding a locale is therefore a one-line edit
|
|
|
|
|
* here plus a restart.
|
|
|
|
|
*/
|
|
|
|
|
const locales = [
|
|
|
|
|
{ code: 'en', name: 'English (en)', isDefault: true },
|
|
|
|
|
{ code: 'ar', name: 'Arabic (ar)' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Plugin => ({
|
|
|
|
|
/**
|
|
|
|
|
* Internationalisation - core plugin, listed explicitly so the two supported
|
|
|
|
|
* locales live in version control rather than only in the database.
|
|
|
|
|
*/
|
|
|
|
|
i18n: {
|
|
|
|
|
enabled: true,
|
|
|
|
|
config: {
|
|
|
|
|
defaultLocale: 'en',
|
|
|
|
|
locales,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
'users-permissions': {
|
|
|
|
|
config: {
|
|
|
|
|
jwtSecret: env('JWT_SECRET'),
|
|
|
|
|
/**
|
|
|
|
|
* "refresh" uses the SessionManager (short-lived access token + refresh
|
|
|
|
|
* token) instead of the legacy long-lived plugin JWT.
|
|
|
|
|
*/
|
|
|
|
|
jwtManagement: 'refresh',
|
|
|
|
|
sessions: {
|
|
|
|
|
// Refresh token is stored in a cookie that JavaScript cannot read.
|
|
|
|
|
httpOnly: true,
|
|
|
|
|
},
|
|
|
|
|
/**
|
|
|
|
|
* Rate limit on the auth endpoints (login, forgot-password, ...).
|
|
|
|
|
* Tightened from the Strapi default of 10 requests per minute.
|
|
|
|
|
*/
|
|
|
|
|
ratelimit: {
|
|
|
|
|
interval: env.int('AUTH_RATE_LIMIT_INTERVAL', 60_000),
|
|
|
|
|
max: env.int('AUTH_RATE_LIMIT_MAX', 5),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Uploads stay on the default `local` provider: files land in
|
|
|
|
|
* `apps/cms/public/uploads` and are served by `strapi::public`.
|
|
|
|
|
*
|
|
|
|
|
* TO SWITCH TO S3 IN PRODUCTION
|
|
|
|
|
* -----------------------------
|
|
|
|
|
* 1. Add the provider package to apps/cms/package.json:
|
|
|
|
|
* "@strapi/provider-upload-aws-s3": "5.53.0"
|
|
|
|
|
* 2. Replace the `provider` / `providerOptions` keys below with:
|
|
|
|
|
*
|
|
|
|
|
* provider: 'aws-s3',
|
|
|
|
|
* providerOptions: {
|
|
|
|
|
* baseUrl: env('CDN_URL'), // optional CloudFront/CDN origin
|
|
|
|
|
* rootPath: env('S3_ROOT_PATH'), // optional key prefix
|
|
|
|
|
* s3Options: {
|
|
|
|
|
* credentials: {
|
|
|
|
|
* accessKeyId: env('AWS_ACCESS_KEY_ID'),
|
|
|
|
|
* secretAccessKey: env('AWS_ACCESS_SECRET'),
|
|
|
|
|
* },
|
|
|
|
|
* region: env('AWS_REGION'),
|
|
|
|
|
* params: {
|
|
|
|
|
* ACL: env('AWS_ACL', 'private'),
|
|
|
|
|
* signedUrlExpires: env.int('AWS_SIGNED_URL_EXPIRES', 15 * 60),
|
|
|
|
|
* Bucket: env('AWS_BUCKET'),
|
|
|
|
|
* },
|
|
|
|
|
* },
|
|
|
|
|
* },
|
|
|
|
|
* actionOptions: { upload: {}, uploadStream: {}, delete: {} },
|
|
|
|
|
*
|
|
|
|
|
* 3. Add the bucket/CDN host to the Content Security Policy in
|
|
|
|
|
* `config/middlewares.ts` (`img-src` and `media-src`), otherwise the admin
|
|
|
|
|
* panel shows broken thumbnails.
|
|
|
|
|
* 4. Set the AWS_* / CDN_URL variables listed in `.env.example`.
|
|
|
|
|
*/
|
|
|
|
|
upload: {
|
|
|
|
|
config: {
|
|
|
|
|
provider: 'local',
|
|
|
|
|
providerOptions: {
|
|
|
|
|
// Cache-Control max-age (seconds) sent for locally served uploads.
|
|
|
|
|
localServer: {
|
|
|
|
|
maxage: env.int('UPLOAD_LOCAL_MAX_AGE', 300_000),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
// Hard ceiling per file, in bytes. Defaults to 50 MB.
|
|
|
|
|
sizeLimit: env.int('UPLOAD_MAX_FILE_SIZE', 50 * 1024 * 1024),
|
|
|
|
|
security: {
|
|
|
|
|
allowedTypes: allowedMediaTypes,
|
|
|
|
|
deniedTypes,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-09-22 10:24:03 +02:00
|
|
|
email: {
|
|
|
|
|
config: {
|
|
|
|
|
provider: "@strapi/provider-email-nodemailer",
|
|
|
|
|
providerOptions: {
|
2026-09-22 12:11:55 +02:00
|
|
|
host: env("SMTP_HOST", "mail.sharper.ly"),
|
2026-09-22 10:24:03 +02:00
|
|
|
port: env("SMTP_PORT", 587),
|
2026-09-22 12:11:55 +02:00
|
|
|
secure: true,
|
2026-09-22 10:24:03 +02:00
|
|
|
auth: {
|
|
|
|
|
user: env("SMTP_USERNAME"),
|
|
|
|
|
pass: env("SMTP_PASSWORD"),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
settings: {
|
2026-09-22 12:11:55 +02:00
|
|
|
defaultFrom: "SHARP-ER <noreply@sharper.ly>",
|
|
|
|
|
defaultReplyTo: "United Efforts <info@unitedefforts.ngo>"
|
2026-09-22 10:24:03 +02:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
2026-09-11 02:46:56 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export default config;
|