PageDepot

Installation

Clone the PageDepot repository. You must also install the following components:

  • Redis

Install dependencies:

npm install

Go to the templates directory and install dependencies for each template:

cd templates/astrowind && npm install

Configuration

Fill out your PageDepot .env:

APP_ENV=local
DEPOT_SECRET=<choose a secret>
# WEBSOCKET_PORT=6001
# PORT=3000

# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379
REDIS_PASSWORD=<specify the password>

# Only needed when you want to use ingress management in your Kubernetes cluster:
# DEPOT_BASE_DOMAIN=kab.page
# DEPOT_INGRESS_NAMESPACE=page-depot
# DEPOT_INGRESS_NAME=external-domains
# DEPOT_SERVICE_NAME=page-depot

# Only needed when you want to use hostnames in your Cloudflare zones:
# CLOUDFLARE_ZONE_ID=
# CLOUDFLARE_TOKEN=

In your Brezel .env, specify the following environment variables:

  • PAGE_DEPOT_URL: the full URL to the PageDepot service. Usually https://kab.page.
  • PAGE_DEPOT_WS_HOST: the hostname of the PageDepot websockets service. Usually ws.kab.page.
  • PAGE_DEPOT_SECRET: the secret used to access the PageDepot service (DEPOT_SECRET).

Starting

To start the web and websocket servers, run the following inside PageDepot:

npm run start

Usage in Brezel

You can use PageDepot in Brezel via the action/buildSite and action/buildSite workflow elements.

For building a site, you need the following modules:

Templates

A template has a name and a base template. It is just a logical entity to specify what base template a layout should use. The actual contents and structures of base templates are not configurable. But choosing the base template has consequences for how to write the code of sections and choosing a **base layout **.

Available base templates in Astro are:

  • Astrowind (astrowind)

Layouts

A layout has a name, a base layout, scripts and slots. The base layout always refers to a layout file inside the base template. This file is usually an Astro file that defines slots that you can fill. While the layout can fill slots defined by the base layout, the layout can also provide slots to be filled by pages that inherit from it.

Available base layouts are:

Astrowind

PageLayout.astro (extends BaseLayout.astro)

Slots: announcements, header, default, footer

This is the recommended base layout.

BaseLayout.astro

Slots: default

MarkdownLayout.astro

Slots: default

Pages

Pages define what pages of the static site are generated. A page has a name, a layout, slots, parameters as well as a title, description and scripts.

Layout

The layout specifies what layout the page inherits, and thus also what slots the page can fill.

Slots

The slots specify the filling of the slots of the parent layout with sections, and what parameters the sections are initialized with.

The slot name defines what slot should be overwritten. Each slot can have a list of sections. Each section, in turn, is an object that has a section definition, section parameters, and section subslots. The section definition is an entity of the module defining the sections (usually called sections). The Section parameters are the inputs to that section, and correspond to the section entity’s props.

Example section entity “Post”:

{
  "id": 10,
  "name": "Post",
  "type": "vue",
  "props": {
    "post": {
      "type": "select",
      "options": {
        "references": "posts"
      }
    }
  },
  "code": "<template><div>{{ post.title }}</div></template><script>export default { props: { post: Object } }</script>"
}

Example value of a page’s slots where this section entity is used:

[
  {
    "slot": "default",
    "sections": [
      {
        "section": {
          "id": 10
        },
        "params": {
          "post": {
            "id": 42
          }
        }
      }
    ]
  }
]

This means that the default slot in the page’s layout is filled with one section, a “Post”, with its post prop filled with the post entity 42.

Name

The name defines the route where the page will be accessible. For example, the name posts will make the page available at https://mysite.kab.page/posts. The name posts/[lang]/[post] will, for example, make the page available at https://mysite.kab.page/posts/en/lorem-ipsum, depending on parameters given for [lang] and [post].

Parameters

What parameter values you can have in your page URL is configured using the parameters. But, because PageDepot uses Astro, and Astro is mainly a static site builder, all possible parameter values must be known at build time. Therefore, Astro needs to be told every combination of parameters that should be possible. Fortunately, you don’t actually need to write every combination down, you can just specify the possible parameter values. PageDepot generates the combinations for Astro when building your site.

