feat(developers): add developer program signup with SilverDESK integration

Add developer application page with form submission that creates tickets
in SilverDESK. Includes provisioning service scaffolding for Mattermost,
Mailcow, and Gitea account creation. Fixes API key header casing
(X-API-Key) and ticket payload to match SilverDESK's CreateTicketDto
contract.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 20:51:36 +00:00
parent d2780e9295
commit 21c07adf54
11 changed files with 1056 additions and 3 deletions

View File

@@ -0,0 +1,80 @@
using System.Text;
using System.Text.Json;
using SilverLabs.Website.Models;
namespace SilverLabs.Website.Services;
public class DeveloperApplicationService
{
private readonly HttpClient _httpClient;
private readonly ILogger<DeveloperApplicationService> _logger;
public DeveloperApplicationService(HttpClient httpClient, ILogger<DeveloperApplicationService> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public async Task<(bool Success, string Message)> SubmitApplicationAsync(DeveloperApplication application)
{
try
{
var ticketBody = FormatTicketBody(application);
var payload = new
{
Subject = $"[Developer Program] {application.Role} Application - {application.FullName}",
Description = ticketBody,
Priority = "Medium",
Category = "Developer Program"
};
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("/api/tickets", content);
if (response.IsSuccessStatusCode)
{
_logger.LogInformation("Developer application submitted for {Email} as {Role}", application.Email, application.Role);
return (true, "Application submitted successfully! We'll review it and get back to you soon.");
}
var errorBody = await response.Content.ReadAsStringAsync();
_logger.LogError("Failed to create ticket: {StatusCode} - {Body}", response.StatusCode, errorBody);
return (false, "Something went wrong submitting your application. Please try again later.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error submitting developer application for {Email}", application.Email);
return (false, "Unable to connect to the application service. Please try again later.");
}
}
private static string FormatTicketBody(DeveloperApplication app)
{
var sb = new StringBuilder();
sb.AppendLine("## Developer Program Application");
sb.AppendLine();
sb.AppendLine($"**Role:** {app.Role}");
sb.AppendLine($"**Full Name:** {app.FullName}");
sb.AppendLine($"**Email:** {app.Email}");
sb.AppendLine($"**Desired Username:** {app.DesiredUsername}");
sb.AppendLine($"**Timezone:** {app.Timezone}");
sb.AppendLine();
sb.AppendLine($"**Platforms:** {string.Join(", ", app.Platforms)}");
sb.AppendLine();
if (app.Role == ApplicationRole.Developer && !string.IsNullOrWhiteSpace(app.Skills))
{
sb.AppendLine("**Skills & Experience:**");
sb.AppendLine(app.Skills);
sb.AppendLine();
}
sb.AppendLine("**Motivation:**");
sb.AppendLine(app.Motivation);
return sb.ToString();
}
}