parent
b79de70345
commit
30bf18a539
14 changed files with 312 additions and 29 deletions
|
@ -2,6 +2,11 @@
|
|||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="HttpUrlsUsage" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
|
@ -1,6 +1,7 @@
|
|||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
|
||||
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
using Iceshrimp.Backend.Controllers.Shared.Attributes;
|
||||
using Iceshrimp.Backend.Core.Database;
|
||||
using Iceshrimp.Backend.Core.Database.Tables;
|
||||
|
@ -8,6 +9,7 @@ using Iceshrimp.Backend.Core.Extensions;
|
|||
using Iceshrimp.Backend.Core.Helpers;
|
||||
using Iceshrimp.Backend.Core.Middleware;
|
||||
using Iceshrimp.Backend.Core.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
|
@ -157,4 +159,44 @@ public class AuthController(DatabaseContext db, MetaService meta) : ControllerBa
|
|||
|
||||
return new object();
|
||||
}
|
||||
|
||||
[Authenticate]
|
||||
[HttpGet("/api/oauth_tokens.json")]
|
||||
[ProducesResults(HttpStatusCode.OK)]
|
||||
public async Task<List<PleromaOauthTokenEntity>> GetOauthTokens()
|
||||
{
|
||||
var user = HttpContext.GetUserOrFail();
|
||||
var oauthTokens = await db.OauthTokens
|
||||
.Where(p => p.User == user)
|
||||
.Include(oauthToken => oauthToken.App)
|
||||
.ToListAsync();
|
||||
|
||||
List<PleromaOauthTokenEntity> result = [];
|
||||
foreach (var token in oauthTokens)
|
||||
{
|
||||
result.Add(new PleromaOauthTokenEntity()
|
||||
{
|
||||
Id = token.Id,
|
||||
AppName = token.App.Name,
|
||||
ValidUntil = token.CreatedAt + TimeSpan.FromDays(365 * 100)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Authenticate]
|
||||
[HttpDelete("/api/oauth_tokens/{id}")]
|
||||
[ProducesResults(HttpStatusCode.Created)]
|
||||
[ProducesErrors(HttpStatusCode.BadRequest, HttpStatusCode.Forbidden)]
|
||||
public async Task RevokeOauthTokenPleroma(string id)
|
||||
{
|
||||
var token = await db.OauthTokens.FirstOrDefaultAsync(p => p.Id == id) ??
|
||||
throw GracefulException.Forbidden("You are not authorized to revoke this token");
|
||||
|
||||
db.Remove(token);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
Response.StatusCode = 201;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
using Iceshrimp.Backend.Core.Configuration;
|
||||
using Iceshrimp.Backend.Core.Database;
|
||||
using Iceshrimp.Backend.Core.Database.Tables;
|
||||
using Iceshrimp.Backend.Core.Extensions;
|
||||
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
|
||||
using Iceshrimp.Backend.Core.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
|
@ -13,7 +15,8 @@ public class UserRenderer(
|
|||
IOptions<Config.InstanceSection> config,
|
||||
IOptionsSnapshot<Config.SecuritySection> security,
|
||||
MfmConverter mfmConverter,
|
||||
DatabaseContext db
|
||||
DatabaseContext db,
|
||||
FlagService flags
|
||||
) : IScopedService
|
||||
{
|
||||
private readonly string _transparent = $"https://{config.Value.WebDomain}/assets/transparent.png";
|
||||
|
@ -48,6 +51,25 @@ public class UserRenderer(
|
|||
var avatarAlt = data?.AvatarAlt.GetValueOrDefault(user.Id);
|
||||
var bannerAlt = data?.BannerAlt.GetValueOrDefault(user.Id);
|
||||
|
||||
string? favicon;
|
||||
string? softwareName;
|
||||
string? softwareVersion;
|
||||
if (user.IsRemoteUser)
|
||||
{
|
||||
var instInfo = await db.Instances
|
||||
.Where(p => p.Host == user.Host)
|
||||
.FirstOrDefaultAsync();
|
||||
favicon = instInfo?.FaviconUrl;
|
||||
softwareName = instInfo?.SoftwareName;
|
||||
softwareVersion = instInfo?.SoftwareVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
favicon = config.Value.WebDomain + "/_content/Iceshrimp.Assets.Branding/favicon.png";
|
||||
softwareName = "iceshrimp";
|
||||
softwareVersion = config.Value.Version;
|
||||
}
|
||||
|
||||
var res = new AccountEntity
|
||||
{
|
||||
Id = user.Id,
|
||||
|
@ -74,7 +96,30 @@ public class UserRenderer(
|
|||
IsBot = user.IsBot,
|
||||
IsDiscoverable = user.IsExplorable,
|
||||
Fields = fields?.ToList() ?? [],
|
||||
Emoji = profileEmoji
|
||||
Emoji = profileEmoji,
|
||||
Pleroma = flags?.IsPleroma.Value == true
|
||||
? new PleromaUserExtensions
|
||||
{
|
||||
IsAdmin = user.IsAdmin,
|
||||
IsModerator = user.IsModerator,
|
||||
Favicon = favicon!
|
||||
} : null,
|
||||
Akkoma = flags?.IsPleroma.Value == true
|
||||
? new AkkomaUserExtensions
|
||||
{
|
||||
Instance = new AkkomaInstanceEntity
|
||||
{
|
||||
Name = user.Host ?? config.Value.AccountDomain,
|
||||
NodeInfo = new AkkomaNodeInfoEntity
|
||||
{
|
||||
Software = new AkkomaNodeInfoSoftwareEntity
|
||||
{
|
||||
Name = softwareName,
|
||||
Version = softwareVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
} : null
|
||||
};
|
||||
|
||||
if (localUser is null && security.Value.PublicPreview == Enums.PublicPreview.RestrictedNoMedia) //TODO
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
using Iceshrimp.Shared.Helpers;
|
||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
|
@ -5,30 +6,32 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
|||
|
||||
public class AccountEntity : IIdentifiable
|
||||
{
|
||||
[J("username")] public required string Username { get; set; }
|
||||
[J("acct")] public required string Acct { get; set; }
|
||||
[J("fqn")] public required string FullyQualifiedName { get; set; }
|
||||
[J("display_name")] public required string DisplayName { get; set; }
|
||||
[J("locked")] public required bool IsLocked { get; set; }
|
||||
[J("created_at")] public required string CreatedAt { get; set; }
|
||||
[J("followers_count")] public required long FollowersCount { get; set; }
|
||||
[J("following_count")] public required long FollowingCount { get; set; }
|
||||
[J("statuses_count")] public required long StatusesCount { get; set; }
|
||||
[J("note")] public required string Note { get; set; }
|
||||
[J("url")] public required string Url { get; set; }
|
||||
[J("uri")] public required string Uri { get; set; }
|
||||
[J("avatar")] public required string AvatarUrl { get; set; }
|
||||
[J("avatar_static")] public required string AvatarStaticUrl { get; set; }
|
||||
[J("header")] public required string HeaderUrl { get; set; }
|
||||
[J("header_static")] public required string HeaderStaticUrl { get; set; }
|
||||
[J("moved")] public required AccountEntity? MovedToAccount { get; set; }
|
||||
[J("bot")] public required bool IsBot { get; set; }
|
||||
[J("discoverable")] public required bool IsDiscoverable { get; set; }
|
||||
[J("fields")] public required List<Field> Fields { get; set; }
|
||||
[J("source")] public AccountSource? Source { get; set; }
|
||||
[J("emojis")] public required List<EmojiEntity> Emoji { get; set; }
|
||||
[J("id")] public required string Id { get; set; }
|
||||
[J("last_status_at")] public string? LastStatusAt { get; set; }
|
||||
[J("username")] public required string Username { get; set; }
|
||||
[J("acct")] public required string Acct { get; set; }
|
||||
[J("fqn")] public required string FullyQualifiedName { get; set; }
|
||||
[J("display_name")] public required string DisplayName { get; set; }
|
||||
[J("locked")] public required bool IsLocked { get; set; }
|
||||
[J("created_at")] public required string CreatedAt { get; set; }
|
||||
[J("followers_count")] public required long FollowersCount { get; set; }
|
||||
[J("following_count")] public required long FollowingCount { get; set; }
|
||||
[J("statuses_count")] public required long StatusesCount { get; set; }
|
||||
[J("note")] public required string Note { get; set; }
|
||||
[J("url")] public required string Url { get; set; }
|
||||
[J("uri")] public required string Uri { get; set; }
|
||||
[J("avatar")] public required string AvatarUrl { get; set; }
|
||||
[J("avatar_static")] public required string AvatarStaticUrl { get; set; }
|
||||
[J("header")] public required string HeaderUrl { get; set; }
|
||||
[J("header_static")] public required string HeaderStaticUrl { get; set; }
|
||||
[J("moved")] public required AccountEntity? MovedToAccount { get; set; }
|
||||
[J("bot")] public required bool IsBot { get; set; }
|
||||
[J("discoverable")] public required bool IsDiscoverable { get; set; }
|
||||
[J("fields")] public required List<Field> Fields { get; set; }
|
||||
[J("source")] public AccountSource? Source { get; set; }
|
||||
[J("emojis")] public required List<EmojiEntity> Emoji { get; set; }
|
||||
[J("id")] public required string Id { get; set; }
|
||||
[J("last_status_at")] public string? LastStatusAt { get; set; }
|
||||
[J("pleroma")] public required PleromaUserExtensions? Pleroma { get; set; }
|
||||
[J("akkoma")] public required AkkomaUserExtensions? Akkoma { get; set; }
|
||||
|
||||
[J("avatar_description")] public required string AvatarDescription { get; set; }
|
||||
[J("header_description")] public required string HeaderDescription { get; set; }
|
||||
|
|
106
Iceshrimp.Backend/Controllers/Pleroma/AdminController.cs
Normal file
106
Iceshrimp.Backend/Controllers/Pleroma/AdminController.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
|
||||
using Iceshrimp.Backend.Controllers.Pleroma.Schemas;
|
||||
using Iceshrimp.Backend.Controllers.Shared.Attributes;
|
||||
using Iceshrimp.Backend.Controllers.Web.Renderers;
|
||||
using Iceshrimp.Backend.Core.Database;
|
||||
using Iceshrimp.Backend.Core.Extensions;
|
||||
using Iceshrimp.Backend.Core.Middleware;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NoteRenderer = Iceshrimp.Backend.Controllers.Mastodon.Renderers.NoteRenderer;
|
||||
using UserRenderer = Iceshrimp.Backend.Controllers.Mastodon.Renderers.UserRenderer;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma;
|
||||
|
||||
[MastodonApiController]
|
||||
[Authenticate]
|
||||
[EnableCors("mastodon")]
|
||||
[EnableRateLimiting("sliding")]
|
||||
[Produces(MediaTypeNames.Application.Json)]
|
||||
public class AdminController(
|
||||
DatabaseContext db,
|
||||
ReportRenderer reportRenderer,
|
||||
NoteRenderer noteRenderer,
|
||||
UserRenderer userRenderer
|
||||
) : ControllerBase
|
||||
{
|
||||
[HttpGet("/api/v1/pleroma/admin/reports")]
|
||||
[Authenticate("admin:read:reports")]
|
||||
[ProducesResults(HttpStatusCode.OK)]
|
||||
public async Task<ReportsQuery> GetReports()
|
||||
{
|
||||
var user = HttpContext.GetUserOrFail();
|
||||
var reports = await db.Reports
|
||||
.IncludeCommonProperties()
|
||||
.ToListAsync();
|
||||
|
||||
var rendered = await reportRenderer.RenderManyAsync(reports);
|
||||
|
||||
var reportsList = new List<Reports>();
|
||||
foreach (var r in rendered)
|
||||
{
|
||||
var reActor = await db.Users
|
||||
.IncludeCommonProperties()
|
||||
.Where(p => p.Id == r.Reporter.Id)
|
||||
.RenderAllForMastodonAsync(userRenderer, user);
|
||||
|
||||
var reTarget = await db.Users
|
||||
.IncludeCommonProperties()
|
||||
.Where(p => p.Id == r.TargetUser.Id)
|
||||
.RenderAllForMastodonAsync(userRenderer, user);
|
||||
|
||||
foreach (var n in r.Notes)
|
||||
{
|
||||
var note = await db.Notes
|
||||
.IncludeCommonProperties()
|
||||
.Where(p => p.Id == n.Id)
|
||||
.RenderAllForMastodonAsync(noteRenderer, user);
|
||||
|
||||
reportsList.Add(new Reports()
|
||||
{
|
||||
Account = reTarget.FirstOrDefault()!,
|
||||
Actor = reActor.FirstOrDefault()!,
|
||||
Id = r.Id,
|
||||
CreatedAt = r.CreatedAt,
|
||||
State = r.Resolved ? "resolved" : "open",
|
||||
Content = r.Comment,
|
||||
Statuses = note,
|
||||
Notes = [] // unsupported
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var resps = new ReportsQuery()
|
||||
{
|
||||
Total = reportsList.Count,
|
||||
Reports = reportsList
|
||||
};
|
||||
|
||||
return resps;
|
||||
}
|
||||
|
||||
[HttpPatch("/api/v1/pleroma/admin/reports")]
|
||||
[Authenticate("admin:read:reports")]
|
||||
[ProducesResults(HttpStatusCode.OK)]
|
||||
[ProducesErrors(HttpStatusCode.NotFound)]
|
||||
// ReSharper disable once AsyncVoidMethod
|
||||
public async Task<ReportsQuery>? SetReportState(ReportsQuery query)
|
||||
{
|
||||
foreach (var list in query.Reports)
|
||||
{
|
||||
var report = await db.Reports.Where(p => p.Id == list.Id).FirstOrDefaultAsync()
|
||||
?? throw GracefulException.NotFound("Report not found");
|
||||
|
||||
report.Resolved = list.State is "resolved" or "closed";
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
|
||||
public class AkkomaInstanceEntity
|
||||
{
|
||||
[J("name")] public required string Name { get; set; }
|
||||
[J("nodeinfo")] public required AkkomaNodeInfoEntity NodeInfo { get; set; }
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
|
||||
public class AkkomaNodeInfoEntity
|
||||
{
|
||||
[J("software")] public required AkkomaNodeInfoSoftwareEntity Software { get; set; }
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
|
||||
public class AkkomaNodeInfoSoftwareEntity
|
||||
{
|
||||
[J("name")] public required string? Name { get; set; }
|
||||
[J("version")] public required string? Version { get; set; }
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
|
||||
[Keyless]
|
||||
public class AkkomaUserExtensions
|
||||
{
|
||||
[J("instance")] public required AkkomaInstanceEntity Instance { get; set; }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.Runtime.InteropServices.JavaScript;
|
||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
|
||||
public class PleromaOauthTokenEntity
|
||||
{
|
||||
[J("id")] public required string Id { get; set; }
|
||||
[J("valid_until")] public required DateTime ValidUntil { get; set; }
|
||||
[J("app_name")] public required string? AppName { get; set; }
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||
|
||||
[Keyless]
|
||||
public class PleromaUserExtensions
|
||||
{
|
||||
[J("is_admin")] public required bool IsAdmin { get; set; }
|
||||
[J("is_moderator")] public required bool IsModerator { get; set; }
|
||||
[J("favicon")] public required string Favicon { get; set; }
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas;
|
||||
|
||||
public class ReportsQuery
|
||||
{
|
||||
[J("total")] public int Total { get; set; }
|
||||
[J("reports")] public required List<Reports> Reports { get; set; }
|
||||
}
|
||||
|
||||
public class Reports
|
||||
{
|
||||
[J("account")] public AccountEntity? Account { get; set; }
|
||||
[J("actor")] public AccountEntity? Actor { get; set; }
|
||||
[J("id")] public required string Id { get; set; }
|
||||
[J("created_at")] public DateTime? CreatedAt { get; set; }
|
||||
[J("state")] public required string State { get; set; }
|
||||
[J("content")] public string? Content { get; set; }
|
||||
[J("statuses")] public IEnumerable<StatusEntity>? Statuses { get; set; }
|
||||
[J("notes")] public string[]? Notes { get; set; }
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"externalUrlConfiguration": true,
|
||||
"commandLineArgs": "--migrate-and-start",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ ListenHost = localhost
|
|||
;;ListenSocketPerms = 660
|
||||
|
||||
;; Caution: changing these settings after initial setup *will* break federation
|
||||
WebDomain = shrimp.example.org
|
||||
AccountDomain = example.org
|
||||
WebDomain = localhost:3000
|
||||
AccountDomain = localhost:3000
|
||||
;; End of problematic settings block
|
||||
|
||||
;; Additional domains this instance allows API access from, separated by commas.
|
||||
|
@ -184,7 +184,7 @@ ProxyRemoteMedia = true
|
|||
|
||||
[Storage:Local]
|
||||
;; Path where media is stored at. Must be writable for the service user.
|
||||
Path = /path/to/media/location
|
||||
Path = /home/luke/Documents/shrimp
|
||||
|
||||
[Storage:ObjectStorage]
|
||||
;;Endpoint = endpoint.example.org
|
||||
|
|
Loading…
Add table
Reference in a new issue