Skip to content

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:

js
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.js

The header logo on the Custom Welcome Page is set in its implementation file src/admin/pages/custom-welcome/Homepage.tsx:

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):

js
'users-permissions': {
  config: {
    jwt: {
      expiresIn: '90d',
    },
  },
},

Configure Mailgun email plugin:

js
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:

js
"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:

js
'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

js
{
  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:

js
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)

js
{
  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

js
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),
  },
});

Released under the MIT License.