Frontend gaps fill work. Testing fixes work. Auditing in progress.

This commit is contained in:
StellaOps Bot
2025-12-30 01:22:58 +02:00
parent 1dc4bcbf10
commit 7a5210e2aa
928 changed files with 183942 additions and 3941 deletions

View File

@@ -0,0 +1,27 @@
using StellaOps.Integrations.Core;
namespace StellaOps.Integrations.WebService;
/// <summary>
/// Publishes integration lifecycle events to downstream consumers.
/// </summary>
public interface IIntegrationEventPublisher
{
Task PublishAsync(IntegrationEvent @event, CancellationToken cancellationToken = default);
}
/// <summary>
/// Logs integration audit events.
/// </summary>
public interface IIntegrationAuditLogger
{
Task LogAsync(string action, Guid integrationId, string? userId, object? details, CancellationToken cancellationToken = default);
}
/// <summary>
/// Resolves AuthRef URIs to secret values.
/// </summary>
public interface IAuthRefResolver
{
Task<string?> ResolveAsync(string authRefUri, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,73 @@
using System.Text.Json;
using Microsoft.Extensions.Logging;
using StellaOps.Integrations.Core;
namespace StellaOps.Integrations.WebService.Infrastructure;
/// <summary>
/// Console/log-based event publisher for development and standalone deployments.
/// In production, replace with message queue implementation.
/// </summary>
public sealed class LoggingEventPublisher : IIntegrationEventPublisher
{
private readonly ILogger<LoggingEventPublisher> _logger;
public LoggingEventPublisher(ILogger<LoggingEventPublisher> logger)
{
_logger = logger;
}
public Task PublishAsync(IntegrationEvent @event, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Integration event: {EventType} - {EventJson}",
@event.GetType().Name,
JsonSerializer.Serialize(@event, @event.GetType()));
return Task.CompletedTask;
}
}
/// <summary>
/// Console/log-based audit logger for development and standalone deployments.
/// In production, replace with proper audit store.
/// </summary>
public sealed class LoggingAuditLogger : IIntegrationAuditLogger
{
private readonly ILogger<LoggingAuditLogger> _logger;
public LoggingAuditLogger(ILogger<LoggingAuditLogger> logger)
{
_logger = logger;
}
public Task LogAsync(string action, Guid integrationId, string? userId, object? details, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Audit: {Action} on {IntegrationId} by {UserId} - {Details}",
action,
integrationId,
userId ?? "system",
details is not null ? JsonSerializer.Serialize(details) : "{}");
return Task.CompletedTask;
}
}
/// <summary>
/// Stub AuthRef resolver for development.
/// In production, integrate with Authority service.
/// </summary>
public sealed class StubAuthRefResolver : IAuthRefResolver
{
private readonly ILogger<StubAuthRefResolver> _logger;
public StubAuthRefResolver(ILogger<StubAuthRefResolver> logger)
{
_logger = logger;
}
public Task<string?> ResolveAsync(string authRefUri, CancellationToken cancellationToken = default)
{
_logger.LogWarning("StubAuthRefResolver: Would resolve {AuthRefUri} - returning null in dev mode", authRefUri);
return Task.FromResult<string?>(null);
}
}

View File

