Kora/developer documentation/Configuration

Configuration

Declare a settings form and Kora builds it in the dashboard.

A config class describes a settings form. Kora draws the form on the Configs page, saves what the admin enters and gives your code the values. You write no dashboard code.

Put config classes in resources/config/. Each file is one group of settings.

modules/example/resources/config/settings.js
import { Configs } from "#kora/modules";

export class SettingsConfig extends Configs {
    constructor(kora) {
        super(kora, { name: "settings", label: "Example", description: "Sync and alert settings" });
    }

    schema() {
        return {
            api: {
                type: "section",
                label: "API",
                fields: {
                    token: { type: "secret", default: "", label: "API Token" },
                    sync_every: { type: "number", default: 5, label: "Sync Every", unit: "sync_unit" },
                    sync_unit: { type: "select", default: "minutes", label: "Unit", options: [{ value: "minutes", label: "Minutes" }, { value: "hours", label: "Hours" }] }
                }
            },
            alerts: {
                type: "section",
                label: "Alerts",
                fields: {
                    enabled: { type: "boolean", default: false, label: "Send Alerts" },
                    channel: { type: "select", default: "", label: "Channel", source: { module: "discord", endpoint: "/channels", label: "name", value: "id" }, empty: "No channels found", visibleWhen: { field: "enabled", value: true } }
                }
            }
        };
    }
}
OptionWhat it does
nameThe group ID. Used to read and save the values.
labelThe heading shown on the Configs page. Defaults to name.
descriptionA line under the heading.

Field Types

TypeSaved asControl
stringstringText input.
textstringMulti-line text.
secretstringHidden input with a show button.
numbernumberNumber input.
booleanbooleanSwitch.
selectstringDropdown.
multiselectstring[]Dropdown with several picks.
tagsstring[]Free text chips.
colorstringColour picker with a hex value.
listobject[]Rows of sub-fields, added and removed by the admin.

Any other type throws when the module loads. A field can also be a bare value, such as retries: 3, which becomes a number field with that default.

Field Options

OptionWhat it does
defaultThe value before anything is saved.
labelThe field label. Defaults to the key.
options{ value, label } choices for select and multiselect.
sourceLoad choices from a route instead of options. See below.
emptyText shown when source returns nothing. The dropdown stays disabled.
unitThe key of a select to join onto this number, such as minutes or hours.
visibleWhenOnly show the field when a condition is true.
disabledWhenGrey the field out when a condition is true.
itemThe sub-fields of a list.
migrate(stored) => value, used once to fill a new key from older saved settings.

Sections

A section entry groups fields under a heading. It holds its own fields object and is not a value itself, so keys inside it are read as if they were at the top level.

Conditions

visibleWhen and disabledWhen compare another field in the same group:

ConditionTrue when
{ field: "mode", value: "auto" }mode is "auto".
{ field: "mode", not: "off" }mode is anything but "off".
{ field: "mode", in: ["a", "b"] }mode is one of the list.
{ field: "mode", notIn: ["a", "b"] }mode is none of the list.
{ field: "token", notEmpty: true }token has a value.
{ field: "enabled" }enabled is set and not false.

Options From a Route

source fills a dropdown from a route that returns { rows: [...] }:

source: { module: "core", endpoint: "/statuses?valid=false", label: "label", value: "value" }

module defaults to your own module. label and value name the columns to use. Kora adds limit=500 to the request.

Lists

rules: {
    type: "list",
    label: "Rules",
    default: [],
    item: {
        name: { type: "string", default: "", label: "Name" },
        valid: { type: "boolean", default: true, label: "Type", states: ["Allow", "Block"] },
        message: { type: "string", default: "", label: "Message", labelBy: { field: "valid", on: "Allow Message", off: "Block Message" } }
    }
}

List sub-fields take the same options, plus:

OptionWhat it does
statesLabels for a boolean's on and off states.
labelByChange the label based on another sub-field.
wideSpan two columns in the row.

Reading Values

const settings = this.kora.modules.config.values("example", "settings");
const token = this.kora.modules.config.value("example", "settings", "token");

values returns every field in the group, with defaults filled in for anything not saved yet. Values are converted to their type on save, so a number is always a number and tags are always an array.

When an admin saves, Kora fires config.saved with { module, group, values }. Listen for it to react straight away:

export class SettingsSaved extends Events {
    constructor(kora) {
        super(kora, { name: "settingsSaved", event: "config.saved" });
    }

    async run({ module, group, values }) {
        if (module === "example" && group === "settings") await this.kora.modules.handlers.get("example", "sync").restart(values);
    }
}

Settings are stored in the database with the rest of Kora's settings, under module:<id>.