R1 cleanup: collAnon and dead code gone, docs current, every peer's scenario in one sweep
Build / Build (push) Successful in 1m7s

Starts the completion program (docs/ROADMAP.md, "Owner decisions on finishing everything", 2026-10-17).

- Dead code: the commented-out collAnon blocks (discussions, seeding, admin user lists, HTTP/3, the old signature
  middleware), the empty PrivaPubWorkersConfiguration, PublicAvatarUsersService and UserPolicyType, unused helpers,
  the second IPasswordHasher registration, the PasswordGenerator package, SignUpAsync/LoginAsync's unused invitation
  parameters. "Starting PrivaPub"; a signed-in login is sent to "/", not to "/discussions".
- Stored fields nothing read: RootUser.Contacts and ResetPasswordToken*, the local Avatar.AvatarServer,
  EmailRecovery.RecoveryCode/RequestIP/CreationDate, MongoSettings.LogsDatabase, and AppConfiguration's
  upload/discussion keys, RequiresFirstTimeSetup and Version (old documents still load: MongoDB.Entities ignores extra
  elements). DmPost's indexes are no longer made. 49 collAnon keys leave FieldsNameResource in every language.
- /clientapi/data/current-version answers the build's ref. UsersIds is a list that can't throw, and the admin routes
  leave the administrator out without mutating the request.
- deploy.yml checks SecureMode on a name no persona can have, and the browser redirect on the directory's first local
  persona: @thepra is the administrator's own now.
- Town: decePub's e2e remnants (expect, client.jsonl, the decePub column) and town/tests go; the dead gaps/load
  commands too. Mobilizon no longer keeps an event for a deleted test.
- tools/pasture/sweep.sh runs every peer's scenario, batch by batch, as CLAUDE.md asks before a change to what
  PrivaPub sends.
- Docs: CLAUDE.md's repo map, route table (every frozen route) and stale lines; ROADMAP's status (setup, v1.25.0,
  v1.26.0, the program C), the 2026-10-17 decisions, and dated notes where earlier text no longer holds; FEDERATION.md's
  FEP list (7888, f228, 7628, ae0c and 8967 supported) and the deploy's persona.

Verified: dotnet build PrivaPub.sln -c Release (0 errors, no new warnings); bash -n on the touched scripts; py_compile
on the town; the pasture's gts, mastodon and lemmy scenarios on this build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-17 02:56:57 +02:00
1 parent 6727e1817a
commit ff5e635dda
71 files changed
+238 -4767

No files matched your search