@@ -0,0 +1,120 @@
using Microsoft.AspNetCore.Mvc;
using StellaOps.Integrations.Contracts;
using StellaOps.Integrations.Core;
namespace StellaOps.Integrations.WebService;
/// <summary>
/// Minimal API endpoints for the Integration Catalog.
/// </summary>
public static class IntegrationEndpoints
{
public static void MapIntegrationEndpoints(this WebApplication app)
{
var group = app.MapGroup("/api/v1/integrations")
.WithTags("Integrations")
.WithOpenApi();
// List integrations
group.MapGet("/", async (
[FromServices] IntegrationService service,
[FromQuery] IntegrationType? type,
[FromQuery] IntegrationProvider? provider,
[FromQuery] IntegrationStatus? status,
[FromQuery] string? search,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] string sortBy = "name",
[FromQuery] bool sortDescending = false,
CancellationToken cancellationToken = default) =>
{
var query = new ListIntegrationsQuery(type, provider, status, search, null, page, pageSize, sortBy, sortDescending);
var result = await service.ListAsync(query, null, cancellationToken);
return Results.Ok(result);
})
.WithName("ListIntegrations")
.WithDescription("Lists integrations with optional filtering and pagination.");
// Get integration by ID
group.MapGet("/{id:guid}", async (
[FromServices] IntegrationService service,
Guid id,
CancellationToken cancellationToken) =>
{
var result = await service.GetByIdAsync(id, cancellationToken);
return result is null ? Results.NotFound() : Results.Ok(result);
})
.WithName("GetIntegration")
.WithDescription("Gets an integration by ID.");
// Create integration
group.MapPost("/", async (
[FromServices] IntegrationService service,
[FromBody] CreateIntegrationRequest request,
CancellationToken cancellationToken) =>
{
var result = await service.CreateAsync(request, null, null, cancellationToken);
return Results.Created($"/api/v1/integrations/{result.Id}", result);
})
.WithName("CreateIntegration")
.WithDescription("Creates a new integration.");
// Update integration
group.MapPut("/{id:guid}", async (
[FromServices] IntegrationService service,
Guid id,
[FromBody] UpdateIntegrationRequest request,
CancellationToken cancellationToken) =>
{
var result = await service.UpdateAsync(id, request, null, cancellationToken);
return result is null ? Results.NotFound() : Results.Ok(result);
})
.WithName("UpdateIntegration")
.WithDescription("Updates an existing integration.");
// Delete integration
group.MapDelete("/{id:guid}", async (
[FromServices] IntegrationService service,
Guid id,
CancellationToken cancellationToken) =>
{
var result = await service.DeleteAsync(id, null, cancellationToken);
return result ? Results.NoContent() : Results.NotFound();
})
.WithName("DeleteIntegration")
.WithDescription("Soft-deletes an integration.");
// Test connection
group.MapPost("/{id:guid}/test", async (
[FromServices] IntegrationService service,
Guid id,
CancellationToken cancellationToken) =>
{
var result = await service.TestConnectionAsync(id, null, cancellationToken);
return result is null ? Results.NotFound() : Results.Ok(result);
})
.WithName("TestIntegrationConnection")
.WithDescription("Tests connectivity and authentication for an integration.");
// Health check
group.MapGet("/{id:guid}/health", async (
[FromServices] IntegrationService service,
Guid id,
CancellationToken cancellationToken) =>
{
var result = await service.CheckHealthAsync(id, cancellationToken);
return result is null ? Results.NotFound() : Results.Ok(result);
})
.WithName("CheckIntegrationHealth")
.WithDescription("Performs a health check on an integration.");
// Get supported providers
group.MapGet("/providers", ([FromServices] IntegrationService service) =>
{
var result = service.GetSupportedProviders();
return Results.Ok(result);
})
.WithName("GetSupportedProviders")
.WithDescription("Gets a list of supported integration providers.");
}
}

View File

@@ -0,0 +1,105 @@
using System.Reflection;
using Microsoft.Extensions.Logging;
using StellaOps.Integrations.Contracts;
using StellaOps.Integrations.Core;
using StellaOps.Plugin;
using StellaOps.Plugin.Hosting;
namespace StellaOps.Integrations.WebService;
/// <summary>
/// Loads and manages integration connector plugins.
/// </summary>
public sealed class IntegrationPluginLoader
{
private readonly ILogger<IntegrationPluginLoader>? _logger;
private readonly List<IIntegrationConnectorPlugin> _plugins = [];
public IntegrationPluginLoader(ILogger<IntegrationPluginLoader>? logger = null)
{
_logger = logger;
}
/// <summary>
/// Gets all loaded plugins.
/// </summary>
public IReadOnlyList<IIntegrationConnectorPlugin> Plugins => _plugins;
/// <summary>
/// Discovers and loads integration connector plugins from the specified directory.
/// </summary>
public IReadOnlyList<IIntegrationConnectorPlugin> LoadFromDirectory(
string pluginDirectory,
string searchPattern = "StellaOps.Integrations.Plugin.*.dll")
{
if (!Directory.Exists(pluginDirectory))
{
_logger?.LogWarning("Plugin directory does not exist: {Directory}", pluginDirectory);
return [];
}
var options = new PluginHostOptions
{
PluginsDirectory = pluginDirectory,
EnsureDirectoryExists = false,
RecursiveSearch = false
};
options.SearchPatterns.Add(searchPattern);
var result = PluginHost.LoadPlugins(options);
var loadedPlugins = new List<IIntegrationConnectorPlugin>();
foreach (var pluginAssembly in result.Plugins)
{
var connectorPlugins = PluginLoader.LoadPlugins<IIntegrationConnectorPlugin>(new[] { pluginAssembly.Assembly });
loadedPlugins.AddRange(connectorPlugins);
foreach (var plugin in connectorPlugins)
{
_logger?.LogDebug("Loaded integration connector plugin: {Name} ({Provider}) from {Assembly}",
plugin.Name, plugin.Provider, pluginAssembly.Assembly.GetName().Name);
}
}
_plugins.AddRange(loadedPlugins);
return loadedPlugins;
}
/// <summary>
/// Loads integration connector plugins from the specified assemblies.
/// </summary>
public IReadOnlyList<IIntegrationConnectorPlugin> LoadFromAssemblies(IEnumerable<Assembly> assemblies)
{
var loadedPlugins = PluginLoader.LoadPlugins<IIntegrationConnectorPlugin>(assemblies);
_plugins.AddRange(loadedPlugins);
return loadedPlugins;
}
/// <summary>
/// Gets a plugin by provider.
/// </summary>
public IIntegrationConnectorPlugin? GetByProvider(IntegrationProvider provider)
{
return _plugins.FirstOrDefault(p => p.Provider == provider);
}
/// <summary>
/// Gets all plugins for a given type.
/// </summary>
public IReadOnlyList<IIntegrationConnectorPlugin> GetByType(IntegrationType type)
{
return _plugins.Where(p => p.Type == type).ToList();
}
/// <summary>
/// Gets all available plugins (checking IsAvailable).
/// </summary>
public IReadOnlyList<IIntegrationConnectorPlugin> GetAvailable(IServiceProvider services)
{
return _plugins.Where(p =>
{
try { return p.IsAvailable(services); }
catch { return false; }
}).ToList();
}
}

View File

