littleshop/LittleShop/Areas/Admin/Views/Bots/DiscoverRemote.cshtml
SysAdmin 521bff2c7d
All checks were successful
Build and Deploy LittleShop / Deploy to Production VPS (Manual Only) (push) Has been skipped
Build and Deploy LittleShop / Deploy to Pre-Production (CT109) (push) Successful in 1m0s
feat: Add Remote TeleBot Discovery & Configuration
- Add discovery API endpoints to TeleBot (probe, initialize, configure, status)
- Add LivenessService for LittleShop connectivity monitoring with 5min shutdown
- Add BotDiscoveryService to LittleShop for remote bot management
- Add Admin UI: DiscoverRemote wizard, RepushConfig page, status badges
- Add remote discovery fields to Bot model (RemoteAddress, RemotePort, etc.)
- Add CheckRemoteStatus and RepushConfig controller actions
- Update Index/Details views to show remote bot indicators
- Shared secret authentication for discovery, BotKey for post-init

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 13:41:36 +00:00

287 lines
14 KiB
Plaintext

@model LittleShop.DTOs.DiscoveryWizardViewModel
@{
ViewData["Title"] = "Discover Remote TeleBot";
}
<h1>Discover Remote TeleBot</h1>
<div class="row">
<div class="col-md-8">
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-triangle"></i> @Model.ErrorMessage
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle"></i> @Model.SuccessMessage
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
}
@if (Model.CurrentStep == 1)
{
<!-- Step 1: Discovery -->
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-search"></i> Step 1: Discover TeleBot Instance</h5>
</div>
<div class="card-body">
<p class="text-muted">
Enter the IP address and port of the TeleBot instance you want to connect.
The TeleBot must be running and configured with the same discovery secret.
</p>
<form asp-area="Admin" asp-controller="Bots" asp-action="ProbeRemote" method="post">
@Html.AntiForgeryToken()
<div class="row mb-3">
<div class="col-md-8">
<label for="IpAddress" class="form-label">IP Address / Hostname</label>
<input name="IpAddress" id="IpAddress" value="@Model.IpAddress" class="form-control"
placeholder="e.g., 192.168.1.100 or telebot.example.com" required />
<small class="text-muted">The IP address or hostname where TeleBot is running</small>
</div>
<div class="col-md-4">
<label for="Port" class="form-label">Port</label>
<input name="Port" id="Port" type="number" value="@(Model.Port == 0 ? 5010 : Model.Port)" class="form-control"
min="1" max="65535" required />
<small class="text-muted">Default: 5010</small>
</div>
</div>
<div class="d-grid gap-2 d-md-flex">
<button type="submit" class="btn btn-primary me-md-2">
<i class="fas fa-satellite-dish"></i> Probe TeleBot
</button>
<a href="/Admin/Bots" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
</div>
}
else if (Model.CurrentStep == 2)
{
<!-- Step 2: Registration -->
<div class="card mb-3">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="fas fa-check-circle"></i> TeleBot Discovered!</h5>
</div>
<div class="card-body">
<table class="table table-sm">
<tr>
<th width="150">Instance ID:</th>
<td><code>@Model.ProbeResponse?.InstanceId</code></td>
</tr>
<tr>
<th>Name:</th>
<td>@Model.ProbeResponse?.Name</td>
</tr>
<tr>
<th>Version:</th>
<td>@Model.ProbeResponse?.Version</td>
</tr>
<tr>
<th>Status:</th>
<td>
<span class="badge bg-@(Model.ProbeResponse?.Status == "Bootstrap" ? "warning" : "info")">
@Model.ProbeResponse?.Status
</span>
</td>
</tr>
<tr>
<th>Address:</th>
<td>@Model.IpAddress:@Model.Port</td>
</tr>
</table>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-robot"></i> Step 2: Register Bot</h5>
</div>
<div class="card-body">
<form asp-area="Admin" asp-controller="Bots" asp-action="RegisterRemote" method="post">
@Html.AntiForgeryToken()
<!-- Hidden fields to preserve discovery data -->
<input type="hidden" name="IpAddress" value="@Model.IpAddress" />
<input type="hidden" name="Port" value="@Model.Port" />
<input type="hidden" name="ProbeResponse.InstanceId" value="@Model.ProbeResponse?.InstanceId" />
<input type="hidden" name="ProbeResponse.Name" value="@Model.ProbeResponse?.Name" />
<input type="hidden" name="ProbeResponse.Version" value="@Model.ProbeResponse?.Version" />
<input type="hidden" name="ProbeResponse.Status" value="@Model.ProbeResponse?.Status" />
<div class="mb-3">
<label for="BotName" class="form-label">Bot Name</label>
<input name="BotName" id="BotName" value="@Model.BotName" class="form-control"
placeholder="e.g., Production TeleBot" required />
<small class="text-muted">A friendly name to identify this bot</small>
</div>
<div class="mb-3">
<label for="PersonalityName" class="form-label">Personality</label>
<select name="PersonalityName" id="PersonalityName" class="form-select">
<option value="Alan" @(Model.PersonalityName == "Alan" ? "selected" : "")>Alan (Professional)</option>
<option value="Dave" @(Model.PersonalityName == "Dave" ? "selected" : "")>Dave (Casual)</option>
<option value="Sarah" @(Model.PersonalityName == "Sarah" ? "selected" : "")>Sarah (Helpful)</option>
<option value="Mike" @(Model.PersonalityName == "Mike" ? "selected" : "")>Mike (Direct)</option>
<option value="Emma" @(Model.PersonalityName == "Emma" ? "selected" : "")>Emma (Friendly)</option>
<option value="Tom" @(Model.PersonalityName == "Tom" ? "selected" : "")>Tom (Efficient)</option>
</select>
<small class="text-muted">Bot conversation style</small>
</div>
<div class="mb-3">
<label for="Description" class="form-label">Description (Optional)</label>
<textarea name="Description" id="Description" class="form-control" rows="2"
placeholder="Brief description of this bot's purpose">@Model.Description</textarea>
</div>
<div class="d-grid gap-2 d-md-flex">
<button type="submit" class="btn btn-success me-md-2">
<i class="fas fa-link"></i> Register & Initialize
</button>
<a href="/Admin/Bots/DiscoverRemote" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Back
</a>
</div>
</form>
</div>
</div>
}
else if (Model.CurrentStep == 3)
{
<!-- Step 3: Configuration -->
<div class="card mb-3">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="fas fa-key"></i> Bot Registered - API Key</h5>
</div>
<div class="card-body">
@if (!string.IsNullOrEmpty(Model.BotKey))
{
<div class="alert alert-warning">
<strong>Save this Bot Key securely!</strong> It won't be shown again.
</div>
<div class="input-group mb-3">
<input type="text" class="form-control font-monospace" value="@Model.BotKey" id="botKeyInput" readonly />
<button class="btn btn-outline-secondary" type="button" onclick="copyBotKey()">
<i class="fas fa-copy"></i> Copy
</button>
</div>
}
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-telegram"></i> Step 3: Configure Telegram Token</h5>
</div>
<div class="card-body">
<p class="text-muted">
Now enter the Telegram bot token from BotFather to activate this bot.
</p>
<form asp-area="Admin" asp-controller="Bots" asp-action="ConfigureRemote" method="post">
@Html.AntiForgeryToken()
<!-- Hidden fields -->
<input type="hidden" name="BotId" value="@Model.BotId" />
<input type="hidden" name="BotKey" value="@Model.BotKey" />
<input type="hidden" name="IpAddress" value="@Model.IpAddress" />
<input type="hidden" name="Port" value="@Model.Port" />
<div class="mb-3">
<label for="BotToken" class="form-label">Telegram Bot Token</label>
<input name="BotToken" id="BotToken" value="@Model.BotToken" class="form-control font-monospace"
placeholder="123456789:ABCdefGHIjklMNOpqrsTUVwxyz" required />
<small class="text-muted">
Get this from <a href="https://t.me/BotFather" target="_blank">@@BotFather</a> on Telegram
</small>
</div>
<div class="d-grid gap-2 d-md-flex">
<button type="submit" class="btn btn-success me-md-2">
<i class="fas fa-rocket"></i> Configure & Activate Bot
</button>
<a href="/Admin/Bots" class="btn btn-secondary">
Skip (configure later)
</a>
</div>
</form>
</div>
</div>
}
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Wizard Progress</h5>
</div>
<div class="card-body">
<ul class="list-unstyled">
<li class="@(Model.CurrentStep == 1 ? "text-primary fw-bold" : Model.CurrentStep > 1 ? "text-success" : "text-muted")">
<i class="fas fa-@(Model.CurrentStep == 1 ? "search" : Model.CurrentStep > 1 ? "check" : "circle")"></i>
1. Discover TeleBot
</li>
<li class="@(Model.CurrentStep == 2 ? "text-primary fw-bold" : Model.CurrentStep > 2 ? "text-success" : "text-muted")">
<i class="fas fa-@(Model.CurrentStep == 2 ? "robot" : Model.CurrentStep > 2 ? "check" : "circle")"></i>
2. Register Bot
</li>
<li class="@(Model.CurrentStep == 3 ? "text-primary fw-bold" : "text-muted")">
<i class="fas fa-@(Model.CurrentStep == 3 ? "telegram" : "circle")"></i>
3. Configure Telegram
</li>
</ul>
</div>
</div>
<div class="card mt-3">
<div class="card-header">
<h5 class="mb-0">Requirements</h5>
</div>
<div class="card-body">
<ul class="small">
<li>TeleBot must be running</li>
<li>Same discovery secret on both sides</li>
<li>Network connectivity to TeleBot</li>
<li>Valid Telegram bot token</li>
</ul>
</div>
</div>
@if (Model.CurrentStep >= 2)
{
<div class="card mt-3">
<div class="card-header">
<h5 class="mb-0">Connection Info</h5>
</div>
<div class="card-body">
<p class="small mb-1"><strong>Address:</strong> @Model.IpAddress</p>
<p class="small mb-0"><strong>Port:</strong> @Model.Port</p>
</div>
</div>
}
</div>
</div>
@section Scripts {
<script>
function copyBotKey() {
var input = document.getElementById('botKeyInput');
input.select();
input.setSelectionRange(0, 99999);
navigator.clipboard.writeText(input.value).then(function() {
alert('Bot Key copied to clipboard!');
});
}
</script>
}