feat: inital public commit
This commit is contained in:
62
host/ark/default.nix
Normal file
62
host/ark/default.nix
Normal file
@@ -0,0 +1,62 @@
|
||||
{ config, lib, pkgs, ... }: {
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
./options.nix
|
||||
../common/core/default.nix
|
||||
./user.nix
|
||||
./sops.nix
|
||||
|
||||
./service/audio.nix
|
||||
./service/auth.nix
|
||||
./service/docs.nix
|
||||
./service/git.nix
|
||||
./service/home.nix
|
||||
./service/photos.nix
|
||||
./service/radicale.nix
|
||||
./service/wakapi.nix
|
||||
];
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
networking.hostName = "ark"; # Define your hostname.
|
||||
|
||||
oauth.name = "KoonFamily";
|
||||
oauth.secrets = import ./oauth-secrets.nix;
|
||||
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
|
||||
services.cloudflared = {
|
||||
enable = true;
|
||||
tunnels = {
|
||||
"91d31395-fbc7-45a1-ae13-148957b32ecd" = {
|
||||
credentialsFile = config.sops.secrets.tunnel-credentials.path;
|
||||
ingress = {
|
||||
"auth.koon.us" = "http://localhost:1411";
|
||||
"photos.koon.us" = "http://localhost:2283";
|
||||
"home.koon.us" = "http://localhost:8123";
|
||||
"docs.koon.us" = "http://localhost:3004";
|
||||
"git.koon.us" = "http://localhost:3000";
|
||||
"ssh.koon.us" = "ssh://localhost:2222";
|
||||
"audio.koon.us" = "http://localhost:8021";
|
||||
"radicale.koon.us" = "http://localhost:5232";
|
||||
"waka.koon.us" = "http://localhost:3006";
|
||||
};
|
||||
default = "http_status:404";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems."/mnt/hdd" = {
|
||||
device = "/dev/sdb";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8123 22 ];
|
||||
|
||||
system.stateVersion = "25.05"; # Did you read the comment?
|
||||
}
|
||||
|
39
host/ark/hardware-configuration.nix
Normal file
39
host/ark/hardware-configuration.nix
Normal file
@@ -0,0 +1,39 @@
|
||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
|
||||
|
||||
boot.initrd.availableKernelModules =
|
||||
[ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/" = {
|
||||
device = "/dev/disk/by-uuid/56034a80-72ea-4e67-be41-2e8cb642fb44";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
fileSystems."/boot" = {
|
||||
device = "/dev/disk/by-uuid/DE8B-01DE";
|
||||
fsType = "vfat";
|
||||
options = [ "fmask=0022" "dmask=0022" ];
|
||||
};
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||
# still possible to use this option, but it's recommended to use it in conjunction
|
||||
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
# networking.interfaces.eno1.useDHCP = lib.mkDefault true;
|
||||
# networking.interfaces.wlp1s0.useDHCP = lib.mkDefault true;
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.intel.updateMicrocode =
|
||||
lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
}
|
7
host/ark/options.nix
Normal file
7
host/ark/options.nix
Normal file
@@ -0,0 +1,7 @@
|
||||
{ lib, ... }: {
|
||||
options.oauth = {
|
||||
name = lib.mkOption { type = lib.types.str; };
|
||||
secrets = lib.mkOption { type = lib.types.attrs; };
|
||||
};
|
||||
|
||||
}
|
6
host/ark/service/audio.nix
Normal file
6
host/ark/service/audio.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{ ... }: {
|
||||
services.audiobookshelf = {
|
||||
enable = true;
|
||||
port = 8021;
|
||||
};
|
||||
}
|
14
host/ark/service/auth.nix
Normal file
14
host/ark/service/auth.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{ config, pkgs, ... }: {
|
||||
services.pocket-id = {
|
||||
enable = true;
|
||||
settings = {
|
||||
APP_URL = "https://auth.koon.us";
|
||||
TRUST_PROXY = true;
|
||||
ANALYTICS_DISABLED = true;
|
||||
|
||||
UI_CONFIG_DISABLED = true;
|
||||
|
||||
APP_NAME = config.oauth.name;
|
||||
};
|
||||
};
|
||||
}
|
94
host/ark/service/docs.nix
Normal file
94
host/ark/service/docs.nix
Normal file
@@ -0,0 +1,94 @@
|
||||
{ config, lib, pkgs, ... }: {
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
services.outline = {
|
||||
enable = true;
|
||||
publicUrl = "https://docs.koon.us";
|
||||
port = 3004;
|
||||
forceHttps = true;
|
||||
storage.storageType = "local";
|
||||
logo = "https://i.imgur.com/fKJ1I63.png";
|
||||
oidcAuthentication = {
|
||||
authUrl = "https://auth.koon.us/authorize";
|
||||
tokenUrl = "https://auth.koon.us/api/oidc/token";
|
||||
userinfoUrl = "https://auth.koon.us/api/oidc/userinfo";
|
||||
clientId = "";
|
||||
clientSecretFile = config.sops.secrets."docs/clientSecret".path;
|
||||
scopes = [ "openid" "email" "profile" ];
|
||||
usernameClaim = "preferred_username";
|
||||
displayName = config.oauth.name;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.outline = {
|
||||
script =
|
||||
let
|
||||
localPostgresqlUrl = "postgres://localhost/outline?host=/run/postgresql";
|
||||
cfg = config.services.outline;
|
||||
in lib.mkForce ''
|
||||
export SECRET_KEY="$(head -n1 ${lib.escapeShellArg cfg.secretKeyFile})"
|
||||
export UTILS_SECRET="$(head -n1 ${lib.escapeShellArg cfg.utilsSecretFile})"
|
||||
${lib.optionalString (cfg.storage.storageType == "s3") ''
|
||||
export AWS_SECRET_ACCESS_KEY="$(head -n1 ${lib.escapeShellArg cfg.storage.secretKeyFile})"
|
||||
''}
|
||||
${lib.optionalString (cfg.slackAuthentication != null) ''
|
||||
export SLACK_CLIENT_SECRET="$(head -n1 ${lib.escapeShellArg cfg.slackAuthentication.secretFile})"
|
||||
''}
|
||||
${lib.optionalString (cfg.googleAuthentication != null) ''
|
||||
export GOOGLE_CLIENT_SECRET="$(head -n1 ${lib.escapeShellArg cfg.googleAuthentication.clientSecretFile})"
|
||||
''}
|
||||
${lib.optionalString (cfg.azureAuthentication != null) ''
|
||||
export AZURE_CLIENT_SECRET="$(head -n1 ${lib.escapeShellArg cfg.azureAuthentication.clientSecretFile})"
|
||||
''}
|
||||
${lib.optionalString (cfg.oidcAuthentication != null) ''
|
||||
export OIDC_CLIENT_SECRET="$(head -n1 ${lib.escapeShellArg cfg.oidcAuthentication.clientSecretFile})"
|
||||
export OIDC_CLIENT_ID="$(cat ${config.sops.secrets."docs/clientId".path})"
|
||||
''}
|
||||
${lib.optionalString (cfg.sslKeyFile != null) ''
|
||||
export SSL_KEY="$(head -n1 ${lib.escapeShellArg cfg.sslKeyFile})"
|
||||
''}
|
||||
${lib.optionalString (cfg.sslCertFile != null) ''
|
||||
export SSL_CERT="$(head -n1 ${lib.escapeShellArg cfg.sslCertFile})"
|
||||
''}
|
||||
${lib.optionalString (cfg.slackIntegration != null) ''
|
||||
export SLACK_VERIFICATION_TOKEN="$(head -n1 ${lib.escapeShellArg cfg.slackIntegration.verificationTokenFile})"
|
||||
''}
|
||||
${lib.optionalString (cfg.smtp != null) ''
|
||||
export SMTP_PASSWORD="$(head -n1 ${lib.escapeShellArg cfg.smtp.passwordFile})"
|
||||
''}
|
||||
|
||||
${
|
||||
if (cfg.databaseUrl == "local") then
|
||||
''
|
||||
export DATABASE_URL=${lib.escapeShellArg localPostgresqlUrl}
|
||||
export PGSSLMODE=disable
|
||||
''
|
||||
else
|
||||
''
|
||||
export DATABASE_URL=${lib.escapeShellArg cfg.databaseUrl}
|
||||
''
|
||||
}
|
||||
|
||||
${cfg.package}/bin/outline-server
|
||||
'';
|
||||
};
|
||||
# systemd.services.outline = {
|
||||
# serviceConfig = {
|
||||
# # Load the client ID from the sops secret file
|
||||
# ExecStartPre = let
|
||||
# script = pkgs.writeShellScript "outline-set-oauth" ''
|
||||
# CLIENT_ID=$(cat ${config.sops.secrets."docs/clientId".path})
|
||||
# # Export as environment variable that Outline will use
|
||||
# echo "OIDC_CLIENT_ID=$CLIENT_ID" >> $RUNTIME_DIRECTORY/env
|
||||
# '';
|
||||
# in "+${script}";
|
||||
#
|
||||
# # Load the environment file
|
||||
# EnvironmentFile = "-/run/outline/env";
|
||||
# };
|
||||
#
|
||||
# # Ensure sops secrets are available before starting
|
||||
# after = [ "sops-nix.service" ];
|
||||
# wants = [ "sops-nix.service" ];
|
||||
# };
|
||||
}
|
111
host/ark/service/git.nix
Normal file
111
host/ark/service/git.nix
Normal file
@@ -0,0 +1,111 @@
|
||||
{ lib, config, pkgs, ... }: {
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
user = "git";
|
||||
group = "git";
|
||||
database = {
|
||||
user = "git";
|
||||
name = "git";
|
||||
type = "postgres";
|
||||
};
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = "git.koon.us";
|
||||
ROOT_URL = "https://git.koon.us";
|
||||
HTTP_PORT = 3000;
|
||||
LANDING_PAGE = "/max";
|
||||
SSH_DOMAIN = "ssh.koon.us";
|
||||
SSH_PORT = 2222;
|
||||
START_SSH_SERVER = true;
|
||||
};
|
||||
oauth2_client = {
|
||||
ACCOUNT_LINKING = "auto";
|
||||
ENABLE_AUTO_REGISTRATION = true;
|
||||
UPDATE_AVATAR = true;
|
||||
USERNAME = "email";
|
||||
};
|
||||
service = {
|
||||
DISABLE_REGISTRATION = true;
|
||||
ENABLE_PASSWORD_SIGNIN_FORM = false;
|
||||
ENABLE_PASSKEY_AUTHENTICATION = false;
|
||||
|
||||
SHOW_MILESTONES_DASHBOARD_PAGE = false;
|
||||
};
|
||||
"service.explore" = {
|
||||
DISABLE_USERS_PAGE = true;
|
||||
DISABLE_ORGANIZATIONS_PAGE = true;
|
||||
DISABLE_CODE_PAGE = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
users.users.git = {
|
||||
isSystemUser = true;
|
||||
group = "git";
|
||||
home = "/var/lib/gitea";
|
||||
description = "Git server (Gitea)";
|
||||
createHome = true;
|
||||
};
|
||||
users.groups.git = { };
|
||||
|
||||
systemd.services.gitea = {
|
||||
serviceConfig = {
|
||||
RestartSec = "60"; # Retry every minute
|
||||
};
|
||||
preStart = let
|
||||
exe = lib.getExe config.services.gitea.package;
|
||||
|
||||
clientIdPath = config.sops.secrets."git/clientId".path;
|
||||
clientSecretPath = config.sops.secrets."git/clientSecret".path;
|
||||
|
||||
args = lib.escapeShellArgs (lib.concatLists [
|
||||
[ "--name" config.oauth.name ]
|
||||
[ "--provider" "openidConnect" ]
|
||||
# [ "--key" config.oauth.secrets.git.clientId ]
|
||||
[
|
||||
"--auto-discover-url"
|
||||
"https://auth.koon.us/.well-known/openid-configuration"
|
||||
]
|
||||
[ "--scopes" "email" ]
|
||||
[ "--scopes" "profile" ]
|
||||
[ "--group-claim-name" "groups" ]
|
||||
[ "--admin-group" "admin" ]
|
||||
[ "--skip-local-2fa" ]
|
||||
]);
|
||||
in lib.mkAfter ''
|
||||
CLIENT_ID=$(cat ${clientIdPath})
|
||||
CLIENT_SECRET=$(cat ${clientSecretPath})
|
||||
|
||||
provider_id=$(${exe} admin auth list | ${pkgs.gnugrep}/bin/grep -w '${config.oauth.name}' | cut -f1)
|
||||
|
||||
if [[ -z "$provider_id" ]]; then
|
||||
${exe} admin auth add-oauth ${args} --key "$CLIENT_ID" --secret "$CLIENT_SECRET"
|
||||
else
|
||||
${exe} admin auth update-oauth --id "$provider_id" ${args} --key "$CLIENT_ID" --secret "$CLIENT_SECRET"
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/gitea/custom/public/assets/img/
|
||||
|
||||
ln -sf ${
|
||||
./git/assets/img/logo.svg
|
||||
} /var/lib/gitea/custom/public/assets/img/logo.svg
|
||||
ln -sf ${
|
||||
./git/assets/img/favicon.svg
|
||||
} /var/lib/gitea/custom/public/assets/img/favicon.svg
|
||||
|
||||
mkdir -p /var/lib/gitea/custom/templates/base/
|
||||
ln -sf ${
|
||||
./git/templates/base/head_navbar.tmpl
|
||||
} /var/lib/gitea/custom/templates/base/head_navbar.tmpl
|
||||
ln -sf ${
|
||||
./git/templates/base/footer_content.tmpl
|
||||
} /var/lib/gitea/custom/templates/base/footer_content.tmpl
|
||||
|
||||
mkdir -p /var/lib/gitea/custom/templates/custom/
|
||||
ln -sf ${
|
||||
./git/templates/custom/header.tmpl
|
||||
} /var/lib/gitea/custom/templates/custom/header.tmpl
|
||||
|
||||
'';
|
||||
};
|
||||
}
|
15
host/ark/service/git/assets/css/custom.css
Normal file
15
host/ark/service/git/assets/css/custom.css
Normal file
@@ -0,0 +1,15 @@
|
||||
/* custom.css */
|
||||
:root {
|
||||
--color-primary: #2e7d32;
|
||||
--color-primary-dark: #1b5e20;
|
||||
}
|
||||
|
||||
/* Hide "Powered by Gitea" */
|
||||
.footer .ui.container .ui.left {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Custom header background */
|
||||
.ui.top.menu {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
6
host/ark/service/git/assets/img/favicon.svg
Normal file
6
host/ark/service/git/assets/img/favicon.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="321" height="524" viewBox="0 0 321 524" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M321 249C267.137 251.03 192 393.5 192 524L192 249L321 249Z" fill="black"/>
|
||||
<path d="M0 190C54.6982 188.598 131 85 131 0V190H0Z" fill="black"/>
|
||||
<path d="M70 249H131V310H70V249Z" fill="black"/>
|
||||
<path d="M192 129H253V190H192V129Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 359 B |
6
host/ark/service/git/assets/img/logo.svg
Normal file
6
host/ark/service/git/assets/img/logo.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="321" height="524" viewBox="0 0 321 524" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M321 249C267.137 251.03 192 393.5 192 524L192 249L321 249Z" fill="black"/>
|
||||
<path d="M0 190C54.6982 188.598 131 85 131 0V190H0Z" fill="black"/>
|
||||
<path d="M70 249H131V310H70V249Z" fill="black"/>
|
||||
<path d="M192 129H253V190H192V129Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 359 B |
3
host/ark/service/git/templates/base/footer_content.tmpl
Normal file
3
host/ark/service/git/templates/base/footer_content.tmpl
Normal file
@@ -0,0 +1,3 @@
|
||||
<footer class="page-footer" role="group" aria-label="{{ctx.Locale.Tr "aria.footer"}}">
|
||||
© MMXXV Koon Family
|
||||
</footer>
|
190
host/ark/service/git/templates/base/head_navbar.tmpl
Normal file
190
host/ark/service/git/templates/base/head_navbar.tmpl
Normal file
@@ -0,0 +1,190 @@
|
||||
{{$notificationUnreadCount := 0}}
|
||||
{{if and .IsSigned .NotificationUnreadCount}}
|
||||
{{$notificationUnreadCount = call .NotificationUnreadCount ctx}}
|
||||
{{end}}
|
||||
{{$activeStopwatch := NIL}}
|
||||
{{if and .IsSigned EnableTimetracking .GetActiveStopwatch}}
|
||||
{{$activeStopwatch = call .GetActiveStopwatch ctx}}
|
||||
{{end}}
|
||||
<nav id="navbar" aria-label="{{ctx.Locale.Tr "aria.navbar"}}">
|
||||
<div class="navbar-left">
|
||||
<!-- the logo -->
|
||||
<a class="item" id="navbar-logo" href="{{AppSubUrl}}/" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}">
|
||||
<img width="30" height="30" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}" aria-hidden="true">
|
||||
</a>
|
||||
|
||||
<!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column -->
|
||||
<div class="ui secondary menu navbar-mobile-right only-mobile">
|
||||
{{if $activeStopwatch}}
|
||||
<a id="mobile-stopwatch-icon" class="active-stopwatch item" href="{{$activeStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{$activeStopwatch.Seconds}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-stopwatch"}}
|
||||
<span class="header-stopwatch-dot"></span>
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .IsSigned}}
|
||||
<a id="mobile-notifications-icon" class="item" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-bell"}}
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
<button class="item ui icon mini button tw-m-0" id="navbar-expand-toggle" aria-label="{{ctx.Locale.Tr "home.nav_menu"}}">{{svg "octicon-three-bars"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- the full dropdown menus -->
|
||||
<div class="navbar-right">
|
||||
{{if and .IsSigned .MustChangePassword}}
|
||||
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<span>
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
|
||||
<span class="only-mobile">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
</span>
|
||||
<div class="menu user-menu">
|
||||
<div class="header">
|
||||
{{ctx.Locale.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
|
||||
{{svg "octicon-sign-out"}}
|
||||
{{ctx.Locale.Tr "sign_out"}}
|
||||
</a>
|
||||
</div><!-- end content avatar menu -->
|
||||
</div><!-- end dropdown avatar menu -->
|
||||
{{else if .IsSigned}}
|
||||
{{if $activeStopwatch}}
|
||||
<a class="item not-mobile active-stopwatch" href="{{$activeStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}" data-seconds="{{$activeStopwatch.Seconds}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-stopwatch"}}
|
||||
<span class="header-stopwatch-dot"></span>
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
<a class="item not-mobile" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-bell"}}
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "create_new"}}">
|
||||
<span class="text">
|
||||
{{svg "octicon-plus"}}
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
<span class="only-mobile">{{ctx.Locale.Tr "create_new"}}</span>
|
||||
</span>
|
||||
<div class="menu">
|
||||
<a class="item" href="{{AppSubUrl}}/repo/create">
|
||||
{{svg "octicon-plus"}} {{ctx.Locale.Tr "new_repo"}}
|
||||
</a>
|
||||
{{if not .DisableMigrations}}
|
||||
<a class="item" href="{{AppSubUrl}}/repo/migrate">
|
||||
{{svg "octicon-repo-push"}} {{ctx.Locale.Tr "new_migrate"}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .SignedUser.CanCreateOrganization}}
|
||||
<a class="item" href="{{AppSubUrl}}/org/create">
|
||||
{{svg "octicon-organization"}} {{ctx.Locale.Tr "new_org"}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div><!-- end content create new menu -->
|
||||
</div><!-- end dropdown menu create new -->
|
||||
|
||||
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<span>
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
|
||||
<span class="only-mobile">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
</span>
|
||||
<div class="menu user-menu">
|
||||
<div class="header">
|
||||
{{ctx.Locale.Tr "signed_in_as"}} <strong>{{.SignedUser.Name}}</strong>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item" href="{{.SignedUser.HomeLink}}">
|
||||
{{svg "octicon-person"}}
|
||||
{{ctx.Locale.Tr "your_profile"}}
|
||||
</a>
|
||||
{{if not .DisableStars}}
|
||||
<a class="item" href="{{.SignedUser.HomeLink}}?tab=stars">
|
||||
{{svg "octicon-star"}}
|
||||
{{ctx.Locale.Tr "your_starred"}}
|
||||
</a>
|
||||
{{end}}
|
||||
<a class="item" href="{{AppSubUrl}}/notifications/subscriptions">
|
||||
{{svg "octicon-bell"}}
|
||||
{{ctx.Locale.Tr "notification.subscriptions"}}
|
||||
</a>
|
||||
<a class="{{if .PageIsUserSettings}}active {{end}}item" href="{{AppSubUrl}}/user/settings">
|
||||
{{svg "octicon-tools"}}
|
||||
{{ctx.Locale.Tr "your_settings"}}
|
||||
</a>
|
||||
<a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.com">
|
||||
{{svg "octicon-question"}}
|
||||
{{ctx.Locale.Tr "help"}}
|
||||
</a>
|
||||
{{if .IsAdmin}}
|
||||
<div class="divider"></div>
|
||||
<a class="{{if .PageIsAdmin}}active {{end}}item" href="{{AppSubUrl}}/-/admin">
|
||||
{{svg "octicon-server"}}
|
||||
{{ctx.Locale.Tr "admin_panel"}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
<div class="divider"></div>
|
||||
<a class="item link-action" href data-url="{{AppSubUrl}}/user/logout">
|
||||
{{svg "octicon-sign-out"}}
|
||||
{{ctx.Locale.Tr "sign_out"}}
|
||||
</a>
|
||||
</div><!-- end content avatar menu -->
|
||||
</div><!-- end dropdown avatar menu -->
|
||||
{{else}}
|
||||
{{if .ShowRegistrationButton}}
|
||||
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up">
|
||||
{{svg "octicon-person"}}
|
||||
<span class="tw-ml-1">{{ctx.Locale.Tr "register"}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/oauth2/KoonFamily{{if not .PageIsSignIn}}?redirect_to={{.CurrentURL}}{{end}}">
|
||||
{{svg "octicon-sign-in"}}
|
||||
<span class="tw-ml-1">{{ctx.Locale.Tr "sign_in"}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
</div><!-- end full right menu -->
|
||||
|
||||
{{if $activeStopwatch}}
|
||||
<div class="active-stopwatch-popup tippy-target">
|
||||
<div class="tw-flex tw-items-center tw-gap-2 tw-p-3">
|
||||
<a class="stopwatch-link tw-flex tw-items-center tw-gap-2 muted" href="{{$activeStopwatch.IssueLink}}">
|
||||
{{svg "octicon-issue-opened" 16}}
|
||||
<span class="stopwatch-issue">{{$activeStopwatch.RepoSlug}}#{{$activeStopwatch.IssueIndex}}</span>
|
||||
</a>
|
||||
<div class="tw-flex tw-gap-1">
|
||||
<form class="stopwatch-commit form-fetch-action" method="post" action="{{$activeStopwatch.IssueLink}}/times/stopwatch/toggle">
|
||||
{{.CsrfTokenHtml}}
|
||||
<button
|
||||
type="submit"
|
||||
class="ui button mini compact basic icon tw-mr-0"
|
||||
data-tooltip-content="{{ctx.Locale.Tr "repo.issues.stop_tracking"}}"
|
||||
>{{svg "octicon-square-fill"}}</button>
|
||||
</form>
|
||||
<form class="stopwatch-cancel form-fetch-action" method="post" action="{{$activeStopwatch.IssueLink}}/times/stopwatch/cancel">
|
||||
{{.CsrfTokenHtml}}
|
||||
<button
|
||||
type="submit"
|
||||
class="ui button mini compact basic icon tw-mr-0"
|
||||
data-tooltip-content="{{ctx.Locale.Tr "repo.issues.cancel_tracking"}}"
|
||||
>{{svg "octicon-trash"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</nav>
|
10
host/ark/service/git/templates/custom/header.tmpl
Normal file
10
host/ark/service/git/templates/custom/header.tmpl
Normal file
@@ -0,0 +1,10 @@
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: #a68746;
|
||||
--color-primary-dark: #a68746;
|
||||
}
|
||||
|
||||
img.ui.avatar {
|
||||
border-radius: 100%;
|
||||
}
|
||||
</style>
|
28
host/ark/service/home.nix
Normal file
28
host/ark/service/home.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{ pkgs, ... }: {
|
||||
virtualisation.oci-containers = let
|
||||
hass_config = pkgs.writeText "configuration.yaml" ''
|
||||
# Discovery
|
||||
default_config:
|
||||
|
||||
# Web Server configuration
|
||||
http:
|
||||
server_host: 127.0.0.1
|
||||
use_x_forwarded_for: true
|
||||
trusted_proxies: 127.0.0.1
|
||||
'';
|
||||
in {
|
||||
backend = "podman";
|
||||
containers.homeassistant = {
|
||||
volumes = [
|
||||
"home-assistant:/config"
|
||||
# "/data/docker/hass:/config"
|
||||
"${hass_config}:/config/configuration.yaml"
|
||||
# "/run/secrets/home-assistant:/config/secrets.yaml"
|
||||
];
|
||||
environment.TZ = "America/New_York";
|
||||
image =
|
||||
"ghcr.io/home-assistant/home-assistant:stable"; # Warning: if the tag does not change, the image will not be updated
|
||||
extraOptions = [ "--network=host" ];
|
||||
};
|
||||
};
|
||||
}
|
117
host/ark/service/photos.nix
Normal file
117
host/ark/service/photos.nix
Normal file
@@ -0,0 +1,117 @@
|
||||
{ lib, config, pkgs, ... }: {
|
||||
sops = {
|
||||
templates = {
|
||||
"immich-config.json" = {
|
||||
content = builtins.toJSON {
|
||||
passwordLogin.enabled = false;
|
||||
|
||||
# We will do this ourselves
|
||||
backup.database.enabled = false;
|
||||
|
||||
oauth = {
|
||||
enabled = true;
|
||||
autoLaunch = true;
|
||||
autoRegister = true;
|
||||
buttonText =
|
||||
lib.strings.concatStrings [ "Login To " config.oauth.name ];
|
||||
clientId = config.sops.placeholder."photos/clientId";
|
||||
clientSecret = config.sops.placeholder."photos/clientSecret";
|
||||
issuerUrl = "https://auth.koon.us/.well-known/openid-configuration";
|
||||
};
|
||||
};
|
||||
owner = config.users.users.immich.name;
|
||||
mode = "0400";
|
||||
restartUnits = [ "immich-server.service" "pocket-id.service" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.immich = {
|
||||
enable = true;
|
||||
port = 2283;
|
||||
environment.IMMICH_CONFIG_FILE = config.sops.templates."immich-config.json".path;
|
||||
accelerationDevices = null;
|
||||
|
||||
machine-learning.environment = {
|
||||
HF_XET_CACHE = "/var/cache/immich/huggingface-xet";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
users.users.immich = {
|
||||
home = "/var/lib/immich";
|
||||
createHome = true;
|
||||
extraGroups = [ "video" "render" ];
|
||||
};
|
||||
|
||||
hardware.graphics = {
|
||||
enable = true;
|
||||
extraPackages = with pkgs; [ intel-media-driver ];
|
||||
};
|
||||
environment.sessionVariables = { LIBVA_DRIVER_NAME = "iHD"; };
|
||||
|
||||
services.restic.backups = {
|
||||
immich-local = {
|
||||
repository = "/mnt/hdd/restic/immich";
|
||||
passwordFile = config.sops.secrets.restic-password.path;
|
||||
initialize = true;
|
||||
paths = [ "/var/lib/immich/upload" "/var/backup/immich" ];
|
||||
backupPrepareCommand = ''
|
||||
mkdir -p /var/backup/immich
|
||||
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl stop immich-server
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl stop immich-machine-learning
|
||||
|
||||
${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/pg_dump \
|
||||
--clean \
|
||||
--if-exists \
|
||||
--dbname=immich > /var/backup/immich/postgres.sql
|
||||
'';
|
||||
backupCleanupCommand = ''
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl start immich-server
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl start immich-machine-learning
|
||||
'';
|
||||
};
|
||||
immich-remote = {
|
||||
repository = "rest:http://m1:8000/immich";
|
||||
passwordFile = config.sops.secrets.restic-password.path;
|
||||
initialize = true;
|
||||
paths = [ "/var/lib/immich/upload" "/var/backup/immich" ];
|
||||
backupPrepareCommand = ''
|
||||
mkdir -p /var/backup/immich
|
||||
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl stop immich-server
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl stop immich-machine-learning
|
||||
|
||||
${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/pg_dump \
|
||||
--clean \
|
||||
--if-exists \
|
||||
--dbname=immich > /var/backup/immich/postgres.sql
|
||||
'';
|
||||
backupCleanupCommand = ''
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl start immich-server
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl start immich-machine-learning
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs;
|
||||
let
|
||||
scripts = with pkgs; {
|
||||
restore_immich_pg = writeShellScriptBin "restore_immich_pg" ''
|
||||
${pkgs.sudo}/bin/sudo -u postgres psql --dbname=immich < /var/backup/immich/postgres.sql
|
||||
'';
|
||||
restore_immich = writeShellScriptBin "restore_immich" ''
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl stop immich-server
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl stop immich-machine-learning
|
||||
|
||||
${pkgs.sudo}/bin/sudo ${restic}/bin/restic -r /mnt/hdd/restic/immich restore latest --target /
|
||||
|
||||
${scripts.restore_immich_pg}/bin/restore_immich_pg
|
||||
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl start immich-server
|
||||
${pkgs.sudo}/bin/sudo ${pkgs.systemd}/bin/systemctl start immich-machine-learning
|
||||
'';
|
||||
};
|
||||
in [ scripts.restore_immich_pg scripts.restore_immich ];
|
||||
}
|
9
host/ark/service/radicale.nix
Normal file
9
host/ark/service/radicale.nix
Normal file
@@ -0,0 +1,9 @@
|
||||
{ ... }: {
|
||||
services.radicale = {
|
||||
enable = true;
|
||||
settings = {
|
||||
auth.type = "none";
|
||||
server.hosts = [ "0.0.0.0:5232" ];
|
||||
};
|
||||
};
|
||||
}
|
28
host/ark/service/wakapi.nix
Normal file
28
host/ark/service/wakapi.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
wakapi = prev.wakapi.overrideAttrs (oldAttrs: rec {
|
||||
src = final.fetchFromGitHub {
|
||||
owner = "k2on";
|
||||
repo = "wakapi";
|
||||
rev = "theming";
|
||||
# hash = "";
|
||||
hash = "sha256-mbQ2cA9tbuDA5OXEP+qVfsrBC90budAzWE7x4oN6ypY=";
|
||||
};
|
||||
# vendorHash = final.lib.fakeHash;
|
||||
vendorHash = "sha256-lb6u9NQbB3bizIRbCRaB7Ngv9T5mAYtSl+g13gL7VEU=";
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
services.wakapi = {
|
||||
enable = true;
|
||||
passwordSaltFile = config.sops.secrets."waka-password-salt".path;
|
||||
settings = {
|
||||
server.port = 3006;
|
||||
app.avatar_url_template = "https://auth.koon.us/api/users/fbffa48a-faf7-4230-a89f-0da184f5948c/profile-picture.png";
|
||||
};
|
||||
};
|
||||
}
|
50
host/ark/sops.nix
Normal file
50
host/ark/sops.nix
Normal file
@@ -0,0 +1,50 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
sops = {
|
||||
|
||||
defaultSopsFile = ../../secrets/sops/host/ark/default.yaml;
|
||||
validateSopsFiles = false;
|
||||
|
||||
age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
|
||||
secrets = {
|
||||
"host_age_key" = {
|
||||
path = "/var/lib/sops-nix/key.txt";
|
||||
};
|
||||
|
||||
"restic-password" = {};
|
||||
"tunnel-credentials" = {};
|
||||
"admin-password" = {};
|
||||
|
||||
"waka-password-salt" = {
|
||||
owner = config.users.users.wakapi.name;
|
||||
};
|
||||
|
||||
"photos/clientId" = {
|
||||
sopsFile = ../../secrets/sops/host/ark/oauth.yaml;
|
||||
};
|
||||
"photos/clientSecret" = {
|
||||
sopsFile = ../../secrets/sops/host/ark/oauth.yaml;
|
||||
};
|
||||
|
||||
"git/clientId" = {
|
||||
sopsFile = ../../secrets/sops/host/ark/oauth.yaml;
|
||||
owner = config.services.gitea.user;
|
||||
};
|
||||
"git/clientSecret" = {
|
||||
sopsFile = ../../secrets/sops/host/ark/oauth.yaml;
|
||||
owner = config.services.gitea.user;
|
||||
};
|
||||
|
||||
"docs/clientId" = {
|
||||
sopsFile = ../../secrets/sops/host/ark/oauth.yaml;
|
||||
owner = config.services.outline.user;
|
||||
};
|
||||
"docs/clientSecret" = {
|
||||
sopsFile = ../../secrets/sops/host/ark/oauth.yaml;
|
||||
owner = config.services.outline.user;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
12
host/ark/user.nix
Normal file
12
host/ark/user.nix
Normal file
@@ -0,0 +1,12 @@
|
||||
{ pkgs, config, ... }: {
|
||||
|
||||
sops.secrets.admin-password.neededForUsers = true;
|
||||
users.mutableUsers = false;
|
||||
|
||||
users.users.admin = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" ];
|
||||
hashedPasswordFile = config.sops.secrets.admin-password.path;
|
||||
packages = with pkgs; [ tree vim tmux restic ];
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user