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).
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.
Filebrowser solves this by separating Code from Settings.
config.yaml): A text file acting as a blueprint. It tells the program how to run (e.g., "Use port 9090").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.
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).
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.
Let's look at a scenario: A user wants to run Filebrowser on Port 9090 with a specific database.
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
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:
settings.Initialize: Reads the text file and converts it into Go variables.storage.InitializeDb: Connects to the database defined in that configuration.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.
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:
go indexing... to start scanning files in the background. We don't want to wait for scanning to finish before the UI becomes available.rootCMD is the function that actually launches the Web Server we built in Chapter 3: HTTP API & Routing.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:
fbhttp.StartHttp: This is the loop that listens for browser requests. Once this runs, the application is "online".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.
In this chapter, we learned how Filebrowser comes to life:
settings.Initialize loads these rules into memory.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