Added: Routes for learnlytics
This commit is contained in:
@@ -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
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
19
hosts/cyper-pi-1/learnlytics.nix
Normal file
19
hosts/cyper-pi-1/learnlytics.nix
Normal 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
108
hosts/cyper-pi-1/nginx.nix
Normal 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
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -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 ];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user