Files
SocialPub/PrivaPub/Data/InitDb.cs
T
thepraandClaude Opus 5.5 075c22228a
Build / Build (push) Successful in 28s
Deploy / privapub.thepra.dev (push) Successful in 19s
net10, the refactor finished, and federation that works
The tree had not compiled since its first commit: Group, DmGroup and
IGroupUsersService were referenced and never written, an IDE rename had
turned the user-settings DTO into the ViewAvatarServer enum, and the settings
were saved as an entity they no longer were.

Built now:
- Group (an ActivityPub Group actor with its own keys, members, invitation
  code and optional password) and DmGroup (a conversation), with
  /clientapi/group/{list,insert,update,join,leave,approve}.
- Posts and DMs: /clientapi/post/{list,insert,delete}, /clientapi/dm/{list,insert};
  DM recipients are local usernames or user@host handles resolved by WebFinger.
- Invitation sign-up and login against the group's invitation code, checking
  the password before any account is created.
- Federation: WebFinger, NodeInfo 2.0, actors at /peasants/{name} (Person,
  Group, and an Application instance actor) with SPKI keys, draft-cavage
  RSA-SHA256 HTTP signatures both ways, an inbox handling Follow (+Accept),
  Undo, Create, Delete and Update, an outbox, notes at /posts/{id}, and a
  persisted, retried, signed delivery queue. A post to a group is announced
  by the group to its followers (FEP-1b12).
- The unused, broken typed ActivityPub models are replaced by a renderer;
  NSign's HMAC setup, which could not federate, is gone.

Upgrade: net10.0, MongoDB.Entities 25.1 (instance DB API, Standard GUIDs),
Swashbuckle 10 / OpenApi 2, Serilog.AspNetCore 10, MailKit 4.18,
PasswordGenerator 3. The JWT keys are 64 bytes (IdentityModel 8 refuses
shorter for HS512). Production runs its own mongod on 127.0.0.1:27022, as
Sintopia's apps do, and deploys to privapub.thepra.dev from the build runner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
2026-10-01 08:05:56 +02:00

213 lines
6.1 KiB
C#

