Загрузка данных


using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;

namespace AlexSelectLauncher;

internal static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new MainForm());
    }
}

public sealed class MainForm : Form
{
    private readonly TextBox keyBox = new();
    private readonly Label status = new();
    private readonly Label code = new();
    private readonly Button activate = new();
    private readonly Button install = new();

    private const string ApiBase = "http://localhost:5255/api/activation";

    public MainForm()
    {
        Text = "ALEX SELECT — MOD BY ALEX";
        Width = 620;
        Height = 420;
        StartPosition = FormStartPosition.CenterScreen;
        BackColor = Color.FromArgb(16, 12, 24);
        ForeColor = Color.White;
        Font = new Font("Segoe UI", 10);

        var title = new Label
        {
            Text = "ALEX SELECT",
            Font = new Font("Segoe UI", 28, FontStyle.Bold),
            AutoSize = true,
            Location = new Point(35, 30)
        };

        var subtitle = new Label
        {
            Text = "GTA V • INSTALLER & LICENSE",
            AutoSize = true,
            Location = new Point(40, 82),
            ForeColor = Color.Plum
        };

        var keyLabel = new Label
        {
            Text = "Ключ активации",
            AutoSize = true,
            Location = new Point(40, 125)
        };

        keyBox.Location = new Point(40, 153);
        keyBox.Width = 520;
        keyBox.PlaceholderText = "XXXX-XXXX-XXXX";

        activate.Text = "АКТИВИРОВАТЬ";
        activate.Location = new Point(40, 200);
        activate.Width = 250;
        activate.Height = 42;
        activate.Click += async (_, _) => await ActivateAsync();

        install.Text = "УСТАНОВИТЬ";
        install.Location = new Point(310, 200);
        install.Width = 250;
        install.Height = 42;
        install.Enabled = false;
        install.Click += async (_, _) => await DownloadFilesAsync();

        status.Text = "Статус: не активирован";
        status.AutoSize = true;
        status.Location = new Point(40, 270);

        code.Text = "ONLINE CODE: —";
        code.AutoSize = true;
        code.Location = new Point(40, 305);
        code.Font = new Font("Consolas", 12, FontStyle.Bold);

        Controls.AddRange(
        [
            title,
            subtitle,
            keyLabel,
            keyBox,
            activate,
            install,
            status,
            code
        ]);
    }

    private async Task ActivateAsync()
    {
        var key = keyBox.Text.Trim();

        if (string.IsNullOrWhiteSpace(key))
        {
            status.Text = "Статус: введите ключ";
            return;
        }

        activate.Enabled = false;
        status.Text = "Статус: проверяем ключ онлайн…";

        try
        {
            using var http = new HttpClient
            {
                Timeout = TimeSpan.FromSeconds(10)
            };

            var machineId = GetMachineId();

            var response = await http.PostAsJsonAsync(
                $"{ApiBase}/activate",
                new
                {
                    key,
                    machine_id = machineId
                });

            if (!response.IsSuccessStatusCode)
            {
                status.Text = "Статус: ключ недействителен или заблокирован";
                return;
            }

            var data =
                await response.Content.ReadFromJsonAsync<ActivationResponse>();

            if (data is null || !data.active)
            {
                status.Text = "Статус: активация отклонена";
                return;
            }

            status.Text =
                $"Статус: АКТИВИРОВАН • версия {data.version}";

            code.Text =
                $"ONLINE CODE: {data.online_code}";

            install.Enabled = true;
        }
        catch
        {
            status.Text = "Статус: нет связи с сервером";
        }
        finally
        {
            activate.Enabled = true;
        }
    }

    private async Task DownloadFilesAsync()
    {
        using var dialog = new FolderBrowserDialog
        {
            Description = "Выберите папку для файлов ALEX SELECT"
        };

        if (dialog.ShowDialog() != DialogResult.OK)
            return;

        var folder = dialog.SelectedPath;

        const string fileUrl =
            "https://cloud.mail.ru/public/Aeig/Mi6K8oZZz/Setup%20GPF%20v2.2.part1.exe";

        var filePath =
            Path.Combine(folder, "Setup GPF v2.2.part1.exe");

        install.Enabled = false;
        status.Text = "Статус: скачиваем part1.exe…";

        try
        {
            using var http = new HttpClient
            {
                Timeout = TimeSpan.FromMinutes(30)
            };

            using var response = await http.GetAsync(
                fileUrl,
                HttpCompletionOption.ResponseHeadersRead);

            response.EnsureSuccessStatusCode();

            await using var input =
                await response.Content.ReadAsStreamAsync();

            await using var output =
                File.Create(filePath);

            await input.CopyToAsync(output);

            status.Text = "Статус: файл скачан ✅";

            MessageBox.Show(
                $"Файл успешно скачан:\n\n{filePath}",
                "ALEX SELECT",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information);
        }
        catch (Exception ex)
        {
            status.Text = "Статус: ошибка скачивания";

            MessageBox.Show(
                $"Не удалось скачать файл.\n\n{ex.Message}",
                "ALEX SELECT",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error);
        }
        finally
        {
            install.Enabled = true;
        }
    }

    private static string GetMachineId()
    {
        var raw =
            $"{Environment.MachineName}|{Environment.UserName}|AlexSelect";

        return Convert.ToHexString(
            SHA256.HashData(
                System.Text.Encoding.UTF8.GetBytes(raw)))[..32];
    }

    private sealed record ActivationResponse(
        bool active,
        string version,
        string online_code);
}