♻️ 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 NodaTime;
|
||||
|
||||
namespace DysonNetwork.Sphere.Developer;
|
||||
namespace DysonNetwork.Develop.Identity;
|
||||
|
||||
public enum CustomAppStatus
|
||||
{
|
@@ -1,10 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DysonNetwork.Sphere.Developer;
|
||||
namespace DysonNetwork.Develop.Identity;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/developers/{pubName}/apps")]
|
@@ -1,8 +1,7 @@
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DysonNetwork.Sphere.Developer;
|
||||
namespace DysonNetwork.Develop.Identity;
|
||||
|
||||
public class CustomAppService(
|
||||
AppDatabase db,
|
@@ -1,18 +1,16 @@
|
||||
using DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Sphere.Developer;
|
||||
namespace DysonNetwork.Develop.Identity;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/developers")]
|
||||
public class DeveloperController(
|
||||
AppDatabase db,
|
||||
PublisherService ps,
|
||||
PublisherService.PublisherServiceClient ps,
|
||||
ActionLogService.ActionLogServiceClient als
|
||||
)
|
||||
: 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,
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
AppDatabase db,
|
||||
PublisherService pub,
|
||||
Publisher.PublisherService pub,
|
||||
PostService ps,
|
||||
DiscoveryService ds,
|
||||
AccountService.AccountServiceClient accounts
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using DysonNetwork.Sphere.Chat;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.Post;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using DysonNetwork.Sphere.Realm;
|
||||
@@ -61,9 +60,6 @@ public class AppDatabase(
|
||||
public DbSet<StickerPack> StickerPacks { 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.WebFeed> WebFeeds { get; set; }
|
||||
|
||||
@@ -103,16 +99,6 @@ public class AppDatabase(
|
||||
.HasIndex(p => p.SearchVector)
|
||||
.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>()
|
||||
.HasOne(p => p.RepliedPost)
|
||||
.WithMany()
|
||||
|
@@ -36,6 +36,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="NodaTime" Version="3.2.2"/>
|
||||
<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="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4"/>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0"/>
|
||||
|
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Sphere;
|
||||
using DysonNetwork.Sphere.Chat;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@@ -397,20 +396,12 @@ namespace DysonNetwork.Sphere.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
b.Property<CloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Sphere.Chat;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
@@ -248,8 +247,6 @@ namespace DysonNetwork.Sphere.Migrations
|
||||
picture = table.Column<CloudFileReferenceObject>(type: "jsonb", nullable: true),
|
||||
background = table.Column<CloudFileReferenceObject>(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),
|
||||
created_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.Sphere;
|
||||
using DysonNetwork.Sphere.Chat;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@@ -397,20 +396,12 @@ namespace DysonNetwork.Sphere.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
b.Property<CloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
@@ -5,7 +5,6 @@ using System.Text.Json;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Sphere;
|
||||
using DysonNetwork.Sphere.Chat;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.Poll;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -399,20 +398,12 @@ namespace DysonNetwork.Sphere.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
b.Property<CloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
@@ -5,7 +5,6 @@ using System.Text.Json;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Sphere;
|
||||
using DysonNetwork.Sphere.Chat;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.Poll;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -399,20 +398,12 @@ namespace DysonNetwork.Sphere.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
b.Property<CloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
@@ -5,7 +5,6 @@ using System.Text.Json;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Sphere;
|
||||
using DysonNetwork.Sphere.Chat;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.Poll;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -396,20 +395,12 @@ namespace DysonNetwork.Sphere.Migrations
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<CustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<CustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
b.Property<CloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
@@ -11,7 +11,7 @@ namespace DysonNetwork.Sphere.PageData;
|
||||
public class PostPageData(
|
||||
AppDatabase db,
|
||||
AccountService.AccountServiceClient accounts,
|
||||
PublisherService pub,
|
||||
Publisher.PublisherService pub,
|
||||
PostService ps,
|
||||
IConfiguration configuration
|
||||
)
|
||||
|
@@ -11,7 +11,7 @@ namespace DysonNetwork.Sphere.Poll;
|
||||
|
||||
[ApiController]
|
||||
[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}")]
|
||||
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);
|
||||
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.");
|
||||
|
||||
var answerQuery = db.PollAnswers
|
||||
@@ -186,7 +186,7 @@ public class PollController(AppDatabase db, PollService polls, PublisherService
|
||||
|
||||
var publisher = await pub.GetPublisherByName(pubName);
|
||||
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.");
|
||||
|
||||
var poll = new Poll
|
||||
@@ -231,7 +231,7 @@ public class PollController(AppDatabase db, PollService polls, PublisherService
|
||||
if (poll == null) return NotFound("Poll not found");
|
||||
|
||||
// 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.");
|
||||
|
||||
// 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");
|
||||
|
||||
// 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.");
|
||||
|
||||
// Delete all answers for this poll
|
||||
|
@@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using PublisherService = DysonNetwork.Sphere.Publisher.PublisherService;
|
||||
|
||||
namespace DysonNetwork.Sphere.Post;
|
||||
|
||||
@@ -301,13 +302,13 @@ public class PostController(
|
||||
{
|
||||
// Use the first personal publisher
|
||||
publisher = await db.Publishers.FirstOrDefaultAsync(e =>
|
||||
e.AccountId == accountId && e.Type == PublisherType.Individual);
|
||||
e.AccountId == accountId && e.Type == Publisher.PublisherType.Individual);
|
||||
}
|
||||
else
|
||||
{
|
||||
publisher = await pub.GetPublisherByName(pubName);
|
||||
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.");
|
||||
}
|
||||
|
||||
@@ -473,14 +474,14 @@ public class PostController(
|
||||
if (post is null) return NotFound();
|
||||
|
||||
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.");
|
||||
|
||||
if (pubName is not null)
|
||||
{
|
||||
var publisher = await pub.GetPublisherByName(pubName);
|
||||
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.");
|
||||
post.PublisherId = publisher.Id;
|
||||
post.Publisher = publisher;
|
||||
@@ -552,7 +553,8 @@ public class PostController(
|
||||
.FirstOrDefaultAsync();
|
||||
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.");
|
||||
|
||||
await ps.DeletePostAsync(post);
|
||||
|
@@ -161,7 +161,7 @@ public partial class PostService(
|
||||
{
|
||||
var sender = post.Publisher;
|
||||
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 accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
||||
try
|
||||
@@ -455,7 +455,7 @@ public partial class PostService(
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
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 accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
||||
try
|
||||
|
@@ -5,6 +5,7 @@ using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Sphere.Post;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using NodaTime.Serialization.Protobuf;
|
||||
using VerificationMark = DysonNetwork.Shared.Data.VerificationMark;
|
||||
using Account = DysonNetwork.Pass.Account.Account;
|
||||
|
||||
@@ -49,6 +50,49 @@ public class Publisher : ModelBase, IIdentifiedResource
|
||||
[NotMapped] public Account? Account { get; set; }
|
||||
|
||||
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
|
||||
@@ -68,6 +112,25 @@ public class PublisherMember : ModelBase
|
||||
|
||||
public PublisherMemberRole Role { get; set; } = PublisherMemberRole.Viewer;
|
||||
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
|
||||
|
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 DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Sphere.Connection;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Prometheus;
|
||||
|
||||
@@ -29,6 +30,7 @@ public static class ApplicationConfiguration
|
||||
|
||||
// Map gRPC services
|
||||
app.MapGrpcService<WebSocketHandlerGrpc>();
|
||||
app.MapGrpcService<PublisherServiceGrpc>();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ using System.Threading.RateLimiting;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.GeoIp;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using DysonNetwork.Sphere.Developer;
|
||||
using DysonNetwork.Sphere.Discovery;
|
||||
using DysonNetwork.Sphere.Poll;
|
||||
using DysonNetwork.Sphere.Translation;
|
||||
@@ -169,7 +168,6 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<WebReaderService>();
|
||||
services.AddScoped<WebFeedService>();
|
||||
services.AddScoped<DiscoveryService>();
|
||||
services.AddScoped<CustomAppService>();
|
||||
services.AddScoped<PollService>();
|
||||
|
||||
var translationProvider = configuration["Translation:Provider"]?.ToLower();
|
||||
|
@@ -2,7 +2,6 @@ using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -16,7 +15,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
||||
private async Task<IActionResult> _CheckStickerPackPermissions(
|
||||
Guid packId,
|
||||
Account currentUser,
|
||||
PublisherMemberRole requiredRole
|
||||
Publisher.PublisherMemberRole requiredRole
|
||||
)
|
||||
{
|
||||
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);
|
||||
if (member is null)
|
||||
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");
|
||||
|
||||
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);
|
||||
if (member is null)
|
||||
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");
|
||||
|
||||
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)
|
||||
return Unauthorized();
|
||||
|
||||
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, PublisherMemberRole.Editor);
|
||||
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor);
|
||||
if (permissionCheck is not OkResult)
|
||||
return permissionCheck;
|
||||
|
||||
@@ -278,7 +277,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, PublisherMemberRole.Editor);
|
||||
var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor);
|
||||
if (permissionCheck is not OkResult)
|
||||
return permissionCheck;
|
||||
|
||||
@@ -309,7 +308,7 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi
|
||||
if (request.ImageId is null)
|
||||
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)
|
||||
return permissionCheck;
|
||||
|
||||
|
@@ -9,7 +9,7 @@ namespace DysonNetwork.Sphere.WebReader;
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[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(
|
||||
[MaxLength(8192)] string? Url,
|
||||
@@ -53,7 +53,7 @@ public class WebFeedController(WebFeedService webFeed, PublisherService ps) : Co
|
||||
if (publisher is null) return NotFound();
|
||||
|
||||
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");
|
||||
|
||||
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();
|
||||
|
||||
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");
|
||||
|
||||
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();
|
||||
|
||||
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");
|
||||
|
||||
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();
|
||||
|
||||
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");
|
||||
|
||||
var feed = await webFeed.GetFeedAsync(id, publisherId: publisher.Id);
|
||||
|
@@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Drive", "Dyson
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Gateway", "DysonNetwork.Gateway\DysonNetwork.Gateway.csproj", "{19EB0086-4049-4B78-91C4-EAC37130A006}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DysonNetwork.Develop", "DysonNetwork.Develop\DysonNetwork.Develop.csproj", "{C577AA78-B11D-4076-89A6-1C7F0ECC04E2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
EndGlobal
|
||||
|
Reference in New Issue
Block a user