3 ‐ Central Server - Bento-Project-SCARS/ProjectSCARS GitHub Wiki

Central Server

The central server is written in Python using the FastAPI framework and utilizes SQLite, MySQL, or PostgreSQL as its database, depending on the selected configuration. It also uses either a pure-Python file object storage or a third-party S3-compatible object storage for storing user avatars and exported PDFs. It is responsible for handling the backend logic and storing the submitted data of canteen managers.

Requirements

The hard requirements are as follows:

Development Setup

  1. Install the required software.

  2. Clone the repository.

    git clone https://github.com/Bento-Project-SCARS/ProjectSCARS.git
    
  3. Navigate to the CentralServer directory.

    cd ProjectSCARS/CentralServer
    
  4. Install dependencies.

    uv sync
    
  5. Copy and edit the configuration file.

    cp .config.example.json config.json
    uv run ./scripts/secret.py sign     # Generate secure secret keys
    uv run ./scripts/secret.py encrypt  # and write it to `config.json`
    uv run ./scripts/secret.py refresh
    

    Edit the config.json file to their appropriate values.

  6. Select the database you want to use. See Database Setup for more information.

  7. Select the object store you want to use. See Object Store Setup for more information.

  8. Set up SMTP Connections.

  9. Enable OAuth Integrations.

  10. Run the FastAPI development server.

    uv run fastapi dev centralserver --host 0.0.0.0 --port 8081  # Run it using fastapi
    uv run uvicorn centralserver:app --host 0.0.0.0 --port 8081  # Run it using uvicorn
    uv run -m centralserver  # Run the server directly (using `config.json` values for host and port)
    

[!IMPORTANT] The default credentials are:

  • username: bento
  • password: ProjectSCARS1

Environment Variables

The following environment variables are used by the central server to further configure its behavior. These variables are optional.

Environment Variable Value Description
CENTRAL_SERVER_CONFIG_FILE ./config.json The path of the configuration file to use.
CENTRAL_SERVER_CONFIG_ENCODING utf-8 The encoding of the configuration file.

[!TIP] If you want to use a different configuration file, you can run the following:

For Linux users:

CENTRAL_SERVER_CONFIG_FILE=.config.dev.json uv run fastapi dev centralserver --host 0.0.0.0 --port 8081

For Windows users:

$env:CENTRAL_SERVER_CONFIG_FILE=".config.dev.json"; uv run fastapi dev centralserver --host 0.0.0.0 --port 8081

Resetting Central Server Data

While contributing to the project, you might have to start your database and object store from scratch. Because of this, you can run scripts/clean.py to reset them.

# Clean the database and object store configured in config.json:
uv run ./scripts/clean.py

# Clean the database and object store configured in another config file:
uv run ./scripts/clean.py --config CONFIG_FILEPATH

Database Setup

The central server supports the following databases:

SQLite Database

To start using SQLite, just edit ./CentralServer/config.json and adjust the database property to use SQLite. The connect_args property is optional and can be used to pass additional SQLAlchemy connection arguments.

{
    /* ... */
    "database": {
        "type": "sqlite",
        "config": {
            "filepath": "database.db",
            "connect_args": {
                // SQLAlchemy connection arguments
            }
        }
    }
    /* ... */
}

On startup, the central server will create a SQLite database file in the file path specified in the filepath property. The default is centralserver.db in the root directory of the central server.

This database is sufficient for development and testing purposes, but for production use, it is recommended to use MySQL or similar database server.

MySQL Database

Since SQLite is a file-based database and lacks the ability to run multiple instances, it is not suitable for production use which needs high throughput and availability. Therefore, the central server also supports MySQL as a database. The MySQL database can be run in a containerized environment using Docker or Podman.

  1. Create a .env file in ./CentralServer/system/mysql/ using the example file.

    cd ./system/mysql/
    cp .env.example .env
    
  2. Adjust the environment variables in ./CentralServer/system/mysql/.env. For more information about MySQL Docker environment variables, see their documentation.

  3. Run the MySQL container.

    docker-compose up -d # Run this if you are using Docker.
    podman-compose up -d # Run this if you are using Podman.
    cd ../..
    
  4. If successful, you should be able to access PHPMyAdmin at http://localhost:8083.

[!IMPORTANT] You might need to log in as the root user in the database in the future. The root password is randomly generated, which can be seen in the logs of the MySQL container with the following format:

