feat: Add AlexHost deployment pipeline and bot control functionality
- Add Gitea Actions workflow for manual AlexHost deployment - Add docker-compose.alexhost.yml for production deployment - Add deploy-alexhost.sh script with server-side build support - Add Bot Control feature (Start/Stop/Restart) for remote bot management - Add discovery control endpoint in TeleBot - Update TeleBot with StartPollingAsync/StopPolling/RestartPollingAsync - Fix platform architecture issues by building on target server - Update docker-compose configurations for all environments Deployment tested successfully: - TeleShop: healthy at https://teleshop.silentmary.mywire.org - TeleBot: healthy with discovery integration - SilverPay: connectivity verified 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -793,5 +793,131 @@ public class BotsController : Controller
|
||||
await _botService.UpdateRemoteInfoAsync(botId, ipAddress, port, instanceId, status);
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/StartBot/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> StartBot(Guid id)
|
||||
{
|
||||
_logger.LogInformation("Start bot requested for {BotId}", id);
|
||||
|
||||
var bot = await _botService.GetBotByIdAsync(id);
|
||||
if (bot == null)
|
||||
{
|
||||
TempData["Error"] = "Bot not found";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
if (!bot.IsRemote)
|
||||
{
|
||||
TempData["Error"] = "Bot control is only available for remote bots";
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _discoveryService.ControlBotAsync(id, "start");
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
TempData["Success"] = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = result.Message;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to start bot {BotId}", id);
|
||||
TempData["Error"] = $"Failed to start bot: {ex.Message}";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/StopBot/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> StopBot(Guid id)
|
||||
{
|
||||
_logger.LogInformation("Stop bot requested for {BotId}", id);
|
||||
|
||||
var bot = await _botService.GetBotByIdAsync(id);
|
||||
if (bot == null)
|
||||
{
|
||||
TempData["Error"] = "Bot not found";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
if (!bot.IsRemote)
|
||||
{
|
||||
TempData["Error"] = "Bot control is only available for remote bots";
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _discoveryService.ControlBotAsync(id, "stop");
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
TempData["Success"] = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = result.Message;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to stop bot {BotId}", id);
|
||||
TempData["Error"] = $"Failed to stop bot: {ex.Message}";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
// POST: Admin/Bots/RestartBot/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RestartBot(Guid id)
|
||||
{
|
||||
_logger.LogInformation("Restart bot requested for {BotId}", id);
|
||||
|
||||
var bot = await _botService.GetBotByIdAsync(id);
|
||||
if (bot == null)
|
||||
{
|
||||
TempData["Error"] = "Bot not found";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
if (!bot.IsRemote)
|
||||
{
|
||||
TempData["Error"] = "Bot control is only available for remote bots";
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _discoveryService.ControlBotAsync(id, "restart");
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
TempData["Success"] = result.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = result.Message;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to restart bot {BotId}", id);
|
||||
TempData["Error"] = $"Failed to restart bot: {ex.Message}";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Details), new { id });
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -58,9 +58,14 @@ public class ProductsController : Controller
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
|
||||
|
||||
var categories = await _categoryService.GetAllCategoriesAsync();
|
||||
ViewData["Categories"] = categories.Where(c => c.IsActive);
|
||||
|
||||
// FIX: Re-populate VariantCollections for view rendering when validation fails
|
||||
var variantCollections = await _variantCollectionService.GetAllVariantCollectionsAsync();
|
||||
ViewData["VariantCollections"] = variantCollections.Where(vc => vc.IsActive);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
@@ -134,6 +139,11 @@ public class ProductsController : Controller
|
||||
{
|
||||
var categories = await _categoryService.GetAllCategoriesAsync();
|
||||
ViewData["Categories"] = categories.Where(c => c.IsActive);
|
||||
|
||||
// FIX: Re-populate VariantCollections for view rendering when validation fails
|
||||
var variantCollections = await _variantCollectionService.GetAllVariantCollectionsAsync();
|
||||
ViewData["VariantCollections"] = variantCollections.Where(vc => vc.IsActive);
|
||||
|
||||
ViewData["ProductId"] = id;
|
||||
return View(model);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user