@@ -29,14 +29,20 @@ namespace PrivaPub.Controllers.ClientToServer
RootUsersService = rootUsersService;
}
// an administrator never removes, bans or unbans itself; the request's own list is left as it came
UsersIds Others(UsersIds usersIds)
{
var selfId = User.GetUserId();
return new UsersIds { UserIdList = usersIds.UserIdList.Where(id => id != selfId).ToList() };
}
[HttpDelete, Route("/clientapi/admin/remove/users"), Authorize(Policy = Policies.IsAdmin)]
public async Task<IActionResult> RemoveUsers([Required] UsersIds usersIds)
{
var result = new WebResult();
try
{
usersIds.UserIdList.Remove(User.GetUserId());
result = await RootUsersService.RemoveUserAsync(usersIds);
result = await RootUsersService.RemoveUserAsync(Others(usersIds));
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
@@ -57,13 +63,7 @@ namespace PrivaPub.Controllers.ClientToServer
var result = new WebResult();
try
{
//var isUserResult = await UsersService.UserIsAdminAsync(User.GetUserId());
//if (isUserResult.IsValid && !(bool)isUserResult.Data)
//if (isUserResult is { IsValid: true } and not { Data: bool })
// return Unauthorized();
usersIds.UserIdList.Remove(User.GetUserId());
result = await RootUsersService.BanUserAsync(usersIds);
result = await RootUsersService.BanUserAsync(Others(usersIds));
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
@@ -85,12 +85,7 @@ namespace PrivaPub.Controllers.ClientToServer
var result = new WebResult();
try
{
//var isUserResult = await UsersService.UserIsAdminAsync(User.GetUserId());
//if (isUserResult.IsValid && !(bool)isUserResult.Data)
// return Unauthorized();
usersIds.UserIdList.Remove(User.GetUserId());
result = await RootUsersService.UnbanUserAsync(usersIds);
result = await RootUsersService.UnbanUserAsync(Others(usersIds));
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
@@ -102,28 +97,5 @@ namespace PrivaPub.Controllers.ClientToServer
return BadRequest(result.Invalidate(Localizer["Something went wrong."]));
}
}
//[HttpGet, Authorize(Policy = Policies.IsAdmin)]
//public async Task<IActionResult> GetUsers()
//{
// var result = new WebResult();
// try
// {
// var isUserResult = await UsersService.UserIsAdminAsync(User.GetUserId());
// if (isUserResult.IsValid && !(bool)isUserResult.Data)
// return Unauthorized();
// result = await UsersService.GetUsersAsync();
// if (!result.IsValid)
// return StatusCode(result.StatusCode, result);
// return Ok(result.Data);
// }
// catch (Exception ex)
// {
// Logger.LogError(ex, $"{nameof(User)}.{nameof(GetUsers)}()");
// return BadRequest(result.Invalidate(Localizer["Something went wrong."]));
// }
//}
}
}
@@ -92,7 +92,7 @@ namespace PrivaPub.Controllers.ClientToServer
[HttpPost, Route("/clientapi/user/login"), EnableRateLimiting(RateLimiting.Accounts), Authorize(Policy = Policies.IsUser), AllowAnonymous]
public async Task<IActionResult> Login(LoginForm loginForm)
{
if (User.Identity?.IsAuthenticated ?? false) return Redirect("/discussions");
if (User.Identity?.IsAuthenticated ?? false) return Redirect("/");
var result = new WebResult();
if (!ModelState.IsValid)
return BadRequest(result.Invalidate(Localizer["Invalid model."]));
@@ -132,7 +132,7 @@ namespace PrivaPub.Controllers.ClientToServer
if (personaProblem != default)
return BadRequest(result.Invalidate(personaProblem));
result = await UsersService.SignUpAsync(signUpForm, signUpForm.InvitationCode);
result = await UsersService.SignUpAsync(signUpForm);
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
var (user, userSettings) = ((RootUser, ViewUserSettings))result.Data;
@@ -171,7 +171,7 @@ namespace PrivaPub.Controllers.ClientToServer
if (!invitation.IsValid)
return StatusCode(invitation.StatusCode, invitation);
result = await UsersService.LoginAsync(loginForm, loginForm.InvitationCode);
result = await UsersService.LoginAsync(loginForm);
if (!result.IsValid)
return StatusCode(result.StatusCode, result);
var (user, userSettings) = ((RootUser, ViewUserSettings))result.Data;
@@ -370,25 +370,6 @@ namespace PrivaPub.Controllers.ClientToServer
}
}
//[HttpGet, Authorize(Policy = Policies.IsUser)]
//public async Task<IActionResult> GetUser()
//{
// var result = new WebResult();
// try
// {
// result = await UsersService.GetUserAsync(User.GetUserId());
// if (!result.IsValid)
// return StatusCode(result.StatusCode, result);
// return Ok(result.Data);
// }
// catch (Exception ex)
// {
// Logger.LogError(ex, $"{nameof(User)}.{nameof(GetUser)}()");
// return BadRequest(result.Invalidate(Localizer["Something went wrong."]));
// }
//}
// what the login's media take, every persona's, and its quota when the server sets one
[HttpGet, Route("/clientapi/user/storage"), Authorize(Policy = Policies.IsUser)]
public async Task<IActionResult> GetStorage([FromServices] Microsoft.Extensions.Options.IOptionsMonitor<Domain.Media.MediaOptions> media, CancellationToken token)