[Note] [Entrypoint]: GENERATED ROOT PASSWORD: bfPy...lqLL

  1. Update ./CentralServer/config.json to use MySQL. Use the same database credentials as the ones in the .env file. The connect_args property is optional and can be used to pass additional SQLAlchemy connection arguments.

    {
        /* ... */
        "database": {
            "type": "mysql",
            "config": {
                "username": "ProjectSCARS_DatabaseAdmin",
                "password": "ProjectSCARS_mysql143",
                "host": "localhost",
                "port": 3306,
                "database": "ProjectSCARS_CentralServer",
                "connect_args": {
                    // sqlalchemy connection arguments
                }
            }
        }
        /* ... */
    }
    

PostgreSQL Database

Another option is to use PostgreSQL as a database. Like the MySQL adapter, you can run PostgreSQL with minimal configuration by following these steps:

  1. Create a .env file in ./CentralServer/system/postgresql/ using the example file.

    cd ./system/postgresql/
    cp .env.example .env
    
  2. Adjust the environment variables in ./CentralServer/system/postgresql/.env. For more information about PostgreSQL Docker environment variables, see their documentation.

  3. Run the PostgreSQL container.

    docker-compose up -d # Run this if you are using Docker.
    podman-compose up -d # Run this if you are using Podman.
    cd ../..
    
  4. If successful, you should be able to access Adminer at http://localhost:8083.

  5. Update ./CentralServer/config.json to use PostgreSQL. Use the same database credentials as the ones in the .env file. The connect_args property is optional and can be used to pass additional SQLAlchemy connection arguments.

    {
        /* ... */
        "database": {
            "type": "postgres",
            "config": {
                "username": "ProjectSCARS_DatabaseAdmin",
                "password": "ProjectSCARS_postgres143",
                "host": "localhost",
                "port": 5432,
                "database": "ProjectSCARS_CentralServer",
                "connect_args": {
                    // sqlalchemy connection arguments
                }
            }
        }
        /* ... */
    }
    

Object Store Setup

The central server supports the following object stores:

Local File Object Store

To start using the local file object store, just edit ./CentralServer/config.json and adjust the object_store property to match the following structure:

{
    /* ... */
    "object_store": {
        "type": "local",
        "config": {
            "filepath": "./data/"
        }
    }
    /* ... */
}

When the central server starts, it will use the provided filepath to store the files (objects). Make sure that the filepath is writable by the server. The default is ./data/ in the root directory of the central server. It is already created in the repository, so no further action is needed.

Because the local file object store is implemented in the central server itself, it does not provide any integrity and redundancy systems. Therefore, it is recommended to use a dedicated object store for production use, and use the local file object store only for development purposes.

MinIO S3-Compatible Object Store

Using MinIO, it is possible to provide multiple nodes to store the objects. This is useful for redundancy and high availability, therefore it is recommended to be used in production. The MinIO object store can be run in a containerized environment using Docker or Podman.

  1. Create a .env file in ./CentralServer/system/minio/ using the example file.

    cd ./system/minio/
    cp .env.example .env
    
  2. Adjust the environment variables in ./CentralServer/system/minio/.env.

  3. Run the MinIO container.

    docker-compose up -d # Run this if you are using Docker.
    podman-compose up -d # Run this if you are using Podman.
    cd ../..
    
  4. If successful, you should be able to access the MinIO dashboard at http://localhost:8084.

  5. Log in to the dashboard using the credentials in the .env file.

  6. Navigate to the Access Keys tab and create a new access key. Make sure to copy the keys, as they will not be shown again.

  7. Edit ./CentralServer/config.json and adjust the object_store to match the following structure:

    {
        /* ... */
        "object_store": {
            "type": "minio",
            "config": {
                "endpoint": "localhost:9000",
                "access_key": "YOUR_ACCESS_KEY",
                "secret_key": "YOUR_SECRET_KEY",
                "secure": false
            }
        }
        /* ... */
    }
    

Garage S3-Compatible Object Store

Garage is another free and open-source S3-compatible object store that can be used with the central server. It is similar to MinIO, but it is designed to be lightweight and easy to use. To use Garage, you can follow the steps below:

  1. Create a garage.toml file in ./CentralServer/system/garage/ using the example file.

    cd ./system/garage/
    cp garage.example.toml garage.toml
    
  2. Open garage.toml and adjust the values accordingly. For more information, read Garage's documentation. As a quick start, update the following values using openssl rand -hex 32 (Linux) or [System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32) | ForEach-Object ToString X2 | Join-String (Windows PowerShell).

    • rpc_secret
    • admin_token
    • metrics_token
  3. Run the Garage container.

    docker-compose up -d # Run this if you are using Docker.
    podman-compose up -d # Run this if you are using Podman.
    cd ../..
    
  4. Now, you have Garage running. Run the following command to see the status of Garage:

    # CONTAINER_NAME would be `garage-centralserver-garage-1` in Podman
    # For Docker, it would be `centralserver-garage-1`
    docker exec -ti CONTAINER_NAME /garage status
    
  5. Next, you will have to create a cluster layout.

