import Fluent import Vapor /// The user database model. /// /// A user is someone who is able to login and generate PO's for employees. Generally a user should also /// have an employee profile, but not all employees are users. Users are generally restricted to office workers /// and administrators. /// /// final class User: Model, @unchecked Sendable { static let schema = "user" @ID(key: .id) var id: UUID? @Field(key: "username") var username: String @Field(key: "email") var email: String @Field(key: "password_hash") var passwordHash: String @Timestamp(key: "created_at", on: .create) var createdAt: Date? @Timestamp(key: "updated_at", on: .update) var updatedAt: Date? init() {} init( id: UUID? = nil, username: String, email: String, passwordHash: String ) { self.id = id self.username = username self.email = email self.passwordHash = passwordHash } func toDTO() -> DTO { .init( id: id, username: $username.value, email: $email.value, createdAt: createdAt, updatedAt: updatedAt ) } func generateToken() throws -> UserToken { try .init( value: [UInt8].random(count: 16).base64, userID: requireID() ) } } extension User { struct Create: Content { var username: String var email: String var password: String var confirmPassword: String } struct DTO: Content { let id: UUID? let username: String? let email: String? let createdAt: Date? let updatedAt: Date? } struct Migrate: AsyncMigration { let name = "CreateUser" func prepare(on database: any Database) async throws { try await database.schema(User.schema) .id() .field("username", .string, .required) .field("email", .string, .required) .field("password_hash", .string, .required) .field("created_at", .datetime) .field("updated_at", .datetime) .unique(on: "email", "username") .create() } func revert(on database: any Database) async throws { try await database.schema(User.schema).delete() } } } extension User: ModelAuthenticatable { static let usernameKey = \User.$username static let passwordHashKey = \User.$passwordHash func verify(password: String) throws -> Bool { try Bcrypt.verify(password, created: passwordHash) } } extension User: ModelSessionAuthenticatable {} extension User: ModelCredentialsAuthenticatable {} extension User.Create: Validatable { static func validations(_ validations: inout Validations) { validations.add("username", as: String.self, is: !.empty) validations.add("email", as: String.self, is: .email) validations.add("password", as: String.self, is: .count(8...)) } }