Configuration
The configuration effort to run Strapi the way we need it are minimal. All configuration files for Strapi can be found in the project directories config folder. Further information about Strapi's configuration can be found here. In the following only changes made to the standard configuration are highlighted.
The theme and logos of the Admin Panel are configured in the src/admin/app.js file.
Color theme & Logos (Admin Panel)
The complete theming of the Admin Panel is implemented in the src/admin/app.js file. Colors, favicon, logos of the Admin Panel itself as well as logo and text of the login window are defined here:
config: {
translations: {
en: {
"Auth.form.welcome.title": "KLHHH Admin Panel",
"Auth.form.welcome.subtitle": "Log in Mis(s)tress if you dare to!",
},
},
auth: { // Replace the Strapi logo in auth (login) views
logo: AuthLogo,
},
menu: { // Replace the Strapi logo in the main navigation
logo: MenuLogo,
},
head: {
favicon: favicon,
},
theme: {
light: { // Replace the Strapi primary colors
colors: {
primary100: '#f5b6da',
primary200: '#f186c0',
primary500: '#ef0078',
primary600: '#dd0074',
primary700: '#c7006e',
buttonNeutral0: '#ffffff',
buttonPrimary500: '#ef0078',
buttonPrimary600: '#dd0074',
}
},
dark: { // Replace the Strapi primary colors
colors: {
primary100: '#f5b6da',
primary200: '#f186c0',
primary500: '#ef0078',
primary600: '#dd0074',
primary700: '#c7006e',
buttonNeutral0: '#ffffff',
buttonPrimary500: '#ef0078',
buttonPrimary600: '#dd0074',
}
},
},The Admin Panel's look and feel can be adjusted by adding pages to /src/admin and imagery to src/admin/extensions where we place icons/images and also the custom homepage :
.
├── app.js
├── extensions
│ ├── bunnies_header.svg
│ ├── bunnies_header-white.svg
│ ├── bunny.svg
│ ├── favicon.png
│ └── favicon_red.png
├── pages
│ ├── custom-welcome
│ │ └── Homepage.tsx
│ └── MetabaseDashboard.jsx
└── vite.config.example.jsThe header logo on the Custom Welcome Page is set in its implementation file src/admin/pages/custom-welcome/Homepage.tsx:
import React from "react";
import bunniesHeader from "../../extensions/bunnies_header-white.svg";
import MetabaseDashboard from "../MetabaseDashboard";
const Homepage = () => {
return (
<div
style={{
textAlign: "center",
backgroundColor: "#ad1457",
padding: "20px",
borderRadius: "15px",
fontFamily: "Arial, sans-serif",
fontSize: "28px",
margin: "50px auto",
color: "white",
fontWeight: "bold",
width: "80%",
position: "relative", // Allows positioning of child elements
}}
>
{/* SVG Header */}
<img
src={bunniesHeader}
alt="Bunnies Header"
style={{
display: "block",
margin: "0 auto 20px auto", // Centers the SVG and adds space below
width: "100%", // Responsive width
maxWidth: "700px", // Limits size for large screens
}}
/>
{/* Headline */}
Welcome to the MissManagement System!
<MetabaseDashboard/>
</div>
);
};
export { Homepage };Plugins plugins.js
We have to configure the additionally installed plugins (see here) and make a small change to the plugins installed by Strapi itself.
Allow the login token for a user to be valid for 90 days (Strapi permissions plugin):
'users-permissions': {
config: {
jwt: {
expiresIn: '90d',
},
},
},Configure Mailgun email plugin:
email: {
config: {
provider: 'mailgun',
providerOptions: {
key: env('MAILGUN_API_KEY'), // Required
domain: env('MAILGUN_DOMAIN'), // Required
url: env('MAILGUN_URL', 'https://api.mailgun.net'), //Optional. If domain region is Europe use 'https://api.eu.mailgun.net'
},
settings: {
defaultFrom: 'no-reply@mailgun.klharriettes.org',
defaultReplyTo: 'no-reply@mailgun.klharriettes.org',
},
},
},Configure Email Designer 5 plugin:
"email-designer-5": {
enabled: true,
config: {
mergeTags: {
company: {
name: "KL Hash House Harriettes",
mergeTags: {
name: {
name: "KL Hash House Harriettes",
value: "KLH3",
sample: "KLH3",
},
},
},
},
},
},Configure Strapi V5 Plugin Populate Deep:
'strapi-v5-plugin-populate-deep': {
config: {
defaultDepth: 3, // Default is 5
}
},Middleware middlewares.js
Security
To allow the Email Designer 5 plugin's editor to work properly we need to allow it as script source. Also, we need the frame-src to allow rendering iFrames from the same plugin but also from the Metabase reporting to show the dashboard on the Admin Panel's homepage
{
name: 'strapi::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
// Default directives
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'", "editor.unlayer.com"],
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", "data:"],
'font-src': ["'self'"],
'connect-src': ["'self'"],
'frame-src': ["'self'", 'https://reporting.klharriettes.org', "editor.unlayer.com"], // Allow Superset embedding
'frame-ancestors': ["'none'"], // Prevent embedding elsewhere
upgradeInsecureRequests: null,
},
},
},
},X-Cached-At header
As outlined here the frontend needs to be enabled to identify "cache-freshness" accurately.
An additional header flag X-Cached-At is hence added to every API response. As this shall be applied to all endpoints, the respective code is created in the folder src/middlewares. All files / route manipulations placed here will be applied to all API responses. The code itself is very straightforward:
module.exports = (config, { strapi }) => {
return async (ctx, next) => {
await next(); // Wait for the response to be prepared
// Add custom header to all successful responses
if (ctx.response && ctx.response.status < 400) {
const now = new Date().toUTCString();
ctx.set('X-Cached-At', now);
}
};
};However, the additional header needs to be added to the allowed CORS headers and additionally needs to be exposed - browsers do not allow javascript access to most response headers except they are explicitly exposed. For the CORS configuration details see the code snippet below (relevant lines highlighted)
Cross-Origin Resource Sharing (CORS)
CORS is a security measure to prevent Strapi to be accessed by unsafe other servers. Allowed origin is set by an environment variable to the website frontend and the allowed methods and headers are configured to the website frontend's requirements (especially adding our non-standard X-Idempotency-Key to the headers)
{
name: 'strapi::cors',
config: {
origin: (process.env.ALLOWED_ORIGINS || '').split(','),
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
headers: ['Content-Type', 'Authorization', 'X-Idempotency-Key', 'X-Cached-At'], // Add the header here
expose: ['X-Cached-At'],
keepHeaderCase: true,
},
},Server server.js
In the server configuration we mainly set the cron tasks and a few standard keys
const anonymizeGuests = require('./cron-tasks.js');
const updateHash = require('./cron-tasks.js');
const sendSignInSheetToHare = require('./cron-tasks.js');
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
url: env('PUBLIC_URL', 'http://klhhh-neu:1337'),
app: {
keys: env.array('APP_KEYS'),
},
cron: {
enabled: true,
tasks: anonymizeGuests, updateHash, sendSignInSheetToHare
},
webhooks: {
populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false),
},
});