NAME

Concierge::Setup - One-time desk creation and configuration for Concierge

VERSION

v0.5.6

SYNOPSIS

use Concierge::Setup;

# Simple setup -- database backends, all standard user fields
my $result = Concierge::Setup::build_quick_desk(
    './desk',
    ['role', 'theme'],       # application-specific user fields
);

# Advanced setup -- full control over backends and field configuration
my $result = Concierge::Setup::build_desk({
    storage => {
        base_dir     => './desk',
        sessions_dir => './desk/sessions',
        users_dir    => './desk/users',
    },
    auth => {
        file => './desk/auth.pwd',
    },
    sessions => {
        backend => 'database',  # or 'file'
    },
    users => {
        backend                 => 'database',  # 'database', 'yaml', or 'file'
        include_standard_fields => [qw/email phone first_name last_name/],
        app_fields              => ['membership_tier', 'department'],
        field_overrides         => [{ field_name => 'email', required => 1 }],
    },
});

DESCRIPTION

Concierge::Setup provides methods for one-time initialization of a Concierge desk -- the storage directory containing configuration and data files for the identity core components (Auth, Sessions, Users).

Setup is separate from runtime operations. Use this module once to create a desk, then use "open_desk" in Concierge at runtime.

The configuration structure passed to build_desk() is organized by component (auth, sessions, users). Applications that introduce additional components under the Concierge:: namespace can extend this structure with their own configuration blocks, following the same pattern. See "Architecture" in Concierge for details.

The ./desk Convention

If $storage_dir is '.', './', or an empty string, it is automatically converted to './desk' to avoid cluttering the application root directory.

METHODS

build_quick_desk

my $result = Concierge::Setup::build_quick_desk(
    $storage_dir,
    \@app_fields,
);

Creates a desk with opinionated defaults: SQLite backends for both Sessions and Users, all standard user fields included, all storage co-located in $storage_dir. The password file (auth.pwd) is created automatically inside $storage_dir.

Parameters:

$storage_dir (required) -- directory for all data files; created if it does not exist
\@app_fields -- additional user data fields beyond the standard set

Returns { success => 1, desk => $desk_location } on success, or { success => 0, message => '...' } on failure.

build_desk

my $result = Concierge::Setup::build_desk(\%config);

Creates a desk with full control over backend selection, storage layout, and user field configuration.

Configuration structure:

{
    storage => {
        base_dir     => $path,       # required
        sessions_dir => $path,       # default: base_dir
        users_dir    => $path,       # default: base_dir
    },
    auth => {
        file => $path,               # default: base_dir/auth.pwd
    },
    sessions => {
        backend => 'database',       # 'database' or 'file'
    },
    users => {
        backend                 => 'database',  # 'database', 'yaml', or 'file'
        include_standard_fields => 'all',        # 'all' or \@field_names
        app_fields              => \@fields,     # custom fields
        field_overrides         => \@overrides,  # modify built-in fields
    },
}

The users block is where field configuration happens. The sections below describe the available fields and show how to customize them.

User Field Reference

Every user record draws from four field categories:

Core fields (always present):

user_id        system   Primary authentication identifier (max 30)
moniker        moniker  Display name, nickname, or initials (max 24)
user_status    enum     Eligible*, OK, Inactive (max 20)
access_level   enum     anon*, visitor, member, staff, admin (max 20)

Standard fields (selectable at setup):

first_name     name     max 50
middle_name    name     max 50
last_name      name     max 50
prefix         enum     (none) Dr Mr Ms Mrs Mx Prof Hon Sir Madam
suffix         enum     (none) Jr Sr II III IV V PhD MD DDS Esq
organization   text     max 100
title          text     max 100
email          email    max 255
phone          phone    max 20
text_ok        boolean
last_login_date timestamp
term_ends      date

System fields (auto-managed, always present):

last_mod_date  system   Updated on every write
created_date   system   Set once on creation

Core and system fields cannot be removed. Standard fields default to required => 0.

Validator Types

Ten built-in validators are available for field definitions and overrides:

text        Any string (max_length enforced if set)
email       user@domain.tld pattern
phone       Digits, spaces, hyphens, parens, optional +; min 7 chars
date        YYYY-MM-DD
timestamp   YYYY-MM-DD HH:MM:SS (or with T separator)
boolean     Strictly 0 or 1
integer     Optional minus, digits only
enum        Value must appear in the field's options list
moniker     2-24 alphanumeric, no spaces
name        Letters (incl. accented), hyphens, apostrophes, spaces

See "VALIDATOR TYPES" in Concierge::Users::Meta for patterns and null values.

Set the environment variable USERS_SKIP_VALIDATION to a true value to bypass all validation -- useful for bulk imports or testing.

Selecting Standard Fields

Include all standard fields:

users => {
    backend                 => 'database',
    include_standard_fields => 'all',
},

Or pick only the ones your application needs:

users => {
    backend                 => 'database',
    include_standard_fields => [qw/first_name last_name email/],
},

Pass an empty arrayref [] to exclude all standard fields -- useful when your application defines its own fields from scratch. Omitting include_standard_fields (or setting it to any falsy value) includes all 12 standard fields, the same as 'all'.

Adding Application Fields

Custom fields are passed as app_fields, an arrayref of field names (string shorthand) or full definition hashrefs:

users => {
    backend    => 'database',
    app_fields => [
        'nickname',                          # string: text, not required
        'bio',                               # string: text, not required
        {                                    # hashref: full control
            field_name  => 'department',
            type        => 'enum',
            options     => ['*Engineering', 'Sales', 'Support'],
            required    => 1,
            label       => 'Department',
            description => 'Primary department assignment',
        },
        {
            field_name    => 'employee_id',
            type          => 'text',
            validate_as   => 'moniker',      # text storage, moniker validation
            required      => 1,
            must_validate => 1,
            max_length    => 12,
        },
    ],
},

String shorthand creates a field with type => 'text', required => 0.

Enum default convention: In an options arrayref, prefix exactly one value with * to mark it as the default for new records. ['*Community', 'Maker', 'Pro'] means new records get Community unless another value is supplied. A bare * (as used by the built-in prefix and suffix fields) designates an empty-string default. The * is stripped internally before validation -- stored values never contain it. If no * option exists and no explicit default is set, the default is "".

Available attributes for field definition hashrefs:

field_name -- internal name (snake_case); required
type -- text, email, phone, date, timestamp, boolean, integer, enum
validate_as -- validator to use when different from type (e.g., validate_as => 'moniker' on a text field applies alphanumeric-only validation while storing as text)
label -- human-readable display label; auto-generated from field_name if omitted (e.g., badge_name becomes "Badge Name")
description -- short explanatory text for documentation or UI hints
required -- 1 if the field must have a non-null value on creation; 0 otherwise
must_validate -- 1 to reject the entire operation on validation failure; 0 to silently drop the invalid value and append a warning (auto-enabled when required => 1)
options -- arrayref of allowed values for enum fields; prefix one with * to designate the default (e.g., ['*Free', 'Premium', 'Enterprise']); a bare * means an empty-string default
default -- value assigned on new-record creation when no value is supplied; for enum fields, auto-set from the *-marked option if not specified explicitly
null_value -- sentinel representing "no data" for the field type (e.g., "" for text, 0 for boolean, "0000-00-00" for date); values equal to null_value are treated as empty
max_length -- maximum character length; enforced by the text validator and available as a UI hint

See "FIELD ATTRIBUTES" in Concierge::Users::Meta for the complete attribute reference.

Modifying Standard Fields

Use field_overrides to change attributes of built-in fields without replacing them:

users => {
    backend                 => 'database',
    include_standard_fields => [qw/email phone organization/],
    field_overrides         => [
        {
            field_name => 'email',
            required   => 1,               # make email mandatory
            label      => 'Work Email',
        },
        {
            field_name => 'organization',
            required   => 1,
            max_length => 200,             # increase from default 100
        },
    ],
},

Overriding enum options: Core fields like user_status and access_level are always present, but their options are not fixed. Replace them with values that fit your domain:

# Makerspace member status instead of the default
# Eligible / OK / Inactive
field_overrides => [
    {
        field_name => 'user_status',
        options    => [qw( *Applicant Novice Skilled Expert Mentor Steward )],
    },
],

Protected fields (cannot be overridden): user_id, created_date, last_mod_date.

Protected attributes (cannot be changed): field_name, category.

Auto-behaviors: changing type auto-updates validate_as to match; setting required => 1 auto-enables must_validate.

See "FIELD CUSTOMIZATION" in Concierge::Users::Meta for the complete customization reference and "FIELD CATALOG" in Concierge::Users::Meta for full field specifications.

Complete Example

A community makerspace tracking members with custom fields, selective standard fields, and modified built-in defaults:

my $result = Concierge::Setup::build_desk({
    storage => {
        base_dir => './makerspace-desk',
    },
    sessions => { backend => 'database' },
    users => {
        backend                 => 'database',
        include_standard_fields => [qw/
            first_name last_name email phone organization
        /],
        field_overrides => [
            {
                field_name => 'user_status',
                options    => [qw( *Applicant Novice Skilled Expert Mentor Steward )],
            },
            {
                field_name => 'email',
                required   => 1,           # mandatory for members
                label      => 'Contact Email',
            },
            {
                field_name => 'organization',
                max_length => 200,         # increase from default 100
            },
        ],
        app_fields => [
            'skills',                      # string shorthand: text, optional
            {
                field_name  => 'membership_tier',
                type        => 'enum',
                options     => ['*Community', 'Maker', 'Pro', 'Sponsor'],
                required    => 1,
                label       => 'Membership Tier',
                description => 'Determines access to equipment and hours',
            },
            {
                field_name    => 'badge_name',
                type          => 'text',
                validate_as   => 'moniker',  # alphanumeric validation
                required      => 1,
                must_validate => 1,
                max_length    => 16,
                label         => 'Badge Name',
                description   => 'Printed on member access badge',
            },
            {
                field_name => 'newsletter_ok',
                type       => 'boolean',
                default    => 0,
                label      => 'Newsletter Opt-in',
            },
        ],
    },
});

This produces a user schema with 4 core fields, 5 selected standard fields (email now required), 4 application fields, and 2 system timestamps -- 15 fields total. The core user_status field keeps its usual role but uses makerspace-specific statuses (defaulting to Applicant). New members default to the Community tier (marked with *) and must provide a badge_name that passes moniker validation (2-24 alphanumeric characters).

Configuration Introspection

After building a desk, the field schema can be inspected at runtime through Concierge::Users (inherited from Concierge::Users::Meta):

# Before setup: view built-in default field definitions
Concierge::Users::Meta->show_default_config();

# After setup: view the active schema for this desk
my $users = Concierge::Users->new('./makerspace-desk/users-config.json');
$users->show_config();

# Get UI-friendly hints for building forms dynamically
my $hints = $users->get_field_hints('membership_tier');
# Returns: { label, type, options, max_length, description, required }

# Get the ordered field list for this schema
my $fields = $users->get_user_fields();

show_default_config() prints the built-in field template to STDOUT -- useful for reviewing available standard fields before writing a setup script. show_config() prints the YAML configuration generated during setup(), reflecting the actual schema including any overrides and application fields. get_field_hints() returns a hashref of display-ready attributes for a single field, suitable for generating form elements programmatically.

Data Archiving

Calling Concierge::Users->setup() when data already exists in the storage directory automatically archives the existing data files (renamed with a timestamp suffix) before creating new storage. This means re-running a setup script to change the field schema will not silently destroy existing user records.

Returns:

{ success => 1, desk => $base_dir, config => \%full_config }

On failure:

{ success => 0, message => '...' }

validate_setup_config

my $result = Concierge::Setup::validate_setup_config(\%config);

Validates a configuration hashref without creating anything. Returns { success => 1 } or { success => 0, errors => [...] }.

SEE ALSO

Concierge -- runtime operations after desk is built

AUTHOR

Bruce Van Allen <bva@cruzio.com>

LICENSE

This module is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.