ISPANA API Server Setup Guide

This guide details the complete configuration, local development, and production deployment of the ISPANA Backend API Service (built with Dart Native and Shelf).

---

📋 1. Prerequisites

Before setting up the API, ensure you have the following installed on your target system:

  • Dart SDK (>= 3.0.0 but < 4.0.0)
  • MongoDB (Local instance or MongoDB Atlas account)
  • Nginx (For reverse proxying and SSL termination in production)
  • Systemd (For managing the backend daemon on Linux)

---

🛠️ 2. Environment Configuration (.env)

  1. Copy the .env.example file to .env:
  2.    cp .env.example .env
  3. Configure the variables:
# Server Configuration
PORT=8080
ENV=PROD # Options: DEV, PROD

# Database Configuration
# Set DEV_MODE=true to use Atlas; DEV_MODE=false to use local Mongo
DEV_MODE=false

# MongoDB Atlas (Cloud - Development)
MONGODB_ATLAS_URI=mongodb+srv://<user>:<password>@cluster0.example.mongodb.net/ispana_db?retryWrites=true&w=majority

# MongoDB Local (Server/Localhost - Production)
MONGODB_LOCAL_URI=mongodb://127.0.0.1:27017/ispana_db

# Security
JWT_SECRET=your_super_secret_jwt_key
ISO_SECRET_KEY=your_tenant_isolation_secret_key

# Email (SMTP) - Gmail Example
SMTP_USERNAME=your_email@gmail.com
SMTP_PASSWORD=your_gmail_app_password

Connections strings, JWT secrets, and SMTP credentials must never be committed to public repository. Ensure .env is listed in your .gitignore file.

---

🚀 3. Local Development

To run the API server locally:

  1. Fetch dependencies:
  2.    dart pub get
  3. Run the server:
  4.    dart bin/server.dart
  5. Verify the server is running by visiting:
  6.    http://localhost:8080/health

---

📦 4. Production Deployment

For production environments, compiling the Dart code to a native binary ensures maximum performance, minimal resource usage, and zero runtime package dependency.

Step 1: Compile Native Binary

Compile the server entrypoint to a self-contained executable binary:

dart compile exe bin/server.dart -o build/server

Step 2: Systemd Daemon Configuration

Create a systemd service file to manage the API process:

sudo nano /etc/systemd/system/ispana-api.service

Paste the following configuration:

[Unit]
Description=ISPANA Backend API Service
After=network.target mongodb.service

[Service]
Type=simple
User=ispana
WorkingDirectory=/var/www/ispana-api
ExecStart=/var/www/ispana-api/build/server
Restart=always
RestartSec=5
EnvironmentFile=/var/www/ispana-api/.env

[Install]
WantedBy=multi-user.target

Reload systemd daemon, start the service, and enable it to run at boot:

sudo systemctl daemon-reload
sudo systemctl start ispana-api
sudo systemctl enable ispana-api

---

🌐 5. Reverse Proxy Configuration (Nginx)

To serve the landing page, the web application, and reverse proxy the API requests under a single configuration, set up Nginx as follows.

Step 1: Create Nginx Config

Create a new server block:

sudo nano /etc/nginx/sites-available/ispana

Add the following site configuration:

server {
    listen 80;
    listen [::]:80;

    server_name ispana.com www.ispana.com;

    # ===== API (RECOMMENDED) =====
    location /api/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Step 2: Enable Site and Reload Nginx

sudo ln -s /etc/nginx/sites-available/ispana /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 3: SSL Setup with Certbot

sudo certbot --nginx -d ispana.com -d www.ispana.com

---

⚠️ 6. Common Issues & Troubleshooting

Ensure the directory for file uploads exists and has correct permissions:

```bash

mkdir -p /var/www/ispana-api/storage/uploads/landmarks

chown -R ispana:ispana /var/www/ispana-api/storage

```

If you are using MongoDB Atlas, make sure that the server's public IP address is whitelisted in your MongoDB Atlas Dashboard's Network Access section.

FreeRADIUS Server Setup Guide

This guide details the complete configuration required to set up FreeRADIUS with the ISPANA Dart API for stateless dynamic client discovery and subscriber authentication.

---

📁 1. Client Configurations

/etc/freeradius/3.0/clients.conf

Configure localhost clients and ensure the directory for dynamic client definitions is included.

client localhost {
        ipaddr = 127.0.0.1
        proto = *
        secret = testing123
        # localhost isn't usually a NAS...
        nas_type = other        
        limit {
                max_connections = 16
                lifetime = 0
                idle_timeout = 30
        }
}

client localhost_ipv6 {
        ipv6addr        = ::1
        secret          = testing123
}

$INCLUDE clients.d/

---

🛠️ 2. Module Configurations

/etc/freeradius/3.0/mods-available/rest

Define the main rest module for subscriber authorize/accounting operations and the rest_nas module for dynamic client discovery.

rest {
        tls {
        }
        connect_uri = "http://127.0.0.1:8080/api"
        authorize {
                uri = "${..connect_uri}/radius/authorize"
                method = 'post'
                body = 'json'
                # Pass the pre-shared secret key for security in headers
                headers = 'Authorization: Bearer YOUR_RADIUS_REST_KEY'
                data = '{ "username": "%{User-Name}", "mac": "%{Calling-Station-Id}", "action": "authorize" }'
        }
        authenticate {
                uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?action=authenticate"
                method = 'get'
                tls = ${..tls}
        }
        preacct {
                uri = "${..connect_uri}/user/%{User-Name}/sessions/%{Acct-Unique-Session-ID}?action=preacct"
                method = 'post'
                tls = ${..tls}
        }
        accounting {
                uri = "${..connect_uri}/radius/accounting"
                method = 'post'
                body = 'json'
                headers = 'Authorization: Bearer YOUR_RADIUS_REST_KEY'
                data = '{ "username": "%{User-Name}", "sessionId": "%{Acct-Session-Id}", "status": "%{Acct-Status-Type}", "nasIp": "%{NAS-IP-Address}", "framedIp": "%{Framed-IP-Address}", "uploadOctets": "%{Acct-Input-Octets}", "downloadOctets": "%{Acct-Output-Octets}" }'
        }
        post-auth {
                uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?action=post-auth"
                method = 'post'
                tls = ${..tls}
        }
        pre-proxy {
                uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?action=pre-proxy"
                method = 'post'
                tls = ${..tls}
        }
        post-proxy {
                uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?action=post-proxy"
                method = 'post'
                tls = ${..tls}
        }
        pool {
                start = ${thread[pool].start_servers}
                min = ${thread[pool].min_spare_servers}
                max = ${thread[pool].max_servers}
                spare = ${thread[pool].max_spare_servers}
                uses = 0
                retry_delay = 30
                lifetime = 0
                idle_timeout = 60
        }
}

rest rest_nas {
    connect_uri = "http://127.0.0.1:8080/api"

    authorize {
        # Query your Dart API nas endpoint passing the router's IP
        uri = "${..connect_uri}/radius/nas?ip=%{Packet-Src-IP-Address}"
        method = "get"
        body = "none"
    }
}

Enable the REST module by symlinking it:

ln -sf /etc/freeradius/3.0/mods-available/rest /etc/freeradius/3.0/mods-enabled/

---

🌐 3. Virtual Server Configurations

/etc/freeradius/3.0/sites-enabled/dynamic-clients

Define the wildcard client mapping and the virtual server responsible for querying the API and instantiating dynamic client routers.

The dynamic client module (rlm_dynamic_clients) expects to run inside a virtual server named exactly dynamic_clients. To satisfy FreeRADIUS's constraint that dynamically registered clients must match the virtual server of the network listener, we map the network wildcard client to default, and inside dynamic_clients we force FreeRADIUS-Client-Virtual-Server = "default".

# Define a catch-all network for dynamic lookups mapping directly to the default server
client dynamic_network {
    ipaddr = 0.0.0.0/0
    secret = dummy               
    virtual_server = default
    dynamic_clients = dynamic_clients

    limit {
        max_connections = 16
        lifetime = 0
        idle_timeout = 30
    }
}

# The hardcoded server block that performs the API lookup
server dynamic_clients {
    authorize {
        # 1. Fetch NAS info from Dart API
        rest_nas

        # 2. Map to "default" to match the parent network listener!
        update control {
            FreeRADIUS-Client-Shortname = "%{reply:FreeRADIUS-Client-Shortname}"
            FreeRADIUS-Client-Secret = "%{reply:FreeRADIUS-Client-Secret}"
            FreeRADIUS-Client-IP-Address = "%{reply:FreeRADIUS-Client-IP-Address}"
            FreeRADIUS-Client-NAS-Type = "%{reply:FreeRADIUS-Client-NAS-Type}"
            FreeRADIUS-Client-Virtual-Server = "default"
        }

        # 3. Instantiate the client in-memory
        dynamic_clients
    }
}

/etc/freeradius/3.0/sites-enabled/default

Enable subscriber authorization and accounting with the REST module.

Inside the authorize block, add the rest module:

authorize {
    ...
    # Query Dart API for subscriber credentials and speed limits
    rest
    ...
}

Inside the accounting block, add the rest module:

accounting {
    ...
    # Send accounting stats to Dart API
    rest
    ...
}

---

⚠️ 4. Crucial Gotchas & Cleanups

FreeRADIUS includes all files in /etc/freeradius/3.0/clients.d/ regardless of extension. Stale files like .save, .bak, or temp files will cause duplicate client definition conflicts and prevent FreeRADIUS from starting. Always clean the directory:

```bash

rm -f /etc/freeradius/3.0/clients.d/*.save

rm -f /etc/freeradius/3.0/clients.d/*.bak

```

Ensure no duplicate server definitions for dynamic_clients exist. If a duplicate server is defined elsewhere in the configuration, FreeRADIUS may compile the empty default block and ignore the custom lookup block.

---

🔍 5. Verification & Testing

Start FreeRADIUS in debug mode to trace requests, API calls, and authentication:

freeradius -X

ISPANA Client Application Changelog Catatan Rilis Aplikasi Klien ISPANA

a1 2026-05-21

Version Transition to v0.0.4

  • **Changes**:
  • [pubspec.yaml]: Bumped version to `0.0.4+1` to start the new development cycle.
  • [build.ps1]: Updated default target to `all` to build apk, web, and windows (exe) by default.
a2 2026-05-21

Transition to v0.0.4

a3 2026-05-21

v0.0.4 release

ISPANA Backend API Service Changelog Catatan Rilis Layanan API Backend ISPANA

a1 2026-03-30

Project Initialization & Core Architecture

  • **Changes**:
  • Initial project baseline.
  • Set up clean architecture folders.
  • Implemented core auth features (OTP, JWT, Registry).
a2 2026-03-30

System Configuration & Feature Isolation

  • **Changes**:
  • **[lib/features/system]**:
  • Created `system_configs` collection for global app settings.
  • Implemented `GET /v1/system/version` for mandatory app version validation.
  • Implemented `POST /v1/system/version` for automated build script sync.
  • **[bin/server.dart]**: Registered the new **SystemController** routes.
a3 2026-03-31

API Documentation & Flexible Auth Identifiers

  • **Changes**:
  • **[API/README.md]**: Developed a complete API reference, endpoint registry, and local setup guide.
  • **[API/lib/features/auth]**: Updated `AuthRepositoryImpl` and `AuthController` to allow `login` via both **Email** and **Token ID**.
a4 2026-03-31

Repository Refactoring & Utility Extraction

  • **Changes**:
  • **[AuthHelper]**: Created a central utility for hashing, password generation, and Token ID orchestration.
  • **[AuthRepositoryImpl]**: Refactored internally to use `AuthHelper`, organized with logical section headers, and optimized imports.
a5 2026-03-31

Vendor Repository Architectural Refactoring

  • **Changes**:
  • **[VendorRepositoryImpl]**: Refactored to delegate Network and GIS logic to internal helpers, reducing monolithic file size from 725 to ~350 lines.
  • **[VendorNetworkLogic]**: [NEW] Encapsulates Router, VPN, and PPPoE Profile management logic.
  • **[VendorGisLogic]**: [NEW] Encapsulates Landmark and Map-related data management.
  • **[AuthHelper]**: Integrated for centralized password hashing and TokenID generation during employee creation.