[!NOTE] Creating a cluster layout for a Garage deployment means informing Garage of the disk space available on each node of the cluster as well as the zone (e.g. datacenter) each machine is located in.

docker exec -ti CONTAINER_NAME /garage layout assign -z ph1 -c 5G NODE_ID

This command will...

  • Set the zone of the node to ph1 (-z ph1)

  • Set the capacity of the node to 5G (-c 5G)

    You will have to adjust the values to your needs. NODE_ID is the ID shown in the garage status command (first column).

  1. Now that you have set the layout of the cluster, you will now have to commit the changes. Run the following command to do so:

    # Set the current layout as the first version of the configuration
    podman exec -ti CONTAINER_NAME /garage layout apply --version 1
    
  2. Now, you will have to generate an API key using Garage's CLI application for the central server. Run the following:

    docker exec -ti CONTAINER_NAME /garage key create centralserver-app-key
    docker exec -ti CONTAINER_NAME /garage key allow --create-bucket centralserver-app-key
    

    It should show you something similar the following:

    ==== ACCESS KEY INFORMATION ====
    Key ID:              GK**********************eb
    Key name:            centralserver-app-key
    Secret key:          63************************************************************12
    Created:             2025-06-01 16:17:23.884 +00:00
    Validity:            valid
    Expiration:          never
    
    Can create buckets:  false
    
    ==== BUCKETS FOR THIS KEY ====
    Permissions  ID  Global aliases  Local aliases
    
  3. From the output of the previous step, copy the Key ID and the Secret Key. Edit ./CentralServer/config.json and adjust the object_store to match the following structure:

    {
        /* ... */
        "object_store": {
            "type": "garage",
            "config": {
                "endpoint": "localhost:3900",
                "access_key": "YOUR_ACCESS_KEY",
                "secret_key": "YOUR_SECRET_KEY",
                "secure": false
            }
        }
        /* ... */
    }
    

Cloudflare R2 Object Store

Cloudflare R2 is a free S3-compatible object store that can be used with the central server. To use Cloudflare R2, you can follow the steps below:

  1. Create a new Cloudflare R2 bucket in your Cloudflare account.
  2. Create a new API key in your Cloudflare account.
  3. Edit ./CentralServer/config.json and adjust the object_store to match the following structure:
{
    /* ... */
    "object_store": {
        "type": "minio",
        "config": {
            "endpoint": "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
            "access_key": "YOUR_ACCESS_KEY",
            "secret_key": "YOUR_SECRET_KEY",
            "secure": true
        }
    }
    /* ... */
}

[!NOTE] The object store type is set to minio because Cloudflare R2 is S3-compatible and can be used with the MinIO client. This is not a typographical error, but rather a design choice to maintain compatibility with the existing MinIO implementation in the central server.

SMTP Connection

To enable email notifications, password resets, and other email-related functionality, you have to connect the central server to an SMTP server.

Gmail SMTP Connection

The most basic approach is to use Gmail as the SMTP server. Just follow the instructions in the link below to create an app password:

https://support.google.com/accounts/answer/185833

This is a 16-character-long string, grouped by 4 characters and separated by spaces. Remove the spaces and you will get your app password. After you have successfully created an app password, update your configuration file:

{
    /* ... */
    "mailing": {
        "enabled": true,
        "server": "smtp.gmail.com",
        "port": 587,
        "from_address": "[email protected]",
        "username": "[email protected]",
        "password": "your-app-password",
        "templates_dir": "./templates/mail/",
        "templates_encoding": "utf-8"
    }
    /* ... */
}

Enabling Open Authentication (OAuth)

Google OAuth

To enable linking a user's account with Google OAuth, follow the steps below:

  1. Go to https://console.cloud.google.com.
  2. Create a new project if needed. Otherwise, select an existing project.
  3. Navigate to Google Auth Platform and click "Get Started".
  4. Follow the steps displayed in the page.
  5. After setting up the OAuth configuration, create a new OAuth client.
  6. Set the Application Type to "Web application".
  7. Set the Authorized JavaScript origins to the web client base URL.
  8. Set the Authorized redirect URIs to the central server base URL.
  9. Get the client ID and client secret and paste it to config.json.
{
    /* ... */
    "authentication": {
        "oauth": {
            "google": {
                "client_id": "UPDATE_THIS_VALUE",
                "client_secret": "UPDATE_THIS_VALUE",
                "redirect_uri": "http://localhost:8080/login/oauth/google"
            }
        }
    }
    /* ... */
}

Microsoft OAuth

This has not yet been implemented.

Facebook OAuth

This has not yet been implemented.