133 lines
4.6 KiB
Swift
133 lines
4.6 KiB
Swift
import Vapor
|
|
import Leaf
|
|
import PostgresKit
|
|
import SwiftSMTPVapor
|
|
|
|
public struct AdminController<User: ManagedUser>: Sendable where User.SessionID == ExpiringUserId {
|
|
public init() {}
|
|
|
|
public func routes (_ router: any RoutesBuilder, api apiRouter: any RoutesBuilder) {
|
|
let admin = router
|
|
.grouped ("admin")
|
|
.grouped (RoleMiddleware<User> (role: "admin"))
|
|
let api = apiRouter
|
|
.grouped ("admin")
|
|
.grouped (RoleAPIMiddleware<User> (role: "admin"))
|
|
|
|
admin.get (use: adminPage (request:))
|
|
api.get (use: list (request:))
|
|
api.get (":id", use: fetch (request:))
|
|
api.put (use: invite (request:))
|
|
api.post (":id", use: save(request:))
|
|
}
|
|
|
|
struct AdminContext: Encodable {
|
|
let roles: [String]
|
|
let section: String
|
|
}
|
|
|
|
@Sendable
|
|
private func adminPage (request: Request) async throws -> View {
|
|
let roles = try await request.db.withSQLConnection { connection in
|
|
return try await UserRole.all (connection: connection)
|
|
}
|
|
return try await request.view.render ("admin", AdminContext (roles: roles.map (\.name), section: "useradmin"))
|
|
}
|
|
|
|
struct UserItem: Content {
|
|
let id: UUID
|
|
let email: String
|
|
let fullName: String
|
|
let isActive: Bool
|
|
let roles: [String]
|
|
|
|
init(user: User) {
|
|
self.id = user.id
|
|
self.email = user.email
|
|
self.fullName = user.fullName
|
|
self.isActive = user.isActive
|
|
self.roles = user.roles
|
|
}
|
|
}
|
|
|
|
@Sendable
|
|
private func list (request: Request) async throws -> [UserItem] {
|
|
return try await request.db.withSQLConnection { connection in
|
|
let users = try await User.all(on: connection)
|
|
return users.map { UserItem (user: $0) }
|
|
}
|
|
}
|
|
|
|
@Sendable
|
|
private func fetch (request: Request) async throws -> UserItem {
|
|
guard let idString = request.parameters.get ("id"), let id = UUID (uuidString: idString) else {
|
|
throw Abort (.badRequest)
|
|
}
|
|
return try await request.db.withSQLConnection { connection in
|
|
guard let user = try await User.fetch (id, on: connection) else {
|
|
throw Abort (.notFound)
|
|
}
|
|
|
|
return UserItem (user: user)
|
|
}
|
|
}
|
|
|
|
struct Invitation: Decodable {
|
|
let email: String
|
|
let fullname: String
|
|
let roles: [String]
|
|
}
|
|
|
|
@Sendable
|
|
private func invite (request: Request) async throws -> Response {
|
|
|
|
let invitation = try request.content.decode(Invitation.self)
|
|
|
|
return try await request.db.withConnection { connection in
|
|
try await connection.transaction { connection in
|
|
let token = try await UserToken.create (connection: connection).token
|
|
try await User.create (email: invitation.email, fullname: invitation.fullname, roles: invitation.roles, token: token, on: connection)
|
|
let host = try Environment.baseURL.host() ?? ""
|
|
let body = try await request.view.render ("email/invite", ["token": token, "host": host])
|
|
.data
|
|
let message = Email (sender: Email.Contact (emailAddress: try Environment.emailSender),
|
|
recipients: [Email.Contact(emailAddress: invitation.email)],
|
|
subject: "Aktivera ditt konto på \(host)",
|
|
body: .plain (String (buffer: body)))
|
|
try await request.swiftSMTP.mailer.send(message)
|
|
|
|
return Response (status: .created)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Save: Decodable {
|
|
let email: String
|
|
let fullname: String
|
|
let roles: [String]
|
|
let isActive: Bool
|
|
}
|
|
|
|
@Sendable
|
|
private func save (request: Request) async throws -> Response {
|
|
|
|
guard let userId = request.parameters.get("id", as: UUID.self) else {
|
|
throw Abort (.badRequest)
|
|
}
|
|
let save = try request.content.decode(Save.self)
|
|
|
|
return try await request.db.withSQLConnection (user: try request.auth.require (BasicUser.self)) { connection in
|
|
guard (try await User.find (userId, on: connection)) != nil else {
|
|
throw Abort (.notFound)
|
|
}
|
|
guard !save.email.isEmpty && !save.fullname.isEmpty else {
|
|
throw Abort (.badRequest)
|
|
}
|
|
|
|
try await User.save (id: userId, email: save.email, fullname: save.fullname, roles: save.roles, isActive: save.isActive, on: connection)
|
|
|
|
return Response (status: .ok)
|
|
}
|
|
}
|
|
}
|