make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); use App\Models\Server; use App\Models\ServerGroup; use App\Models\ServerMachine; use App\Models\User; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; $opts = getopt('', ['key:', 'name:', 'relay-host:', 'relay-port:', 'listen-port:', 'tls-domain:', 'backup:']); foreach (['key', 'name', 'relay-host', 'relay-port', 'listen-port', 'tls-domain', 'backup'] as $key) { if (empty($opts[$key])) throw new RuntimeException("Missing --$key"); } if (!preg_match('/^[a-z0-9-]+$/', $opts['key'])) throw new RuntimeException('Invalid key'); foreach (['relay-port', 'listen-port'] as $key) { if (!ctype_digit((string)$opts[$key]) || $opts[$key] < 1024 || $opts[$key] > 65535) throw new RuntimeException('Invalid port'); } $dir = rtrim($opts['backup'], '/'); umask(0077); if (!is_dir($dir) && !mkdir($dir, 0700, true)) throw new RuntimeException('Backup creation failed'); if (is_file("$dir/manifest.json")) { $manifest = json_decode(file_get_contents("$dir/manifest.json"), true, flags: JSON_THROW_ON_ERROR); if ($manifest['key'] !== $opts['key']) throw new RuntimeException('Backup belongs to another node'); echo json_encode(['already_staged' => true, 'node_id' => $manifest['node_id']]) . PHP_EOL; exit; } if (ServerMachine::where('name', 'dpl-' . $opts['key'])->exists() || Server::where('name', $opts['name'])->exists()) { throw new RuntimeException('Existing resources found without this backup manifest'); } file_put_contents("$dir/before.json", json_encode([ 'servers' => DB::table('v2_server')->get(), 'groups' => DB::table('v2_server_group')->get(), 'machines' => DB::table('v2_server_machine')->get(), ], JSON_THROW_ON_ERROR)); $manifest = DB::transaction(function () use ($opts) { $group = new ServerGroup(); $group->name = 'dpl 接入验收 ' . $opts['key']; $group->save(); $machine = ServerMachine::create([ 'name' => 'dpl-' . $opts['key'], 'notes' => 'Independent AnyTLS service; existing XrayR preserved', 'is_active' => true, 'token' => ServerMachine::generateToken(), ]); $node = Server::create([ 'name' => $opts['name'], 'type' => 'anytls', 'host' => $opts['relay-host'], 'port' => (string)$opts['relay-port'], 'server_port' => (string)$opts['listen-port'], 'group_ids' => [(string)$group->id], 'route_ids' => [], 'tags' => [], 'rate' => 1, 'show' => true, 'enabled' => true, 'machine_id' => $machine->id, 'sort' => 1, 'protocol_settings' => ['tls' => ['server_name' => $opts['tls-domain'], 'allow_insecure' => false]], 'cert_config' => [ 'cert_mode' => 'file', 'domain' => $opts['tls-domain'], 'cert_file' => '/etc/letsencrypt/live/' . $opts['tls-domain'] . '/fullchain.pem', 'key_file' => '/etc/letsencrypt/live/' . $opts['tls-domain'] . '/privkey.pem', ], ]); $user = new User([ 'email' => 'node-qa-' . $opts['key'] . '-' . Str::lower(Str::random(8)) . '@example.invalid', 'password' => password_hash(Str::random(40), PASSWORD_BCRYPT), 'uuid' => (string)Str::uuid(), 'token' => Str::random(32), 'group_id' => $group->id, 'transfer_enable' => 104857600, 'expired_at' => time() + 86400, 'banned' => 0, 'is_admin' => 0, 'remind_expire' => 0, 'remind_traffic' => 0, 'device_limit' => 2, ]); $user->saveQuietly(); return ['key' => $opts['key'], 'node_id' => $node->id, 'machine_id' => $machine->id, 'group_id' => $group->id, 'test_user_id' => $user->id]; }); file_put_contents("$dir/manifest.json", json_encode($manifest, JSON_THROW_ON_ERROR)); $machine = ServerMachine::findOrFail($manifest['machine_id']); $user = User::findOrFail($manifest['test_user_id']); $config = "panel:\n url: \"https://dp-link.net\"\nmachine:\n machine_id: {$machine->id}\n token_env: DPL_MACHINE_TOKEN\nkernel:\n type: singbox\n config_dir: /var/lib/dpl-node/kernel\nlog:\n level: info\nruntime:\n gomemlimit: 384MiB\n gogc: 75\nnode:\n push_interval: 30\n pull_interval: 30\n"; file_put_contents("$dir/node-config.yml", $config); file_put_contents("$dir/node-secret.env", 'DPL_MACHINE_TOKEN=' . $machine->token . "\n"); file_put_contents("$dir/qa-private.json", json_encode([ 'user_id' => $user->id, 'uuid' => $user->uuid, 'subscribe_token' => $user->token, 'host' => $opts['relay-host'], 'port' => (int)$opts['relay-port'], 'sni' => $opts['tls-domain'], ], JSON_THROW_ON_ERROR)); echo json_encode($manifest, JSON_UNESCAPED_UNICODE) . PHP_EOL;