Instead, you can add a post parameter and make it refer to the posts module, and say that the parameter value comes from the slug field. You can also add a lang parameter for example, where you define the value en, and then add a new entry for lang, where you define de. Assuming you have posts with slugs lorem-ipsum, dolor and oppenheimer, the resulting paths would be:

  • /posts/en/lorem-ipsum
  • /posts/de/lorem-ipsum
  • /posts/en/dolor
  • /posts/de/dolor
  • /posts/en/oppenheimer
  • /posts/de/oppenheimer

However, even moderate amounts of parameters can add up to huge amounts of combinations, so much that even when PageDepot generates them for you, the runtime and memory requirements become too high to handle. In that case, you need server-side rendering (SSR) for your page. This has the benefit of evaluating each parameter when the page is accessed, instead of generating them all in advance. Currently, the SSR feature is under construction.

Sections

A section is the most basic building block, and is basically nothing more than a component in Astro or Vue. In fact, a section in PageDepot is directly converted to a component file during the provisioning step, be it an .astro file or .vue single-file-component.

Sections have a name, a type, props and code.

The type can be one of astro or vue. It decides what type of code you can write.

The props define the input parameters your section can receive. This is used for Brezel to be able to define a fitting UX to edit these props. It is also used by PageDepot to prepare the prop values accordingly. For example, if the prop’s type is select, PageDepot loads the respective entity from Brezel. The syntax is

{
  "propName": {
    "type": "propType"
  }
}

or its shorthand

{
  "propName": "propType"
}

For types like object or list, you can nest prop definitions:

{
  "myObject": {
    "type": "object",
    "props": {
      "someProp": "text"
    }
  },
  "myList": {
    "type": "list",
    "listType": {
      "type": "object",
      "props": {
        "someProp": "text"
      }
    }
  }
}

Then, when using the section in a page slot, you would give the params like this:

[
  {
    "slot": "default",
    "sections": [
      {
        "section": {
          "id": 10
        },
        "myObject": {
          "someProp": "lorem ipsum"
        },
        "myList": [
          {
            "someProp": "dolor sit amet"
          }
        ]
      }
    ]
  }
]

Available types

Text (text)
{
  "type": "text"
}
Number (number)
{
  "type": "number"
}
Object (object)
{
  "type": "object",
  "props": {} // define the object's props here
}
List (list)
{
  "type": "list",
  "listType": {} // define the list item type here
}
Entity (select)
Usage in props
{
  "type": "select",
  "options": {
    "references": "the_module"
  }
}
Type schema for params

An entity object with id.

type EntityParams = { 
  id: string | number 
};
Entities (entities)
Usage in props

An object with optional property module (the module identifier).

{
  "type": "entities",
  "module": "the_module"
}
Example for section code
const entities = props.entities;
Type schema for params
type EntitiesParams = {
  module?: string;
  filters?: Array<Record<string, unknown>> | Array<Array<Record<string, unknown>>>;
  results?: number;
}
Module (module)
Example for section code
const module = props.module;
module.fields.forEach(field => {
    console.log(field);
})
Type schema for params
type ModuleParams = { 
  identifier: string;
};
Image (image)
Example for section code
const {src, alt} = props.image;
Type schema for params
type ImageParams = {
  id: string | number;
  module?: string;
}

Code

The code is the actual code that defines your section. Here, you write the markup and any logic you need for the rendering. You also need to define the props again. For example:

<template>
  <article>
    <h3>{{ post.title }}</h3>
    <WYSIWYG :content="post.content"/>
    <ShareButtons :post="post"/>
  </article>
</template>

<script setup>
import {defineProps} from 'vue';
import WYSIWYG from '~/components/WYSIWYG.vue';
import ShareButtons from '~/components/sections/ShareButtons.vue';

const props = defineProps({
  post: Object,
  astro: Object,
});
</script>

Here, there is also the astro property, which gives access to the global Astro object . You can also import other sections you have defined, like ShareButtons in this case.