From b37fa18f1b224b93439419438339a0d12f574a62 Mon Sep 17 00:00:00 2001 From: pinpox Date: Fri, 4 Jul 2025 15:54:44 +0200 Subject: [PATCH] Remove clanModules --- .../sops/users/admin/key.json | 4 - clanModules/flake-module.nix | 62 ----- clanServices/admin/root-password.nix | 55 ---- clanServices/flake-module.nix | 7 + formatter.nix | 2 - .../machine-id/tests/flake-module.nix | 2 +- nixosModules/clanCore/postgresql/default.nix | 236 ------------------ .../clan_cli/tests/fixtures_flakes.py | 5 - .../clan_cli/tests/test_flake/flake.nix | 14 +- pkgs/clan-cli/flake-module.nix | 1 - pkgs/flake-module.nix | 2 +- pkgs/moonlight-sunshine-accept/.envrc | 2 - pkgs/moonlight-sunshine-accept/default.nix | 37 --- .../moonlight_sunshine_accept/__init__.py | 40 --- .../moonlight_sunshine_accept/__main__.py | 0 .../moonlight_sunshine_accept/errors.py | 2 - .../moonlight/__init__.py | 37 --- .../moonlight/init_certificates.py | 74 ------ .../moonlight/init_config.py | 15 -- .../moonlight/join.py | 131 ---------- .../moonlight/run.py | 63 ----- .../moonlight/state.py | 149 ----------- .../moonlight/uri.py | 22 -- .../sunshine/__init__.py | 63 ----- .../moonlight_sunshine_accept/sunshine/api.py | 63 ----- .../sunshine/config.py | 49 ---- .../sunshine/init_certificates.py | 76 ------ .../sunshine/init_state.py | 16 -- .../sunshine/listen.py | 90 ------- .../sunshine/state.py | 67 ----- pkgs/moonlight-sunshine-accept/pyproject.toml | 9 - 31 files changed, 17 insertions(+), 1378 deletions(-) delete mode 100644 checks/service-dummy-test-from-flake/sops/users/admin/key.json delete mode 100644 clanModules/flake-module.nix delete mode 100644 clanServices/admin/root-password.nix delete mode 100644 nixosModules/clanCore/postgresql/default.nix delete mode 100644 pkgs/moonlight-sunshine-accept/.envrc delete mode 100644 pkgs/moonlight-sunshine-accept/default.nix delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/__init__.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/__main__.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/errors.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/__init__.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_certificates.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_config.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/join.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/run.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/state.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/uri.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/__init__.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/api.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/config.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_certificates.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_state.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/listen.py delete mode 100644 pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/state.py delete mode 100644 pkgs/moonlight-sunshine-accept/pyproject.toml diff --git a/checks/service-dummy-test-from-flake/sops/users/admin/key.json b/checks/service-dummy-test-from-flake/sops/users/admin/key.json deleted file mode 100644 index e408aa96b..000000000 --- a/checks/service-dummy-test-from-flake/sops/users/admin/key.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", - "type": "age" -} diff --git a/clanModules/flake-module.nix b/clanModules/flake-module.nix deleted file mode 100644 index 43d0d83d1..000000000 --- a/clanModules/flake-module.nix +++ /dev/null @@ -1,62 +0,0 @@ -{ ... }: - -let - error = builtins.throw '' - - ############################################################################### - # # - # Clan modules (clanModules) have been deprecated and removed in favor of # - # Clan services! # - # # - # Refer to https://docs.clan.lol/guides/migrations/migrate-inventory-services # - # for migration instructions. # - # # - ############################################################################### - - ''; - - modnames = [ - "admin" - "borgbackup" - "borgbackup-static" - "deltachat" - "disk-id" - "dyndns" - "ergochat" - "garage" - "heisenbridge" - "iwd" - "localbackup" - "localsend" - "matrix-synapse" - "moonlight" - "mumble" - "nginx" - "packages" - "postgresql" - "root-password" - "single-disk" - "sshd" - "state-version" - "static-hosts" - "sunshine" - "syncthing" - "syncthing-static-peers" - "thelounge" - "trusted-nix-caches" - "user-password" - "vaultwarden" - "xfce" - "zerotier-static-peers" - "zt-tcp-relay" - ]; -in - -{ - flake.clanModules = builtins.listToAttrs ( - map (name: { - inherit name; - value = error; - }) modnames - ); -} diff --git a/clanServices/admin/root-password.nix b/clanServices/admin/root-password.nix deleted file mode 100644 index 9b4044e32..000000000 --- a/clanServices/admin/root-password.nix +++ /dev/null @@ -1,55 +0,0 @@ -# We don't have a way of specifying dependencies between clanServices for now. -# When it get's added this file should be removed and the users module used instead. -{ - roles.default.perInstance = - { ... }: - { - nixosModule = - { - config, - pkgs, - ... - }: - { - - users.mutableUsers = false; - users.users.root.hashedPasswordFile = - config.clan.core.vars.generators.root-password.files.password-hash.path; - - clan.core.vars.generators.root-password = { - files.password-hash.neededFor = "users"; - - files.password.deploy = false; - - runtimeInputs = [ - pkgs.coreutils - pkgs.mkpasswd - pkgs.xkcdpass - ]; - - prompts.password.display = { - group = "Root User"; - label = "Password"; - required = false; - helperText = '' - Your password will be encrypted and stored securely using the secret store you've configured. - ''; - }; - - prompts.password.type = "hidden"; - prompts.password.persist = true; - prompts.password.description = "Leave empty to generate automatically"; - - script = '' - prompt_value="$(cat "$prompts"/password)" - if [[ -n "''${prompt_value-}" ]]; then - echo "$prompt_value" | tr -d "\n" > "$out"/password - else - xkcdpass --numwords 5 --delimiter - --count 1 | tr -d "\n" > "$out"/password - fi - mkpasswd -s -m sha-512 < "$out"/password | tr -d "\n" > "$out"/password-hash - ''; - }; - }; - }; -} diff --git a/clanServices/flake-module.nix b/clanServices/flake-module.nix index 97a4c9115..adefd5458 100644 --- a/clanServices/flake-module.nix +++ b/clanServices/flake-module.nix @@ -18,4 +18,11 @@ imports = map (name: ./. + "/${name}/flake-module.nix") validModuleDirs; in imports; + + flake.clanModules = builtins.throw '' + clanModules have been removed! + + Refer to https://docs.clan.lol/guides/migrations/migrate-inventory-services for migration. + ''; + } diff --git a/formatter.nix b/formatter.nix index 5dbbc7754..079af6710 100644 --- a/formatter.nix +++ b/formatter.nix @@ -46,8 +46,6 @@ "checks/lib/ssh/privkey" "checks/lib/ssh/pubkey" "checks/matrix-synapse/synapse-registration_shared_secret" - "checks/mumble/machines/peer1/facts/mumble-cert" - "checks/mumble/machines/peer2/facts/mumble-cert" "checks/secrets/clan-secrets" "checks/secrets/sops/groups/group/machines/machine" "checks/syncthing/introducer/introducer_device_id" diff --git a/nixosModules/clanCore/machine-id/tests/flake-module.nix b/nixosModules/clanCore/machine-id/tests/flake-module.nix index ddcace433..b15299355 100644 --- a/nixosModules/clanCore/machine-id/tests/flake-module.nix +++ b/nixosModules/clanCore/machine-id/tests/flake-module.nix @@ -5,7 +5,7 @@ { clan.nixosTests.machine-id = { - name = "service-machine-id"; + name = "machine-id"; clan = { directory = ./.; diff --git a/nixosModules/clanCore/postgresql/default.nix b/nixosModules/clanCore/postgresql/default.nix deleted file mode 100644 index fef7c6507..000000000 --- a/nixosModules/clanCore/postgresql/default.nix +++ /dev/null @@ -1,236 +0,0 @@ -{ - lib, - config, - pkgs, - ... -}: -let - - cfg = config.clan.core.postgresql; - - createDatabaseState = - db: - let - folder = "/var/backup/postgres/${db.name}"; - current = "${folder}/pg-dump"; - compression = lib.optionalString (lib.versionAtLeast config.services.postgresql.package.version "16") "--compress=zstd"; - in - { - folders = [ folder ]; - preBackupScript = '' - export PATH=${ - lib.makeBinPath [ - config.services.postgresql.package - config.systemd.package - pkgs.coreutils - pkgs.util-linux - pkgs.zstd - ] - } - while [[ "$(systemctl is-active postgresql)" == activating ]]; do - sleep 1 - done - - mkdir -p "${folder}" - runuser -u postgres -- pg_dump ${compression} --dbname=${db.name} -Fc -c > "${current}.tmp" - mv "${current}.tmp" ${current} - ''; - postRestoreScript = '' - export PATH=${ - lib.makeBinPath [ - config.services.postgresql.package - config.systemd.package - pkgs.coreutils - pkgs.util-linux - pkgs.zstd - pkgs.gnugrep - ] - } - while [[ "$(systemctl is-active postgresql)" == activating ]]; do - sleep 1 - done - echo "Waiting for postgres to be ready..." - while ! runuser -u postgres -- psql --port=${builtins.toString config.services.postgresql.settings.port} -d postgres -c "" ; do - if ! systemctl is-active postgresql; then exit 1; fi - sleep 0.1 - done - - if [[ -e "${current}" ]]; then - ( - systemctl stop ${lib.concatStringsSep " " db.restore.stopOnRestore} - trap "systemctl start ${lib.concatStringsSep " " db.restore.stopOnRestore}" EXIT - - mkdir -p "${folder}" - if runuser -u postgres -- psql -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${db.name}'" | grep -q 1; then - runuser -u postgres -- dropdb "${db.name}" - fi - runuser -u postgres -- pg_restore -C -d postgres "${current}" - ) - else - echo No database backup found, skipping restore - fi - ''; - }; - - createDatabase = db: '' - CREATE DATABASE "${db.name}" ${ - lib.concatStringsSep " " ( - lib.mapAttrsToList (name: value: "${name} = '${value}'") db.create.options - ) - } - ''; - - userClauses = lib.mapAttrsToList ( - _: user: - ''$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"' '' - ) cfg.users; - databaseClauses = lib.mapAttrsToList ( - name: db: - lib.optionalString db.create.enable ''$PSQL -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${name}'" | grep -q 1 || $PSQL -d postgres -c ${lib.escapeShellArg (createDatabase db)} '' - ) cfg.databases; -in -{ - options.clan.core.postgresql = { - - enable = lib.mkEnableOption "Whether to enable PostgreSQL Server"; - - # we are reimplemeting ensureDatabase and ensureUser options here to allow to create databases with options - databases = lib.mkOption { - description = "Databases to create"; - default = { }; - type = lib.types.attrsOf ( - lib.types.submodule ( - { name, ... }: - { - options = { - name = lib.mkOption { - type = lib.types.str; - default = name; - description = "Database name."; - }; - service = lib.mkOption { - type = lib.types.str; - default = name; - description = "Service name that we associate with the database."; - }; - # set to false, in case the upstream module uses ensureDatabase option - create.enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Create the database if it does not exist."; - }; - create.options = lib.mkOption { - description = "Options to pass to the CREATE DATABASE command."; - type = lib.types.lazyAttrsOf lib.types.str; - default = { }; - example = { - TEMPLATE = "template0"; - LC_COLLATE = "C"; - LC_CTYPE = "C"; - ENCODING = "UTF8"; - OWNER = "foo"; - }; - }; - restore.stopOnRestore = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; - description = "List of systemd services to stop before restoring the database."; - }; - }; - } - ) - ); - }; - users = lib.mkOption { - description = "Users to create"; - default = { }; - type = lib.types.attrsOf ( - lib.types.submodule ( - { name, ... }: - { - options.name = lib.mkOption { - description = "User name"; - type = lib.types.str; - default = name; - }; - } - ) - ); - }; - }; - - config = lib.mkIf (config.clan.core.postgresql.enable) { - - clan.core.settings.state-version.enable = true; - - # services.postgresql.package = lib.mkDefault pkgs.postgresql_16; - - services.postgresql.enable = true; - - services.postgresql.settings = { - wal_level = "replica"; - max_wal_senders = 3; - }; - - # We are duplicating a bit the upstream module but allow to create databases with options - systemd.services.postgresql.postStart = '' - PSQL="psql --port=${builtins.toString config.services.postgresql.settings.port}" - - while ! $PSQL -d postgres -c "" 2> /dev/null; do - if ! kill -0 "$MAINPID"; then exit 1; fi - sleep 0.1 - done - ${lib.concatStringsSep "\n" userClauses} - ${lib.concatStringsSep "\n" databaseClauses} - ''; - - clan.core.state = lib.mapAttrs' ( - _: db: lib.nameValuePair db.service (createDatabaseState db) - ) config.clan.core.postgresql.databases; - - environment.systemPackages = builtins.map ( - db: - let - folder = "/var/backup/postgres/${db.name}"; - current = "${folder}/pg-dump"; - in - pkgs.writeShellScriptBin "postgres-db-restore-command-${db.name}" '' - export PATH=${ - lib.makeBinPath [ - config.services.postgresql.package - config.systemd.package - pkgs.coreutils - pkgs.util-linux - pkgs.zstd - pkgs.gnugrep - ] - } - while [[ "$(systemctl is-active postgresql)" == activating ]]; do - sleep 1 - done - echo "Waiting for postgres to be ready..." - while ! runuser -u postgres -- psql --port=${builtins.toString config.services.postgresql.settings.port} -d postgres -c "" ; do - if ! systemctl is-active postgresql; then exit 1; fi - sleep 0.1 - done - - if [[ -e "${current}" ]]; then - ( - ${lib.optionalString (db.restore.stopOnRestore != [ ]) '' - systemctl stop ${builtins.toString db.restore.stopOnRestore} - trap "systemctl start ${builtins.toString db.restore.stopOnRestore}" EXIT - ''} - - mkdir -p "${folder}" - if runuser -u postgres -- psql -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${db.name}'" | grep -q 1; then - runuser -u postgres -- dropdb "${db.name}" - fi - runuser -u postgres -- pg_restore -C -d postgres "${current}" - ) - else - echo No database backup found, skipping restore - fi - '' - ) (builtins.attrValues config.clan.core.postgresql.databases); - }; -} diff --git a/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py b/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py index 060776503..45f80fe76 100644 --- a/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py +++ b/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py @@ -173,7 +173,6 @@ class ClanFlake: "git+https://git.clan.lol/clan/clan-core": clan_core_replacement, "https://git.clan.lol/clan/clan-core/archive/main.tar.gz": clan_core_replacement, } - self.clan_modules: list[str] = [] self.temporary_home = temporary_home self.path = temporary_home / "flake" if not suppress_tmp_home_warning: @@ -235,9 +234,6 @@ class ClanFlake: if self.inventory: inventory_path = self.path / "inventory.json" inventory_path.write_text(json.dumps(self.inventory, indent=2)) - imports = "\n".join( - [f"clan-core.clanModules.{module}" for module in self.clan_modules] - ) for machine_name, machine_config in self.machines.items(): configuration_nix = ( self.path / "machines" / machine_name / "configuration.nix" @@ -249,7 +245,6 @@ class ClanFlake: {{ imports = [ (builtins.fromJSON (builtins.readFile ./configuration.json)) - {imports} ]; }} """ diff --git a/pkgs/clan-cli/clan_cli/tests/test_flake/flake.nix b/pkgs/clan-cli/clan_cli/tests/test_flake/flake.nix index fddba1f55..774d8a995 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_flake/flake.nix +++ b/pkgs/clan-cli/clan_cli/tests/test_flake/flake.nix @@ -6,12 +6,14 @@ inputs': let # fake clan-core input - fake-clan-core = { - clanModules.fake-module = ./fake-module.nix; - }; - inputs = inputs' // { - clan-core = fake-clan-core; - }; + # TODO should this be removed as well? + # fake-clan-core = { + # clanModules.fake-module = ./fake-module.nix; + # }; + inputs = inputs'; + # inputs = inputs' // { + # clan-core = fake-clan-core; + # }; lib = inputs.nixpkgs.lib; clan_attrs_json = if lib.pathExists ./clan_attrs.json then diff --git a/pkgs/clan-cli/flake-module.nix b/pkgs/clan-cli/flake-module.nix index d2b045875..f935ead90 100644 --- a/pkgs/clan-cli/flake-module.nix +++ b/pkgs/clan-cli/flake-module.nix @@ -16,7 +16,6 @@ clanCoreWithVendoredDeps = self'.packages.clan-core-flake.override { clanCore = self.filter { include = [ - "clanModules" "flakeModules" "lib" "nixosModules" diff --git a/pkgs/flake-module.nix b/pkgs/flake-module.nix index 3ad2b3708..9718424d1 100644 --- a/pkgs/flake-module.nix +++ b/pkgs/flake-module.nix @@ -19,13 +19,13 @@ agit = pkgs.callPackage ./agit { }; tea-create-pr = pkgs.callPackage ./tea-create-pr { }; zerotier-members = pkgs.callPackage ./zerotier-members { }; - moonlight-sunshine-accept = pkgs.callPackage ./moonlight-sunshine-accept { }; merge-after-ci = pkgs.callPackage ./merge-after-ci { inherit (config.packages) tea-create-pr; }; minifakeroot = pkgs.callPackage ./minifakeroot { }; pending-reviews = pkgs.callPackage ./pending-reviews { }; editor = pkgs.callPackage ./editor/clan-edit-codium.nix { }; classgen = pkgs.callPackage ./classgen { }; zerotierone = pkgs.callPackage ./zerotierone { }; + update-clan-core-for-checks = pkgs.callPackage ./update-clan-core-for-checks { }; }; }; } diff --git a/pkgs/moonlight-sunshine-accept/.envrc b/pkgs/moonlight-sunshine-accept/.envrc deleted file mode 100644 index 1799d252a..000000000 --- a/pkgs/moonlight-sunshine-accept/.envrc +++ /dev/null @@ -1,2 +0,0 @@ -# shellcheck shell=bash -use flake .#moonlight-sunshine-accept diff --git a/pkgs/moonlight-sunshine-accept/default.nix b/pkgs/moonlight-sunshine-accept/default.nix deleted file mode 100644 index 95a40af40..000000000 --- a/pkgs/moonlight-sunshine-accept/default.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ - lib, - python3Packages, - makeDesktopItem, - copyDesktopItems, -}: -let - desktop-file = makeDesktopItem { - name = "org.clan.moonlight-sunset-accept"; - exec = "moonlight-sunshine-accept moonlight join %u"; - desktopName = "moonlight-handler"; - startupWMClass = "moonlight-handler"; - mimeTypes = [ "x-scheme-handler/moonlight" ]; - }; -in -python3Packages.buildPythonApplication { - name = "moonlight-sunshine-accept"; - - src = ./.; - - format = "pyproject"; - - propagatedBuildInputs = [ python3Packages.cryptography ]; - nativeBuildInputs = [ - python3Packages.setuptools - copyDesktopItems - ]; - - desktopItems = [ desktop-file ]; - - meta = with lib; { - description = "Moonlight Sunshine Bridge"; - license = licenses.mit; - maintainers = with maintainers; [ a-kenji ]; - mainProgram = "moonlight-sunshine-accept"; - }; -} diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/__init__.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/__init__.py deleted file mode 100644 index e05a45abf..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python - -import argparse - -from . import moonlight, sunshine - - -def main() -> None: - parser = argparse.ArgumentParser( - prog="moonlight-sunshine-accept", - description="Manage moonlight machines", - ) - subparsers = parser.add_subparsers() - - parser_sunshine = subparsers.add_parser( - "sunshine", - aliases=["sun"], - description="Sunshine configuration", - help="Sunshine configuration", - ) - sunshine.register_parser(parser_sunshine) - - parser_moonlight = subparsers.add_parser( - "moonlight", - aliases=["moon"], - description="Moonlight configuration", - help="Moonlight configuration", - ) - moonlight.register_parser(parser_moonlight) - - args = parser.parse_args() - - if not hasattr(args, "func"): - parser.print_help() - else: - args.func(args) - - -if __name__ == "__main__": - main() diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/__main__.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/__main__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/errors.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/errors.py deleted file mode 100644 index 9c69c25f8..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/errors.py +++ /dev/null @@ -1,2 +0,0 @@ -class Error(Exception): - pass diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/__init__.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/__init__.py deleted file mode 100644 index 6524a4d86..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -import argparse - -from .init_certificates import register_initialization_parser -from .init_config import register_config_initialization_parser -from .join import register_join_parser - - -def register_parser(parser: argparse.ArgumentParser) -> None: - subparser = parser.add_subparsers( - title="command", - description="the command to run", - help="the command to run", - required=True, - ) - - initialization_parser = subparser.add_parser( - "init", - aliases=["i"], - description="Initialize the moonlight credentials", - help="Initialize the moonlight credentials", - ) - register_initialization_parser(initialization_parser) - - config_initialization_parser = subparser.add_parser( - "init-config", - description="Initialize the moonlight configuration", - help="Initialize the moonlight configuration", - ) - register_config_initialization_parser(config_initialization_parser) - - join_parser = subparser.add_parser( - "join", - aliases=["j"], - description="Join a sunshine host", - help="Join a sunshine host", - ) - register_join_parser(join_parser) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_certificates.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_certificates.py deleted file mode 100644 index c368e9bde..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_certificates.py +++ /dev/null @@ -1,74 +0,0 @@ -import argparse -import datetime -from datetime import timedelta -from pathlib import Path - -from cryptography import hazmat, x509 -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID - - -def generate_private_key() -> rsa.RSAPrivateKey: - private_key = rsa.generate_private_key( - public_exponent=65537, key_size=2048, backend=hazmat.backends.default_backend() - ) - return private_key - - -def generate_certificate(private_key: rsa.RSAPrivateKey) -> bytes: - subject = issuer = x509.Name( - [ - x509.NameAttribute(NameOID.COMMON_NAME, "NVIDIA GameStream Client"), - ] - ) - cert_builder = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(private_key.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(tz=datetime.UTC)) - .not_valid_after( - datetime.datetime.now(tz=datetime.UTC) + timedelta(days=365 * 20) - ) - .add_extension( - x509.SubjectAlternativeName([x509.DNSName("localhost")]), - critical=False, - ) - .sign(private_key, hashes.SHA256(), default_backend()) - ) - pem_certificate = cert_builder.public_bytes(serialization.Encoding.PEM) - return pem_certificate - - -def private_key_to_pem(private_key: rsa.RSAPrivateKey) -> bytes: - pem_private_key = private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - # format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ) - return pem_private_key - - -def init_credentials() -> tuple[bytes, bytes]: - private_key = generate_private_key() - certificate = generate_certificate(private_key) - private_key_pem = private_key_to_pem(private_key) - return certificate, private_key_pem - - -def write_credentials(_args: argparse.Namespace) -> None: - pem_certificate, pem_private_key = init_credentials() - credentials_path = Path.cwd() / "credentials" - Path(credentials_path).mkdir(parents=True, exist_ok=True) - - (credentials_path / "cacert.pem").write_bytes(pem_certificate) - (credentials_path / "cakey.pem").write_bytes(pem_private_key) - print("Finished writing moonlight credentials") - - -def register_initialization_parser(parser: argparse.ArgumentParser) -> None: - parser.set_defaults(func=write_credentials) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_config.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_config.py deleted file mode 100644 index a2f9e3174..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/init_config.py +++ /dev/null @@ -1,15 +0,0 @@ -import argparse -from pathlib import Path - -from .state import init_state - - -def init_config(args: argparse.Namespace) -> None: - init_state(args.certificate.read_text(), args.key.read_text()) - print("Finished initializing moonlight state.") - - -def register_config_initialization_parser(parser: argparse.ArgumentParser) -> None: - parser.add_argument("--certificate", type=Path) - parser.add_argument("--key", type=Path) - parser.set_defaults(func=init_config) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/join.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/join.py deleted file mode 100644 index 69f6af46e..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/join.py +++ /dev/null @@ -1,131 +0,0 @@ -import argparse -import base64 -import json -import socket - -from .run import MoonlightPairing -from .state import add_sunshine_host, gen_pin, get_moonlight_certificate -from .uri import parse_moonlight_uri - - -def send_join_request(host: str, port: int, cert: str) -> bool: - max_tries = 3 - response = False - for _tries in range(max_tries): - response = send_join_request_api(host, port) - if response: - return response - return bool(send_join_request_native(host, port, cert)) - - -# This is the preferred join method, but sunshines pin mechanism -# seems to be somewhat brittle in repeated testing, retry then fallback to native -def send_join_request_api(host: str, port: int) -> bool: - moonlight = MoonlightPairing() - # is_paired = moonlight.check(host) - is_paired = False - if is_paired: - print(f"Moonlight is already paired with this host: {host}") - return True - pin = gen_pin() - moonlight.init_pairing(host, pin) - moonlight.wait_until_started() - - with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: - s.connect((host, port)) - json_body = json.dumps({"type": "api", "pin": pin}) - request = ( - f"POST / HTTP/1.1\r\n" - f"Content-Type: application/json\r\n" - f"Content-Length: {len(json_body)}\r\n" - f"Connection: close\r\n\r\n" - f"{json_body}" - ) - try: - s.sendall(request.encode("utf-8")) - response = s.recv(16384).decode("utf-8") - print(response) - body = response.split("\n")[-1] - print(body) - moonlight.terminate() - except Exception as e: - print(f"An error occurred: {e}") - moonlight.terminate() - return False - else: - return True - - -def send_join_request_native(host: str, port: int, cert: str) -> bool: - # This is the hardcoded UUID for the moonlight client - uuid = "123456789ABCD" - with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as s: - s.connect((host, port)) - encoded_cert = base64.urlsafe_b64encode(cert.encode("utf-8")).decode("utf-8") - json_body_str = json.dumps( - {"type": "native", "uuid": uuid, "cert": encoded_cert} - ) - request = ( - f"POST / HTTP/1.1\r\n" - f"Content-Type: application/json\r\n" - f"Content-Length: {len(json_body_str)}\r\n" - f"Connection: close\r\n\r\n" - f"{json_body_str}" - ) - try: - s.sendall(request.encode("utf-8")) - response = s.recv(16384).decode("utf-8") - print(response) - lines = response.split("\n") - body = "\n".join(lines[2:])[2:] - print(body) - except Exception as e: - print(f"An error occurred: {e}") - else: - return True - # TODO: fix - try: - print(f"response: {response}") - data = json.loads(response) - print(f"Data: {data}") - print(f"Host uuid: {data['uuid']}") - print(f"Host certificate: {data['cert']}") - print("Joining sunshine host") - cert = data["cert"] - cert = base64.urlsafe_b64decode(cert).decode("utf-8") - uuid = data["uuid"] - hostname = data["hostname"] - add_sunshine_host(hostname, host, cert, uuid) - except json.JSONDecodeError as e: - print(f"Failed to decode JSON: {e}") - pos = e.pos - print(f"Failed to decode JSON: unexpected character {response[pos]}") - return False - - -def join(args: argparse.Namespace) -> None: - if args.url: - (host, port) = parse_moonlight_uri(args.url) - if port is None: - port = 48011 - else: - port = args.port - host = args.host - - print(f"Host: {host}, port: {port}") - # TODO: If cert is not provided parse from config - # cert = args.cert - cert = get_moonlight_certificate() - assert port is not None - if send_join_request(host, port, cert): - print(f"Successfully joined sunshine host: {host}") - else: - print(f"Failed to join sunshine host: {host}") - - -def register_join_parser(parser: argparse.ArgumentParser) -> None: - parser.add_argument("url", nargs="?") - parser.add_argument("--port", type=int, default=48011) - parser.add_argument("--host") - parser.add_argument("--cert") - parser.set_defaults(func=join) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/run.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/run.py deleted file mode 100644 index ee4752ced..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/run.py +++ /dev/null @@ -1,63 +0,0 @@ -import subprocess -import sys -import threading - - -class MoonlightPairing: - def __init__(self) -> None: - self.process: subprocess.Popen | None = None - self.output = "" - self.found = threading.Event() - - def init_pairing(self, host: str, pin: str) -> bool: - # host = f"[{host}]" - args = ["moonlight", "pair", host, "--pin", pin] - print("Trying to pair") - try: - print(f"Running command: {args}") - self.process = subprocess.Popen(args, stdout=subprocess.PIPE) - print("Pairing initiated") - thread = threading.Thread( - target=self.stream_output, - args=('Latest supported GFE server: "99.99.99.99"',), - ) - thread.start() - print("Thread started") - except Exception as e: - print( - "Error occurred while starting the process: ", str(e), file=sys.stderr - ) - return False - else: - return True - - def check(self, host: str) -> bool: - try: - result = subprocess.run( - ["moonlight", "list", "localhost", host], check=True - ) - except subprocess.CalledProcessError: - return False - else: - return result.returncode == 0 - - def terminate(self) -> None: - if self.process: - self.process.terminate() - self.process.wait() - - def stream_output(self, target_string: str) -> None: - assert self.process is not None - assert self.process.stdout is not None - for line in iter(self.process.stdout.readline, b""): - line = line.decode() - self.output += line - if target_string in line: - self.found.set() - break - - def wait_until_started(self, timeout: int = 10) -> None: - if self.found.wait(timeout): - print("Started up.") - else: - print("Starting up took took too long. Terminated the process.") diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/state.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/state.py deleted file mode 100644 index 6d7803805..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/state.py +++ /dev/null @@ -1,149 +0,0 @@ -import contextlib -import random -import string -from configparser import ConfigParser, DuplicateSectionError, NoOptionError -from pathlib import Path - - -def moonlight_config_dir() -> Path: - return Path.home() / ".config" / "Moonlight Game Streaming Project" - - -def moonlight_state_file() -> Path: - return moonlight_config_dir() / "Moonlight.conf" - - -def load_state() -> ConfigParser | None: - try: - with moonlight_state_file().open() as file: - config = ConfigParser() - config.read_file(file) - print(config.sections()) - return config - except FileNotFoundError: - print("Sunshine state file not found.") - return None - - -# prepare the string for the config file -# this is how qt handles byte arrays -def convert_string_to_bytearray(data: str) -> str: - byte_array = '"@ByteArray(' - byte_array += data.replace("\n", "\\n") - byte_array += ')"' - return byte_array - - -def convert_bytearray_to_string(byte_array: str) -> str: - if byte_array.startswith('"@ByteArray(') and byte_array.endswith(')"'): - byte_array = byte_array[12:-2] - return byte_array.replace("\\n", "\n") - return byte_array - - -# this must be created before moonlight is first run -def init_state(certificate: str, key: str) -> None: - print("Initializing moonlight state.") - moonlight_config_dir().mkdir(parents=True, exist_ok=True) - print("Initialized moonlight config directory.") - - print("Writing moonlight state file.") - # write the initial bootstrap config file - with moonlight_state_file().open("w") as file: - config = ConfigParser() - # bytearray objects are not supported by ConfigParser, - # so we need to adjust them ourselves - config.add_section("General") - config.set("General", "certificate", convert_string_to_bytearray(certificate)) - config.set("General", "key", convert_string_to_bytearray(key)) - config.set("General", "latestsupportedversion-v1", "99.99.99.99") - config.add_section("gcmapping") - config.set("gcmapping", "size", "0") - - config.write(file) - - -def write_state(data: ConfigParser) -> None: - with moonlight_state_file().open("w") as file: - data.write(file) - - -def add_sunshine_host_to_parser( - config: ConfigParser, hostname: str, manual_host: str, certificate: str, uuid: str -) -> bool: - with contextlib.suppress(DuplicateSectionError): - config.add_section("hosts") - - # amount of hosts - try: - no_hosts = int(config.get("hosts", "size")) - except NoOptionError: - no_hosts = 0 - - new_host = no_hosts + 1 - - config.set( - "hosts", f"{new_host}\\srvcert", convert_string_to_bytearray(certificate) - ) - config.set("hosts", "size", str(new_host)) - config.set("hosts", f"{new_host}\\uuid", uuid) - config.set("hosts", f"{new_host}\\hostname", hostname) - config.set("hosts", f"{new_host}\\nvidiasv", "false") - config.set("hosts", f"{new_host}\\customname", "false") - config.set("hosts", f"{new_host}\\manualaddress", manual_host) - config.set("hosts", f"{new_host}\\manualport", "47989") - config.set("hosts", f"{new_host}\\remoteport", "0") - config.set("hosts", f"{new_host}\\remoteaddress", "") - config.set("hosts", f"{new_host}\\localaddress", "") - config.set("hosts", f"{new_host}\\localport", "0") - config.set("hosts", f"{new_host}\\ipv6port", "0") - config.set("hosts", f"{new_host}\\ipv6address", "") - config.set( - "hosts", f"{new_host}\\mac", convert_string_to_bytearray("\\xceop\\x8d\\xfc{") - ) - add_app(config, "Desktop", new_host, 1, 881448767) - add_app(config, "Low Res Desktop", new_host, 2, 303580669) - add_app(config, "Steam Big Picture", new_host, 3, 1093255277) - - print(config.items("hosts")) - return True - - -# set default apps for the host for now -# TODO: do this dynamically -def add_app( - config: ConfigParser, name: str, host_id: int, app_id: int, app_no: int -) -> None: - identifier = f"{host_id}\\apps\\{app_id}\\" - config.set("hosts", f"{identifier}appcollector", "false") - config.set("hosts", f"{identifier}directlaunch", "false") - config.set("hosts", f"{identifier}hdr", "false") - config.set("hosts", f"{identifier}hidden", "false") - config.set("hosts", f"{identifier}id", f"{app_no}") - config.set("hosts", f"{identifier}name", f"{name}") - - -def get_moonlight_certificate() -> str: - config = load_state() - if config is None: - msg = "Moonlight state file not found." - raise FileNotFoundError(msg) - certificate = config.get("General", "certificate") - certificate = convert_bytearray_to_string(certificate) - return certificate - - -def gen_pin() -> str: - return "".join(random.choice(string.digits) for _ in range(4)) - - -def add_sunshine_host( - hostname: str, manual_host: str, certificate: str, uuid: str -) -> bool: - config = load_state() - if config is None: - return False - hostname = "test" - add_sunshine_host_to_parser(config, hostname, manual_host, certificate, uuid) - write_state(config) - return True diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/uri.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/uri.py deleted file mode 100644 index 339cb5e37..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/moonlight/uri.py +++ /dev/null @@ -1,22 +0,0 @@ -from urllib.parse import urlparse - -from moonlight_sunshine_accept.errors import Error - - -def parse_moonlight_uri(uri: str) -> tuple[str, int | None]: - print(uri) - if uri.startswith("moonlight:"): - # Fixes a bug where moonlight:// is not parsed correctly - uri = uri[10:] - uri = "moonlight://" + uri - print(uri) - parsed = urlparse(uri) - if parsed.scheme != "moonlight": - msg = f"Invalid moonlight URI: {uri}" - raise Error(msg) - hostname = parsed.hostname - if hostname is None: - msg = f"Invalid moonlight URI: {uri}" - raise Error(msg) - port = parsed.port - return (hostname, port) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/__init__.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/__init__.py deleted file mode 100644 index 88872b0a6..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -import argparse - -from .init_certificates import register_initialization_parser -from .init_state import register_state_initialization_parser -from .listen import register_socket_listener - - -def register_parser(parser: argparse.ArgumentParser) -> None: - subparser = parser.add_subparsers( - title="command", - description="the command to run", - help="the command to run", - required=True, - ) - - subparser.add_parser( - "generate", - # title="command", - aliases=["gen"], - description="Generate a shareable link", - help="Generate a shareable link", - ) - # TODO: add a timeout for the link - # generate.add_argument( - # "--timeout", - # default="10", - # ) - # copy = subparsers.add_parser("copy", description="Copy the link to the clipboard") - - initialization_parser = subparser.add_parser( - "init", - aliases=["i"], - description="Initialize the sunshine credentials", - help="Initialize the sunshine credentials", - ) - register_initialization_parser(initialization_parser) - - state_initialization_parser = subparser.add_parser( - "init-state", - description="Initialize the sunshine state file", - help="Initialize the sunshine state file", - ) - register_state_initialization_parser(state_initialization_parser) - - listen_parser = subparser.add_parser( - "listen", - description="Listen for incoming connections", - help="Listen for incoming connections", - ) - register_socket_listener(listen_parser) - - # TODO: Add a machine directly <- useful when using dependent secrets - # sunshine_add = subparser.add_parser( - # "add", - # aliases=["a"], - # description="Add a new moonlight machine to sunshine", - # help="Add a new moonlight machine to sunshine", - # ) - # sunshine_add.add_argument("--url", type=str, help="URL of the moonlight machine") - # sunshine_add.add_argument( - # "--cert", type=str, help="Certificate of the moonlight machine" - # ) - # sunshine_add.add_argument("--uuid", type=str, help="UUID of the moonlight machine") diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/api.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/api.py deleted file mode 100644 index 1df45e1dc..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/api.py +++ /dev/null @@ -1,63 +0,0 @@ -import base64 -import http.client -import json - - -def get_context() -> http.client.ssl.SSLContext: # type: ignore - # context = http.client.ssl.create_default_context() - # # context.load_cert_chain( - # # certfile="/var/lib/sunshine/sunshine.cert", keyfile="/var/lib/sunshine/sunshine.key" - # # ) - # context.load_cert_chain( - # certfile="/home/kenji/.config/sunshine/credentials/cacert.pem", - # keyfile="/home/kenji/.config/sunshine/credentials/cakey.pem", - # ) - return http.client.ssl._create_unverified_context() # type: ignore # noqa: SLF001 - - -def pair(pin: str) -> str: - conn = http.client.HTTPSConnection("localhost", 47990, context=get_context()) - - # TODO: dynamic username and password - user_and_pass = base64.b64encode(b"sunshine:sunshine").decode("ascii") - headers = { - "Content-Type": "application/json", - "Authorization": f"Basic {user_and_pass}", - } - - # Define the parameters - params = json.dumps({"pin": f"{pin}"}) - - # Make the request - conn.request("POST", "/api/pin", params, headers) - - # Get and print the response - res = conn.getresponse() - data = res.read() - - print(data.decode("utf-8")) - return data.decode("utf-8") - - -def restart() -> None: - # Define the connection - conn = http.client.HTTPSConnection( - "localhost", - 47990, - context=http.client.ssl._create_unverified_context(), # type: ignore # noqa: SLF001 - ) - user_and_pass = base64.b64encode(b"sunshine:sunshine").decode("ascii") - headers = { - "Content-Type": "application/json", - "Authorization": f"Basic {user_and_pass}", - } - - # Make the request - conn.request("POST", "/api/restart", {}, headers) - - # Get and print the response - # There wont be a response, because it is restarted - res = conn.getresponse() - data = res.read() - - print(data.decode("utf-8")) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/config.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/config.py deleted file mode 100644 index 83e79bce2..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/config.py +++ /dev/null @@ -1,49 +0,0 @@ -import configparser -from dataclasses import dataclass -from pathlib import Path - -# address_family = both -# channels = 5 -# pkey = /var/lib/sunshine/sunshine.key -# cert = /var/lib/sunshine/sunshine.cert -# file_state = /var/lib/sunshine/state.json -# credentials_file = /var/lib/sunshine/credentials.json - -PSEUDO_SECTION = "DEFAULT" - - -@dataclass -class Config: - config: configparser.ConfigParser - config_location: Path - _instance = None - - def __new__(cls, config_location: Path | None = None) -> "Config": - if cls._instance is None: - cls._instance = super().__new__(cls) - cls._instance.config = configparser.ConfigParser() - config = config_location or cls._instance.default_sunshine_config_file() - cls._instance.config_location = config - with config.open() as f: - config_string = f"[{PSEUDO_SECTION}]\n" + f.read() - print(config_string) - cls._instance.config.read_string(config_string) - return cls._instance - - def default_sunshine_config_dir(self) -> Path: - return Path.home() / ".config" / "sunshine" - - def default_sunshine_config_file(self) -> Path: - return self.default_sunshine_config_dir() / "sunshine.conf" - - def get_private_key(self) -> str: - return self.config.get(PSEUDO_SECTION, "pkey") - - def get_certificate(self) -> str: - return self.config.get(PSEUDO_SECTION, "cert") - - def get_state_file(self) -> str: - return self.config.get(PSEUDO_SECTION, "file_state") - - def get_credentials_file(self) -> str: - return self.config.get(PSEUDO_SECTION, "credentials_file") diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_certificates.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_certificates.py deleted file mode 100644 index a422482db..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_certificates.py +++ /dev/null @@ -1,76 +0,0 @@ -import argparse -import datetime -import uuid -from pathlib import Path - -from cryptography import hazmat, x509 -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID - - -def generate_private_key() -> rsa.RSAPrivateKey: - private_key = rsa.generate_private_key( - public_exponent=65537, key_size=2048, backend=hazmat.backends.default_backend() - ) - return private_key - - -def generate_certificate(private_key: rsa.RSAPrivateKey) -> bytes: - subject = issuer = x509.Name( - [ - x509.NameAttribute(NameOID.COMMON_NAME, "Sunshine Gamestream Host"), - ] - ) - cert_builder = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(issuer) - .public_key(private_key.public_key()) - .serial_number(61093384576940497812448570031200738505731293357) - .not_valid_before(datetime.datetime(2024, 2, 27, tzinfo=datetime.UTC)) - .not_valid_after(datetime.datetime(2044, 2, 22, tzinfo=datetime.UTC)) - .add_extension( - x509.SubjectAlternativeName([x509.DNSName("localhost")]), - critical=False, - ) - .sign(private_key, hashes.SHA256(), default_backend()) - ) - pem_certificate = cert_builder.public_bytes(serialization.Encoding.PEM) - return pem_certificate - - -def private_key_to_pem(private_key: rsa.RSAPrivateKey) -> bytes: - pem_private_key = private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ) - return pem_private_key - - -def init_credentials() -> tuple[bytes, bytes]: - private_key = generate_private_key() - certificate = generate_certificate(private_key) - private_key_pem = private_key_to_pem(private_key) - return certificate, private_key_pem - - -def uniqueid() -> str: - return str(uuid.uuid4()).upper() - - -def write_credentials(_args: argparse.Namespace) -> None: - print("Writing sunshine credentials") - pem_certificate, pem_private_key = init_credentials() - credentials_dir = Path("credentials") - credentials_dir.mkdir(parents=True, exist_ok=True) - (credentials_dir / "cacert.pem").write_bytes(pem_certificate) - (credentials_dir / "cakey.pem").write_bytes(pem_private_key) - print("Generating sunshine UUID") - Path("uuid").write_text(uniqueid()) - - -def register_initialization_parser(parser: argparse.ArgumentParser) -> None: - parser.set_defaults(func=write_credentials) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_state.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_state.py deleted file mode 100644 index 95f2028b4..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/init_state.py +++ /dev/null @@ -1,16 +0,0 @@ -import argparse - -from .state import init_state - - -def init_state_file(args: argparse.Namespace) -> None: - uuid = args.uuid - state_file = args.state_file - init_state(uuid, state_file) - print("Finished initializing sunshine state file.") - - -def register_state_initialization_parser(parser: argparse.ArgumentParser) -> None: - parser.add_argument("--uuid") - parser.add_argument("--state-file") - parser.set_defaults(func=init_state_file) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/listen.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/listen.py deleted file mode 100644 index 5fe24adaa..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/listen.py +++ /dev/null @@ -1,90 +0,0 @@ -import argparse -import base64 -import json -import socket -import traceback - -from .api import pair -from .state import default_sunshine_state_file - - -# listen on a specific port for information from the moonlight side -def listen(port: int, cert: str, uuid: str, state_file: str) -> bool: - host = "" - # Create a socket object with dual-stack support - server_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - # Enable dual-stack support - server_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) - # Bind the socket to the host and port - server_socket.bind((host, port)) - # Listen for incoming connections (accept up to 5) - server_socket.listen(5) - - while True: - # Accept incoming connection - client_socket, addr = server_socket.accept() - - print(f"Connection accepted from {addr}") - - # Receive data from the client - - data = client_socket.recv(16384) - - try: - request = data.decode("utf-8") - raw_body = request.split("\n")[-1] - print(raw_body) - body = json.loads(raw_body) - - pair_type = body.get("type", "") - - if pair_type == "api": - print("Api request") - status = pair(body.get("pin", "")) - status = json.dumps(status) - response = f"HTTP/1.1 200 OK\r\nContent-Type:application/json\r\n\r\\{status}\r\n" - client_socket.sendall(response.encode("utf-8")) - - if pair_type == "native": - # url = unquote(data_str.split()[1]) - # rec_uuid = parse_qs(urlparse(url).query).get("uuid", [""])[0] - # rec_cert = parse_qs(urlparse(url).query).get("cert", [""])[0] - # decoded_cert = base64.urlsafe_b64decode(rec_cert).decode("utf-8") - # print(f"Received URL: {url}") - # print(f"Extracted UUID: {rec_uuid}") - # print(f"Extracted Cert: {decoded_cert}") - encoded_cert = base64.urlsafe_b64encode(cert.encode("utf-8")).decode( - "utf-8" - ) - json_data = {} - json_data["uuid"] = uuid - json_data["cert"] = encoded_cert - json_data["hostname"] = socket.gethostname() - json_body = json.dumps(json_data) - response = f"HTTP/1.1 200 OK\r\nContent-Type:application/json\r\n\r\\{json_body}\r\n" - client_socket.sendall(response.encode("utf-8")) - # add_moonlight_client(decoded_cert, state_file, rec_uuid) - - except UnicodeDecodeError: - print(f"UnicodeDecodeError: Cannot decode byte {data[8]}") - traceback.print_exc() - - client_socket.close() - - -def init_listener(args: argparse.Namespace) -> None: - port = args.port - cert = args.cert - uuid = args.uuid - state = args.state - listen(port, cert, uuid, state) - - -def register_socket_listener(parser: argparse.ArgumentParser) -> None: - parser.add_argument("--port", default=48011, type=int) - parser.add_argument("--cert") - parser.add_argument("--uuid") - parser.add_argument("--state", default=default_sunshine_state_file()) - # TODO: auto accept - # parser.add_argument("--auto-accept") - parser.set_defaults(func=init_listener) diff --git a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/state.py b/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/state.py deleted file mode 100644 index 0f96d755b..000000000 --- a/pkgs/moonlight-sunshine-accept/moonlight_sunshine_accept/sunshine/state.py +++ /dev/null @@ -1,67 +0,0 @@ -import json -from pathlib import Path -from typing import Any - - -def default_sunshine_config_dir() -> Path: - return Path.home() / ".config" / "sunshine" - - -def default_sunshine_state_file() -> Path: - return default_sunshine_config_dir() / "sunshine_state.json" - - -def load_state(sunshine_state_path: Path) -> str | None: - sunshine_state_path = sunshine_state_path or default_sunshine_state_file() - print(f"Loading sunshine state from {sunshine_state_path}") - try: - return json.loads(sunshine_state_path.read_text()) - except FileNotFoundError: - print("Sunshine state file not found.") - return None - - -# this needs to be created before sunshine is first run -def init_state(uuid: str, sunshine_state_path: Path) -> None: - print("Initializing sunshine state.") - - data: dict[str, Any] = {} - data["root"] = {} - data["root"]["uniqueid"] = uuid - data["root"]["devices"] = [] - - # write the initial bootstrap config file - write_state(data, sunshine_state_path) - - -def write_state(data: dict[str, Any], sunshine_state_path: Path) -> None: - sunshine_state_path = sunshine_state_path or default_sunshine_state_file() - with sunshine_state_path.open("w") as file: - json.dump(data, file, indent=4) - - -# this is used by moonlight-qt -def pseudo_uuid() -> str: - return "0123456789ABCDEF" - - -# TODO: finish this function -def add_moonlight_client( - certificate: str, sunshine_state_path: Path, uuid: str -) -> None: - print("Adding moonlight client to sunshine state.") - raw_state = load_state(sunshine_state_path) - if raw_state: - state = json.loads(raw_state) - - if not state["root"]["devices"]: - state["root"]["devices"].append( - {"uniqueid": pseudo_uuid(), "certs": [certificate]} - ) - write_state(state, sunshine_state_path) - if certificate not in state["root"]["devices"][0]["certs"]: - state["root"]["devices"][0]["certs"].append(certificate) - state["root"]["devices"][0]["uniqueid"] = pseudo_uuid() - write_state(state, sunshine_state_path) - else: - print("Moonlight certificate already added.") diff --git a/pkgs/moonlight-sunshine-accept/pyproject.toml b/pkgs/moonlight-sunshine-accept/pyproject.toml deleted file mode 100644 index 43a982145..000000000 --- a/pkgs/moonlight-sunshine-accept/pyproject.toml +++ /dev/null @@ -1,9 +0,0 @@ -[build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" - -[project] -name = "moonlight-sunshine-accept" -description = "Moonlight Sunshine Bridge" -dynamic = ["version"] -scripts = { moonlight-sunshine-accept = "moonlight_sunshine_accept:main" }