wow
This commit is contained in:
commit
5a58e0e55b
8 changed files with 84 additions and 1235 deletions
1116
flake.lock
generated
1116
flake.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -36,6 +36,8 @@
|
||||||
|
|
||||||
nixcord.url = "github:4evy/nixcord";
|
nixcord.url = "github:4evy/nixcord";
|
||||||
|
|
||||||
|
concord.url = "github:chojs23/concord";
|
||||||
|
|
||||||
vscode-nigo = {
|
vscode-nigo = {
|
||||||
url = "git+https://git.pari.ng/imnyang/vscode-nigo.git?ref=main";
|
url = "git+https://git.pari.ng/imnyang/vscode-nigo.git?ref=main";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,34 @@
|
||||||
let
|
let
|
||||||
nr = import "${inputs.self}/modules/mizukios/features/packages/nr.nix" { inherit pkgs; };
|
nr = import "${inputs.self}/modules/mizukios/features/packages/nr.nix" { inherit pkgs; };
|
||||||
nrr = import "${inputs.self}/modules/mizukios/features/packages/nrr.nix" { inherit pkgs; };
|
nrr = import "${inputs.self}/modules/mizukios/features/packages/nrr.nix" { inherit pkgs; };
|
||||||
|
concord = pkgs.writeShellScriptBin "concord" ''
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
runtime_dir="''${XDG_RUNTIME_DIR:-/tmp}"
|
||||||
|
session_dir="$(mktemp -d "$runtime_dir/concord-session.XXXXXX")"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf -- "$session_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
trap 'exit 129' HUP
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
|
|
||||||
|
config_home="$session_dir/config"
|
||||||
|
state_home="$session_dir/state"
|
||||||
|
mkdir -p "$config_home" "$state_home"
|
||||||
|
|
||||||
|
cat > "$config_home/config.toml" <<'EOF'
|
||||||
|
[credentials]
|
||||||
|
store = "plain"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$config_home" \
|
||||||
|
XDG_STATE_HOME="$state_home" \
|
||||||
|
"${inputs.concord.packages.${pkgs.system}.default}/bin/concord" "$@"
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.stateVersion = "26.05";
|
home.stateVersion = "26.05";
|
||||||
|
|
@ -13,6 +41,7 @@ in
|
||||||
home.homeDirectory = "/home/imnyang";
|
home.homeDirectory = "/home/imnyang";
|
||||||
|
|
||||||
home.packages = [
|
home.packages = [
|
||||||
|
concord
|
||||||
nr
|
nr
|
||||||
nrr
|
nrr
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,8 @@ let
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# These were previously served by *.imnya.ng. Listing them explicitly lets
|
# Keep these explicit hosts for their existing per-host content. The
|
||||||
# stock Caddy obtain an individual certificate without a DNS challenge.
|
# wildcard host below handles any other one-level subdomain.
|
||||||
staticHosts = [
|
staticHosts = [
|
||||||
"26layer7beginner.imnya.ng"
|
"26layer7beginner.imnya.ng"
|
||||||
"alt.imnya.ng"
|
"alt.imnya.ng"
|
||||||
|
|
@ -149,7 +149,20 @@ in
|
||||||
enable = true;
|
enable = true;
|
||||||
email = "imnyang@pm.me";
|
email = "imnyang@pm.me";
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
package = pkgs.caddy;
|
package = pkgs.caddy.withPlugins {
|
||||||
|
plugins = [
|
||||||
|
"github.com/caddy-dns/cloudflare@v0.2.4"
|
||||||
|
"github.com/aksdb/caddy-cgi/v2@v2.2.6"
|
||||||
|
"github.com/shift72/caddy-geo-ip@v0.6.0"
|
||||||
|
"github.com/lolPants/caddy-requestid@v1.1.2"
|
||||||
|
"github.com/WeidiDeng/caddy-cloudflare-ip@v0.0.0-20231130002422-f53b62aa13cb"
|
||||||
|
"github.com/caddyserver/ntlm-transport@v0.1.2"
|
||||||
|
"github.com/ueffel/caddy-brotli@v1.6.0"
|
||||||
|
"github.com/RussellLuo/caddy-ext/ratelimit@v0.3.0"
|
||||||
|
"github.com/neodyme-labs/user_agent_parse@v0.0.1"
|
||||||
|
];
|
||||||
|
hash = "sha256-r4MgRk8HpKYx7f8FmA3PKgD/5ZNTIHpQvlE+zRbP0lw=";
|
||||||
|
};
|
||||||
|
|
||||||
globalConfig = ''
|
globalConfig = ''
|
||||||
admin unix//run/caddy/admin.socket
|
admin unix//run/caddy/admin.socket
|
||||||
|
|
@ -170,6 +183,15 @@ in
|
||||||
}) staticHosts
|
}) staticHosts
|
||||||
)
|
)
|
||||||
// {
|
// {
|
||||||
|
"*.imnya.ng".extraConfig = withErrors ''
|
||||||
|
tls {
|
||||||
|
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
||||||
|
}
|
||||||
|
|
||||||
|
root * /var/static/{host}
|
||||||
|
file_server
|
||||||
|
'';
|
||||||
|
|
||||||
"api.imnya.ng".extraConfig = reverseProxyWithHeaders "127.0.0.1:10118";
|
"api.imnya.ng".extraConfig = reverseProxyWithHeaders "127.0.0.1:10118";
|
||||||
"docs.imnya.ng".extraConfig = reverseProxy "100.70.1.1:3939";
|
"docs.imnya.ng".extraConfig = reverseProxy "100.70.1.1:3939";
|
||||||
"panel.imnya.ng".extraConfig = reverseProxy "127.0.0.1:32981";
|
"panel.imnya.ng".extraConfig = reverseProxy "127.0.0.1:32981";
|
||||||
|
|
@ -372,6 +394,9 @@ in
|
||||||
requires = [ "mnt-static.mount" ];
|
requires = [ "mnt-static.mount" ];
|
||||||
after = [ "mnt-static.mount" "systemd-tmpfiles-setup.service" ];
|
after = [ "mnt-static.mount" "systemd-tmpfiles-setup.service" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
|
# Keep the manually provisioned token outside NixOS-managed /etc.
|
||||||
|
# /var/lib/caddy is persistent across system generations.
|
||||||
|
EnvironmentFile = "/var/lib/caddy/cloudflare.env";
|
||||||
RuntimeDirectory = "caddy";
|
RuntimeDirectory = "caddy";
|
||||||
RuntimeDirectoryMode = "0755";
|
RuntimeDirectoryMode = "0755";
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
DISABLE_REGISTRATION = false;
|
DISABLE_REGISTRATION = false;
|
||||||
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
|
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
|
||||||
ENABLE_CAPTCHA = false;
|
ENABLE_CAPTCHA = false;
|
||||||
REQUIRE_SIGNIN_VIEW = false;
|
REQUIRE_SIGNIN_VIEW = lib.mkForce false;
|
||||||
DEFAULT_KEEP_EMAIL_PRIVATE = false;
|
DEFAULT_KEEP_EMAIL_PRIVATE = false;
|
||||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true;
|
DEFAULT_ALLOW_CREATE_ORGANIZATION = true;
|
||||||
DEFAULT_ENABLE_TIMETRACKING = true;
|
DEFAULT_ENABLE_TIMETRACKING = true;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (config.virtualisation.quadlet) containers networks;
|
inherit (config.virtualisation.quadlet) containers networks;
|
||||||
|
authentikVersion = "2026.5.6";
|
||||||
authentikEnv = config.sops.secrets."containers/authentik.env".path;
|
authentikEnv = config.sops.secrets."containers/authentik.env".path;
|
||||||
outpostEnv = config.sops.secrets."containers/authentik-outpost.env".path;
|
outpostEnv = config.sops.secrets."containers/authentik-outpost.env".path;
|
||||||
in
|
in
|
||||||
|
|
@ -53,7 +54,7 @@ in
|
||||||
|
|
||||||
authentik-server = {
|
authentik-server = {
|
||||||
containerConfig = {
|
containerConfig = {
|
||||||
image = "ghcr.io/goauthentik/server:2026.5.0";
|
image = "ghcr.io/goauthentik/server:${authentikVersion}";
|
||||||
exec = "server";
|
exec = "server";
|
||||||
networks = [ networks.authentik.ref ];
|
networks = [ networks.authentik.ref ];
|
||||||
publishPorts = [
|
publishPorts = [
|
||||||
|
|
@ -65,6 +66,11 @@ in
|
||||||
"/home/imnyang/Docker/authentik/custom-templates:/templates:Z"
|
"/home/imnyang/Docker/authentik/custom-templates:/templates:Z"
|
||||||
];
|
];
|
||||||
environmentFiles = [ authentikEnv ];
|
environmentFiles = [ authentikEnv ];
|
||||||
|
environments = {
|
||||||
|
# Authentik 2026.5 defaults to [::], but hako is reached over IPv4.
|
||||||
|
AUTHENTIK_LISTEN__HTTP = "0.0.0.0:9000";
|
||||||
|
AUTHENTIK_LISTEN__HTTPS = "0.0.0.0:9443";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
unitConfig = {
|
unitConfig = {
|
||||||
|
|
@ -77,7 +83,7 @@ in
|
||||||
|
|
||||||
authentik-worker = {
|
authentik-worker = {
|
||||||
containerConfig = {
|
containerConfig = {
|
||||||
image = "ghcr.io/goauthentik/server:2026.5.0";
|
image = "ghcr.io/goauthentik/server:${authentikVersion}";
|
||||||
exec = "worker";
|
exec = "worker";
|
||||||
user = "root";
|
user = "root";
|
||||||
networks = [ networks.authentik.ref ];
|
networks = [ networks.authentik.ref ];
|
||||||
|
|
@ -85,9 +91,11 @@ in
|
||||||
"/home/imnyang/Docker/authentik/certs:/certs:Z"
|
"/home/imnyang/Docker/authentik/certs:/certs:Z"
|
||||||
"/home/imnyang/Docker/authentik/media:/media:Z"
|
"/home/imnyang/Docker/authentik/media:/media:Z"
|
||||||
"/home/imnyang/Docker/authentik/custom-templates:/templates:Z"
|
"/home/imnyang/Docker/authentik/custom-templates:/templates:Z"
|
||||||
"/var/run/docker.sock:/var/run/docker.sock"
|
|
||||||
];
|
];
|
||||||
environmentFiles = [ authentikEnv ];
|
environmentFiles = [ authentikEnv ];
|
||||||
|
environments = {
|
||||||
|
AUTHENTIK_LISTEN__HTTP = "0.0.0.0:9000";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
unitConfig = {
|
unitConfig = {
|
||||||
|
|
@ -100,7 +108,7 @@ in
|
||||||
|
|
||||||
authentik-outpost-ldap = {
|
authentik-outpost-ldap = {
|
||||||
containerConfig = {
|
containerConfig = {
|
||||||
image = "ghcr.io/goauthentik/ldap:2026.5.0";
|
image = "ghcr.io/goauthentik/ldap:${authentikVersion}";
|
||||||
entrypoint = [ "/ldap" ];
|
entrypoint = [ "/ldap" ];
|
||||||
user = "1000";
|
user = "1000";
|
||||||
networks = [ networks.authentik.ref ];
|
networks = [ networks.authentik.ref ];
|
||||||
|
|
@ -109,6 +117,15 @@ in
|
||||||
"636:6636"
|
"636:6636"
|
||||||
];
|
];
|
||||||
environmentFiles = [ outpostEnv ];
|
environmentFiles = [ outpostEnv ];
|
||||||
|
environments = {
|
||||||
|
# Keep the outpost-to-core API traffic on the private Podman network.
|
||||||
|
# The public hostname resolves through Cloudflare and hairpins back
|
||||||
|
# through the reverse proxy, which currently returns 502 from hako.
|
||||||
|
AUTHENTIK_HOST = "http://authentik-server:9000";
|
||||||
|
AUTHENTIK_INSECURE = "true";
|
||||||
|
AUTHENTIK_LISTEN__LDAP = "0.0.0.0:3389";
|
||||||
|
AUTHENTIK_LISTEN__LDAPS = "0.0.0.0:6636";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
unitConfig = {
|
unitConfig = {
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,4 @@
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
let
|
|
||||||
streamPlayer = pkgs.writeTextDir "index.html" ''
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>Mizuki Stream</title>
|
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
min-height: 100%;
|
|
||||||
background: #0d0f12;
|
|
||||||
color: #f5f3ee;
|
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: 1fr auto;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
video {
|
|
||||||
width: 100%;
|
|
||||||
max-height: calc(100vh - 72px);
|
|
||||||
aspect-ratio: 16 / 9;
|
|
||||||
align-self: center;
|
|
||||||
background: #000;
|
|
||||||
border: 1px solid #2f343c;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 12px;
|
|
||||||
color: #a9acb3;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
color: #f5f3ee;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
|
||||||
footer {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<video id="video" controls autoplay playsinline></video>
|
|
||||||
<footer>
|
|
||||||
<span id="stream"></span>
|
|
||||||
<code>rtmp://stream.mizuki.guru:1935/live</code>
|
|
||||||
</footer>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
|
||||||
<script>
|
|
||||||
var video = document.getElementById("video");
|
|
||||||
var label = document.getElementById("stream");
|
|
||||||
var match = window.location.pathname.match(/^\/play\/([^/]+)\/?$/);
|
|
||||||
var name = match ? decodeURIComponent(match[1]) : "stream";
|
|
||||||
var source = "https://stream.mizuki.guru/live/" + name + "/" + name + ".m3u8";
|
|
||||||
|
|
||||||
label.textContent = source;
|
|
||||||
|
|
||||||
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
||||||
video.src = source;
|
|
||||||
video.play();
|
|
||||||
} else if (Hls.isSupported()) {
|
|
||||||
var hls = new Hls();
|
|
||||||
hls.loadSource(source);
|
|
||||||
hls.attachMedia(video);
|
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, function () {
|
|
||||||
video.play();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
services.caddy = {
|
services.caddy = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
@ -182,25 +91,8 @@ in
|
||||||
reverse_proxy 127.0.0.1:31774
|
reverse_proxy 127.0.0.1:31774
|
||||||
'';
|
'';
|
||||||
|
|
||||||
virtualHosts."stream.mizuki.guru".extraConfig = ''
|
virtualHosts."pass.imnya.ng".extraConfig = ''
|
||||||
handle /live/* {
|
reverse_proxy 127.0.0.1:10302
|
||||||
reverse_proxy 127.0.0.1:10023
|
|
||||||
}
|
|
||||||
|
|
||||||
handle /play/* {
|
|
||||||
root * ${streamPlayer}
|
|
||||||
rewrite * /index.html
|
|
||||||
file_server
|
|
||||||
}
|
|
||||||
|
|
||||||
handle / {
|
|
||||||
root * ${streamPlayer}
|
|
||||||
file_server
|
|
||||||
}
|
|
||||||
|
|
||||||
handle {
|
|
||||||
respond "Not Found" 404
|
|
||||||
}
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
virtualHosts."netbird.mizuki.guru".extraConfig = ''
|
virtualHosts."netbird.mizuki.guru".extraConfig = ''
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ in
|
||||||
|
|
||||||
src = builtins.fetchTarball {
|
src = builtins.fetchTarball {
|
||||||
url = "https://code.visualstudio.com/sha/download?build=insider&os=linux-x64";
|
url = "https://code.visualstudio.com/sha/download?build=insider&os=linux-x64";
|
||||||
sha256 = "0b1127828nk7hr1j66vwi98ip6zwzwrnacqpsa5klnw1xagz52aj";
|
sha256 = "1d2my418b5np4m5pch3s06gfq6b8676j983b43rqxqsck8j7fzyy";
|
||||||
};
|
};
|
||||||
|
|
||||||
buildInputs = oldAttrs.buildInputs ++ [
|
buildInputs = oldAttrs.buildInputs ++ [
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue