Website Docs
Important URLs
dev: https://ui.dev.pokersnowie.com/
prod: https://ui.prod.pokersnowie.com/
Strapi cms: https://cms.dev.pokersnowie.com/admin
Introduction to Strapi CMS
Overview
Strapi is an open-source headless CMS built with Node.js. It allows you to manage content through a user-friendly admin panel and provides a robust API to serve the content.
Navigating the Dashboard
Content Manager: Manage your content types and entries here.
Content-Type Builder: Define and manage the structure of your content types.
Media Library: Manage and upload media assets.
Managing Content in Strapi
Content Manager
The Content Manager is where you handle the actual content entries for each content type. It is divided into several collection types such as Blog, Footer, Header, Page, and User.
Viewing Entries
Navigate to the Content Manager.
Select a content type (e.g., Page).
View the list of entries for the selected content type.
Filtering and Searching
Use the search bar and filter options to find specific entries quickly.
Content-Type Builder
The Content-Type Builder is used to define and manage the schema for your content types. You can create new collection types or single types and add fields to them.
Creating a New Content Type
Navigate to Content-Type Builder.
Click on Create new collection type.
Define the fields and their types (text, boolean, component, etc.).
Save the content type.
Modifying Existing Content Types
Navigate to Content-Type Builder.
Select the content type you want to modify.
Add or edit fields as needed.
Save your changes.
Example of a Page Collection
A Page collection type includes fields like shortName, metadata, and contentSections. The contentSections field is a dynamic zone that can hold various components. The example of this can be found on the Strapi CMS.
Components
Components are reusable content structures that can be used across different content types. They help maintain consistency and reduce redundancy.
Adding/Modifying Pages
Creating a New Page
Navigate to the Content Manager.
Select Page under Collection Types.
Click on Create new entry.
Fill in the fields:
shortName: A short identifier for the page.
metadata: Any additional metadata for the page.
contentSections: Add components to this dynamic zone.
Click Save to create the page.
Editing Existing Pages
Navigate to the Content Manager.
Select Page under Collection Types.
Choose the page you want to edit from the list.
Make the necessary changes.
Click Save to update the page.
Adding Components to a Page
Within the Page entry, find the contentSections field.
Click on Add a component.
Select the desired component (e.g., Accordian, Carousel).
Fill in the component fields as needed.
Click Save to add the component to the page.
Managing Components
Adding a New Component
Navigate to Content-Type Builder.
Under Components, click Create new component.
Define the component fields and their types (e.g., text, boolean).
Save the component.
Modifying Existing Components
Navigate to Content-Type Builder.
Select the component you want to modify.
Edit the fields as necessary.
Save the changes.
Example Component: Accordian
import * as S from './styled';
import AccordianRow from './../accordian-row';
type ItemList = {
header: string;
text: string;
};
type AccordianProps = {
id?: string;
header: string;
accordianItem: ItemList[];
};
const Accordian = ({ header, accordianItem }: AccordianProps) => {
return (
<S.Wrapper>
<S.Title>{header}</S.Title>
{accordianItem.map((item, i) => (
<AccordianRow key={i} header={item.header} text={item.text} />
))}
</S.Wrapper>
);
};
export default Accordian;Frontend Components and Integration
Component Structure
Frontend components are located in the pokersnowie-client/components/{component_name}/ directory. Each component typically has:
index.tsx: Contains the component logic.
styled.tsx: Contains the component styling.
Props and Data Flow
Components receive props containing data, which are fetched from Strapi CMS. The pokersnowie-client/pages/[[...slug]].tsx file handles the routing and data fetching.
Example of Data Fetching with Apollo Client
import { ApolloClient, gql, HttpLink, InMemoryCache } from '@apollo/client';
import { GetStaticPaths } from 'next';
import fetch from 'cross-fetch';
import { server } from '../config';
export async function getStaticProps(context: any) {
const client = new ApolloClient({
link: new HttpLink({ uri: `${server}/graphql?populate=*`, fetch }),
cache: new InMemoryCache(),
});
const { data } = await client.query({
query: gql`
query {
pages(filters: { slug: { eq: "${context.params.slug?.join('/') || '/'}"} }, locale: "${context.locale}") {
data {
attributes {
shortName
slug
headerType
metaTitle
footerType
protected
contentSections {
__typename
// ... other sections
}
}
}
}
}
`,
});
return {
props: {
//...data processing
},
};
}Data Fetching with GraphQL
Setting Up Apollo Client
Apollo Client is used to fetch data from the Strapi CMS using GraphQL. It is configured with an HTTP link and in-memory cache.
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
import fetch from 'cross-fetch';
const client = new ApolloClient({
link: new HttpLink({ uri: `${process.env.NEXT_PUBLIC_CMS_ENDPOINT}/graphql`, fetch }),
cache: new InMemoryCache(),
});
Fetching Data
Data is fetched in the getStaticProps function, which runs at build time to fetch data for static generation.
export async function getStaticProps(context: any) {
const client = new ApolloClient({
link: new HttpLink({ uri: `${process.env.NEXT_PUBLIC_CMS_ENDPOINT}/graphql`, fetch }),
cache: new InMemoryCache(),
});
const slug = context.params.slug?.join('/') || '/';
const { data } = await client.query({
query: gql`
query {
pages(filters: { slug: { eq: "${slug}" } }, locale: "${context.locale}") {
data {
id
attributes {
shortName
slug
headerType
metaTitle
footerType
protected
contentSections {
__typename
// ... other sections
}
}
}
}
}
`,
});
return {
props: {
page: data.pages.data[0]?.attributes || {},
},
};
}Setting Up the Environment
Environment variables are essential for configuring the application. These variables are defined in a .env file in the root of your project.
Example .env File:
NEXT_PUBLIC_CMS_ENDPOINT=https://cms.example.com
NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID=your-google-client-id
NEXT_PUBLIC_WEBCIS_API=https://api.example.com
NEXT_PUBLIC_WEBCIS_SECRET=your-webcis-secret
REGISTRY_URL=https://registry.example.com
REGISTRY_USER=username
REGISTRY_PSW=password
CI_PROJECT_NAME=project-name
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=your-recaptcha-site-key
RECAPTCHA_SECRET_KEY=your-recaptcha-secret-key
MAILCHIMP_LIST_ID=your-mailchimp-list-id
MAILCHIMP_API_KEY=your-mailchimp-api-key
Explanation:
NEXT_PUBLIC_CMS_ENDPOINT: URL for the CMS backend.NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID: Google OAuth client ID for authentication.NEXT_PUBLIC_WEBCIS_API: URL for the WebCIS API.NEXT_PUBLIC_WEBCIS_SECRET: Secret key for WebCIS API.REGISTRY_URL: Docker registry URL.REGISTRY_USER: Docker registry username.REGISTRY_PSW: Docker registry password.CI_PROJECT_NAME: Project name for CI/CD.NEXT_PUBLIC_RECAPTCHA_SITE_KEY: Google reCAPTCHA site key.RECAPTCHA_SECRET_KEY: Google reCAPTCHA secret key.MAILCHIMP_LIST_ID: Mailchimp list ID.MAILCHIMP_API_KEY: Mailchimp API key.
Ensure that this file is properly configured before proceeding with the deployment.
Building and Running the Application
Dockerfiles
Dockerfiles define the environment in which the application runs. We have different Dockerfiles for development and production.
Development Dockerfile
The development Dockerfile sets up the environment for development, including all necessary dependencies and starting the application in development mode.
Dockerfile:
FROM node:18 AS builder
# Set work directory
WORKDIR /usr/app
# Copy package files and install dependencies
COPY ./package*.json ./
RUN npm ci
# Copy the rest of the application files
COPY . .
# Declare environment variables
ARG google_oauth_client_id
ARG cms_endpoint
ARG webcis_api
ARG webcis_secret
ARG registry_url
ARG registry_user
ARG registry_password
ARG project_name
ARG recaptcha_site_key
ARG recaptcha_secret_key
ARG mailchimp_list_id
ARG mailchimp_api_key
# Set environment variables
ENV NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID=$google_oauth_client_id
ENV NEXT_PUBLIC_CMS_ENDPOINT=$cms_endpoint
ENV NEXT_PUBLIC_WEBCIS_API=$webcis_api
ENV NEXT_PUBLIC_WEBCIS_SECRET=$webcis_secret
ENV REGISTRY_URL=$registry_url
ENV REGISTRY_USER=$registry_user
ENV REGISTRY_PSW=$registry_password
ENV CI_PROJECT_NAME=$project_name
ENV NEXT_PUBLIC_RECAPTCHA_SITE_KEY=$recaptcha_site_key
ENV RECAPTCHA_SECRET_KEY=$recaptcha_secret_key
ENV MAILCHIMP_LIST_ID=$mailchimp_list_id
ENV MAILCHIMP_API_KEY=$mailchimp_api_key
# Expose the port
EXPOSE 3000
# Build the application
CMD npm run dev
Explanation:
Base Image: Uses Node.js version 18 as the base image.
Working Directory: Sets the working directory to
/usr/app.Copy and Install Dependencies: Copies
package.jsonfiles and installs dependencies usingnpm ci.Copy Application Files: Copies the rest of the application files to the container.
Environment Variables: Declares and sets environment variables.
Expose Port: Exposes port 3000 for accessing the application.
Start Application: Run
npm run devto start the application in development mode.
Production Dockerfile
The production Dockerfile is optimized for production deployment. It includes stages for installing dependencies, building the application, and setting up the runtime environment.
Prod_Dockerfile:
# Install dependencies only when needed
FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Production image, copy all the files and run next
FROM node:16-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]Explanation:
Multi-stage Builds: Uses multi-stage builds to optimize the image size.
Dependencies Stage: The
depsstage installs dependencies.Build Stage: The
builderstage builds the application.Runner Stage: The
runnerstage sets up the production environment, copying necessary files from the build stage and running the application.
Deployment Process
Development Deployment
For development deployment, we use the Dockerfile and the deploy.sh script. This script automates the process of building, pushing, and deploying the Docker image.
deploy.sh Script for Development
#!/bin/bash
# Load environment variables from .env file
while read -r variableDeclaration; do
export "${variableDeclaration?}"
done <.env
# Build the Docker image
docker build -t "$REGISTRY_URL/$CI_PROJECT_NAME" \
--build-arg google_oauth_client_id="$NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID" \
--build-arg cms_endpoint="$NEXT_PUBLIC_CMS_ENDPOINT" \
--build-arg webcis_api="$NEXT_PUBLIC_WEBCIS_API" \
--build-arg webcis_secret="$NEXT_PUBLIC_WEBCIS_SECRET" \
--build-arg registry_url="$REGISTRY_URL" \
--build-arg registry_user="$REGISTRY_USER" \
--build-arg registry_password="$REGISTRY_PSW" \
--build-arg project_name="$CI_PROJECT_NAME" \
--build-arg recaptcha_site_key="$NEXT_PUBLIC_RECAPTCHA_SITE_KEY" \
--build-arg recaptcha_secret_key="$RECAPTCHA_SECRET_KEY" \
--build-arg mailchimp_list_id="$MAILCHIMP_LIST_ID" \
--build-arg mailchimp_api_key="$MAILCHIMP_API_KEY" .
# Log in to the Docker registry
docker login -u "$REGISTRY_USER" -p "$REGISTRY_PSW" "$REGISTRY_URL"
# Push the Docker image to the registry
docker push "$REGISTRY_URL/$CI_PROJECT_NAME"
# SSH into the server and deploy the Docker image using docker-compose
ssh pokersnowie@54.155.78.158 "docker login -u $REGISTRY_USER -p $REGISTRY_PSW $REGISTRY_URL && \
cd pokersnowie-docker/ui && \
docker-compose down --rmi all && \
docker-compose up -d"Explanation:
Load Environment Variables: Reads the
.envfile and exports the variables.Build Docker Image: Uses
docker buildto create the Docker image, passing necessary build arguments.Log in to Docker Registry: Logs into the Docker registry using provided credentials.
Push Docker Image: Pushes the Docker image to the registry.
Deploy on Server: SSHs into the server, logs into the Docker registry, and uses
docker-composeto deploy the application.