diff --git a/Sources/VernissageServer/Controllers/SettingsController.swift b/Sources/VernissageServer/Controllers/SettingsController.swift index 3cca1ce6..80393c42 100644 --- a/Sources/VernissageServer/Controllers/SettingsController.swift +++ b/Sources/VernissageServer/Controllers/SettingsController.swift @@ -367,6 +367,13 @@ final class SettingsController { transaction: database) } + if settingsDto.systemDefaultUserId != settings.getString(.systemDefaultUserId) { + try await self.update(.systemDefaultUserId, + with: .string(settingsDto.systemDefaultUserId), + on: request, + transaction: database) + } + try await self.update(.eventsToStore, with: .string(settingsDto.eventsToStore.map({ $0.rawValue }).joined(separator: ",")), on: request, diff --git a/Sources/VernissageServer/DataTransferObjects/SettingsDto.swift b/Sources/VernissageServer/DataTransferObjects/SettingsDto.swift index a7a593fd..736fe641 100644 --- a/Sources/VernissageServer/DataTransferObjects/SettingsDto.swift +++ b/Sources/VernissageServer/DataTransferObjects/SettingsDto.swift @@ -35,6 +35,7 @@ struct SettingsDto { var maximumNumberOfInvitations: Int var corsOrigin: String var eventsToStore: [EventType] + var systemDefaultUserId: String init(basedOn settings: [Setting]) { self.isRegistrationOpened = settings.getBool(.isRegistrationOpened) ?? false @@ -66,6 +67,7 @@ struct SettingsDto { self.webThumbnail = settings.getString(.webThumbnail) ?? "" self.webLanguages = settings.getString(.webLanguages) ?? "" self.webContactUserId = settings.getString(.webContactUserId) ?? "" + self.systemDefaultUserId = settings.getString(.systemDefaultUserId) ?? "" } } diff --git a/Sources/VernissageServer/Extensions/Application+Seed.swift b/Sources/VernissageServer/Extensions/Application+Seed.swift index 46ca387f..8c6421b6 100644 --- a/Sources/VernissageServer/Extensions/Application+Seed.swift +++ b/Sources/VernissageServer/Extensions/Application+Seed.swift @@ -25,6 +25,7 @@ extension Application { func seedAdmin() async throws { let database = self.db try await users(on: database) + try await setSystemDefaultUser(on: database) } private func settings(on database: Database) async throws { @@ -45,7 +46,8 @@ extension Application { try await ensureSettingExists(on: database, existing: settings, key: .maxCharacters, value: .int(500)) try await ensureSettingExists(on: database, existing: settings, key: .maxMediaAttachments, value: .int(4)) try await ensureSettingExists(on: database, existing: settings, key: .imageSizeLimit, value: .int(10_485_760)) - + try await ensureSettingExists(on: database, existing: settings, key: .systemDefaultUserId, value: .string("")) + // Recaptcha. try await ensureSettingExists(on: database, existing: settings, key: .isRecaptchaEnabled, value: .boolean(false)) try await ensureSettingExists(on: database, existing: settings, key: .recaptchaKey, value: .string("")) @@ -100,6 +102,25 @@ extension Application { try await ensureAdminExist(on: database) } + private func setSystemDefaultUser(on database: Database) async throws { + guard let systemDefaultUserIdSetting = try await Setting.query(on: database) + .filter(\.$key == SettingKey.systemDefaultUserId.rawValue) + .first() else { + return + } + + if systemDefaultUserIdSetting.value != "" { + return + } + + guard let systemUser = try await User.query(on: database).filter(\.$userName == "admin").first() else { + return + } + + systemDefaultUserIdSetting.value = systemUser.stringId() ?? "" + try await systemDefaultUserIdSetting.save(on: database) + } + private func licenses(on database: Database) async throws { let licenses = try await License.query(on: database).all() diff --git a/Sources/VernissageServer/Models/Setting.swift b/Sources/VernissageServer/Models/Setting.swift index cfaab9dc..cbc9dc56 100644 --- a/Sources/VernissageServer/Models/Setting.swift +++ b/Sources/VernissageServer/Models/Setting.swift @@ -61,6 +61,7 @@ public enum SettingKey: String { case maxCharacters case maxMediaAttachments case imageSizeLimit + case systemDefaultUserId // Recaptcha. case isRecaptchaEnabled diff --git a/Sources/VernissageServer/Services/ActivityPubService.swift b/Sources/VernissageServer/Services/ActivityPubService.swift index 35ab650c..19a75712 100644 --- a/Sources/VernissageServer/Services/ActivityPubService.swift +++ b/Sources/VernissageServer/Services/ActivityPubService.swift @@ -635,16 +635,17 @@ final class ActivityPubService: ActivityPubServiceType { throw ActivityPubError.invalidNoteUrl(activityPubId) } - guard let user = try await User.query(on: context.application.db).filter(\.$userName == "admin").first() else { + let usersService = context.application.services.usersService + guard let defaultSystemUser = try await usersService.getDefaultSystemUser(on: context.application.db) else { throw ActivityPubError.missingInstanceAdminAccount } - - guard let privateKey = user.privateKey else { + + guard let privateKey = defaultSystemUser.privateKey else { throw ActivityPubError.missingInstanceAdminPrivateKey } let activityPubClient = ActivityPubClient(privatePemKey: privateKey, userAgent: Constants.userAgent, host: noteUrl.host) - return try await activityPubClient.note(url: noteUrl, activityPubProfile: user.activityPubProfile) + return try await activityPubClient.note(url: noteUrl, activityPubProfile: defaultSystemUser.activityPubProfile) } catch { context.logger.error("Error during download status: '\(activityPubId)'. Error: \(error).") throw ActivityPubError.statusHasNotBeenDownloaded(activityPubId) diff --git a/Sources/VernissageServer/Services/SearchService.swift b/Sources/VernissageServer/Services/SearchService.swift index e4c80607..0c442301 100644 --- a/Sources/VernissageServer/Services/SearchService.swift +++ b/Sources/VernissageServer/Services/SearchService.swift @@ -104,11 +104,12 @@ final class SearchService: SearchServiceType { private func downloadProfile(activityPubProfile: String, application: Application) async -> PersonDto? { do { - guard let user = try await User.query(on: application.db).filter(\.$userName == "admin").first() else { + let usersService = application.services.usersService + guard let defaultSystemUser = try await usersService.getDefaultSystemUser(on: application.db) else { throw ActivityPubError.missingInstanceAdminAccount } - guard let privateKey = user.privateKey else { + guard let privateKey = defaultSystemUser.privateKey else { throw ActivityPubError.missingInstanceAdminPrivateKey } @@ -117,7 +118,7 @@ final class SearchService: SearchServiceType { } let activityPubClient = ActivityPubClient(privatePemKey: privateKey, userAgent: Constants.userAgent, host: activityPubProfileUrl.host) - let userProfile = try await activityPubClient.person(id: activityPubProfile, activityPubProfile: user.activityPubProfile) + let userProfile = try await activityPubClient.person(id: activityPubProfile, activityPubProfile: defaultSystemUser.activityPubProfile) return userProfile } catch { diff --git a/Sources/VernissageServer/Services/UsersService.swift b/Sources/VernissageServer/Services/UsersService.swift index bd5f20e2..f61e7bcc 100644 --- a/Sources/VernissageServer/Services/UsersService.swift +++ b/Sources/VernissageServer/Services/UsersService.swift @@ -34,6 +34,7 @@ protocol UsersServiceType { func get(on database: Database, account: String) async throws -> User? func get(on database: Database, activityPubProfile: String) async throws -> User? func getModerators(on database: Database) async throws -> [User] + func getDefaultSystemUser(on database: Database) async throws -> User? func login(on request: Request, userNameOrEmail: String, password: String) async throws -> User func login(on request: Request, authenticateToken: String) async throws -> User func forgotPassword(on request: Request, email: String) async throws -> User @@ -817,4 +818,22 @@ final class UsersService: UsersServiceType { } } } + + func getDefaultSystemUser(on database: Database) async throws -> User? { + guard let systemDefaultUserIdSetting = try await Setting.query(on: database) + .filter(\.$key == SettingKey.systemDefaultUserId.rawValue) + .first() else { + return nil + } + + if systemDefaultUserIdSetting.value == "" { + return nil + } + + guard let systemUserId = systemDefaultUserIdSetting.value.toId() else { + return nil + } + + return try await User.query(on: database).filter(\.$id == systemUserId).first() + } }