using MongoDB.Entities;
using PrivaPub.Models.Data;
using PrivaPub.StaticServices;
using System.Text;
using System.Text.Json;
namespace PrivaPub.Data
{
public static class InitDb
{
public static async Task Init(this DbEntities dbClient, IPasswordHasher passwordHasher)
{
await SyncLanguages(dbClient);
await UpdateNewLanguages(dbClient);
//await SyncUserSettings(dbClient);
//await ClearUsers(dbClient);
//var botsInsertionAwaiter = InsertBots(dbClient, passwordHasher);
//var updateAnswersAwaiter = UpdateAnswersLikes(dbClient);
//Task.WaitAll(botsInsertionAwaiter);
//await SyncShareDiscussions(dbClient);
}
//static async Task UpdateAnswersLikes(DbCollections dbClient)
//{
// if (await dbClient.AnswersLikes.Find(al => al.AnswerId != null).AnyAsync()) return;
// foreach (var answerLikes in await dbClient.AnswersLikes.Find(al => true).ToListAsync())
// {
// answerLikes.AnswerId = answerLikes.Id;
// answerLikes.Id = ObjectId.GenerateNewId();
// await dbClient.AnswersLikes.InsertOneAsync(answerLikes);
// _ = await dbClient.AnswersLikes.DeleteOneAsync(al => al.Id == answerLikes.AnswerId);
// }
//}
//static async Task ClearUsers(DbColl dbClient)
//{
// await dbClient.Users.DeleteManyAsync(u => true);
// await dbClient.UsersSettings.DeleteManyAsync(u => true);
// await dbClient.Answers.DeleteManyAsync(u => true);
// await dbClient.AnswersLikes.DeleteManyAsync(u => true);
// await dbClient.Comments.DeleteManyAsync(u => true);
// await dbClient.EmailRecoveries.DeleteManyAsync(u => true);
// await dbClient.Threads.DeleteManyAsync(u => true);
// await dbClient.EDiscussions.DeleteManyAsync(u => true);
// await dbClient.Logs.DeleteManyAsync(u => true);
// await dbClient.ShareDiscussions.De(u => true);
// await dbClient.DiscussionFiles.DeleteManyAsync(u => true);
// await dbClient.UsersGroups.DeleteManyAsync(u => true);
//}
// static async Task SyncShareDiscussions(DbColl dbClient)
// {
// var anyThread = await dbClient.Discussions.Match(t => true).ExecuteAnyAsync();
// var anyShareDiscussion = await dbClient.ShareDiscussions.Match(st => true).ExecuteAnyAsync();
//
// if (anyThread == anyShareDiscussion) return;
//
// var discussions = await dbClient.Discussions.ManyAsync(t => true);
//
// foreach (var discussion in discussions)
// {
// var newShareDiscussion = new ShareDiscussion
// {
// DiscussionId = discussion.ID,
// InvitationCode = $"{Guid.NewGuid():N}{Guid.NewGuid():N}"
// };
// await newShareDiscussion.SaveAsync();
// }
// }
static async Task SyncLanguages(DbEntities dbClient)
{
if (await dbClient.Languages.ExecuteAnyAsync()) return;
var languagesJson = await File.ReadAllTextAsync(Path.Combine(Directory.GetCurrentDirectory(), "Data", "languagesNative.json"), Encoding.UTF8);
var languagesRows = JsonSerializer.Deserialize<IEnumerable<LanguagesRow>>(languagesJson);
var languages = new List<Language>();
foreach (var languageRow in languagesRows ?? new LanguagesRow[]
{
})
languages.Add(new Language
{
EnglishName = languageRow.EnglishName,
NativeName = languageRow.NativeName,
International2Code = languageRow.International2Code
});
await DB.Default.SaveAsync(languages);
}
static async Task UpdateNewLanguages(DbEntities dbClient)
{
var bulgarianLanguage = await dbClient.Languages.Match(l => l.International2Code == "bg").ExecuteFirstAsync();
if (bulgarianLanguage.NativeName == "Български") return;
var languagesToUpdate = new Dictionary<string, string>()
{
{
"bg", "Български"
},
{
"cs", "Česky"
},
{
"da", "Dansk"
},
{
"nl", "Nederlands"
},
{
"et", "Eesti"
},
{
"fi", "Suomalainen"
},
{
"el", "Ελληνική"
},
{
"hu", "Magyar"
},
{
"lv", "Latviešu"
},
{
"lt", "Lietuvių kalba"
},
{
"pl", "Polski"
},
{
"pt", "Português"
},
{
"ro", "Românesc"
},
{
"sk", "Slovenská"
},
{
"sl", "Slovenski"
},
{
"sv", "Svenska"
}
};
foreach (var languageToUpdate in languagesToUpdate)
{
var bgLang = await dbClient.Languages.Match(l => l.International2Code == languageToUpdate.Key).ExecuteFirstAsync();
bgLang.NativeName = languageToUpdate.Value;
await DB.Default.SaveAsync(bgLang);
}
}
// static async Task SyncUserSettings(DbColl dbClient)
// {
// if (!await dbClient.Users.ExecuteAnyAsync()) return;
//
// var users = await dbClient.Users.ManyAsync(u => true);
// var usersId = users.Select(u => u.ID).ToList();
// var usersSettings = await dbClient.UsersSettings
// .ManyAsync(us => true);
//
// var usersWithoutSettings = users.Where(u => !usersSettings.Any(us => us.UserId == u.ID)).Select(u => u.ID).ToList();
// if (usersWithoutSettings.Count == 0) return;
//
// var defaultLanguage = await dbClient.Languages
// .Match(l => l.International2Code == "en")
// .ExecuteFirstAsync();
// foreach (var userId in usersWithoutSettings)
// await DB.SaveAsync(new UserSettings
// {
// UserId = userId,
// LanguageId = defaultLanguage.International2Code
// });
// }
//static async Task InsertBots(DbEntities dbClient, IPasswordHasher passwordHasher)
//{
// if (await dbClient.Users.Match(u => u.UserName == "bot0").ExecuteAnyAsync()) return;
// var newBots = new List<User>();
// var newBotsUserSettings = new List<UserSettings>();
// for (int i = 0; i < 100; i++)
// newBots.Add(new User
// {
// HashedPassword = passwordHasher.Hash("Asdfmov13!!!"),
// UserName = $"bot{i}"
// });
// await newBots.SaveAsync();
// var defaultLanguage = await dbClient.Languages
// .Match(l => l.International2Code == "en")
// .ExecuteFirstAsync();
// foreach (var newBot in newBots)
// newBotsUserSettings.Add(new()
// {
// UserId = newBot.ID,
// LanguageId = defaultLanguage.International2Code,
// });
// await newBotsUserSettings.SaveAsync();
//}
}
}