Chapter 4 ยท CORE

Configuration & Startup

๐Ÿ“„ 04_configuration___startup.md ๐Ÿท Core

Chapter 4: Configuration & Startup

Welcome back!

In Chapter 3: HTTP API & Routing, we built the "Receptionist" that handles incoming web requests. In Chapter 2: Authentication & User Storage, we built the "Security Guard" to check IDs. In Chapter 1: File System & Indexing, we built the "Library" to manage files.

We have all the parts of a building, but we haven't actually opened the doors yet.

This chapter is about Startup. It is the process where the application wakes up, puts on its clothes (loads configuration), turns on the lights (connects to databases), and finally unlocks the front door (starts the server).

The Problem: The "Hardcoded" House

Imagine if our application was hardcoded to always listen on port 8080 and always look for files in /srv.

If you tried to run this on a computer where port 8080 was already used by another program, Filebrowser would crash. If you wanted to store files in D:\Photos, you couldn't.

We need a way to change the behavior of the program without rewriting the code.

The Solution: The Blueprint (Configuration)

Filebrowser solves this by separating Code from Settings.

  1. Configuration File (config.yaml): A text file acting as a blueprint. It tells the program how to run (e.g., "Use port 9090").
  2. The Startup Sequence: A strict checklist the program follows to ensure the Database and Index are ready before allowing users to connect.

Key Concepts

1. The Global Settings (settings package)

Instead of passing variables around to every single function, Filebrowser loads all configuration into a global object called Config.

This object mirrors the structure of the YAML file. If the YAML says server.port: 80, the Go code accesses it via settings.Config.Server.Port.

2. The Entry Point (main.go)

Every Go program starts in a function called main(). In Filebrowser, this is very short. It immediately passes control to the Command Line Interface (CLI) handler, which parses flags (like --config my_config.yaml).

3. The Graceful Shutdown

What happens if you press Ctrl+C to stop the server? If we just cut the power, we might corrupt the database. The startup process also handles the Shutdown process, ensuring all files are closed properly before exiting.


How it Works: A Use Case

Let's look at a scenario: A user wants to run Filebrowser on Port 9090 with a specific database.

1. The Input (Configuration)

The user provides a config.yaml file:

# backend/config.yaml
server:
  port: 9090              # Custom port
  database: "/data/fb.db" # Custom DB location
  sources:
    - path: "/photos"     # Where to look for files

2. Loading the Settings

When the program starts, it reads this file. Here is how the code initializes the settings:

// backend/cmd/root.go

func getStore(configFile string) bool {
    // 1. Read the YAML file and fill the 'settings.Config' object
    settings.Initialize(configFile)

    // 2. Open the User Database (BoltDB) using the path from config
    //    (Refers to Chapter 2: User Storage)
    s, hasDB, err := storage.InitializeDb(settings.Config.Server.Database)
    
    // 3. Save the database connection globally
    store = s
    return hasDB
}

Explanation:


Under the Hood: The Startup Sequence

Starting a server isn't just about running one function. It's a chain reaction. We must ensure the Database is ready before we start the Indexer, and the Indexer must be ready before we accept HTTP requests.

The Flow Diagram

sequenceDiagram participant Main as Main (CLI) participant Set as Settings participant DB as Database (Bolt) participant IDX as Indexer participant HTTP as HTTP Server Main->>Set: 1. Load config.yaml Main->>DB: 2. Open/Create User DB Main->>IDX: 3. Initialize Index DB par Background Services Main->>IDX: 4. Start Scanners Main->>Main: 5. Start Image Resizers end Main->>HTTP: 6. Start Listening (Port 9090) Note over HTTP: Server is now Live!

Deep Dive: The StartFilebrowser Function

This function in backend/cmd/root.go is the conductor of the orchestra. It calls everyone to their places.

// backend/cmd/root.go

func StartFilebrowser() {
    // 1. Load Config & Connect to User Database (Chapter 2)
    dbExists := getStore(configPath)

    // 2. Initialize the File Indexer (Chapter 1)
    isNewDb, _ := indexing.InitializeIndexDB()

    // 3. Start background indexing for all configured folders
    for _, source := range settings.Config.Server.SourceMap {
        go indexing.Initialize(source, false, isNewDb)
    }

    // 4. Start the HTTP Server and other services
    go func() {
        rootCMD(ctx, store, &settings.Config.Server, shutdownComplete)
    }()

    // 5. Wait here until someone presses Ctrl+C
    <-signalChan 
}

Explanation:

Deep Dive: Starting Services (rootCMD)

The rootCMD function is responsible for the final steps: preparing media processors and opening the HTTP port.

// backend/cmd/root.go

func rootCMD(ctx context.Context, store *bolt.BoltStore, ...) error {
    // 1. Setup tools for generating thumbnails (FFmpeg)
    ffmpeg.SetFFmpegPaths()
    
    // 2. Start the Preview Generator (background worker)
    //    This creates small images for your photos/videos.
    preview.StartPreviewGenerator(numWorkers, cacheDir)
    
    // 3. Finally, start the Web Server (Chapter 3)
    fbhttp.StartHttp(ctx, store, shutdownComplete)
    
    return nil
}

Explanation:


User Defaults & Scope

One interesting part of the startup is applying User Defaults.

When a new user signs up (or is created via CLI), we don't want to manually set every single permission. The settings package applies a template.

// backend/common/settings/settings.go

func ApplyUserDefaults(u *users.User) {
    // Apply global defaults to the specific user object
    u.DarkMode = boolValueOrDefault(Config.UserDefaults.DarkMode, true)
    u.Locale   = Config.UserDefaults.Locale
    
    // Set default permissions (Can they delete? Can they download?)
    u.Permissions.Delete = Config.UserDefaults.Permissions.Delete
    u.Permissions.Download = true
}

This ensures that if the administrator sets delete: false in config.yaml, all new users will be unable to delete files by default.


Summary

In this chapter, we learned how Filebrowser comes to life:

  1. Configuration allows users to customize ports, paths, and rules without changing code.
  2. settings.Initialize loads these rules into memory.
  3. StartFilebrowser orchestrates the startup, ensuring the Database and Indexer are ready before the web server begins.

Now we have a backend that is fully configured, secured, indexed, and listening for connections. But if you visit the URL, you just get raw JSON data. We need a user interface.

Next Chapter: Frontend Data Layer


Generated by Code IQ