Added: Routes for learnlytics

This commit is contained in:
2026-02-25 11:54:11 +01:00
parent 6c842d4793
commit b204c1d133
4 changed files with 150 additions and 43 deletions

View File

@@ -8,6 +8,8 @@
./postgres.nix ./postgres.nix
./postgrest.nix ./postgrest.nix
./swagger.nix ./swagger.nix
./nginx.nix
./learnlytics.nix
# ./k3s-master.nix # ./k3s-master.nix
]; ];

View File

@@ -0,0 +1,19 @@
{ pkgs, ... }:
{
systemd.services.learnlytics-avatar = {
description = "Learnlytics Avatar Upload Server";
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
"postgrest.service"
];
serviceConfig = {
ExecStart = "${pkgs.python3}/bin/python3
/etc/learnlytics/avatar_server.py";
WorkingDirectory = "/etc/learnlytics";
Restart = "on-failure";
User = "postgres"; # or whatever user runs postgrest
StateDirectory = "learnlytics";
};
};
}

108
hosts/cyper-pi-1/nginx.nix Normal file
View File

@@ -0,0 +1,108 @@
{
pkgs,
...
}:
{
services.nginx = {
enable = true;
# ── Frontend ────────────────────────────────────────────────────────────
virtualHosts."learnlytics-frontend" = {
listen = [
{
addr = "0.0.0.0";
port = 80;
}
];
root = "/var/www/learnlytics";
locations."/" = {
tryFiles = "$uri $uri/ /index.html";
};
};
# ── API + avatars + upload ───────────────────────────────────────────────
virtualHosts."learnlytics-api" = {
listen = [
{
addr = "0.0.0.0";
port = 3002;
}
];
# PostgREST proxy (default)
locations."/" = {
proxyPass = "http://127.0.0.1:3001";
extraConfig = ''
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Prefer, Accept, Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Range, Content-Location' always;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Prefer, Accept, Range';
add_header 'Content-Length' '0';
add_header 'Content-Type' 'text/plain';
return 204;
}
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;
proxy_intercept_errors off;
client_max_body_size 1m;
'';
};
# Serve avatar images directly from disk
locations."/avatars/" = {
alias = "/var/lib/learnlytics/avatars/";
extraConfig = ''
expires 7d;
add_header Cache-Control "public, immutable";
add_header 'Access-Control-Allow-Origin' '*';
'';
};
# Proxy avatar uploads to the Python upload server on port 3003
locations."/upload/" = {
proxyPass = "http://127.0.0.1:3003";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 3m;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
add_header 'Content-Length' '0';
add_header 'Content-Type' 'text/plain';
return 204;
}
'';
};
};
};
# Avatar storage directory
systemd.tmpfiles.rules = [
"d /var/www/learnlytics 0755 nginx nginx -"
"d /var/lib/learnlytics 0755 postgres postgres -"
"d /var/lib/learnlytics/avatars 0755 postgres postgres -"
];
networking.firewall.allowedTCPPorts = [
3000
3001
3002
80
];
}

View File

@@ -2,84 +2,62 @@
pkgs, pkgs,
... ...
}: }:
{ {
services.postgresql = { services.postgresql = {
enable = true; enable = true;
package = pkgs.postgresql_15; package = pkgs.postgresql_15;
enableTCPIP = true; enableTCPIP = true;
# Initial database setup extraPlugins = with pkgs.postgresql15Packages; [
initialScript = pkgs.writeText "backend-init-script" '' pgjwt
CREATE USER postgres WITH SUPERUSER PASSWORD 'postgres'; ];
-- Create web_anon role for PostgREST initialScript = /etc/learnlytics/init.sql;
CREATE ROLE web_anon NOLOGIN;
GRANT USAGE ON SCHEMA public TO web_anon;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO web_anon;
-- Create example users table
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW()
);
-- Grant permissions
GRANT SELECT, INSERT, UPDATE, DELETE ON users TO web_anon;
GRANT USAGE, SELECT ON SEQUENCE users_id_seq TO web_anon;
'';
# Raspberry Pi 4 optimized settings (2GB RAM assumed)
settings = { settings = {
# Should match firewall
port = 5432; port = 5432;
# Memory settings (RPi 4 has limited RAM)
shared_buffers = "128MB"; shared_buffers = "128MB";
effective_cache_size = "512MB"; effective_cache_size = "512MB";
maintenance_work_mem = "32MB"; maintenance_work_mem = "32MB";
work_mem = "2MB"; work_mem = "2MB";
wal_buffers = "4MB"; wal_buffers = "4MB";
# Connection settings
max_connections = 20; max_connections = 20;
# Performance tuning for ARM/RPi
random_page_cost = 2.0; random_page_cost = 2.0;
effective_io_concurrency = 100; effective_io_concurrency = 100;
# WAL settings (conservative for SD card)
wal_level = "replica"; wal_level = "replica";
checkpoint_timeout = "15min"; checkpoint_timeout = "15min";
checkpoint_completion_target = 0.7; checkpoint_completion_target = 0.7;
min_wal_size = "1GB"; min_wal_size = "1GB";
max_wal_size = "4GB"; max_wal_size = "4GB";
# Query planning
default_statistics_target = 50; default_statistics_target = 50;
# Logging
log_min_duration_statement = 1000; log_min_duration_statement = 1000;
log_duration = false; log_duration = false;
# ARM/RPi specific
cpu_index_tuple_cost = 0.1; cpu_index_tuple_cost = 0.1;
cpu_operator_cost = 0.05; cpu_operator_cost = 0.05;
}; };
authentication = '' authentication = ''
local all all trust local all all trust
host all all 127.0.0.1/32 md5 host all all 127.0.0.1/32 md5
host all all ::1/128 md5 host all all ::1/128 md5
host all all 192.168.2.0/24 md5 host all all 192.168.2.0/24 md5
''; '';
}; };
# Enable the PostgreSQL service to start on boot
systemd.services.postgresql.wantedBy = [ "multi-user.target" ]; systemd.services.postgresql.wantedBy = [ "multi-user.target" ];
# Open firewall port for PostgreSQL systemd.services.learnlytics-auth-migration = {
description = "Learnlytics auth schema migration";
after = [ "postgresql.service" ];
wants = [ "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "postgres";
ExecStart = "${pkgs.postgresql_15}/bin/psql -U postgres -f /etc/learnlytics/auth-migration.sql";
};
};
networking.firewall.allowedTCPPorts = [ 5432 ]; networking.firewall.allowedTCPPorts = [ 5432 ];
} }