Initial commit
This commit is contained in:
108
Sources/ExodaiAcademy/Infrastructure/Database/ArticleModel.swift
Normal file
108
Sources/ExodaiAcademy/Infrastructure/Database/ArticleModel.swift
Normal file
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// ArticleModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class ArticleModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.articles.rawValue
|
||||
|
||||
// MARK: - ID
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
// MARK: - CMS Fields (all optional)
|
||||
|
||||
@OptionalField(key: FieldKeys.title)
|
||||
var title: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.description)
|
||||
var description: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.slug)
|
||||
var slug: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.excerpt)
|
||||
var excerpt: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.image)
|
||||
var image: String?
|
||||
|
||||
@OptionalEnum(key: FieldKeys.status)
|
||||
var status: Status?
|
||||
|
||||
@OptionalField(key: FieldKeys.authorID)
|
||||
var authorID: UserModel.IDValue?
|
||||
|
||||
@OptionalField(key: FieldKeys.categories)
|
||||
var categories: [CategoryModel.IDValue]?
|
||||
|
||||
@OptionalField(key: FieldKeys.tags)
|
||||
var tags: [TagModel.IDValue]?
|
||||
|
||||
// MARK: - Timestamps
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.updatedAt, on: .update)
|
||||
var updatedAt: Date?
|
||||
|
||||
@OptionalField(key: FieldKeys.publishDate)
|
||||
var publishDate: Date?
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
extension ArticleModel {
|
||||
struct FieldKeys {
|
||||
static var title: FieldKey { "title" }
|
||||
static var description: FieldKey { "description" }
|
||||
static var slug: FieldKey { "slug" }
|
||||
static var excerpt: FieldKey { "excerpt" }
|
||||
static var image: FieldKey { "image" }
|
||||
static var status: FieldKey { "status" }
|
||||
static var authorID: FieldKey { "authorID" }
|
||||
static var categories: FieldKey { "categories" }
|
||||
static var tags: FieldKey { "tags" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
static var updatedAt: FieldKey { "updatedAt" }
|
||||
static var publishDate: FieldKey { "publishDate" }
|
||||
}
|
||||
}
|
||||
|
||||
import Fluent
|
||||
|
||||
extension ArticleModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(ArticleModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.title, .string)
|
||||
.field(FieldKeys.description, .string)
|
||||
.field(FieldKeys.slug, .string)
|
||||
.field(FieldKeys.excerpt, .string)
|
||||
.field(FieldKeys.image, .string)
|
||||
.field(FieldKeys.status, .string)
|
||||
.field(FieldKeys.authorID, .uuid)
|
||||
.field(FieldKeys.categories, .array(of: .uuid))
|
||||
.field(FieldKeys.tags, .array(of: .uuid))
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.field(FieldKeys.updatedAt, .datetime)
|
||||
.field(FieldKeys.publishDate, .datetime)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(ArticleModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
107
Sources/ExodaiAcademy/Infrastructure/Database/CampusModel.swift
Normal file
107
Sources/ExodaiAcademy/Infrastructure/Database/CampusModel.swift
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// CampusModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class CampusModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.campuses.rawValue
|
||||
|
||||
// MARK: - ID
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
@OptionalField(key: FieldKeys.title)
|
||||
var title: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.description)
|
||||
var description: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.slug)
|
||||
var slug: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.content)
|
||||
var content: String?
|
||||
|
||||
@Enum(key: FieldKeys.status)
|
||||
var status: Status
|
||||
|
||||
@Field(key: FieldKeys.instructorID)
|
||||
var instructorID: UserModel.IDValue
|
||||
|
||||
@OptionalField(key: FieldKeys.price)
|
||||
var price: Double?
|
||||
|
||||
@OptionalField(key: FieldKeys.tags)
|
||||
var tags: [TagModel.IDValue]?
|
||||
|
||||
@OptionalField(key: FieldKeys.categories)
|
||||
var categories: [CategoryModel.IDValue]?
|
||||
|
||||
// MARK: - Timestamps
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.updatedAt, on: .update)
|
||||
var updatedAt: Date?
|
||||
|
||||
@Field(key: FieldKeys.publishDate)
|
||||
var publishDate: Date?
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension CampusModel {
|
||||
struct FieldKeys {
|
||||
static var title: FieldKey { "title" }
|
||||
static var description: FieldKey { "description" }
|
||||
static var slug: FieldKey {"slug"}
|
||||
static var content: FieldKey { "content" }
|
||||
static var status: FieldKey { "status" }
|
||||
static var instructorID: FieldKey { "instructorID" }
|
||||
static var price: FieldKey { "price" }
|
||||
static var tags: FieldKey { "tags" }
|
||||
static var categories: FieldKey { "categories" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
static var updatedAt: FieldKey { "updatedAt" }
|
||||
static var publishDate: FieldKey { "publishDate" }
|
||||
}
|
||||
}
|
||||
|
||||
import Fluent
|
||||
|
||||
extension CampusModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(CampusModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.title, .string)
|
||||
.field(FieldKeys.description, .string)
|
||||
.field(FieldKeys.slug, .string)
|
||||
.field(FieldKeys.content, .string)
|
||||
.field(FieldKeys.status, .string, .required)
|
||||
.field(FieldKeys.instructorID, .uuid, .required)
|
||||
.field(FieldKeys.price, .double)
|
||||
.field(FieldKeys.tags, .array(of: .uuid))
|
||||
.field(FieldKeys.categories, .array(of: .uuid))
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.field(FieldKeys.updatedAt, .datetime)
|
||||
.field(FieldKeys.publishDate, .datetime)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(CampusModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// CategoryModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class CategoryModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.categories.rawValue
|
||||
|
||||
// MARK: - ID
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
@Field(key: FieldKeys.name)
|
||||
var name: String
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
init() {}
|
||||
|
||||
init(
|
||||
id: UUID? = nil,
|
||||
name: String
|
||||
) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
extension CategoryModel {
|
||||
struct FieldKeys {
|
||||
static var name: FieldKey { "name" }
|
||||
}
|
||||
}
|
||||
|
||||
extension CategoryModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(CategoryModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.name, .string, .required)
|
||||
.unique(on: FieldKeys.name)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(CategoryModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// ConfirmationModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
import Vapor
|
||||
|
||||
import Fluent
|
||||
|
||||
final class ConfirmationModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.confirmations.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.userID)
|
||||
var userID: UserModel.IDValue
|
||||
|
||||
@Field(key: FieldKeys.confirmationCode)
|
||||
var confirmationCode: String
|
||||
|
||||
@Field(key: FieldKeys.email)
|
||||
var email: String
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.validTill, on: .none)
|
||||
var validTill: Date?
|
||||
|
||||
@Field(key: FieldKeys.isConfirmed)
|
||||
var isConfirmed: Bool
|
||||
|
||||
init() {}
|
||||
|
||||
init(id: UUID? = nil, userID: UserModel.IDValue, email: String?, confirmationCode: String, validTill: Date, isConfirmed: Bool = false) {
|
||||
self.id = id
|
||||
self.userID = userID
|
||||
self.confirmationCode = confirmationCode
|
||||
self.validTill = validTill
|
||||
self.isConfirmed = isConfirmed
|
||||
}
|
||||
}
|
||||
|
||||
extension ConfirmationModel {
|
||||
struct FieldKeys {
|
||||
static var userID: FieldKey { "userID" }
|
||||
static var confirmationCode: FieldKey { "confirmationCode" }
|
||||
static var email: FieldKey { "email" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
static var validTill: FieldKey { "validTill" }
|
||||
static var isConfirmed: FieldKey { "isConfirmed" }
|
||||
}
|
||||
}
|
||||
|
||||
extension ConfirmationModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(ConfirmationModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.userID, .uuid, .required)
|
||||
.field(FieldKeys.confirmationCode, .string, .required)
|
||||
.field(FieldKeys.email, .string, .required)
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.field(FieldKeys.validTill, .datetime)
|
||||
.field(FieldKeys.isConfirmed, .bool, .required)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(ConfirmationModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
103
Sources/ExodaiAcademy/Infrastructure/Database/CourseModel.swift
Normal file
103
Sources/ExodaiAcademy/Infrastructure/Database/CourseModel.swift
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// CourseModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class CourseModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.courses.rawValue
|
||||
|
||||
// MARK: - ID
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
@OptionalField(key: FieldKeys.title)
|
||||
var title: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.description)
|
||||
var description: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.slug)
|
||||
var slug: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.excerpt)
|
||||
var excerpt: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.content)
|
||||
var content: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.campusID)
|
||||
var campusID: CampusModel.IDValue?
|
||||
|
||||
@Field(key: FieldKeys.authorID)
|
||||
var authorID: UserModel.IDValue
|
||||
|
||||
@OptionalField(key: FieldKeys.image)
|
||||
var image: String?
|
||||
|
||||
@Enum(key: FieldKeys.status)
|
||||
var status: Status
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.updatedAt, on: .update)
|
||||
var updatedAt: Date?
|
||||
|
||||
@OptionalField(key: FieldKeys.publishDate)
|
||||
var publishDate: Date?
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension CourseModel {
|
||||
struct FieldKeys {
|
||||
static var title: FieldKey { "title" }
|
||||
static var description: FieldKey { "description" }
|
||||
static var slug: FieldKey { "slug" }
|
||||
static var excerpt: FieldKey { "excerpt" }
|
||||
static var content: FieldKey { "content" }
|
||||
static var campusID: FieldKey { "campusID" }
|
||||
static var authorID: FieldKey { "authorID" }
|
||||
static var image: FieldKey { "image" }
|
||||
static var status: FieldKey { "status" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
static var updatedAt: FieldKey { "updatedAt" }
|
||||
static var publishDate: FieldKey { "publishDate" }
|
||||
}
|
||||
}
|
||||
|
||||
extension CourseModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(CourseModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.title, .string)
|
||||
.field(FieldKeys.description, .string)
|
||||
.field(FieldKeys.slug, .string)
|
||||
.field(FieldKeys.excerpt, .string)
|
||||
.field(FieldKeys.content, .string)
|
||||
.field(FieldKeys.campusID, .uuid)
|
||||
.field(FieldKeys.authorID, .uuid, .required)
|
||||
.field(FieldKeys.image, .string)
|
||||
.field(FieldKeys.status, .string, .required)
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.field(FieldKeys.updatedAt, .datetime)
|
||||
.field(FieldKeys.publishDate, .datetime)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(CourseModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// FileModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 24/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class FileModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.files.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.folderID)
|
||||
var folderID: FolderModel.IDValue
|
||||
|
||||
@Field(key: FieldKeys.name)
|
||||
var name: String
|
||||
|
||||
@Field(key: FieldKeys.type)
|
||||
var type: String
|
||||
|
||||
@Field(key: FieldKeys.mimeType)
|
||||
var mimeType: String
|
||||
|
||||
@Field(key: FieldKeys.storageKey)
|
||||
var storageKey: String
|
||||
|
||||
@Field(key: FieldKeys.size)
|
||||
var size: Int64
|
||||
|
||||
@OptionalField(key: FieldKeys.metadata)
|
||||
var metadata: String?
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension FileModel {
|
||||
struct FieldKeys {
|
||||
static var folderID: FieldKey { "folderID" }
|
||||
static var name: FieldKey { "name" }
|
||||
static var type: FieldKey { "type" }
|
||||
static var mimeType: FieldKey { "mimeType" }
|
||||
static var storageKey: FieldKey { "storageKey" }
|
||||
static var size: FieldKey { "size" }
|
||||
static var metadata: FieldKey { "metadata" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
}
|
||||
}
|
||||
|
||||
import Fluent
|
||||
|
||||
extension FileModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(FileModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.folderID, .uuid, .required)
|
||||
.field(FieldKeys.name, .string, .required)
|
||||
.field(FieldKeys.type, .string, .required)
|
||||
.field(FieldKeys.mimeType, .string, .required)
|
||||
.field(FieldKeys.storageKey, .string, .required)
|
||||
.field(FieldKeys.size, .int64, .required)
|
||||
.field(FieldKeys.metadata, .string)
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(FileModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// FolderModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 24/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class FolderModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.folders.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.name)
|
||||
var name: String
|
||||
|
||||
@OptionalField(key: FieldKeys.parentFolderID)
|
||||
var parentFolderID: FolderModel.IDValue?
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.updatedAt, on: .update)
|
||||
var updatedAt: Date?
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension FolderModel {
|
||||
struct FieldKeys {
|
||||
static var name: FieldKey { "name" }
|
||||
static var parentFolderID: FieldKey { "parentFolderID" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
static var updatedAt: FieldKey { "updatedAt" }
|
||||
}
|
||||
}
|
||||
|
||||
import Fluent
|
||||
|
||||
extension FolderModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(FolderModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.name, .string, .required)
|
||||
.field(FieldKeys.parentFolderID, .uuid)
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.field(FieldKeys.updatedAt, .datetime)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(FolderModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Sources/ExodaiAcademy/Infrastructure/Database/LogModel.swift
Normal file
72
Sources/ExodaiAcademy/Infrastructure/Database/LogModel.swift
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// LogModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 24/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class LogModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.logs.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.event)
|
||||
var event: String
|
||||
|
||||
@OptionalField(key: FieldKeys.actorID)
|
||||
var actorID: UserModel.IDValue?
|
||||
|
||||
@OptionalField(key: FieldKeys.targetType)
|
||||
var targetType: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.targetID)
|
||||
var targetID: UUID?
|
||||
|
||||
@OptionalField(key: FieldKeys.context)
|
||||
var context: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.ipAddress)
|
||||
var ipAddress: String?
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension LogModel {
|
||||
struct FieldKeys {
|
||||
static var event: FieldKey { "event" }
|
||||
static var actorID: FieldKey { "actorID" }
|
||||
static var ipAddress: FieldKey { "ipAddress" }
|
||||
static var targetType: FieldKey { "targetType" }
|
||||
static var targetID: FieldKey { "targetID" }
|
||||
static var context: FieldKey { "context" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
}
|
||||
}
|
||||
|
||||
extension LogModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(LogModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.event, .string, .required)
|
||||
.field(FieldKeys.actorID, .uuid)
|
||||
.field(FieldKeys.ipAddress, .string)
|
||||
.field(FieldKeys.targetType, .string)
|
||||
.field(FieldKeys.targetID, .uuid)
|
||||
.field(FieldKeys.context, .string)
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(LogModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// PurchaseModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 24/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class PurchaseModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.purchases.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.userID)
|
||||
var userID: UserModel.IDValue
|
||||
|
||||
@Field(key: FieldKeys.courseID)
|
||||
var courseID: CourseModel.IDValue
|
||||
|
||||
@Field(key: FieldKeys.pricePaid)
|
||||
var pricePaid: Double
|
||||
|
||||
@Field(key: FieldKeys.currency)
|
||||
var currency: String
|
||||
|
||||
@Field(key: FieldKeys.paymentProvider)
|
||||
var paymentProvider: String
|
||||
|
||||
@Field(key: FieldKeys.paymentReference)
|
||||
var paymentReference: String
|
||||
|
||||
@Timestamp(key: FieldKeys.purchasedAt, on: .create)
|
||||
var purchasedAt: Date?
|
||||
|
||||
@OptionalField(key: FieldKeys.refundedAt)
|
||||
var refundedAt: Date?
|
||||
|
||||
@Field(key: FieldKeys.grantedByAdmin)
|
||||
var grantedByAdmin: Bool
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension PurchaseModel {
|
||||
struct FieldKeys {
|
||||
static var userID: FieldKey { "userID" }
|
||||
static var courseID: FieldKey { "courseID" }
|
||||
static var pricePaid: FieldKey { "pricePaid" }
|
||||
static var currency: FieldKey { "currency" }
|
||||
static var paymentProvider: FieldKey { "paymentProvider" }
|
||||
static var paymentReference: FieldKey { "paymentReference" }
|
||||
static var purchasedAt: FieldKey { "purchasedAt" }
|
||||
static var refundedAt: FieldKey { "refundedAt" }
|
||||
static var grantedByAdmin: FieldKey { "grantedByAdmin" }
|
||||
}
|
||||
}
|
||||
|
||||
extension PurchaseModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(PurchaseModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.userID, .uuid, .required)
|
||||
.field(FieldKeys.courseID, .uuid, .required)
|
||||
.field(FieldKeys.pricePaid, .double, .required)
|
||||
.field(FieldKeys.currency, .string, .required)
|
||||
.field(FieldKeys.paymentProvider, .string, .required)
|
||||
.field(FieldKeys.paymentReference, .string, .required)
|
||||
.field(FieldKeys.purchasedAt, .datetime)
|
||||
.field(FieldKeys.refundedAt, .datetime)
|
||||
.field(FieldKeys.grantedByAdmin, .bool, .required)
|
||||
.unique(on: FieldKeys.userID, FieldKeys.courseID)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(PurchaseModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Sources/ExodaiAcademy/Infrastructure/Database/SessionModel.swift
Normal file
100
Sources/ExodaiAcademy/Infrastructure/Database/SessionModel.swift
Normal file
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// SessionModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class SessionModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.sessions.rawValue
|
||||
|
||||
// MARK: - ID
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
// MARK: - CMS Fields (ALL optional)
|
||||
|
||||
@OptionalField(key: FieldKeys.title)
|
||||
var title: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.description)
|
||||
var description: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.slug)
|
||||
var slug: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.mp4URL)
|
||||
var mp4URL: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.hlsURL)
|
||||
var hlsURL: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.content)
|
||||
var content: String?
|
||||
|
||||
@OptionalEnum(key: FieldKeys.status)
|
||||
var status: Status?
|
||||
|
||||
@OptionalField(key: FieldKeys.courseID)
|
||||
var courseID: CourseModel.IDValue?
|
||||
|
||||
// MARK: - Timestamps
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.updatedAt, on: .update)
|
||||
var updatedAt: Date?
|
||||
|
||||
@OptionalField(key: FieldKeys.publishDate)
|
||||
var publishDate: Date?
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
extension SessionModel {
|
||||
struct FieldKeys {
|
||||
static var title: FieldKey { "title" }
|
||||
static var description: FieldKey { "description" }
|
||||
static var slug: FieldKey { "slug" }
|
||||
static var mp4URL: FieldKey { "mp4URL" }
|
||||
static var hlsURL: FieldKey { "hlsURL" }
|
||||
static var content: FieldKey { "content" }
|
||||
static var status: FieldKey { "status" }
|
||||
static var courseID: FieldKey { "courseID" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
static var updatedAt: FieldKey { "updatedAt" }
|
||||
static var publishDate: FieldKey { "publishDate" }
|
||||
}
|
||||
}
|
||||
|
||||
extension SessionModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(SessionModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.title, .string)
|
||||
.field(FieldKeys.description, .string)
|
||||
.field(FieldKeys.slug, .string)
|
||||
.field(FieldKeys.mp4URL, .string)
|
||||
.field(FieldKeys.hlsURL, .string)
|
||||
.field(FieldKeys.content, .string)
|
||||
.field(FieldKeys.status, .string)
|
||||
.field(FieldKeys.courseID, .uuid)
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.field(FieldKeys.updatedAt, .datetime)
|
||||
.field(FieldKeys.publishDate, .datetime)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(SessionModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Sources/ExodaiAcademy/Infrastructure/Database/TagModel.swift
Normal file
31
Sources/ExodaiAcademy/Infrastructure/Database/TagModel.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// TagModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class TagModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.tags.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.name)
|
||||
var name: String
|
||||
|
||||
init() {}
|
||||
|
||||
init(id: UUID? = nil, name: String) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
extension TagModel {
|
||||
struct FieldKeys {
|
||||
static var name: FieldKey { "name" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// TokenModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 23/01/2026.
|
||||
//
|
||||
|
||||
import Fluent
|
||||
|
||||
final class TokenModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.tokens.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.userID)
|
||||
var userID: UserModel.IDValue
|
||||
|
||||
@Field(key: FieldKeys.value)
|
||||
var value: String
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.validTill, on: .none)
|
||||
var validTill: Date?
|
||||
|
||||
init() {}
|
||||
|
||||
init(id: UUID? = nil, userID: UserModel.IDValue, value: String, validTill: Date?) {
|
||||
self.id = id
|
||||
self.userID = userID
|
||||
self.value = value
|
||||
self.validTill = validTill
|
||||
}
|
||||
}
|
||||
|
||||
extension TokenModel {
|
||||
struct FieldKeys {
|
||||
static var userID: FieldKey { "userID" }
|
||||
static var value: FieldKey { "value" }
|
||||
static var createdAt: FieldKey { "createdAt" }
|
||||
static var validTill: FieldKey { "validTill" }
|
||||
}
|
||||
}
|
||||
177
Sources/ExodaiAcademy/Infrastructure/Database/UserModel.swift
Normal file
177
Sources/ExodaiAcademy/Infrastructure/Database/UserModel.swift
Normal file
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// UserModel.swift
|
||||
// ExodaiAcademy
|
||||
//
|
||||
// Created by Exodai on 22/01/2026.
|
||||
//
|
||||
|
||||
import Vapor
|
||||
import Fluent
|
||||
import FluentKit
|
||||
|
||||
final class UserModel: Model, @unchecked Sendable {
|
||||
static let schema: String = Database.users.rawValue
|
||||
|
||||
@ID(key: .id)
|
||||
var id: UUID?
|
||||
|
||||
@Field(key: FieldKeys.username)
|
||||
var username: String
|
||||
|
||||
@Field(key: FieldKeys.email)
|
||||
var email: String
|
||||
|
||||
@Field(key: FieldKeys.password)
|
||||
var password: String
|
||||
|
||||
@Field(key: FieldKeys.role)
|
||||
var role: Role
|
||||
|
||||
@OptionalField(key: FieldKeys.name)
|
||||
var name: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.lastname)
|
||||
var lastname: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.address)
|
||||
var address: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.zipCode)
|
||||
var zipCode: String?
|
||||
|
||||
@OptionalField(key: FieldKeys.city)
|
||||
var city: String?
|
||||
|
||||
@OptionalEnum(key: FieldKeys.country)
|
||||
var Country: CountryCode?
|
||||
|
||||
@OptionalField(key: FieldKeys.stripeID)
|
||||
var stripeID: String?
|
||||
|
||||
@Timestamp(key: FieldKeys.createdAt, on: .create)
|
||||
var createdAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.updatedAt, on: .update)
|
||||
var updatedAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.deletedAt, on: .delete)
|
||||
var deletedAt: Date?
|
||||
|
||||
@Timestamp(key: FieldKeys.lastLogin, on: .none)
|
||||
var lastLogin: Date?
|
||||
|
||||
init() {
|
||||
|
||||
}
|
||||
|
||||
init(id: UUID? = nil, username: String, email: String, password: String, role: Role) {
|
||||
self.id = id
|
||||
self.username = username
|
||||
self.email = email
|
||||
self.password = password
|
||||
self.role = role
|
||||
}
|
||||
}
|
||||
|
||||
extension UserModel {
|
||||
struct FieldKeys {
|
||||
static var username: FieldKey {"username"}
|
||||
static var email: FieldKey {"email"}
|
||||
static var password: FieldKey {"password"}
|
||||
static var role: FieldKey {"role"}
|
||||
static var name: FieldKey {"name"}
|
||||
static var lastname: FieldKey {"lastname"}
|
||||
static var address: FieldKey {"address"}
|
||||
static var zipCode: FieldKey {"zipCode"}
|
||||
static var city: FieldKey {"city"}
|
||||
static var country: FieldKey {"country"}
|
||||
static var stripeID: FieldKey {"stripeID"}
|
||||
static var createdAt: FieldKey {"createdAt"}
|
||||
static var updatedAt: FieldKey {"updatedAt"}
|
||||
static var deletedAt: FieldKey {"deletedAt"}
|
||||
static var lastLogin: FieldKey {"lastLogin"}
|
||||
}
|
||||
}
|
||||
|
||||
import Fluent
|
||||
|
||||
extension UserModel {
|
||||
struct Migration: AsyncMigration {
|
||||
|
||||
func prepare(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(UserModel.schema)
|
||||
.id()
|
||||
.field(FieldKeys.username, .string, .required)
|
||||
.field(FieldKeys.email, .string, .required)
|
||||
.field(FieldKeys.password, .string, .required)
|
||||
.field(FieldKeys.role, .string, .required)
|
||||
.field(FieldKeys.name, .string)
|
||||
.field(FieldKeys.lastname, .string)
|
||||
.field(FieldKeys.address, .string)
|
||||
.field(FieldKeys.zipCode, .string)
|
||||
.field(FieldKeys.city, .string)
|
||||
.field(FieldKeys.country, .string)
|
||||
.field(FieldKeys.stripeID, .string)
|
||||
.field(FieldKeys.createdAt, .datetime)
|
||||
.field(FieldKeys.updatedAt, .datetime)
|
||||
.field(FieldKeys.deletedAt, .datetime)
|
||||
.field(FieldKeys.lastLogin, .datetime)
|
||||
.unique(on: FieldKeys.username)
|
||||
.unique(on: FieldKeys.email)
|
||||
.create()
|
||||
}
|
||||
|
||||
func revert(on database: any FluentKit.Database) async throws {
|
||||
try await database.schema(UserModel.schema).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UserModel {
|
||||
|
||||
struct Public: Content {
|
||||
let id: UUID?
|
||||
let username: String
|
||||
let email: String
|
||||
let role: Role
|
||||
let name: String?
|
||||
let lastname: String?
|
||||
let address: String?
|
||||
let zipCode: String?
|
||||
let city: String?
|
||||
let country: CountryCode?
|
||||
let stripeID: String?
|
||||
let createdAt: Date?
|
||||
let updatedAt: Date?
|
||||
let lastLogin: Date?
|
||||
}
|
||||
}
|
||||
|
||||
extension UserModel {
|
||||
|
||||
func convertToPublic() -> Public {
|
||||
.init(
|
||||
id: self.id,
|
||||
username: self.username,
|
||||
email: self.email,
|
||||
role: self.role,
|
||||
name: self.name,
|
||||
lastname: self.lastname,
|
||||
address: self.address,
|
||||
zipCode: self.zipCode,
|
||||
city: self.city,
|
||||
country: self.Country,
|
||||
stripeID: self.stripeID,
|
||||
createdAt: self.createdAt,
|
||||
updatedAt: self.updatedAt,
|
||||
lastLogin: self.lastLogin
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Collection where Element == UserModel {
|
||||
|
||||
func convertToPublic() -> [UserModel.Public] {
|
||||
self.map { $0.convertToPublic() }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user