@@ -0,0 +1,316 @@
using System.Text.Json;
using Microsoft.Extensions.Logging;
using StellaOps.Integrations.Contracts;
using StellaOps.Integrations.Core;
using StellaOps.Integrations.Persistence;
namespace StellaOps.Integrations.WebService;
/// <summary>
/// Core service for integration catalog operations.
/// </summary>
public sealed class IntegrationService
{
private readonly IIntegrationRepository _repository;
private readonly IntegrationPluginLoader _pluginLoader;
private readonly IIntegrationEventPublisher _eventPublisher;
private readonly IIntegrationAuditLogger _auditLogger;
private readonly IAuthRefResolver _authRefResolver;
private readonly ILogger<IntegrationService> _logger;
public IntegrationService(
IIntegrationRepository repository,
IntegrationPluginLoader pluginLoader,
IIntegrationEventPublisher eventPublisher,
IIntegrationAuditLogger auditLogger,
IAuthRefResolver authRefResolver,
ILogger<IntegrationService> logger)
{
_repository = repository;
_pluginLoader = pluginLoader;
_eventPublisher = eventPublisher;
_auditLogger = auditLogger;
_authRefResolver = authRefResolver;
_logger = logger;
}
public async Task<IntegrationResponse> CreateAsync(CreateIntegrationRequest request, string? userId, string? tenantId, CancellationToken cancellationToken = default)
{
var integration = new Integration
{
Id = Guid.NewGuid(),
Name = request.Name,
Description = request.Description,
Type = request.Type,
Provider = request.Provider,
Status = IntegrationStatus.Pending,
Endpoint = request.Endpoint,
AuthRefUri = request.AuthRefUri,
OrganizationId = request.OrganizationId,
ConfigJson = request.ExtendedConfig is not null ? JsonSerializer.Serialize(request.ExtendedConfig) : null,
Tags = request.Tags?.ToList() ?? [],
CreatedBy = userId,
UpdatedBy = userId,
TenantId = tenantId
};
var created = await _repository.CreateAsync(integration, cancellationToken);
await _eventPublisher.PublishAsync(new IntegrationCreatedEvent(
created.Id,
created.Name,
created.Type,
created.Provider,
userId,
DateTimeOffset.UtcNow), cancellationToken);
await _auditLogger.LogAsync("integration.created", created.Id, userId, new { created.Name, created.Type, created.Provider }, cancellationToken);
_logger.LogInformation("Integration created: {Id} ({Name}) by {User}", created.Id, created.Name, userId);
return MapToResponse(created);
}
public async Task<IntegrationResponse?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
{
var integration = await _repository.GetByIdAsync(id, cancellationToken);
return integration is null ? null : MapToResponse(integration);
}
public async Task<PagedIntegrationsResponse> ListAsync(ListIntegrationsQuery query, string? tenantId, CancellationToken cancellationToken = default)
{
var repoQuery = new IntegrationQuery(
Type: query.Type,
Provider: query.Provider,
Status: query.Status,
Search: query.Search,
Tags: query.Tags,
TenantId: tenantId,
Skip: (query.Page - 1) * query.PageSize,
Take: query.PageSize,
SortBy: query.SortBy,
SortDescending: query.SortDescending);
var integrations = await _repository.GetAllAsync(repoQuery, cancellationToken);
var totalCount = await _repository.CountAsync(repoQuery, cancellationToken);
var totalPages = (int)Math.Ceiling(totalCount / (double)query.PageSize);
return new PagedIntegrationsResponse(
integrations.Select(MapToResponse).ToList(),
totalCount,
query.Page,
query.PageSize,
totalPages);
}
public async Task<IntegrationResponse?> UpdateAsync(Guid id, UpdateIntegrationRequest request, string? userId, CancellationToken cancellationToken = default)
{
var integration = await _repository.GetByIdAsync(id, cancellationToken);
if (integration is null) return null;
var oldStatus = integration.Status;
if (request.Name is not null) integration.Name = request.Name;
if (request.Description is not null) integration.Description = request.Description;
if (request.Endpoint is not null) integration.Endpoint = request.Endpoint;
if (request.AuthRefUri is not null) integration.AuthRefUri = request.AuthRefUri;
if (request.OrganizationId is not null) integration.OrganizationId = request.OrganizationId;
if (request.ExtendedConfig is not null) integration.ConfigJson = JsonSerializer.Serialize(request.ExtendedConfig);
if (request.Tags is not null) integration.Tags = request.Tags.ToList();
if (request.Status.HasValue) integration.Status = request.Status.Value;
integration.UpdatedAt = DateTimeOffset.UtcNow;
integration.UpdatedBy = userId;
var updated = await _repository.UpdateAsync(integration, cancellationToken);
await _eventPublisher.PublishAsync(new IntegrationUpdatedEvent(
updated.Id,
updated.Name,
userId,
DateTimeOffset.UtcNow), cancellationToken);
if (oldStatus != updated.Status)
{
await _eventPublisher.PublishAsync(new IntegrationStatusChangedEvent(
updated.Id,
oldStatus,
updated.Status,
DateTimeOffset.UtcNow), cancellationToken);
}
await _auditLogger.LogAsync("integration.updated", updated.Id, userId, new { updated.Name, OldStatus = oldStatus, NewStatus = updated.Status }, cancellationToken);
_logger.LogInformation("Integration updated: {Id} ({Name}) by {User}", updated.Id, updated.Name, userId);
return MapToResponse(updated);
}
public async Task<bool> DeleteAsync(Guid id, string? userId, CancellationToken cancellationToken = default)
{
var integration = await _repository.GetByIdAsync(id, cancellationToken);
if (integration is null) return false;
await _repository.DeleteAsync(id, cancellationToken);
await _eventPublisher.PublishAsync(new IntegrationDeletedEvent(
id,
userId,
DateTimeOffset.UtcNow), cancellationToken);
await _auditLogger.LogAsync("integration.deleted", id, userId, new { integration.Name }, cancellationToken);
_logger.LogInformation("Integration deleted: {Id} ({Name}) by {User}", id, integration.Name, userId);
return true;
}
public async Task<TestConnectionResponse?> TestConnectionAsync(Guid id, string? userId, CancellationToken cancellationToken = default)
{
var integration = await _repository.GetByIdAsync(id, cancellationToken);
if (integration is null) return null;
var plugin = _pluginLoader.GetByProvider(integration.Provider);
if (plugin is null)
{
_logger.LogWarning("No plugin found for provider {Provider}", integration.Provider);
return new TestConnectionResponse(
id,
false,
$"No connector plugin available for provider {integration.Provider}",
null,
TimeSpan.Zero,
DateTimeOffset.UtcNow);
}
var resolvedSecret = integration.AuthRefUri is not null
? await _authRefResolver.ResolveAsync(integration.AuthRefUri, cancellationToken)
: null;
var config = BuildConfig(integration, resolvedSecret);
var startTime = DateTimeOffset.UtcNow;
var result = await plugin.TestConnectionAsync(config, cancellationToken);
var endTime = DateTimeOffset.UtcNow;
// Update integration status based on result
var newStatus = result.Success ? IntegrationStatus.Active : IntegrationStatus.Failed;
if (integration.Status != newStatus)
{
var oldStatus = integration.Status;
integration.Status = newStatus;
integration.UpdatedAt = endTime;
await _repository.UpdateAsync(integration, cancellationToken);
await _eventPublisher.PublishAsync(new IntegrationStatusChangedEvent(
id, oldStatus, newStatus, endTime), cancellationToken);
}
await _eventPublisher.PublishAsync(new IntegrationTestConnectionEvent(
id, result.Success, result.Message, endTime), cancellationToken);
await _auditLogger.LogAsync("integration.test_connection", id, userId, new { result.Success, result.Message }, cancellationToken);
return new TestConnectionResponse(
id,
result.Success,
result.Message,
result.Details,
result.Duration,
endTime);
}
public async Task<HealthCheckResponse?> CheckHealthAsync(Guid id, CancellationToken cancellationToken = default)
{
var integration = await _repository.GetByIdAsync(id, cancellationToken);
if (integration is null) return null;
var plugin = _pluginLoader.GetByProvider(integration.Provider);
if (plugin is null)
{
return new HealthCheckResponse(
id,
HealthStatus.Unknown,
$"No connector plugin available for provider {integration.Provider}",
null,
DateTimeOffset.UtcNow,
TimeSpan.Zero);
}
var resolvedSecret = integration.AuthRefUri is not null
? await _authRefResolver.ResolveAsync(integration.AuthRefUri, cancellationToken)
: null;
var config = BuildConfig(integration, resolvedSecret);
var result = await plugin.CheckHealthAsync(config, cancellationToken);
var oldHealth = integration.LastHealthStatus;
if (oldHealth != result.Status)
{
await _repository.UpdateHealthStatusAsync(id, result.Status, result.CheckedAt, cancellationToken);
await _eventPublisher.PublishAsync(new IntegrationHealthChangedEvent(
id, oldHealth, result.Status, result.CheckedAt), cancellationToken);
}
return new HealthCheckResponse(
id,
result.Status,
result.Message,
result.Details,
result.CheckedAt,
result.Duration);
}
public IReadOnlyList<ProviderInfo> GetSupportedProviders()
{
return _pluginLoader.Plugins.Select(p => new ProviderInfo(
p.Name,
p.Type,
p.Provider)).ToList();
}
private static IntegrationConfig BuildConfig(Integration integration, string? resolvedSecret)
{
IReadOnlyDictionary<string, object>? extendedConfig = null;
if (!string.IsNullOrEmpty(integration.ConfigJson))
{
extendedConfig = JsonSerializer.Deserialize<Dictionary<string, object>>(integration.ConfigJson);
}
return new IntegrationConfig(
integration.Id,
integration.Type,
integration.Provider,
integration.Endpoint,
resolvedSecret,
integration.OrganizationId,
extendedConfig);
}
private static IntegrationResponse MapToResponse(Integration integration)
{
return new IntegrationResponse(
integration.Id,
integration.Name,
integration.Description,
integration.Type,
integration.Provider,
integration.Status,
integration.Endpoint,
integration.AuthRefUri is not null,
integration.OrganizationId,
integration.LastHealthStatus,
integration.LastHealthCheckAt,
integration.CreatedAt,
integration.UpdatedAt,
integration.CreatedBy,
integration.UpdatedBy,
integration.Tags);
}
}
/// <summary>
/// Information about a supported provider.
/// </summary>
public sealed record ProviderInfo(string Name, IntegrationType Type, IntegrationProvider Provider);

