OPC # 0001: Extract OPC into standalone repo
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
public enum BuildStatus { Running, Succeeded, Failed }
|
||||
public enum BuildKind { DockerImage, DotnetProject, NpmProject }
|
||||
|
||||
/// <summary>
|
||||
/// Persisted record of a single build run — image build, dotnet build, or npm build.
|
||||
/// Stored in ClientAssets/builds.json.
|
||||
/// </summary>
|
||||
public class BuildRecord
|
||||
{
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString("N")[..8];
|
||||
public BuildKind Kind { get; set; }
|
||||
public string Target { get; set; } = string.Empty; // image name or project path
|
||||
public BuildStatus Status { get; set; } = BuildStatus.Running;
|
||||
public DateTimeOffset StartedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? FinishedAt { get; set; }
|
||||
public int? DurationMs { get; set; }
|
||||
public string? ImageDigest { get; set; } // populated for DockerImage builds
|
||||
public List<string> Log { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Defines where a specific infrastructure component (Postgres, Keycloak, Vault, MinIO)
|
||||
/// is hosted for a given tenant. Each component in a StackConfig is configured independently.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum ComponentMode
|
||||
{
|
||||
/// <summary>Shared platform instance — logical slice only (realm, schema, bucket, namespace).</summary>
|
||||
SharedPlatform,
|
||||
|
||||
/// <summary>Baked into the app image itself via supervisord. Trial tier only.</summary>
|
||||
Bundled,
|
||||
|
||||
/// <summary>Own sidecar container on ControlPlane's shared Docker host.</summary>
|
||||
OwnContainer,
|
||||
|
||||
/// <summary>Own VM with the component running inside Docker on it.</summary>
|
||||
VpsDocker,
|
||||
|
||||
/// <summary>Own VM with the component running as a native OS process (no Docker).</summary>
|
||||
VpsBareMetal
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
public record GitCommit(
|
||||
string Hash,
|
||||
string ShortHash,
|
||||
string Author,
|
||||
string Date,
|
||||
string Subject,
|
||||
string[] Files
|
||||
);
|
||||
@@ -0,0 +1,63 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
// ── Repository ────────────────────────────────────────────────────────────────
|
||||
|
||||
public record GiteaRepo(
|
||||
long Id,
|
||||
string Name,
|
||||
string FullName,
|
||||
string DefaultBranch,
|
||||
string CloneUrl,
|
||||
string SshUrl,
|
||||
bool Private
|
||||
);
|
||||
|
||||
// ── Branch ────────────────────────────────────────────────────────────────────
|
||||
|
||||
public record GiteaBranch(
|
||||
string Name,
|
||||
string CommitSha,
|
||||
bool Protected
|
||||
);
|
||||
|
||||
// ── Pull Request ──────────────────────────────────────────────────────────────
|
||||
|
||||
public record GiteaPullRequest(
|
||||
long Number,
|
||||
string Title,
|
||||
string State, // open | closed | merged
|
||||
string HeadBranch,
|
||||
string BaseBranch,
|
||||
string HtmlUrl,
|
||||
string CreatedAt,
|
||||
string UpdatedAt,
|
||||
GiteaUser? User,
|
||||
GiteaMergeInfo? MergeInfo
|
||||
);
|
||||
|
||||
public record GiteaUser(string Login, string AvatarUrl);
|
||||
|
||||
public record GiteaMergeInfo(bool Mergeable, bool Merged, string? MergedAt);
|
||||
|
||||
// ── Tag ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
public record GiteaTag(string Name, string CommitSha, string ZipUrl);
|
||||
|
||||
// ── Webhook ───────────────────────────────────────────────────────────────────
|
||||
|
||||
public record GiteaWebhook(long Id, string Url, bool Active, string[] Events);
|
||||
|
||||
// ── Request shapes ────────────────────────────────────────────────────────────
|
||||
|
||||
public record CreateBranchRequest(string OpcNumber, string OpcTitle, string From = "master");
|
||||
|
||||
public record CreatePullRequestRequest(
|
||||
string Title,
|
||||
string Head,
|
||||
string Base,
|
||||
string Body
|
||||
);
|
||||
|
||||
public record CreateTagRequest(string TagName, string Message, string CommitSha);
|
||||
|
||||
public record CreateWebhookRequest(string TargetUrl, string[] Events);
|
||||
@@ -0,0 +1,73 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
public record OpcRecord(
|
||||
Guid Id,
|
||||
string Number,
|
||||
string Title,
|
||||
string Description,
|
||||
string Type,
|
||||
string Status,
|
||||
string Priority,
|
||||
string Assignee,
|
||||
DateTime CreatedAt,
|
||||
DateTime UpdatedAt
|
||||
);
|
||||
|
||||
public record OpcNote(
|
||||
Guid Id,
|
||||
Guid OpcId,
|
||||
string Author,
|
||||
string Content,
|
||||
DateTime CreatedAt
|
||||
);
|
||||
|
||||
public record OpcArtifact(
|
||||
Guid Id,
|
||||
Guid OpcId,
|
||||
string ArtifactType,
|
||||
string Title,
|
||||
string Content,
|
||||
DateTime CreatedAt,
|
||||
DateTime UpdatedAt
|
||||
);
|
||||
|
||||
// Request / response shapes used by the API endpoints
|
||||
|
||||
public record CreateOpcRequest(
|
||||
string Title,
|
||||
string Type,
|
||||
string Priority,
|
||||
string Assignee,
|
||||
string Description
|
||||
);
|
||||
|
||||
public record UpdateOpcRequest(
|
||||
string? Title,
|
||||
string? Description,
|
||||
string? Type,
|
||||
string? Status,
|
||||
string? Priority,
|
||||
string? Assignee
|
||||
);
|
||||
|
||||
public record AddNoteRequest(string Author, string Content);
|
||||
|
||||
public record UpsertArtifactRequest(
|
||||
string ArtifactType,
|
||||
string Title,
|
||||
string Content
|
||||
);
|
||||
|
||||
public record AiAssistRequest(string Prompt, string? Context);
|
||||
|
||||
public record OpcPinnedCommit(
|
||||
Guid OpcId,
|
||||
string Hash,
|
||||
string ShortHash,
|
||||
string Subject,
|
||||
string Author,
|
||||
DateTime PinnedAt,
|
||||
string PinnedBy
|
||||
);
|
||||
|
||||
public record PinCommitRequest(string Hash, string PinnedBy);
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
public enum PromotionStatus { Pending, Running, Succeeded, Failed }
|
||||
|
||||
/// <summary>
|
||||
/// Represents a request to promote (merge) one environment branch into the next.
|
||||
/// e.g. develop → staging, staging → uat, uat → main
|
||||
/// </summary>
|
||||
public class PromotionRequest
|
||||
{
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString("N")[..8];
|
||||
public string FromBranch { get; set; } = string.Empty;
|
||||
public string ToBranch { get; set; } = string.Empty;
|
||||
public string RequestedBy { get; set; } = "system";
|
||||
public string? Note { get; set; }
|
||||
public PromotionStatus Status { get; set; } = PromotionStatus.Pending;
|
||||
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? CompletedAt { get; set; }
|
||||
public List<string> Log { get; set; } = [];
|
||||
public int CommitCount { get; set; } // commits in from that are not in to
|
||||
public string[] CommitLines { get; set; } = []; // oneline summary of those commits
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
public enum ProvisioningStatus
|
||||
{
|
||||
Pending,
|
||||
Running,
|
||||
Compensating,
|
||||
Failed,
|
||||
Completed
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CompletedSteps
|
||||
{
|
||||
None = 0,
|
||||
InfrastructureProvisioned = 1 << 0,
|
||||
KeycloakProvisioned = 1 << 1,
|
||||
VaultVerified = 1 << 2,
|
||||
DatabaseMigrated = 1 << 3,
|
||||
HandoffSent = 1 << 4
|
||||
}
|
||||
|
||||
public class ProvisioningJob
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public string ClientName { get; set; } = string.Empty;
|
||||
public string StateCode { get; set; } = string.Empty;
|
||||
public string Subdomain { get; set; } = string.Empty;
|
||||
public string AdminEmail { get; set; } = string.Empty;
|
||||
public string SiteCode { get; set; } = string.Empty;
|
||||
public string Environment { get; set; } = "fdev";
|
||||
|
||||
public TenantTier Tier { get; set; } = TenantTier.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Snapshot of the StackConfig at the time provisioning was requested.
|
||||
/// Immutable after the job is created.
|
||||
/// </summary>
|
||||
public StackConfig StackConfig { get; set; } = StackConfig.DefaultForTier(TenantTier.Shared);
|
||||
|
||||
public ProvisioningStatus Status { get; set; } = ProvisioningStatus.Pending;
|
||||
public CompletedSteps CompletedSteps { get; set; } = CompletedSteps.None;
|
||||
|
||||
public string? FailureReason { get; set; }
|
||||
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? CompletedAt { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
public class ProvisioningRequest
|
||||
{
|
||||
public string ClientName { get; set; } = string.Empty;
|
||||
public string StateCode { get; set; } = string.Empty;
|
||||
public string Subdomain { get; set; } = string.Empty;
|
||||
public string AdminEmail { get; set; } = string.Empty;
|
||||
public string SiteCode { get; set; } = string.Empty;
|
||||
public string Environment { get; set; } = "fdev";
|
||||
public TenantTier Tier { get; set; } = TenantTier.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Per-component infrastructure configuration. Defaults to the standard profile
|
||||
/// for the selected tier if not explicitly specified.
|
||||
/// </summary>
|
||||
public StackConfig StackConfig { get; set; } = StackConfig.DefaultForTier(TenantTier.Shared);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
public enum ReleaseStatus { Running, Succeeded, PartialFailure, Failed }
|
||||
|
||||
/// <summary>
|
||||
/// Persisted record of a release — a coordinated redeploy of all tenant containers
|
||||
/// in a target environment to the latest clarity-server image.
|
||||
/// Stored in ClientAssets/releases.json.
|
||||
/// </summary>
|
||||
public class ReleaseRecord
|
||||
{
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString("N")[..8];
|
||||
public string Environment { get; set; } = string.Empty; // fdev | uat | prod | all
|
||||
public string ImageName { get; set; } = string.Empty;
|
||||
public ReleaseStatus Status { get; set; } = ReleaseStatus.Running;
|
||||
public DateTimeOffset StartedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? FinishedAt { get; set; }
|
||||
public List<TenantReleaseResult> Tenants { get; set; } = [];
|
||||
}
|
||||
|
||||
public class TenantReleaseResult
|
||||
{
|
||||
public string Subdomain { get; set; } = string.Empty;
|
||||
public string ContainerName { get; set; } = string.Empty;
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the exact infrastructure composition for a provisioned tenant.
|
||||
/// Each component is configured independently — the TenantTier gates which
|
||||
/// ComponentMode values are available in the UI.
|
||||
///
|
||||
/// Allowed modes per tier:
|
||||
///
|
||||
/// | Trial | Shared | Dedicated | Enterprise |
|
||||
/// SharedPlatform | ✅ | ✅ | ✅ | ✅ |
|
||||
/// Bundled | ✅ | ❌ | ❌ | ❌ |
|
||||
/// OwnContainer | ❌ | ❌ | ✅ | ✅ |
|
||||
/// VpsDocker | ❌ | ❌ | ❌ | ✅ |
|
||||
/// VpsBareMetal | ❌ | ❌ | ❌ | ✅ |
|
||||
/// </summary>
|
||||
public class StackConfig
|
||||
{
|
||||
public ComponentMode Postgres { get; set; } = ComponentMode.SharedPlatform;
|
||||
public ComponentMode Keycloak { get; set; } = ComponentMode.SharedPlatform;
|
||||
public ComponentMode Vault { get; set; } = ComponentMode.SharedPlatform;
|
||||
public ComponentMode Minio { get; set; } = ComponentMode.SharedPlatform;
|
||||
|
||||
/// <summary>Returns a default StackConfig for the given tier.</summary>
|
||||
public static StackConfig DefaultForTier(TenantTier tier) => tier switch
|
||||
{
|
||||
TenantTier.Trial => new StackConfig
|
||||
{
|
||||
Postgres = ComponentMode.Bundled,
|
||||
Keycloak = ComponentMode.SharedPlatform,
|
||||
Vault = ComponentMode.SharedPlatform,
|
||||
Minio = ComponentMode.SharedPlatform
|
||||
},
|
||||
TenantTier.Shared => new StackConfig(),
|
||||
TenantTier.Dedicated => new StackConfig
|
||||
{
|
||||
Postgres = ComponentMode.OwnContainer,
|
||||
Keycloak = ComponentMode.OwnContainer,
|
||||
Vault = ComponentMode.OwnContainer,
|
||||
Minio = ComponentMode.OwnContainer
|
||||
},
|
||||
TenantTier.Enterprise => new StackConfig
|
||||
{
|
||||
Postgres = ComponentMode.VpsDocker,
|
||||
Keycloak = ComponentMode.VpsDocker,
|
||||
Vault = ComponentMode.VpsDocker,
|
||||
Minio = ComponentMode.VpsDocker
|
||||
},
|
||||
_ => new StackConfig()
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
[XmlRoot("Tenant")]
|
||||
public class TenantRecord
|
||||
{
|
||||
// ── Identity ──────────────────────────────────────────────────────────
|
||||
[XmlAttribute]
|
||||
public string Subdomain { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement]
|
||||
public string ClientName { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement]
|
||||
public string StateCode { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement]
|
||||
public string AdminEmail { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement]
|
||||
public string SiteCode { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement]
|
||||
public string Environment { get; set; } = "fdev";
|
||||
|
||||
[XmlElement]
|
||||
public string Tier { get; set; } = string.Empty;
|
||||
|
||||
[XmlElement]
|
||||
public string Status { get; set; } = "Provisioning";
|
||||
|
||||
[XmlElement]
|
||||
public string ProvisionedAt { get; set; } = DateTimeOffset.UtcNow.ToString("o");
|
||||
|
||||
[XmlElement]
|
||||
public string JobId { get; set; } = string.Empty;
|
||||
|
||||
// ── Container (written by InfrastructureStep / LaunchStep) ────────────
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? ContainerName { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? ContainerPort { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? ContainerImage { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? ContainerNetwork { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? NginxConfPath { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? ApiBaseUrl { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? PublicUrl { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? LastProvisioningStep { get; set; }
|
||||
|
||||
[XmlElement(IsNullable = true)]
|
||||
public string? ProvisioningNotes { get; set; }
|
||||
|
||||
// ── web.config-style sections ─────────────────────────────────────────
|
||||
|
||||
[XmlElement("ConnectionStrings")]
|
||||
public ConnectionStringsSection ConnectionStrings { get; set; } = new();
|
||||
|
||||
[XmlElement("AppSettings")]
|
||||
public AppSettingsSection AppSettings { get; set; } = new();
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
public void SetConnectionString(string name, string connectionString)
|
||||
{
|
||||
var existing = ConnectionStrings.Entries.FirstOrDefault(e => e.Name == name);
|
||||
if (existing is not null)
|
||||
existing.ConnectionString = connectionString;
|
||||
else
|
||||
ConnectionStrings.Entries.Add(new ConnectionStringEntry { Name = name, ConnectionString = connectionString });
|
||||
}
|
||||
|
||||
public string? GetConnectionString(string name) =>
|
||||
ConnectionStrings.Entries.FirstOrDefault(e => e.Name == name)?.ConnectionString;
|
||||
|
||||
public void SetAppSetting(string key, string value)
|
||||
{
|
||||
var existing = AppSettings.Entries.FirstOrDefault(e => e.Key == key);
|
||||
if (existing is not null)
|
||||
existing.Value = value;
|
||||
else
|
||||
AppSettings.Entries.Add(new AppSettingEntry { Key = key, Value = value });
|
||||
}
|
||||
|
||||
public string? GetAppSetting(string key) =>
|
||||
AppSettings.Entries.FirstOrDefault(e => e.Key == key)?.Value;
|
||||
}
|
||||
|
||||
// ── Section types ──────────────────────────────────────────────────────────
|
||||
|
||||
public class ConnectionStringsSection
|
||||
{
|
||||
[XmlElement("add")]
|
||||
public List<ConnectionStringEntry> Entries { get; set; } = [];
|
||||
}
|
||||
|
||||
public class AppSettingsSection
|
||||
{
|
||||
[XmlElement("add")]
|
||||
public List<AppSettingEntry> Entries { get; set; } = [];
|
||||
}
|
||||
|
||||
public class ConnectionStringEntry
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[XmlAttribute("connectionString")]
|
||||
public string ConnectionString { get; set; } = string.Empty;
|
||||
|
||||
[XmlAttribute("providerName")]
|
||||
public string ProviderName { get; set; } = "System.Data.SqlClient";
|
||||
}
|
||||
|
||||
public class AppSettingEntry
|
||||
{
|
||||
[XmlAttribute("key")]
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
[XmlAttribute("value")]
|
||||
public string Value { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ControlPlane.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the billing and support level for a provisioned tenant.
|
||||
/// The tier gates which ComponentMode values are available per component in the StackConfig.
|
||||
///
|
||||
/// Trial - ephemeral sandbox, all-in-one image, no persistent data guarantee.
|
||||
/// Shared - real production data, shared platform infrastructure (logical slices only).
|
||||
/// Dedicated - full container isolation per component, still on ControlPlane's shared host.
|
||||
/// Enterprise - full VM isolation per component (VpsDocker or VpsBareMetal), Pulumi provisioned.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum TenantTier
|
||||
{
|
||||
Trial,
|
||||
Shared,
|
||||
Dedicated,
|
||||
Enterprise
|
||||
}
|
||||
Reference in New Issue
Block a user