♻️ Extract the Developer to new service, add PublisherServiceGrpc
This commit is contained in:
27
DysonNetwork.Develop/AppDatabase.cs
Normal file
27
DysonNetwork.Develop/AppDatabase.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Develop;
|
||||||
|
|
||||||
|
public class AppDatabase(
|
||||||
|
DbContextOptions<AppDatabase> options,
|
||||||
|
IConfiguration configuration
|
||||||
|
) : DbContext(options)
|
||||||
|
{
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
optionsBuilder.UseNpgsql(
|
||||||
|
configuration.GetConnectionString("App"),
|
||||||
|
opt => opt
|
||||||
|
.ConfigureDataSource(optSource => optSource.EnableDynamicJson())
|
||||||
|
.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)
|
||||||
|
.UseNodaTime()
|
||||||
|
).UseSnakeCaseNamingConvention();
|
||||||
|
|
||||||
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
}
|
||||||
|
}
|
23
DysonNetwork.Develop/Dockerfile
Normal file
23
DysonNetwork.Develop/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||||
|
USER $APP_UID
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["DysonNetwork.Develop/DysonNetwork.Develop.csproj", "DysonNetwork.Develop/"]
|
||||||
|
RUN dotnet restore "DysonNetwork.Develop/DysonNetwork.Develop.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/DysonNetwork.Develop"
|
||||||
|
RUN dotnet build "./DysonNetwork.Develop.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./DysonNetwork.Develop.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "DysonNetwork.Develop.dll"]
|
37
DysonNetwork.Develop/DysonNetwork.Develop.csproj
Normal file
37
DysonNetwork.Develop/DysonNetwork.Develop.csproj
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.7"/>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="NodaTime.Serialization.Protobuf" Version="2.0.2" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4"/>
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.4" />
|
||||||
|
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1"/>
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3"/>
|
||||||
|
<PackageReference Include="NodaTime" Version="3.2.2"/>
|
||||||
|
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.3.0"/>
|
||||||
|
<PackageReference Include="Grpc.AspNetCore.Server" Version="2.71.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\.dockerignore">
|
||||||
|
<Link>.dockerignore</Link>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DysonNetwork.Shared\DysonNetwork.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@@ -4,7 +4,7 @@ using System.Text.Json.Serialization;
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Developer;
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
public enum CustomAppStatus
|
public enum CustomAppStatus
|
||||||
{
|
{
|
@@ -1,10 +1,9 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using DysonNetwork.Sphere.Publisher;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Developer;
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/api/developers/{pubName}/apps")]
|
[Route("/api/developers/{pubName}/apps")]
|
@@ -1,8 +1,7 @@
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Developer;
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
public class CustomAppService(
|
public class CustomAppService(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
@@ -1,18 +1,16 @@
|
|||||||
using DysonNetwork.Shared.Auth;
|
using DysonNetwork.Shared.Auth;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using DysonNetwork.Sphere.Publisher;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Developer;
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/api/developers")]
|
[Route("/api/developers")]
|
||||||
public class DeveloperController(
|
public class DeveloperController(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
PublisherService ps,
|
PublisherService.PublisherServiceClient ps,
|
||||||
ActionLogService.ActionLogServiceClient als
|
ActionLogService.ActionLogServiceClient als
|
||||||
)
|
)
|
||||||
: ControllerBase
|
: ControllerBase
|
29
DysonNetwork.Develop/Program.cs
Normal file
29
DysonNetwork.Develop/Program.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using DysonNetwork.Develop;
|
||||||
|
using DysonNetwork.Shared.Auth;
|
||||||
|
using DysonNetwork.Shared.Http;
|
||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
|
using DysonNetwork.Develop.Startup;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.ConfigureAppKestrel(builder.Configuration);
|
||||||
|
|
||||||
|
builder.Services.AddRegistryService(builder.Configuration);
|
||||||
|
builder.Services.AddAppServices(builder.Configuration);
|
||||||
|
builder.Services.AddAppAuthentication();
|
||||||
|
builder.Services.AddAppSwagger();
|
||||||
|
builder.Services.AddDysonAuth();
|
||||||
|
builder.Services.AddPublisherService();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<AppDatabase>();
|
||||||
|
await db.Database.MigrateAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.ConfigureAppMiddleware(builder.Configuration);
|
||||||
|
|
||||||
|
app.Run();
|
23
DysonNetwork.Develop/Properties/launchSettings.json
Normal file
23
DysonNetwork.Develop/Properties/launchSettings.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": false,
|
||||||
|
"applicationUrl": "http://localhost:5156",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": false,
|
||||||
|
"applicationUrl": "https://localhost:7192;http://localhost:5156",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
DysonNetwork.Develop/Startup/ApplicationConfiguration.cs
Normal file
52
DysonNetwork.Develop/Startup/ApplicationConfiguration.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Net;
|
||||||
|
using DysonNetwork.Shared.Auth;
|
||||||
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using Prometheus;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Develop.Startup;
|
||||||
|
|
||||||
|
public static class ApplicationConfiguration
|
||||||
|
{
|
||||||
|
public static WebApplication ConfigureAppMiddleware(this WebApplication app, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
app.MapMetrics();
|
||||||
|
app.MapOpenApi();
|
||||||
|
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
|
app.UseRequestLocalization();
|
||||||
|
|
||||||
|
ConfigureForwardedHeaders(app, configuration);
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseMiddleware<PermissionMiddleware>();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureForwardedHeaders(WebApplication app, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var knownProxiesSection = configuration.GetSection("KnownProxies");
|
||||||
|
var forwardedHeadersOptions = new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.All };
|
||||||
|
|
||||||
|
if (knownProxiesSection.Exists())
|
||||||
|
{
|
||||||
|
var proxyAddresses = knownProxiesSection.Get<string[]>();
|
||||||
|
if (proxyAddresses != null)
|
||||||
|
foreach (var proxy in proxyAddresses)
|
||||||
|
if (IPAddress.TryParse(proxy, out var ipAddress))
|
||||||
|
forwardedHeadersOptions.KnownProxies.Add(ipAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
forwardedHeadersOptions.KnownProxies.Add(IPAddress.Any);
|
||||||
|
forwardedHeadersOptions.KnownProxies.Add(IPAddress.IPv6Any);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseForwardedHeaders(forwardedHeadersOptions);
|
||||||
|
}
|
||||||
|
}
|
68
DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs
Normal file
68
DysonNetwork.Develop/Startup/ServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.SystemTextJson;
|
||||||
|
using System.Text.Json;
|
||||||
|
using DysonNetwork.Shared.Cache;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Develop.Startup;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddAppServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddLocalization();
|
||||||
|
|
||||||
|
services.AddDbContext<AppDatabase>();
|
||||||
|
services.AddSingleton<IClock>(SystemClock.Instance);
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
services.AddSingleton<ICacheService, CacheServiceRedis>();
|
||||||
|
|
||||||
|
services.AddHttpClient();
|
||||||
|
|
||||||
|
services.AddControllers().AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower;
|
||||||
|
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower;
|
||||||
|
options.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddGrpc(options => { options.EnableDetailedErrors = true; });
|
||||||
|
|
||||||
|
services.Configure<RequestLocalizationOptions>(options =>
|
||||||
|
{
|
||||||
|
var supportedCultures = new[]
|
||||||
|
{
|
||||||
|
new CultureInfo("en-US"),
|
||||||
|
new CultureInfo("zh-Hans"),
|
||||||
|
};
|
||||||
|
|
||||||
|
options.SupportedCultures = supportedCultures;
|
||||||
|
options.SupportedUICultures = supportedCultures;
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAppAuthentication(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddCors();
|
||||||
|
services.AddAuthorization();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddAppSwagger(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddEndpointsApiExplorer();
|
||||||
|
services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.SwaggerDoc("v1", new OpenApiInfo
|
||||||
|
{
|
||||||
|
Version = "v1",
|
||||||
|
Title = "Develop API",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
services.AddOpenApi();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
30
DysonNetwork.Develop/appsettings.json
Normal file
30
DysonNetwork.Develop/appsettings.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"Debug": true,
|
||||||
|
"BaseUrl": "http://localhost:5071",
|
||||||
|
"SiteUrl": "https://solian.app",
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"App": "Host=localhost;Port=5432;Database=dyson_network_dev;Username=postgres;Password=postgres;Include Error Detail=True;Maximum Pool Size=20;Connection Idle Lifetime=60",
|
||||||
|
"FastRetrieve": "localhost:6379",
|
||||||
|
"Etcd": "etcd.orb.local:2379"
|
||||||
|
},
|
||||||
|
"KnownProxies": [
|
||||||
|
"127.0.0.1",
|
||||||
|
"::1"
|
||||||
|
],
|
||||||
|
"Etcd": {
|
||||||
|
"Insecure": true
|
||||||
|
},
|
||||||
|
"Service": {
|
||||||
|
"Name": "DysonNetwork.Develop",
|
||||||
|
"Url": "https://localhost:7099",
|
||||||
|
"ClientCert": "../Certificates/client.crt",
|
||||||
|
"ClientKey": "../Certificates/client.key"
|
||||||
|
}
|
||||||
|
}
|
@@ -123,4 +123,16 @@ public static class GrpcClientHelper
|
|||||||
return new FileReferenceService.FileReferenceServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
return new FileReferenceService.FileReferenceServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||||
clientCertPassword));
|
clientCertPassword));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static async Task<PublisherService.PublisherServiceClient> CreatePublisherServiceClient(
|
||||||
|
IEtcdClient etcdClient,
|
||||||
|
string clientCertPath,
|
||||||
|
string clientKeyPath,
|
||||||
|
string? clientCertPassword = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Sphere");
|
||||||
|
return new PublisherService.PublisherServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||||
|
clientCertPassword));
|
||||||
|
}
|
||||||
|
}
|
108
DysonNetwork.Shared/Proto/publisher.proto
Normal file
108
DysonNetwork.Shared/Proto/publisher.proto
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package proto;
|
||||||
|
|
||||||
|
option csharp_namespace = "DysonNetwork.Shared.Proto";
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "google/protobuf/wrappers.proto";
|
||||||
|
import "file.proto";
|
||||||
|
|
||||||
|
enum PublisherType {
|
||||||
|
PUBLISHER_TYPE_UNSPECIFIED = 0;
|
||||||
|
INDIVIDUAL = 1;
|
||||||
|
ORGANIZATIONAL = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PublisherMemberRole {
|
||||||
|
PUBLISHER_MEMBER_ROLE_UNSPECIFIED = 0;
|
||||||
|
OWNER = 100;
|
||||||
|
MANAGER = 75;
|
||||||
|
EDITOR = 50;
|
||||||
|
VIEWER = 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PublisherFeature {
|
||||||
|
string id = 1;
|
||||||
|
string flag = 2;
|
||||||
|
google.protobuf.Timestamp expired_at = 3;
|
||||||
|
string publisher_id = 4;
|
||||||
|
google.protobuf.Timestamp created_at = 5;
|
||||||
|
google.protobuf.Timestamp updated_at = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PublisherMember {
|
||||||
|
string publisher_id = 1;
|
||||||
|
string account_id = 2;
|
||||||
|
PublisherMemberRole role = 3;
|
||||||
|
google.protobuf.Timestamp joined_at = 4;
|
||||||
|
google.protobuf.Timestamp created_at = 5;
|
||||||
|
google.protobuf.Timestamp updated_at = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Publisher {
|
||||||
|
string id = 1;
|
||||||
|
PublisherType type = 2;
|
||||||
|
string name = 3;
|
||||||
|
string nick = 4;
|
||||||
|
google.protobuf.StringValue bio = 5;
|
||||||
|
CloudFile picture = 8;
|
||||||
|
CloudFile background = 9;
|
||||||
|
optional bytes verification_mark = 10;
|
||||||
|
string account_id = 11;
|
||||||
|
string realm_id = 12;
|
||||||
|
google.protobuf.Timestamp created_at = 13;
|
||||||
|
google.protobuf.Timestamp updated_at = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetPublisherRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetPublisherResponse {
|
||||||
|
Publisher publisher = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListPublishersRequest {
|
||||||
|
string account_id = 1; // filter by owner/member account
|
||||||
|
string realm_id = 2; // filter by realm
|
||||||
|
int32 page_size = 3;
|
||||||
|
string page_token = 4;
|
||||||
|
string order_by = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListPublishersResponse {
|
||||||
|
repeated Publisher publishers = 1;
|
||||||
|
string next_page_token = 2;
|
||||||
|
int32 total_size = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListPublisherMembersRequest {
|
||||||
|
string publisher_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListPublisherMembersResponse {
|
||||||
|
repeated PublisherMember members = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetPublisherFeatureFlagRequest {
|
||||||
|
string publisher_id = 1;
|
||||||
|
string flag = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HasPublisherFeatureRequest {
|
||||||
|
string publisher_id = 1;
|
||||||
|
string flag = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HasPublisherFeatureResponse {
|
||||||
|
bool enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service PublisherService {
|
||||||
|
rpc GetPublisher(GetPublisherRequest) returns (GetPublisherResponse);
|
||||||
|
rpc ListPublishers(ListPublishersRequest) returns (ListPublishersResponse);
|
||||||
|
rpc ListPublisherMembers(ListPublisherMembersRequest) returns (ListPublisherMembersResponse);
|
||||||
|
rpc SetPublisherFeatureFlag(SetPublisherFeatureFlagRequest) returns (google.protobuf.StringValue); // returns optional message
|
||||||
|
rpc HasPublisherFeature(HasPublisherFeatureRequest) returns (HasPublisherFeatureResponse);
|
||||||
|
}
|
@@ -92,4 +92,23 @@ public static class ServiceInjectionHelper
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddPublisherService(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<PublisherService.PublisherServiceClient>(sp =>
|
||||||
|
{
|
||||||
|
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||||
|
var config = sp.GetRequiredService<IConfiguration>();
|
||||||
|
var clientCertPath = config["Service:ClientCert"]!;
|
||||||
|
var clientKeyPath = config["Service:ClientKey"]!;
|
||||||
|
var clientCertPassword = config["Service:CertPassword"];
|
||||||
|
|
||||||
|
return GrpcClientHelper
|
||||||
|
.CreatePublisherServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -10,7 +10,7 @@ namespace DysonNetwork.Sphere.Activity;
|
|||||||
|
|
||||||
public class ActivityService(
|
public class ActivityService(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
PublisherService pub,
|
Publisher.PublisherService pub,
|
||||||
PostService ps,
|
PostService ps,
|
||||||
DiscoveryService ds,
|
DiscoveryService ds,
|
||||||
AccountService.AccountServiceClient accounts
|
AccountService.AccountServiceClient accounts
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.Post;
|
using DysonNetwork.Sphere.Post;
|
||||||
using DysonNetwork.Sphere.Publisher;
|
using DysonNetwork.Sphere.Publisher;
|
||||||
using DysonNetwork.Sphere.Realm;
|
using DysonNetwork.Sphere.Realm;
|
||||||
@@ -61,9 +60,6 @@ public class AppDatabase(
|
|||||||
public DbSet<StickerPack> StickerPacks { get; set; }
|
public DbSet<StickerPack> StickerPacks { get; set; }
|
||||||
public DbSet<StickerPackOwnership> StickerPackOwnerships { get; set; }
|
public DbSet<StickerPackOwnership> StickerPackOwnerships { get; set; }
|
||||||
|
|
||||||
public DbSet<CustomApp> CustomApps { get; set; }
|
|
||||||
public DbSet<CustomAppSecret> CustomAppSecrets { get; set; }
|
|
||||||
|
|
||||||
public DbSet<WebReader.WebArticle> WebArticles { get; set; }
|
public DbSet<WebReader.WebArticle> WebArticles { get; set; }
|
||||||
public DbSet<WebReader.WebFeed> WebFeeds { get; set; }
|
public DbSet<WebReader.WebFeed> WebFeeds { get; set; }
|
||||||
|
|
||||||
@@ -103,16 +99,6 @@ public class AppDatabase(
|
|||||||
.HasIndex(p => p.SearchVector)
|
.HasIndex(p => p.SearchVector)
|
||||||
.HasMethod("GIN");
|
.HasMethod("GIN");
|
||||||
|
|
||||||
modelBuilder.Entity<CustomAppSecret>()
|
|
||||||
.HasIndex(s => s.Secret)
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
modelBuilder.Entity<CustomApp>()
|
|
||||||
.HasMany(c => c.Secrets)
|
|
||||||
.WithOne(s => s.App)
|
|
||||||
.HasForeignKey(s => s.AppId)
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
|
|
||||||
modelBuilder.Entity<Post.Post>()
|
modelBuilder.Entity<Post.Post>()
|
||||||
.HasOne(p => p.RepliedPost)
|
.HasOne(p => p.RepliedPost)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="NodaTime" Version="3.2.2"/>
|
<PackageReference Include="NodaTime" Version="3.2.2"/>
|
||||||
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.2.0"/>
|
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.2.0"/>
|
||||||
|
<PackageReference Include="NodaTime.Serialization.Protobuf" Version="2.0.2" />
|
||||||
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.3.0"/>
|
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.3.0"/>
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4"/>
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4"/>
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0"/>
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0"/>
|
||||||
|
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Sphere;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
@@ -397,20 +396,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("character varying(4096)")
|
.HasColumnType("character varying(4096)")
|
||||||
.HasColumnName("description");
|
.HasColumnName("description");
|
||||||
|
|
||||||
b.Property<CustomAppLinks>("Links")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)")
|
.HasColumnType("character varying(1024)")
|
||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("oauth_config");
|
|
||||||
|
|
||||||
b.Property<CloudFileReferenceObject>("Picture")
|
b.Property<CloudFileReferenceObject>("Picture")
|
||||||
.HasColumnType("jsonb")
|
.HasColumnType("jsonb")
|
||||||
.HasColumnName("picture");
|
.HasColumnName("picture");
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
@@ -248,8 +247,6 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
picture = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true),
|
picture = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||||
background = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true),
|
background = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||||
verification = table.Column<VerificationMark>(type: "jsonb", nullable: true),
|
verification = table.Column<VerificationMark>(type: "jsonb", nullable: true),
|
||||||
oauth_config = table.Column<CustomAppOauthConfig>(type: "jsonb", nullable: true),
|
|
||||||
links = table.Column<CustomAppLinks>(type: "jsonb", nullable: true),
|
|
||||||
publisher_id = table.Column<Guid>(type: "uuid", nullable: false),
|
publisher_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||||
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||||
|
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Sphere;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
@@ -397,20 +396,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("character varying(4096)")
|
.HasColumnType("character varying(4096)")
|
||||||
.HasColumnName("description");
|
.HasColumnName("description");
|
||||||
|
|
||||||
b.Property<CustomAppLinks>("Links")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)")
|
.HasColumnType("character varying(1024)")
|
||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("oauth_config");
|
|
||||||
|
|
||||||
b.Property<CloudFileReferenceObject>("Picture")
|
b.Property<CloudFileReferenceObject>("Picture")
|
||||||
.HasColumnType("jsonb")
|
.HasColumnType("jsonb")
|
||||||
.HasColumnName("picture");
|
.HasColumnName("picture");
|
||||||
|
@@ -5,7 +5,6 @@ using System.Text.Json;
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Sphere;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.Poll;
|
using DysonNetwork.Sphere.Poll;
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -399,20 +398,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("character varying(4096)")
|
.HasColumnType("character varying(4096)")
|
||||||
.HasColumnName("description");
|
.HasColumnName("description");
|
||||||
|
|
||||||
b.Property<CustomAppLinks>("Links")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)")
|
.HasColumnType("character varying(1024)")
|
||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("oauth_config");
|
|
||||||
|
|
||||||
b.Property<CloudFileReferenceObject>("Picture")
|
b.Property<CloudFileReferenceObject>("Picture")
|
||||||
.HasColumnType("jsonb")
|
.HasColumnType("jsonb")
|
||||||
.HasColumnName("picture");
|
.HasColumnName("picture");
|
||||||
|
@@ -5,7 +5,6 @@ using System.Text.Json;
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Sphere;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.Poll;
|
using DysonNetwork.Sphere.Poll;
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -399,20 +398,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("character varying(4096)")
|
.HasColumnType("character varying(4096)")
|
||||||
.HasColumnName("description");
|
.HasColumnName("description");
|
||||||
|
|
||||||
b.Property<CustomAppLinks>("Links")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)")
|
.HasColumnType("character varying(1024)")
|
||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("oauth_config");
|
|
||||||
|
|
||||||
b.Property<CloudFileReferenceObject>("Picture")
|
b.Property<CloudFileReferenceObject>("Picture")
|
||||||
.HasColumnType("jsonb")
|
.HasColumnType("jsonb")
|
||||||
.HasColumnName("picture");
|
.HasColumnName("picture");
|
||||||
|
@@ -5,7 +5,6 @@ using System.Text.Json;
|
|||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Sphere;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Chat;
|
using DysonNetwork.Sphere.Chat;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.Poll;
|
using DysonNetwork.Sphere.Poll;
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -396,20 +395,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("character varying(4096)")
|
.HasColumnType("character varying(4096)")
|
||||||
.HasColumnName("description");
|
.HasColumnName("description");
|
||||||
|
|
||||||
b.Property<CustomAppLinks>("Links")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("links");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)")
|
.HasColumnType("character varying(1024)")
|
||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
|
||||||
.HasColumnType("jsonb")
|
|
||||||
.HasColumnName("oauth_config");
|
|
||||||
|
|
||||||
b.Property<CloudFileReferenceObject>("Picture")
|
b.Property<CloudFileReferenceObject>("Picture")
|
||||||
.HasColumnType("jsonb")
|
.HasColumnType("jsonb")
|
||||||
.HasColumnName("picture");
|
.HasColumnName("picture");
|
||||||
|
@@ -11,7 +11,7 @@ namespace DysonNetwork.Sphere.PageData;
|
|||||||
public class PostPageData(
|
public class PostPageData(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
AccountService.AccountServiceClient accounts,
|
AccountService.AccountServiceClient accounts,
|
||||||
PublisherService pub,
|
Publisher.PublisherService pub,
|
||||||
PostService ps,
|
PostService ps,
|
||||||
IConfiguration configuration
|
IConfiguration configuration
|
||||||
)
|
)
|
||||||
|
@@ -11,7 +11,7 @@ namespace DysonNetwork.Sphere.Poll;
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/api/polls")]
|
[Route("/api/polls")]
|
||||||
public class PollController(AppDatabase db, PollService polls, PublisherService pub) : ControllerBase
|
public class PollController(AppDatabase db, PollService polls, Publisher.PublisherService pub) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet("{id:guid}")]
|
[HttpGet("{id:guid}")]
|
||||||
public async Task<ActionResult<PollWithStats>> GetPoll(Guid id)
|
public async Task<ActionResult<PollWithStats>> GetPoll(Guid id)
|
||||||
@@ -85,7 +85,7 @@ public class PollController(AppDatabase db, PollService polls, PublisherService
|
|||||||
.FirstOrDefaultAsync(p => p.Id == id);
|
.FirstOrDefaultAsync(p => p.Id == id);
|
||||||
if (poll is null) return NotFound("Poll not found");
|
if (poll is null) return NotFound("Poll not found");
|
||||||
|
|
||||||
if (!await pub.IsMemberWithRole(poll.PublisherId, accountId, PublisherMemberRole.Viewer))
|
if (!await pub.IsMemberWithRole(poll.PublisherId, accountId, Publisher.PublisherMemberRole.Viewer))
|
||||||
return StatusCode(403, "You need to be a viewer to view this poll's feedback.");
|
return StatusCode(403, "You need to be a viewer to view this poll's feedback.");
|
||||||
|
|
||||||
var answerQuery = db.PollAnswers
|
var answerQuery = db.PollAnswers
|
||||||
@@ -186,7 +186,7 @@ public class PollController(AppDatabase db, PollService polls, PublisherService
|
|||||||
|
|
||||||
var publisher = await pub.GetPublisherByName(pubName);
|
var publisher = await pub.GetPublisherByName(pubName);
|
||||||
if (publisher is null) return BadRequest("Publisher was not found.");
|
if (publisher is null) return BadRequest("Publisher was not found.");
|
||||||
if (!await pub.IsMemberWithRole(publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await pub.IsMemberWithRole(publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You need at least be an editor to create polls as this publisher.");
|
return StatusCode(403, "You need at least be an editor to create polls as this publisher.");
|
||||||
|
|
||||||
var poll = new Poll
|
var poll = new Poll
|
||||||
@@ -231,7 +231,7 @@ public class PollController(AppDatabase db, PollService polls, PublisherService
|
|||||||
if (poll == null) return NotFound("Poll not found");
|
if (poll == null) return NotFound("Poll not found");
|
||||||
|
|
||||||
// Check if user is an editor of the publisher that owns the poll
|
// Check if user is an editor of the publisher that owns the poll
|
||||||
if (!await pub.IsMemberWithRole(poll.PublisherId, accountId, PublisherMemberRole.Editor))
|
if (!await pub.IsMemberWithRole(poll.PublisherId, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You need to be at least an editor to update this poll.");
|
return StatusCode(403, "You need to be at least an editor to update this poll.");
|
||||||
|
|
||||||
// Update properties if they are provided in the request
|
// Update properties if they are provided in the request
|
||||||
@@ -294,7 +294,7 @@ public class PollController(AppDatabase db, PollService polls, PublisherService
|
|||||||
if (poll == null) return NotFound("Poll not found");
|
if (poll == null) return NotFound("Poll not found");
|
||||||
|
|
||||||
// Check if user is an editor of the publisher that owns the poll
|
// Check if user is an editor of the publisher that owns the poll
|
||||||
if (!await pub.IsMemberWithRole(poll.PublisherId, accountId, PublisherMemberRole.Editor))
|
if (!await pub.IsMemberWithRole(poll.PublisherId, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You need to be at least an editor to delete this poll.");
|
return StatusCode(403, "You need to be at least an editor to delete this poll.");
|
||||||
|
|
||||||
// Delete all answers for this poll
|
// Delete all answers for this poll
|
||||||
|
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
using PublisherService = DysonNetwork.Sphere.Publisher.PublisherService;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Post;
|
namespace DysonNetwork.Sphere.Post;
|
||||||
|
|
||||||
@@ -301,13 +302,13 @@ public class PostController(
|
|||||||
{
|
{
|
||||||
// Use the first personal publisher
|
// Use the first personal publisher
|
||||||
publisher = await db.Publishers.FirstOrDefaultAsync(e =>
|
publisher = await db.Publishers.FirstOrDefaultAsync(e =>
|
||||||
e.AccountId == accountId && e.Type == PublisherType.Individual);
|
e.AccountId == accountId && e.Type == Publisher.PublisherType.Individual);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
publisher = await pub.GetPublisherByName(pubName);
|
publisher = await pub.GetPublisherByName(pubName);
|
||||||
if (publisher is null) return BadRequest("Publisher was not found.");
|
if (publisher is null) return BadRequest("Publisher was not found.");
|
||||||
if (!await pub.IsMemberWithRole(publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await pub.IsMemberWithRole(publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You need at least be an editor to post as this publisher.");
|
return StatusCode(403, "You need at least be an editor to post as this publisher.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,14 +474,14 @@ public class PostController(
|
|||||||
if (post is null) return NotFound();
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
if (!await pub.IsMemberWithRole(post.Publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await pub.IsMemberWithRole(post.Publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You need at least be an editor to edit this publisher's post.");
|
return StatusCode(403, "You need at least be an editor to edit this publisher's post.");
|
||||||
|
|
||||||
if (pubName is not null)
|
if (pubName is not null)
|
||||||
{
|
{
|
||||||
var publisher = await pub.GetPublisherByName(pubName);
|
var publisher = await pub.GetPublisherByName(pubName);
|
||||||
if (publisher is null) return NotFound();
|
if (publisher is null) return NotFound();
|
||||||
if (!await pub.IsMemberWithRole(publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await pub.IsMemberWithRole(publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You need at least be an editor to transfer this post to this publisher.");
|
return StatusCode(403, "You need at least be an editor to transfer this post to this publisher.");
|
||||||
post.PublisherId = publisher.Id;
|
post.PublisherId = publisher.Id;
|
||||||
post.Publisher = publisher;
|
post.Publisher = publisher;
|
||||||
@@ -552,7 +553,8 @@ public class PostController(
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (post is null) return NotFound();
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
if (!await pub.IsMemberWithRole(post.Publisher.Id, Guid.Parse(currentUser.Id), PublisherMemberRole.Editor))
|
if (!await pub.IsMemberWithRole(post.Publisher.Id, Guid.Parse(currentUser.Id),
|
||||||
|
Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You need at least be an editor to delete the publisher's post.");
|
return StatusCode(403, "You need at least be an editor to delete the publisher's post.");
|
||||||
|
|
||||||
await ps.DeletePostAsync(post);
|
await ps.DeletePostAsync(post);
|
||||||
|
@@ -161,7 +161,7 @@ public partial class PostService(
|
|||||||
{
|
{
|
||||||
var sender = post.Publisher;
|
var sender = post.Publisher;
|
||||||
using var scope = factory.CreateScope();
|
using var scope = factory.CreateScope();
|
||||||
var pub = scope.ServiceProvider.GetRequiredService<PublisherService>();
|
var pub = scope.ServiceProvider.GetRequiredService<Publisher.PublisherService>();
|
||||||
var nty = scope.ServiceProvider.GetRequiredService<PusherService.PusherServiceClient>();
|
var nty = scope.ServiceProvider.GetRequiredService<PusherService.PusherServiceClient>();
|
||||||
var accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
var accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
||||||
try
|
try
|
||||||
@@ -455,7 +455,7 @@ public partial class PostService(
|
|||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
using var scope = factory.CreateScope();
|
using var scope = factory.CreateScope();
|
||||||
var pub = scope.ServiceProvider.GetRequiredService<PublisherService>();
|
var pub = scope.ServiceProvider.GetRequiredService<Publisher.PublisherService>();
|
||||||
var nty = scope.ServiceProvider.GetRequiredService<PusherService.PusherServiceClient>();
|
var nty = scope.ServiceProvider.GetRequiredService<PusherService.PusherServiceClient>();
|
||||||
var accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
var accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
||||||
try
|
try
|
||||||
|
@@ -5,6 +5,7 @@ using DysonNetwork.Shared.Data;
|
|||||||
using DysonNetwork.Sphere.Post;
|
using DysonNetwork.Sphere.Post;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
using VerificationMark = DysonNetwork.Shared.Data.VerificationMark;
|
using VerificationMark = DysonNetwork.Shared.Data.VerificationMark;
|
||||||
using Account = DysonNetwork.Pass.Account.Account;
|
using Account = DysonNetwork.Pass.Account.Account;
|
||||||
|
|
||||||
@@ -49,6 +50,49 @@ public class Publisher : ModelBase, IIdentifiedResource
|
|||||||
[NotMapped] public Account? Account { get; set; }
|
[NotMapped] public Account? Account { get; set; }
|
||||||
|
|
||||||
public string ResourceIdentifier => $"publisher:{Id}";
|
public string ResourceIdentifier => $"publisher:{Id}";
|
||||||
|
|
||||||
|
public Shared.Proto.Publisher ToProto(AppDatabase db)
|
||||||
|
{
|
||||||
|
var p = new Shared.Proto.Publisher()
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Type = Type == PublisherType.Individual
|
||||||
|
? Shared.Proto.PublisherType.Individual
|
||||||
|
: Shared.Proto.PublisherType.Organizational,
|
||||||
|
Name = Name,
|
||||||
|
Nick = Nick,
|
||||||
|
Bio = Bio,
|
||||||
|
AccountId = AccountId?.ToString() ?? string.Empty,
|
||||||
|
RealmId = RealmId?.ToString() ?? string.Empty,
|
||||||
|
CreatedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTimeOffset(CreatedAt.ToDateTimeOffset()),
|
||||||
|
UpdatedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTimeOffset(UpdatedAt.ToDateTimeOffset())
|
||||||
|
};
|
||||||
|
if (Picture is not null)
|
||||||
|
{
|
||||||
|
p.Picture = new Shared.Proto.CloudFile
|
||||||
|
{
|
||||||
|
Id = Picture.Id,
|
||||||
|
Name = Picture.Name,
|
||||||
|
MimeType = Picture.MimeType,
|
||||||
|
Hash = Picture.Hash,
|
||||||
|
Size = Picture.Size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Background is not null)
|
||||||
|
{
|
||||||
|
p.Background = new Shared.Proto.CloudFile
|
||||||
|
{
|
||||||
|
Id = Background.Id,
|
||||||
|
Name = Background.Name,
|
||||||
|
MimeType = Background.MimeType,
|
||||||
|
Hash = Background.Hash,
|
||||||
|
Size = Background.Size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PublisherMemberRole
|
public enum PublisherMemberRole
|
||||||
@@ -68,6 +112,25 @@ public class PublisherMember : ModelBase
|
|||||||
|
|
||||||
public PublisherMemberRole Role { get; set; } = PublisherMemberRole.Viewer;
|
public PublisherMemberRole Role { get; set; } = PublisherMemberRole.Viewer;
|
||||||
public Instant? JoinedAt { get; set; }
|
public Instant? JoinedAt { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public Shared.Proto.PublisherMember ToProto()
|
||||||
|
{
|
||||||
|
return new Shared.Proto.PublisherMember()
|
||||||
|
{
|
||||||
|
PublisherId = PublisherId.ToString(),
|
||||||
|
AccountId = AccountId.ToString(),
|
||||||
|
Role = Role switch
|
||||||
|
{
|
||||||
|
PublisherMemberRole.Owner => Shared.Proto.PublisherMemberRole.Owner,
|
||||||
|
PublisherMemberRole.Manager => Shared.Proto.PublisherMemberRole.Manager,
|
||||||
|
PublisherMemberRole.Editor => Shared.Proto.PublisherMemberRole.Editor,
|
||||||
|
PublisherMemberRole.Viewer => Shared.Proto.PublisherMemberRole.Viewer,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(Role), Role, null)
|
||||||
|
},
|
||||||
|
JoinedAt = JoinedAt?.ToTimestamp()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PublisherSubscriptionStatus
|
public enum PublisherSubscriptionStatus
|
||||||
|
68
DysonNetwork.Sphere/Publisher/PublisherServiceGrpc.cs
Normal file
68
DysonNetwork.Sphere/Publisher/PublisherServiceGrpc.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using Grpc.Core;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Publisher;
|
||||||
|
|
||||||
|
public class PublisherServiceGrpc(PublisherService service, AppDatabase db)
|
||||||
|
: Shared.Proto.PublisherService.PublisherServiceBase
|
||||||
|
{
|
||||||
|
public override async Task<GetPublisherResponse> GetPublisher(GetPublisherRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var p = await service.GetPublisherByName(request.Name);
|
||||||
|
if (p is null) throw new RpcException(new Status(StatusCode.NotFound, "publisher not found"));
|
||||||
|
return new GetPublisherResponse { Publisher = p.ToProto(db) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<ListPublishersResponse> ListPublishers(ListPublishersRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
IQueryable<Publisher> query = db.Publishers.AsQueryable();
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.AccountId) && Guid.TryParse(request.AccountId, out var aid))
|
||||||
|
{
|
||||||
|
var ids = await db.PublisherMembers.Where(m => m.AccountId == aid).Select(m => m.PublisherId).ToListAsync();
|
||||||
|
query = query.Where(p => ids.Contains(p.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.RealmId) && Guid.TryParse(request.RealmId, out var rid))
|
||||||
|
{
|
||||||
|
query = query.Where(p => p.RealmId == rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = await query.Take(request.PageSize > 0 ? request.PageSize : 100).ToListAsync();
|
||||||
|
var resp = new ListPublishersResponse();
|
||||||
|
resp.Publishers.AddRange(list.Select(p => p.ToProto(db)));
|
||||||
|
resp.TotalSize = list.Count;
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<ListPublisherMembersResponse> ListPublisherMembers(ListPublisherMembersRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (!Guid.TryParse(request.PublisherId, out var pid))
|
||||||
|
throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid publisher_id"));
|
||||||
|
var members = await service.GetPublisherMembers(pid);
|
||||||
|
var resp = new ListPublisherMembersResponse();
|
||||||
|
resp.Members.AddRange(members.Select(m => m.ToProto()));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Google.Protobuf.WellKnownTypes.StringValue> SetPublisherFeatureFlag(
|
||||||
|
SetPublisherFeatureFlagRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (!Guid.TryParse(request.PublisherId, out var pid))
|
||||||
|
throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid publisher_id"));
|
||||||
|
await service.SetFeatureFlag(pid, request.Flag);
|
||||||
|
return new Google.Protobuf.WellKnownTypes.StringValue { Value = request.Flag };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<HasPublisherFeatureResponse> HasPublisherFeature(HasPublisherFeatureRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (!Guid.TryParse(request.PublisherId, out var pid))
|
||||||
|
throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid publisher_id"));
|
||||||
|
var enabled = await service.HasFeature(pid, request.Flag);
|
||||||
|
return new HasPublisherFeatureResponse { Enabled = enabled };
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using DysonNetwork.Shared.Auth;
|
using DysonNetwork.Shared.Auth;
|
||||||
using DysonNetwork.Sphere.Connection;
|
using DysonNetwork.Sphere.Connection;
|
||||||
|
using DysonNetwork.Sphere.Publisher;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ public static class ApplicationConfiguration
|
|||||||
|
|
||||||
// Map gRPC services
|
// Map gRPC services
|
||||||
app.MapGrpcService<WebSocketHandlerGrpc>();
|
app.MapGrpcService<WebSocketHandlerGrpc>();
|
||||||
|
app.MapGrpcService<PublisherServiceGrpc>();
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,6 @@ using System.Threading.RateLimiting;
|
|||||||
using DysonNetwork.Shared.Cache;
|
using DysonNetwork.Shared.Cache;
|
||||||
using DysonNetwork.Shared.GeoIp;
|
using DysonNetwork.Shared.GeoIp;
|
||||||
using DysonNetwork.Sphere.WebReader;
|
using DysonNetwork.Sphere.WebReader;
|
||||||
using DysonNetwork.Sphere.Developer;
|
|
||||||
using DysonNetwork.Sphere.Discovery;
|
using DysonNetwork.Sphere.Discovery;
|
||||||
using DysonNetwork.Sphere.Poll;
|
using DysonNetwork.Sphere.Poll;
|
||||||
using DysonNetwork.Sphere.Translation;
|
using DysonNetwork.Sphere.Translation;
|
||||||
@@ -169,7 +168,6 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddScoped<WebReaderService>();
|
services.AddScoped<WebReaderService>();
|
||||||
services.AddScoped<WebFeedService>();
|
services.AddScoped<WebFeedService>();
|
||||||
services.AddScoped<DiscoveryService>();
|
services.AddScoped<DiscoveryService>();
|
||||||
services.AddScoped<CustomAppService>();
|
|
||||||
services.AddScoped<PollService>();
|
services.AddScoped<PollService>();
|
||||||
|
|
||||||
var translationProvider = configuration["Translation:Provider"]?.ToLower();
|
var translationProvider = configuration["Translation:Provider"]?.ToLower();
|
||||||
|
@@ -2,7 +2,6 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using DysonNetwork.Shared.Auth;
|
using DysonNetwork.Shared.Auth;
|
||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using DysonNetwork.Sphere.Publisher;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -16,7 +15,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
|||||||
private async Task<IActionResult> _CheckStickerPackPermissions(
|
private async Task<IActionResult> _CheckStickerPackPermissions(
|
||||||
Guid packId,
|
Guid packId,
|
||||||
Account currentUser,
|
Account currentUser,
|
||||||
PublisherMemberRole requiredRole
|
Publisher.PublisherMemberRole requiredRole
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var pack = await db.StickerPacks
|
var pack = await db.StickerPacks
|
||||||
@@ -149,7 +148,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
|||||||
.FirstOrDefaultAsync(m => m.AccountId == accountId && m.PublisherId == pack.PublisherId);
|
.FirstOrDefaultAsync(m => m.AccountId == accountId && m.PublisherId == pack.PublisherId);
|
||||||
if (member is null)
|
if (member is null)
|
||||||
return StatusCode(403, "You are not a member of this publisher");
|
return StatusCode(403, "You are not a member of this publisher");
|
||||||
if (member.Role < PublisherMemberRole.Editor)
|
if (member.Role < Publisher.PublisherMemberRole.Editor)
|
||||||
return StatusCode(403, "You need to be at least an editor to update sticker packs");
|
return StatusCode(403, "You need to be at least an editor to update sticker packs");
|
||||||
|
|
||||||
if (request.Name is not null)
|
if (request.Name is not null)
|
||||||
@@ -181,7 +180,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
|||||||
.FirstOrDefaultAsync(m => m.AccountId == accountId && m.PublisherId == pack.PublisherId);
|
.FirstOrDefaultAsync(m => m.AccountId == accountId && m.PublisherId == pack.PublisherId);
|
||||||
if (member is null)
|
if (member is null)
|
||||||
return StatusCode(403, "You are not a member of this publisher");
|
return StatusCode(403, "You are not a member of this publisher");
|
||||||
if (member.Role < PublisherMemberRole.Editor)
|
if (member.Role < Publisher.PublisherMemberRole.Editor)
|
||||||
return StatusCode(403, "You need to be an editor to delete sticker packs");
|
return StatusCode(403, "You need to be an editor to delete sticker packs");
|
||||||
|
|
||||||
await st.DeleteStickerPackAsync(pack);
|
await st.DeleteStickerPackAsync(pack);
|
||||||
@@ -242,7 +241,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
|||||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, PublisherMemberRole.Editor);
|
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor);
|
||||||
if (permissionCheck is not OkResult)
|
if (permissionCheck is not OkResult)
|
||||||
return permissionCheck;
|
return permissionCheck;
|
||||||
|
|
||||||
@@ -278,7 +277,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
|||||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, PublisherMemberRole.Editor);
|
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor);
|
||||||
if (permissionCheck is not OkResult)
|
if (permissionCheck is not OkResult)
|
||||||
return permissionCheck;
|
return permissionCheck;
|
||||||
|
|
||||||
@@ -309,7 +308,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
|||||||
if (request.ImageId is null)
|
if (request.ImageId is null)
|
||||||
return BadRequest("Image is required.");
|
return BadRequest("Image is required.");
|
||||||
|
|
||||||
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, PublisherMemberRole.Editor);
|
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor);
|
||||||
if (permissionCheck is not OkResult)
|
if (permissionCheck is not OkResult)
|
||||||
return permissionCheck;
|
return permissionCheck;
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ namespace DysonNetwork.Sphere.WebReader;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/api/publishers/{pubName}/feeds")]
|
[Route("/api/publishers/{pubName}/feeds")]
|
||||||
public class WebFeedController(WebFeedService webFeed, PublisherService ps) : ControllerBase
|
public class WebFeedController(WebFeedService webFeed, Publisher.PublisherService ps) : ControllerBase
|
||||||
{
|
{
|
||||||
public record WebFeedRequest(
|
public record WebFeedRequest(
|
||||||
[MaxLength(8192)] string? Url,
|
[MaxLength(8192)] string? Url,
|
||||||
@@ -53,7 +53,7 @@ public class WebFeedController(WebFeedService webFeed, PublisherService ps) : Co
|
|||||||
if (publisher is null) return NotFound();
|
if (publisher is null) return NotFound();
|
||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
if (!await ps.IsMemberWithRole(publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await ps.IsMemberWithRole(publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You must be an editor of the publisher to create a web feed");
|
return StatusCode(403, "You must be an editor of the publisher to create a web feed");
|
||||||
|
|
||||||
var feed = await webFeed.CreateWebFeedAsync(publisher, request);
|
var feed = await webFeed.CreateWebFeedAsync(publisher, request);
|
||||||
@@ -70,7 +70,7 @@ public class WebFeedController(WebFeedService webFeed, PublisherService ps) : Co
|
|||||||
if (publisher is null) return NotFound();
|
if (publisher is null) return NotFound();
|
||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
if (!await ps.IsMemberWithRole(publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await ps.IsMemberWithRole(publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You must be an editor of the publisher to update a web feed");
|
return StatusCode(403, "You must be an editor of the publisher to update a web feed");
|
||||||
|
|
||||||
var feed = await webFeed.GetFeedAsync(id, publisherId: publisher.Id);
|
var feed = await webFeed.GetFeedAsync(id, publisherId: publisher.Id);
|
||||||
@@ -91,7 +91,7 @@ public class WebFeedController(WebFeedService webFeed, PublisherService ps) : Co
|
|||||||
if (publisher is null) return NotFound();
|
if (publisher is null) return NotFound();
|
||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
if (!await ps.IsMemberWithRole(publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await ps.IsMemberWithRole(publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You must be an editor of the publisher to delete a web feed");
|
return StatusCode(403, "You must be an editor of the publisher to delete a web feed");
|
||||||
|
|
||||||
var feed = await webFeed.GetFeedAsync(id, publisherId: publisher.Id);
|
var feed = await webFeed.GetFeedAsync(id, publisherId: publisher.Id);
|
||||||
@@ -114,7 +114,7 @@ public class WebFeedController(WebFeedService webFeed, PublisherService ps) : Co
|
|||||||
if (publisher is null) return NotFound();
|
if (publisher is null) return NotFound();
|
||||||
|
|
||||||
var accountId = Guid.Parse(currentUser.Id);
|
var accountId = Guid.Parse(currentUser.Id);
|
||||||
if (!await ps.IsMemberWithRole(publisher.Id, accountId, PublisherMemberRole.Editor))
|
if (!await ps.IsMemberWithRole(publisher.Id, accountId, Publisher.PublisherMemberRole.Editor))
|
||||||
return StatusCode(403, "You must be an editor of the publisher to scrape a web feed");
|
return StatusCode(403, "You must be an editor of the publisher to scrape a web feed");
|
||||||
|
|
||||||
var feed = await webFeed.GetFeedAsync(id, publisherId: publisher.Id);
|
var feed = await webFeed.GetFeedAsync(id, publisherId: publisher.Id);
|
||||||
|
@@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Drive", "Dyson
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Gateway", "DysonNetwork.Gateway\DysonNetwork.Gateway.csproj", "{19EB0086-4049-4B78-91C4-EAC37130A006}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Gateway", "DysonNetwork.Gateway\DysonNetwork.Gateway.csproj", "{19EB0086-4049-4B78-91C4-EAC37130A006}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Develop", "DysonNetwork.Develop\DysonNetwork.Develop.csproj", "{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -48,5 +50,9 @@ Global
|
|||||||
{19EB0086-4049-4B78-91C4-EAC37130A006}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{19EB0086-4049-4B78-91C4-EAC37130A006}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{19EB0086-4049-4B78-91C4-EAC37130A006}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{19EB0086-4049-4B78-91C4-EAC37130A006}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{19EB0086-4049-4B78-91C4-EAC37130A006}.Release|Any CPU.Build.0 = Release|Any CPU
|
{19EB0086-4049-4B78-91C4-EAC37130A006}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
Reference in New Issue
Block a user