View File

@@ -0,0 +1,94 @@
using Microsoft.EntityFrameworkCore;
using StellaOps.Integrations.Persistence;
using StellaOps.Integrations.WebService;
using StellaOps.Integrations.WebService.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new() { Title = "StellaOps Integration Catalog API", Version = "v1" });
});
// Database
var connectionString = builder.Configuration.GetConnectionString("IntegrationsDb")
?? "Host=localhost;Database=stellaops_integrations;Username=postgres;Password=postgres";
builder.Services.AddDbContext<IntegrationDbContext>(options =>
options.UseNpgsql(connectionString));
// Repository
builder.Services.AddScoped<IIntegrationRepository, PostgresIntegrationRepository>();
// Plugin loader
builder.Services.AddSingleton<IntegrationPluginLoader>(sp =>
{
var logger = sp.GetRequiredService<ILogger<IntegrationPluginLoader>>();
var loader = new IntegrationPluginLoader(logger);
// Load from plugins directory
var pluginsDir = builder.Configuration.GetValue<string>("Integrations:PluginsDirectory")
?? Path.Combine(AppContext.BaseDirectory, "plugins");
if (Directory.Exists(pluginsDir))
{
loader.LoadFromDirectory(pluginsDir);
}
// Also load from current assembly (for built-in plugins)
loader.LoadFromAssemblies([typeof(Program).Assembly]);
return loader;
});
// Infrastructure
builder.Services.AddScoped<IIntegrationEventPublisher, LoggingEventPublisher>();
builder.Services.AddScoped<IIntegrationAuditLogger, LoggingAuditLogger>();
builder.Services.AddScoped<IAuthRefResolver, StubAuthRefResolver>();
// Core service
builder.Services.AddScoped<IntegrationService>();
// CORS for Angular dev server
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("http://localhost:4200", "https://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
// Configure pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors();
// Map endpoints
app.MapIntegrationEndpoints();
// Health endpoint
app.MapGet("/health", () => Results.Ok(new { Status = "Healthy", Timestamp = DateTimeOffset.UtcNow }))
.WithTags("Health")
.WithName("HealthCheck");
// Ensure database is created (dev only)
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<IntegrationDbContext>();
await dbContext.Database.EnsureCreatedAsync();
}
app.Run();
public partial class Program { }

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<RootNamespace>StellaOps.Integrations.WebService</RootNamespace>
<AssemblyName>StellaOps.Integrations.WebService</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\__Libraries\StellaOps.Integrations.Core\StellaOps.Integrations.Core.csproj" />
<ProjectReference Include="..\__Libraries\StellaOps.Integrations.Contracts\StellaOps.Integrations.Contracts.csproj" />
<ProjectReference Include="..\__Libraries\StellaOps.Integrations.Persistence\StellaOps.Integrations.Persistence.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Plugin\StellaOps.Plugin.csproj" />
<ProjectReference Include="..\..\Router\__Libraries\StellaOps.Messaging\StellaOps.Messaging.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.AspNetCore": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"ConnectionStrings": {
"IntegrationsDb": "Host=localhost;Database=stellaops_integrations_dev;Username=postgres;Password=postgres"
},
"Integrations": {
"PluginsDirectory": "plugins"
}
}

View File

@@ -0,0 +1,16 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"IntegrationsDb": "Host=localhost;Database=stellaops_integrations;Username=postgres;Password=postgres"
},
"Integrations": {
"PluginsDirectory": "plugins"
}
}