Initial commit
This commit is contained in:
43
home-server/dns/update-dns.sh
Executable file
43
home-server/dns/update-dns.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
# curl -X GET https://api.cloudflare.com/client/v4/zones/bf7a05315be9bf7a39d50dd4001e7a97/dns_records -H "X-Auth-Email: alexmickelson96@gmail.com" -H "X-Auth-Key: jo7GntHEEBtANFsuteAM8EJ-stLUqyNbOk2x4Czr" | python -m json.tool
|
||||
|
||||
source /home/alex/actions-runner/_work/infrastructure/infrastructure/home-pi/dns/cloudflare.env
|
||||
|
||||
NETWORK_INTERFACE=wlan0
|
||||
IP=$(ip a s $NETWORK_INTERFACE | awk '/inet / {print$2}' | cut -d/ -f1)
|
||||
EMAIL="alexmickelson96@gmail.com";
|
||||
ZONE_ID="bf7a05315be9bf7a39d50dd4001e7a97";
|
||||
|
||||
|
||||
update_record() {
|
||||
LOCAL_NAME=$1
|
||||
LOCAL_RECORD_ID=$2
|
||||
|
||||
echo "UPDATING RECORD FOR $LOCAL_NAME TO $IP"
|
||||
|
||||
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$LOCAL_RECORD_ID" \
|
||||
-H "X-Auth-Email: alexmickelson96@gmail.com" \
|
||||
-H "X-Auth-Key: $CLOUDFLARE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{"type":"A","name":"'"$LOCAL_NAME"'","content":"'"$IP"'","ttl":1}' \
|
||||
| python3 -m json.tool;
|
||||
|
||||
echo
|
||||
echo "------------------------------------"
|
||||
echo
|
||||
}
|
||||
|
||||
NAME="ha.alexmickelson.guru";
|
||||
RECORD_ID="09eac5a17fa4302091532dabdbe73a68"
|
||||
update_record $NAME $RECORD_ID
|
||||
|
||||
NAME="jellyfin.alexmickelson.guru";
|
||||
RECORD_ID="577293ab0488913308fda78010a7483b"
|
||||
update_record $NAME $RECORD_ID
|
||||
|
||||
NAME="next.alexmickelson.guru";
|
||||
RECORD_ID="cc686333d2421a4e558a04589b375ded"
|
||||
update_record $NAME $RECORD_ID
|
||||
|
||||
|
||||
256
home-server/docker-compose.yml
Normal file
256
home-server/docker-compose.yml
Normal file
@@ -0,0 +1,256 @@
|
||||
services:
|
||||
jellyfin:
|
||||
image: jellyfin/jellyfin
|
||||
container_name: jellyfin
|
||||
user: 1000:1000
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- /data/jellyfin/config:/config
|
||||
- /data/jellyfin/cache:/cache
|
||||
- /data/media/music/tagged:/music
|
||||
- /data/media/movies:/movies
|
||||
- /data/media/tvshows:/tvshows
|
||||
restart: "unless-stopped"
|
||||
environment:
|
||||
- JELLYFIN_PublishedServerUrl=https://jellyfin.alexmickelson.guru
|
||||
|
||||
nextcloud:
|
||||
build:
|
||||
context: nextcloud
|
||||
container_name: nextcloud
|
||||
environment:
|
||||
- TZ=America/Denver
|
||||
- OVERWRITEPROTOCOL=https
|
||||
- MYSQL_PASSWORD=slkdnflksnelkfnsdweoinv
|
||||
- MYSQL_DATABASE=nextcloud
|
||||
- MYSQL_USER=nextcloud
|
||||
- MYSQL_HOST=nextcloud-db
|
||||
volumes:
|
||||
- /data/nextcloud/html:/var/www/html
|
||||
- /data/media/music:/music
|
||||
- /data/media/movies:/movies
|
||||
- /data/media/tvshows:/tvshows
|
||||
- /data/media/shared:/shared
|
||||
- /data/media/audiobooks:/audiobooks
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
nextcloud-cron:
|
||||
build:
|
||||
context: nextcloud
|
||||
container_name: nextcloud-cron
|
||||
environment:
|
||||
- TZ=America/Denver
|
||||
- OVERWRITEPROTOCOL=https
|
||||
- MYSQL_PASSWORD=slkdnflksnelkfnsdweoinv
|
||||
- MYSQL_DATABASE=nextcloud
|
||||
- MYSQL_USER=nextcloud
|
||||
- MYSQL_HOST=nextcloud-db
|
||||
volumes:
|
||||
- /data/nextcloud/html:/var/www/html
|
||||
- /data/media/music:/music
|
||||
- /data/media/movies:/movies
|
||||
- /data/media/tvshows:/tvshows
|
||||
- /data/media/shared:/shared
|
||||
- /data/media/audiobooks:/audiobooks
|
||||
restart: unless-stopped
|
||||
entrypoint: /cron.sh
|
||||
depends_on:
|
||||
- nextcloud
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
nextcloud-db:
|
||||
image: mariadb:10.6
|
||||
container_name: nextcloud_db
|
||||
# mysql -u$MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE
|
||||
restart: always
|
||||
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
|
||||
volumes:
|
||||
- /data/nextcloud-db/:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=klsdnofinsodkflksen34tesrg
|
||||
- MYSQL_PASSWORD=slkdnflksnelkfnsdweoinv
|
||||
- MYSQL_DATABASE=nextcloud
|
||||
- MYSQL_USER=nextcloud
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
homeassistant:
|
||||
container_name: homeassistant
|
||||
image: homeassistant/home-assistant:stable
|
||||
volumes:
|
||||
- /data/homeAssistant/config:/config
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /dev/serial/by-id:/dev/serial/by-id
|
||||
devices:
|
||||
- /dev/ttyUSB0:/dev/ttyUSB0
|
||||
- /dev/ttyUSB1:/dev/ttyUSB1
|
||||
environment:
|
||||
- TZ=America/Denver
|
||||
restart: always
|
||||
network_mode: host
|
||||
|
||||
# octoprint:
|
||||
# image: octoprint/octoprint
|
||||
# container_name: octoprint
|
||||
# restart: unless-stopped
|
||||
# # ports:
|
||||
# # - 80:80
|
||||
# # devices:
|
||||
# # # use `python -m serial.tools.miniterm` to see what the name is of the printer, this requires pyserial
|
||||
# # - /dev/ttyACM0:/dev/ttyACM0
|
||||
# # - /dev/video0:/dev/video0
|
||||
# volumes:
|
||||
# - /data/octoprint:/octoprint
|
||||
# # uncomment the lines below to ensure camera streaming is enabled when
|
||||
# # you add a video device
|
||||
# environment:
|
||||
# - ENABLE_MJPG_STREAMER=true
|
||||
# - MJPG_SREAMER_INPUT=-n -r 1280x720 -f 30
|
||||
|
||||
prometheus:
|
||||
image: bitnami/prometheus:2
|
||||
container_name: prometheus
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- HOMEASSISTANT_TOKEN=${HOMEASSISTANT_TOKEN}
|
||||
volumes:
|
||||
- ./prometheus.yml:/opt/bitnami/prometheus/conf/prometheus.yml
|
||||
- /data/prometheus:/opt/bitnami/prometheus/data
|
||||
# command:
|
||||
# - '--config.file=/etc/prometheus/prometheus.yml'
|
||||
# - '--storage.tsdb.path=/prometheus'
|
||||
# - '--web.console.libraries=/etc/prometheus/console_libraries'
|
||||
# - '--web.console.templates=/etc/prometheus/consoles'
|
||||
# - '--web.enable-lifecycle'
|
||||
# expose:
|
||||
# - 9090
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:main
|
||||
container_name: grafana
|
||||
restart: always
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=admin
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
|
||||
volumes:
|
||||
- /data/grafana:/var/lib/grafana
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/robots.txt"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 3s
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
# acpupsd_exporter:
|
||||
# image: sfudeus/apcupsd_exporter:master_1.19
|
||||
# container_name: apcupsd_exporter
|
||||
# restart: always
|
||||
# extra_hosts:
|
||||
# - host.docker.internal:host-gateway
|
||||
# command: -apcupsd.addr host.docker.internal:3551
|
||||
# ports:
|
||||
# - 0.0.0.0:9162:9162
|
||||
# docker run -it --rm -p 9162:9162 --net=host sfudeus/apcupsd_exporter:master_1.19
|
||||
|
||||
reverse-proxy:
|
||||
image: ghcr.io/linuxserver/swag
|
||||
container_name: reverse-proxy
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=America/Denver
|
||||
- URL=alexmickelson.guru
|
||||
- SUBDOMAINS=wildcard
|
||||
- VALIDATION=dns
|
||||
- DNSPLUGIN=cloudflare
|
||||
volumes:
|
||||
- ./nginx.conf:/config/nginx/site-confs/default.conf
|
||||
- /data/swag:/config
|
||||
- /data/cloudflare/cloudflare.ini:/config/dns-conf/cloudflare.ini
|
||||
ports:
|
||||
- 0.0.0.0:80:80
|
||||
- 0.0.0.0:443:443
|
||||
extra_hosts:
|
||||
- host.docker.internal:host-gateway
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
|
||||
audiobookshelf:
|
||||
image: ghcr.io/advplyr/audiobookshelf:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 13378:80
|
||||
volumes:
|
||||
- /data/media/audiobooks:/audiobooks
|
||||
# - </path/to/podcasts>:/podcasts
|
||||
- /data/audiobookshelf/config:/config
|
||||
- /data/audiobookshelf/metadata:/metadata
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
docker-registry:
|
||||
image: registry:2
|
||||
container_name: docker-registry
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
|
||||
REGISTRY_HTTP_TLS_CERTIFICATE: /etc/docker/certs.d/server.alexmickelson.guru/cert.pem
|
||||
REGISTRY_HTTP_TLS_KEY: /etc/docker/certs.d/server.alexmickelson.guru/key.pem
|
||||
volumes:
|
||||
- /data/docker-registry:/data
|
||||
- /data/swag/keys/letsencrypt/fullchain.pem:/etc/docker/certs.d/server.alexmickelson.guru/cert.pem
|
||||
- /data/swag/keys/letsencrypt/privkey.pem:/etc/docker/certs.d/server.alexmickelson.guru/key.pem
|
||||
depends_on:
|
||||
- reverse-proxy
|
||||
networks:
|
||||
- proxy
|
||||
# github-actions-exporter:
|
||||
# # ports:
|
||||
# # - 9999:9999
|
||||
# image: ghcr.io/labbs/github-actions-exporter
|
||||
# environment:
|
||||
# - GITHUB_REPOS=alexmickelson/infrastructure
|
||||
# - GITHUB_TOKEN=${MY_GITHUB_TOKEN}
|
||||
|
||||
|
||||
# pihole:
|
||||
# container_name: pihole
|
||||
# image: pihole/pihole:latest
|
||||
# # For DHCP it is recommended to remove these ports and instead add: network_mode: "host"
|
||||
# ports:
|
||||
# # - "0.0.0.0:53:53/tcp"
|
||||
# # - "0.0.0.0:53:53/udp"
|
||||
# # - "127.0.0.1:53:53/tcp"
|
||||
# # - "127.0.0.1:53:53/udp"
|
||||
# - "100.122.128.107:53:53/tcp"
|
||||
# - "100.122.128.107:53:53/udp"
|
||||
# # # - "67:67/udp" # Only required if you are using Pi-hole as your DHCP server
|
||||
# - "8580:80"
|
||||
# environment:
|
||||
# TZ: 'America/Denver'
|
||||
# # WEBPASSWORD: 'set a secure password here or it will be random'
|
||||
# volumes:
|
||||
# - '/data/pihole/etc-pihole:/etc/pihole'
|
||||
# - '/data/pihole/etc-dnsmasq.d:/etc/dnsmasq.d'
|
||||
# # https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
|
||||
# cap_add:
|
||||
# - NET_ADMIN # Required if you are using Pi-hole as your DHCP server, else not needed
|
||||
# restart: unless-stopped
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external:
|
||||
name: proxy
|
||||
4
home-server/nextcloud/dockerfile
Normal file
4
home-server/nextcloud/dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM nextcloud:production
|
||||
|
||||
RUN usermod -u 1000 www-data
|
||||
RUN groupmod -g 1000 www-data
|
||||
202
home-server/nginx.conf
Normal file
202
home-server/nginx.conf
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
# include mime.types;
|
||||
# default_type application/octet-stream;
|
||||
|
||||
# log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
# '$status $body_bytes_sent "$http_referer" '
|
||||
# '"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
# access_log /var/log/nginx/access.log main;
|
||||
|
||||
# sendfile on;
|
||||
# keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name ha.alexmickelson.guru;
|
||||
include /config/nginx/ssl.conf;
|
||||
include /config/nginx/proxy.conf;
|
||||
include /config/nginx/resolver.conf;
|
||||
|
||||
location / {
|
||||
proxy_pass http://host.docker.internal:8123;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name next.alexmickelson.guru;
|
||||
include /config/nginx/ssl.conf;
|
||||
include /config/nginx/proxy.conf;
|
||||
include /config/nginx/resolver.conf;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
|
||||
|
||||
location /.well-known/carddav {
|
||||
return 301 $scheme://$host/remote.php/dav;
|
||||
}
|
||||
|
||||
location /.well-known/caldav {
|
||||
return 301 $scheme://$host/remote.php/dav;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://nextcloud:80;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Host $server_name;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
client_max_body_size 1G;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name plex.alexmickelson.guru;
|
||||
|
||||
location / {
|
||||
proxy_pass http://host.docker.internal:32400;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name jellyfin.alexmickelson.guru;
|
||||
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header X-XSS-Protection "0"; # Do NOT enable. This is obsolete/dangerous
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
client_max_body_size 20M;
|
||||
|
||||
|
||||
location / {
|
||||
# Proxy main Jellyfin traffic
|
||||
proxy_pass http://host.docker.internal:8096;
|
||||
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_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
|
||||
proxy_buffering off;
|
||||
}
|
||||
|
||||
location /socket {
|
||||
# Proxy Jellyfin Websockets traffic
|
||||
proxy_pass http://host.docker.internal:8096;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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_set_header X-Forwarded-Protocol $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name audiobook.alexmickelson.guru;
|
||||
|
||||
location / {
|
||||
proxy_pass http://audiobookshelf:80;
|
||||
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
}
|
||||
|
||||
# server {
|
||||
# listen 443 ssl;
|
||||
# listen [::]:443 ssl;
|
||||
# server_name octoprint.alexmickelson.guru;
|
||||
|
||||
# location / {
|
||||
# proxy_pass http://octoprint:80;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# }
|
||||
# }
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name prometheus.alexmickelson.guru;
|
||||
|
||||
location / {
|
||||
proxy_pass http://prometheus:9090;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name grafana.alexmickelson.guru;
|
||||
|
||||
location / {
|
||||
proxy_pass http://grafana:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name photos.alexmickelson.guru;
|
||||
|
||||
# allow large file uploads
|
||||
client_max_body_size 50000M;
|
||||
|
||||
# Set headers
|
||||
proxy_set_header Host $http_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;
|
||||
|
||||
# enable websockets: http://nginx.org/en/docs/http/websocket.html
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_redirect off;
|
||||
|
||||
# set timeout
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
send_timeout 600s;
|
||||
|
||||
location / {
|
||||
proxy_pass http://immich_server:2283;
|
||||
}
|
||||
}
|
||||
340
home-server/nix/home-server.nix
Normal file
340
home-server/nix/home-server.nix
Normal file
@@ -0,0 +1,340 @@
|
||||
# Edit this configuration file to define what should be installed on
|
||||
# your system. Help is available in the configuration.nix(5) man page
|
||||
# and in the NixOS manual (accessible by running ‘nixos-help’).
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ # Include the results of the hardware scan.
|
||||
./hardware-configuration.nix
|
||||
<home-manager/nixos>
|
||||
];
|
||||
|
||||
# Bootloader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
networking.hostName = "home-server"; # Define your hostname.
|
||||
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
# Configure network proxy if necessary
|
||||
# networking.proxy.default = "http://user:password@proxy:port/";
|
||||
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
|
||||
|
||||
# Enable networking
|
||||
networking.networkmanager.enable = true;
|
||||
|
||||
networking.nat.enable = true;
|
||||
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1;
|
||||
# Set your time zone.
|
||||
time.timeZone = "America/Denver";
|
||||
|
||||
# Select internationalisation properties.
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
|
||||
i18n.extraLocaleSettings = {
|
||||
LC_ADDRESS = "en_US.UTF-8";
|
||||
LC_IDENTIFICATION = "en_US.UTF-8";
|
||||
LC_MEASUREMENT = "en_US.UTF-8";
|
||||
LC_MONETARY = "en_US.UTF-8";
|
||||
LC_NAME = "en_US.UTF-8";
|
||||
LC_NUMERIC = "en_US.UTF-8";
|
||||
LC_PAPER = "en_US.UTF-8";
|
||||
LC_TELEPHONE = "en_US.UTF-8";
|
||||
LC_TIME = "en_US.UTF-8";
|
||||
};
|
||||
|
||||
# Configure keymap in X11
|
||||
services.xserver.xkb = {
|
||||
layout = "us";
|
||||
variant = "";
|
||||
};
|
||||
|
||||
users.users.github = {
|
||||
isNormalUser = true;
|
||||
description = "github";
|
||||
extraGroups = [ "docker" ];
|
||||
shell = pkgs.fish;
|
||||
};
|
||||
users.users.alex = {
|
||||
isNormalUser = true;
|
||||
description = "alex";
|
||||
extraGroups = [ "networkmanager" "wheel" "docker" "users" "libvirtd" "cdrom" ];
|
||||
shell = pkgs.fish;
|
||||
};
|
||||
home-manager.users.alex = { pgks, ...}: {
|
||||
home.stateVersion = "24.05";
|
||||
home.packages = with pkgs; [
|
||||
openldap
|
||||
k9s
|
||||
jwt-cli
|
||||
thefuck
|
||||
fish
|
||||
kubectl
|
||||
lazydocker
|
||||
btop
|
||||
nix-index
|
||||
usbutils
|
||||
makemkv
|
||||
mbuffer
|
||||
lzop
|
||||
lsof
|
||||
code-server
|
||||
];
|
||||
programs.fish = {
|
||||
enable = true;
|
||||
shellAliases = {
|
||||
dang="fuck";
|
||||
};
|
||||
shellInit = ''
|
||||
function commit
|
||||
git add --all
|
||||
git commit -m "$argv"
|
||||
git push
|
||||
end
|
||||
|
||||
# have ctrl+backspace delete previous word
|
||||
bind \e\[3\;5~ kill-word
|
||||
# have ctrl+delete delete following word
|
||||
bind \b backward-kill-word
|
||||
|
||||
set -U fish_user_paths ~/.local/bin $fish_user_paths
|
||||
#set -U fish_user_paths ~/.dotnet $fish_user_paths
|
||||
#set -U fish_user_paths ~/.dotnet/tools $fish_user_paths
|
||||
|
||||
export VISUAL=vim
|
||||
export EDITOR="$VISUAL"
|
||||
export DOTNET_WATCH_RESTART_ON_RUDE_EDIT=1
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
set -x LIBVIRT_DEFAULT_URI qemu:///system
|
||||
|
||||
thefuck --alias | source
|
||||
'';
|
||||
};
|
||||
home.file = {
|
||||
".config/lazydocker/config.yml".text = ''
|
||||
gui:
|
||||
returnImmediately: true
|
||||
'';
|
||||
".config/k9s/config.yaml".text = ''
|
||||
k9s:
|
||||
liveViewAutoRefresh: true
|
||||
screenDumpDir: /home/alexm/.local/state/k9s/screen-dumps
|
||||
refreshRate: 2
|
||||
maxConnRetry: 5
|
||||
readOnly: false
|
||||
noExitOnCtrlC: false
|
||||
ui:
|
||||
enableMouse: false
|
||||
headless: false
|
||||
logoless: false
|
||||
crumbsless: false
|
||||
reactive: false
|
||||
noIcons: false
|
||||
defaultsToFullScreen: false
|
||||
skipLatestRevCheck: false
|
||||
disablePodCounting: false
|
||||
shellPod:
|
||||
image: busybox:1.35.0
|
||||
namespace: default
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
imageScans:
|
||||
enable: false
|
||||
exclusions:
|
||||
namespaces: []
|
||||
labels: {}
|
||||
logger:
|
||||
tail: 1000
|
||||
buffer: 5000
|
||||
sinceSeconds: -1
|
||||
textWrap: false
|
||||
showTime: false
|
||||
thresholds:
|
||||
cpu:
|
||||
critical: 90
|
||||
warn: 70
|
||||
memory:
|
||||
critical: 90
|
||||
warn: 70
|
||||
namespace:
|
||||
lockFavorites: false
|
||||
'';
|
||||
};
|
||||
home.sessionVariables = {
|
||||
EDITOR = "vim";
|
||||
};
|
||||
};
|
||||
home-manager.useGlobalPkgs = true;
|
||||
|
||||
|
||||
# Allow unfree packages
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
# List packages installed in system profile. To search, run:
|
||||
# $ nix search wget
|
||||
environment.systemPackages = with pkgs; [
|
||||
vim
|
||||
wget
|
||||
curl
|
||||
docker
|
||||
fish
|
||||
git
|
||||
zfs
|
||||
gcc-unwrapped
|
||||
github-runner
|
||||
sanoid
|
||||
virtiofsd
|
||||
tmux
|
||||
];
|
||||
|
||||
services.openssh.enable = true;
|
||||
programs.fish.enable = true;
|
||||
virtualisation.docker.enable = true;
|
||||
#virtualisation.docker.extraOptions = "--dns 1.1.1.1 --dns 8.8.8.8 --dns 100.100.100.100";
|
||||
services.tailscale.enable = true;
|
||||
services.tailscale.extraSetFlags = [
|
||||
"--stateful-filtering=false"
|
||||
];
|
||||
services.envfs.enable = true;
|
||||
|
||||
# printing
|
||||
services.printing = {
|
||||
enable = true;
|
||||
drivers = [ pkgs.brgenml1lpr pkgs.brgenml1cupswrapper pkgs.brlaser];
|
||||
listenAddresses = [ "*:631" ];
|
||||
|
||||
extraConf = ''
|
||||
ServerAlias server.alexmickelson.guru
|
||||
'';
|
||||
allowFrom = [ "all" ];
|
||||
browsing = true;
|
||||
defaultShared = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
services.avahi = {
|
||||
enable = true;
|
||||
nssmdns4 = true;
|
||||
openFirewall = true;
|
||||
publish = {
|
||||
enable = true;
|
||||
userServices = true;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.printing-server = {
|
||||
description = "Web Printing Server Service";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.nix}/bin/nix run .#fastapi-server";
|
||||
Restart = "always";
|
||||
WorkingDirectory = "/home/alex/infrastructure/home-server/printing/server";
|
||||
User = "alex";
|
||||
};
|
||||
};
|
||||
|
||||
# virtualization stuff
|
||||
virtualisation.libvirtd.enable = true;
|
||||
|
||||
# zfs stuff
|
||||
boot.supportedFilesystems = [ "zfs" ];
|
||||
boot.zfs.forceImportRoot = false;
|
||||
networking.hostId = "eafe9551";
|
||||
boot.zfs.extraPools = [ "data-ssd" "backup" ];
|
||||
services.sanoid = {
|
||||
enable = true;
|
||||
templates.production = {
|
||||
hourly = 24;
|
||||
daily = 14;
|
||||
monthly = 5;
|
||||
autoprune = true;
|
||||
autosnap = true;
|
||||
};
|
||||
|
||||
datasets."data-ssd/data" = {
|
||||
useTemplate = [ "production" ];
|
||||
};
|
||||
datasets."data-ssd/media" = {
|
||||
useTemplate = [ "production" ];
|
||||
};
|
||||
|
||||
|
||||
templates.backup = {
|
||||
hourly = 24;
|
||||
daily = 14;
|
||||
monthly = 5;
|
||||
autoprune = true;
|
||||
autosnap = false;
|
||||
};
|
||||
datasets."backup/data" = {
|
||||
useTemplate = [ "backup" ];
|
||||
};
|
||||
datasets."backup/media" = {
|
||||
useTemplate = [ "backup" ];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
services.github-runners = {
|
||||
infrastructure = {
|
||||
enable = true;
|
||||
name = "infrastructure-runner";
|
||||
user = "github";
|
||||
tokenFile = "/data/runner/github-infrastructure-token.txt";
|
||||
url = "https://github.com/alexmickelson/infrastructure";
|
||||
extraLabels = [ "home-server" ];
|
||||
#workDir = "/data/runner/infrastructure/";
|
||||
replace = true;
|
||||
serviceOverrides = {
|
||||
ReadWritePaths = [
|
||||
"/data/cloudflare/"
|
||||
"/data/runner/infrastructure"
|
||||
"/data/runner"
|
||||
"/home/github/infrastructure"
|
||||
];
|
||||
PrivateDevices = false;
|
||||
DeviceAllow = "/dev/zfs rw";
|
||||
ProtectProc = false;
|
||||
ProtectSystem = false;
|
||||
PrivateMounts = false;
|
||||
PrivateUsers = false;
|
||||
#DynamicUser = true;
|
||||
#NoNewPrivileges = false;
|
||||
ProtectHome = false;
|
||||
#RuntimeDirectoryPreserve = "yes";
|
||||
};
|
||||
extraPackages = with pkgs; [
|
||||
docker
|
||||
git-secret
|
||||
zfs
|
||||
sanoid
|
||||
mbuffer
|
||||
lzop
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# Open ports in the firewall.
|
||||
# networking.firewall.allowedTCPPorts = [ ... ];
|
||||
# networking.firewall.allowedUDPPorts = [ ... ];
|
||||
# Or disable the firewall altogether.
|
||||
networking.firewall.enable = false;
|
||||
# networking.firewall.trustedInterfaces = [ "docker0" ];
|
||||
|
||||
# This value determines the NixOS release from which the default
|
||||
# settings for stateful data, like file locations and database versions
|
||||
# on your system were taken. It‘s perfectly fine and recommended to leave
|
||||
# this value at the release version of the first install of this system.
|
||||
# Before changing this value read the documentation for this option
|
||||
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
||||
system.stateVersion = "24.05"; # Did you read the comment?
|
||||
|
||||
}
|
||||
198
home-server/printing/cupsd.conf
Normal file
198
home-server/printing/cupsd.conf
Normal file
@@ -0,0 +1,198 @@
|
||||
#
|
||||
# Configuration file for the CUPS scheduler. See "man cupsd.conf" for a
|
||||
# complete description of this file.
|
||||
#
|
||||
|
||||
# Log general information in error_log - change "warn" to "debug"
|
||||
# for troubleshooting...
|
||||
LogLevel warn
|
||||
PageLogFormat
|
||||
|
||||
# Specifies the maximum size of the log files before they are rotated. The value "0" disables log rotation.
|
||||
MaxLogSize 0
|
||||
|
||||
# Default error policy for printers
|
||||
ErrorPolicy retry-job
|
||||
|
||||
# Allow remote access
|
||||
Listen *:631 # important
|
||||
ServerAlias * # important
|
||||
|
||||
# Show shared printers on the local network.
|
||||
Browsing Yes
|
||||
BrowseLocalProtocols dnssd
|
||||
|
||||
# Default authentication type, when authentication is required...
|
||||
DefaultAuthType Basic
|
||||
DefaultEncryption IfRequested
|
||||
|
||||
# Web interface setting...
|
||||
WebInterface Yes
|
||||
|
||||
# Timeout after cupsd exits if idle (applied only if cupsd runs on-demand - with -l)
|
||||
IdleExitTimeout 60
|
||||
|
||||
# Restrict access to the server...
|
||||
<Location />
|
||||
Order allow,deny
|
||||
Allow all
|
||||
</Location>
|
||||
|
||||
# Restrict access to the admin pages...
|
||||
<Location /admin>
|
||||
Order allow,deny
|
||||
Allow all
|
||||
</Location>
|
||||
|
||||
# Restrict access to configuration files...
|
||||
<Location /admin/conf>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order allow,deny
|
||||
Allow all
|
||||
</Location>
|
||||
|
||||
# Restrict access to log files...
|
||||
<Location /admin/log>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order allow,deny
|
||||
Allow all
|
||||
</Location>
|
||||
|
||||
# Set the default printer/job policies...
|
||||
<Policy default>
|
||||
# Job/subscription privacy...
|
||||
JobPrivateAccess default
|
||||
JobPrivateValues default
|
||||
SubscriptionPrivateAccess default
|
||||
SubscriptionPrivateValues default
|
||||
|
||||
# Job-related operations must be done by the owner or an administrator...
|
||||
<Limit Create-Job Print-Job Print-URI Validate-Job>
|
||||
Order deny,allow
|
||||
# Allow all # mine...
|
||||
</Limit>
|
||||
|
||||
<Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# All administration operations require an administrator to authenticate...
|
||||
<Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default CUPS-Get-Devices>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# All printer operations require a printer operator to authenticate...
|
||||
<Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# Only the owner or an administrator can cancel or authenticate a job...
|
||||
<Limit Cancel-Job CUPS-Authenticate-Job>
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit All>
|
||||
Order deny,allow
|
||||
# Allow all # mine
|
||||
</Limit>
|
||||
</Policy>
|
||||
|
||||
# Set the authenticated printer/job policies...
|
||||
<Policy authenticated>
|
||||
# Job/subscription privacy...
|
||||
JobPrivateAccess default
|
||||
JobPrivateValues default
|
||||
SubscriptionPrivateAccess default
|
||||
SubscriptionPrivateValues default
|
||||
|
||||
# Job-related operations must be done by the owner or an administrator...
|
||||
<Limit Create-Job Print-Job Print-URI Validate-Job>
|
||||
AuthType Default
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
|
||||
AuthType Default
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# All administration operations require an administrator to authenticate...
|
||||
<Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# All printer operations require a printer operator to authenticate...
|
||||
<Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# Only the owner or an administrator can cancel or authenticate a job...
|
||||
<Limit Cancel-Job CUPS-Authenticate-Job>
|
||||
AuthType Default
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit All>
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
</Policy>
|
||||
|
||||
# Set the kerberized printer/job policies...
|
||||
<Policy kerberos>
|
||||
# Job/subscription privacy...
|
||||
JobPrivateAccess default
|
||||
JobPrivateValues default
|
||||
SubscriptionPrivateAccess default
|
||||
SubscriptionPrivateValues default
|
||||
|
||||
# Job-related operations must be done by the owner or an administrator...
|
||||
<Limit Create-Job Print-Job Print-URI Validate-Job>
|
||||
AuthType Negotiate
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
|
||||
AuthType Negotiate
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# All administration operations require an administrator to authenticate...
|
||||
<Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class CUPS-Delete-Class CUPS-Set-Default>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# All printer operations require a printer operator to authenticate...
|
||||
<Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
|
||||
AuthType Default
|
||||
Require user @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
# Only the owner or an administrator can cancel or authenticate a job...
|
||||
<Limit Cancel-Job CUPS-Authenticate-Job>
|
||||
AuthType Negotiate
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
<Limit All>
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
</Policy>
|
||||
19
home-server/printing/docker-compose.yml
Normal file
19
home-server/printing/docker-compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
cups:
|
||||
image: olbat/cupsd:stable-2024-01-19 # admin user/password: print/print
|
||||
container_name: cups
|
||||
privileged: true
|
||||
volumes:
|
||||
- "/dev/bus/usb:/dev/bus/usb" # keep this under volumes, not devices
|
||||
- "/run/dbus:/run/dbus"
|
||||
- "./cupsd.conf:/etc/cups/cupsd.conf:ro"
|
||||
#- "./data/printers.conf:/etc/cups/printers.conf:ro"
|
||||
ports:
|
||||
- "631:631/tcp" # CUPS
|
||||
restart: "always"
|
||||
|
||||
cups-webpage:
|
||||
buid: server
|
||||
ports:
|
||||
- 6311:6311
|
||||
24
home-server/printing/printer.conf
Normal file
24
home-server/printing/printer.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
# Printer configuration file for CUPS v2.4.2
|
||||
# Written by cupsd
|
||||
# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING
|
||||
NextPrinterId 2
|
||||
<Printer Brother_HL-L2300D_series>
|
||||
PrinterId 1
|
||||
UUID urn:uuid:8ac038d7-8659-3de9-57d0-0a7f97b956cc
|
||||
Info Brother HL-L2300D series
|
||||
Location
|
||||
MakeModel Brother HL-L2300D series, using brlaser v6
|
||||
DeviceURI usb://Brother/HL-L2300D%20series?serial=U63878J0N375067
|
||||
State Idle
|
||||
StateTime 1714021523
|
||||
ConfigTime 1714021523
|
||||
Type 4180
|
||||
Accepting Yes
|
||||
Shared Yes
|
||||
JobSheets none none
|
||||
QuotaPeriod 0
|
||||
PageLimit 0
|
||||
KLimit 0
|
||||
OpPolicy default
|
||||
ErrorPolicy retry-job
|
||||
</Printer>
|
||||
13
home-server/printing/readme.md
Normal file
13
home-server/printing/readme.md
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
|
||||
## what I am running on office server
|
||||
|
||||
```bash
|
||||
sudo apt install python3-pip cups python3-cups hplip
|
||||
pip install pycups fastapi "uvicorn[standard]" python-multipart
|
||||
sudo hp-setup -i # manually configure printer...
|
||||
python -m uvicorn print_api:app --reload --host 0.0.0.0
|
||||
```
|
||||
|
||||
url: http://100.103.75.97:8000/
|
||||
11
home-server/printing/server/Dockerfile
Normal file
11
home-server/printing/server/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM python:3
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y libcups2-dev python3-pip cups python3-cups gcc \
|
||||
&& pip install pycups fastapi "uvicorn[standard]" python-multipart
|
||||
|
||||
|
||||
WORKDIR /app
|
||||
COPY ./src .
|
||||
|
||||
CMD python -m uvicorn print_api:app --reload --host 0.0.0.0 --port 6311
|
||||
27
home-server/printing/server/flake.lock
generated
Normal file
27
home-server/printing/server/flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1725634671,
|
||||
"narHash": "sha256-v3rIhsJBOMLR8e/RNWxr828tB+WywYIoajrZKFM+0Gg=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "574d1eac1c200690e27b8eb4e24887f8df7ac27c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
38
home-server/printing/server/flake.nix
Normal file
38
home-server/printing/server/flake.nix
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
description = "Printer Server Flake";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
};
|
||||
outputs = { self, nixpkgs, ... }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
myPython = pkgs.python3.withPackages (python-pkgs: with pkgs; [
|
||||
python312Packages.fastapi
|
||||
python312Packages.fastapi-cli
|
||||
python312Packages.pycups
|
||||
python312Packages.python-multipart
|
||||
python312Packages.uvicorn
|
||||
]);
|
||||
in
|
||||
{
|
||||
devShells.${system}.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
python312Packages.fastapi
|
||||
python312Packages.fastapi-cli
|
||||
python312Packages.pycups
|
||||
python312Packages.python-multipart
|
||||
python312Packages.uvicorn
|
||||
];
|
||||
};
|
||||
|
||||
packages.${system} = rec {
|
||||
fastapi-server = pkgs.writeShellScriptBin "start-server" ''
|
||||
${myPython}/bin/fastapi run ${self}/src/print_api.py
|
||||
'';
|
||||
|
||||
default = fastapi-server;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
149
home-server/printing/server/src/index.html
Normal file
149
home-server/printing/server/src/index.html
Normal file
@@ -0,0 +1,149 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Document Upload</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
background: #09373e;
|
||||
color: #85bfc8;
|
||||
}
|
||||
#form-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
form {
|
||||
border: 1px solid #ccc;
|
||||
padding: 20px;
|
||||
background: #05252a;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
input[type="file"] {
|
||||
display: none; /* Hide the file input */
|
||||
}
|
||||
input[type="submit"] {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
background: #007bff;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
input[type="submit"]:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
#dropzone {
|
||||
border: 2px dashed #ccc;
|
||||
padding: 20px;
|
||||
width: 300px;
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#dropzone.dragover {
|
||||
border-color: #000;
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Upload Document</h2>
|
||||
<br />
|
||||
<section id="form-container">
|
||||
<form
|
||||
id="printForm"
|
||||
action="/print/"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
>
|
||||
<div id="dropzone">Drop file to upload or click to select</div>
|
||||
<input type="file" id="fileInput" />
|
||||
<br />
|
||||
<input type="submit" value="Upload Document" name="submit" />
|
||||
</form>
|
||||
</section>
|
||||
<script>
|
||||
var stagedFile = undefined;
|
||||
const formElement = document.getElementById("printForm");
|
||||
const fileInputElement = document.getElementById("fileInput");
|
||||
|
||||
formElement.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData();
|
||||
formData.append("file", stagedFile);
|
||||
|
||||
const response = await fetch("/print/", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById("dropzone")
|
||||
.addEventListener("dragover", function (event) {
|
||||
event.preventDefault(); // Prevent default behavior (Prevent file from being opened)
|
||||
event.stopPropagation();
|
||||
event.target.classList.add("dragover"); // Optional: add a style change
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById("dropzone")
|
||||
.addEventListener("dragleave", function (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.target.classList.remove("dragover"); // Optional: revert style change
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById("dropzone")
|
||||
.addEventListener("drop", function (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.target.classList.remove("dragover"); // Optional: revert style change
|
||||
|
||||
// Process files
|
||||
var files = event.dataTransfer.files;
|
||||
handleFiles(files);
|
||||
});
|
||||
|
||||
// Handle file selection when clicked
|
||||
document
|
||||
.getElementById("dropzone")
|
||||
.addEventListener("click", function () {
|
||||
fileInputElement.click(); // Trigger the hidden file input's click
|
||||
});
|
||||
|
||||
fileInputElement.addEventListener("change", function (event) {
|
||||
const files = event.target.files;
|
||||
handleFiles(files);
|
||||
});
|
||||
|
||||
const handleFiles = (files) => {
|
||||
stagedFile = files[0];
|
||||
renderStagedFile();
|
||||
};
|
||||
|
||||
const renderStagedFile = () => {
|
||||
const element = document.getElementById("dropzone");
|
||||
if (!stagedFile) {
|
||||
element.textContent = "Drop file to upload or click to select";
|
||||
} else {
|
||||
element.textContent = `FILE: ${stagedFile.name}`;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
69
home-server/printing/server/src/print_api.py
Normal file
69
home-server/printing/server/src/print_api.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import os
|
||||
from pprint import pprint
|
||||
import tempfile
|
||||
from fastapi import FastAPI, File, UploadFile, Request
|
||||
import cups
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
# @app.post("/print/")
|
||||
# async def print_document(file: UploadFile = File(...)):
|
||||
# temp_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
# temp_file.write(await file.read())
|
||||
# temp_file.close()
|
||||
|
||||
# conn = cups.Connection(host="server.alexmickelson.guru")
|
||||
|
||||
# printers = conn.getPrinters()
|
||||
# print(file.filename)
|
||||
# print(temp_file.name)
|
||||
# pprint(printers)
|
||||
# for printer in printers:
|
||||
# print(printer, printers[printer]["device-uri"])
|
||||
|
||||
# default_printer = list(printers.keys())[0]
|
||||
|
||||
|
||||
# job_id = conn.printFile(default_printer, temp_file.name, f"FastAPI Print Job for {temp_file.name}", {})
|
||||
# os.unlink(temp_file.name)
|
||||
|
||||
# return {"job_id": job_id, "file_name": file.filename}
|
||||
|
||||
|
||||
@app.post("/print/")
|
||||
async def print_document(file: UploadFile = File(...)):
|
||||
# Save the uploaded file to a temporary file
|
||||
temp_file = tempfile.NamedTemporaryFile(delete=False)
|
||||
temp_file.write(await file.read())
|
||||
temp_file.close()
|
||||
|
||||
# Connect to the CUPS server on the host (use default CUPS connection)
|
||||
conn = cups.Connection() # This will connect to localhost CUPS
|
||||
|
||||
# Get the list of available printers
|
||||
printers = conn.getPrinters()
|
||||
print(file.filename)
|
||||
print(temp_file.name)
|
||||
pprint(printers)
|
||||
for printer in printers:
|
||||
print(printer, printers[printer]["device-uri"])
|
||||
|
||||
# Use the default printer (first one in the list)
|
||||
default_printer = list(printers.keys())[0]
|
||||
|
||||
# Print the file
|
||||
job_id = conn.printFile(default_printer, temp_file.name, f"FastAPI Print Job for {temp_file.name}", {})
|
||||
|
||||
# Clean up the temporary file
|
||||
os.unlink(temp_file.name)
|
||||
|
||||
return {"job_id": job_id, "file_name": file.filename}
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def read_root(request: Request):
|
||||
with open('src/index.html', 'r') as f:
|
||||
html_content = f.read()
|
||||
return HTMLResponse(content=html_content, status_code=200)
|
||||
|
||||
52
home-server/prometheus.yml
Normal file
52
home-server/prometheus.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
# my global config
|
||||
global:
|
||||
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
|
||||
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
|
||||
# scrape_timeout is set to the global default (10s).
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
# - alertmanager:9093
|
||||
|
||||
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
||||
rule_files:
|
||||
# - "first_rules.yml"
|
||||
# - "second_rules.yml"
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape:
|
||||
# Here it's Prometheus itself.
|
||||
scrape_configs:
|
||||
- job_name: "prometheus"
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
|
||||
- job_name: "node"
|
||||
static_configs:
|
||||
- targets:
|
||||
- 100.119.183.105:9100 # desktop
|
||||
- 100.122.128.107:9100 # home server
|
||||
- 100.64.229.40:9100 # linode
|
||||
|
||||
- job_name: "docker"
|
||||
static_configs:
|
||||
- targets:
|
||||
# - 100.119.183.105:9323 # desktop
|
||||
- 100.122.128.107:9323 # home server
|
||||
- 100.64.229.40:9323 # linode
|
||||
|
||||
- job_name: ups
|
||||
static_configs:
|
||||
- targets:
|
||||
- 100.122.128.107:9162 # home server
|
||||
|
||||
- job_name: homeassistant
|
||||
scrape_interval: 60s
|
||||
metrics_path: /api/prometheus
|
||||
authorization:
|
||||
credentials: '%{HOMEASSITANT_TOKEN}'
|
||||
scheme: https
|
||||
static_configs:
|
||||
- targets: ['ha.alexmickelson.guru']
|
||||
Reference in New Issue
Block a user