add mailstore schema: accounts, mailboxes, messages, threads, FTS5
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
101
Packages/MagnumOpusCore/Sources/MailStore/DatabaseSetup.swift
Normal file
101
Packages/MagnumOpusCore/Sources/MailStore/DatabaseSetup.swift
Normal file
@@ -0,0 +1,101 @@
|
||||
import GRDB
|
||||
|
||||
public enum DatabaseSetup {
|
||||
public static func migrator() -> DatabaseMigrator {
|
||||
var migrator = DatabaseMigrator()
|
||||
|
||||
migrator.registerMigration("v1_initial") { db in
|
||||
try db.create(table: "account") { t in
|
||||
t.primaryKey("id", .text)
|
||||
t.column("name", .text).notNull()
|
||||
t.column("email", .text).notNull()
|
||||
t.column("imapHost", .text).notNull()
|
||||
t.column("imapPort", .integer).notNull()
|
||||
}
|
||||
|
||||
try db.create(table: "mailbox") { t in
|
||||
t.primaryKey("id", .text)
|
||||
t.belongsTo("account", onDelete: .cascade).notNull()
|
||||
t.column("name", .text).notNull()
|
||||
t.column("uidValidity", .integer).notNull()
|
||||
t.column("uidNext", .integer).notNull()
|
||||
}
|
||||
|
||||
try db.create(table: "message") { t in
|
||||
t.primaryKey("id", .text)
|
||||
t.belongsTo("account", onDelete: .cascade).notNull()
|
||||
t.belongsTo("mailbox", onDelete: .cascade).notNull()
|
||||
t.column("uid", .integer).notNull()
|
||||
t.column("messageId", .text)
|
||||
t.column("inReplyTo", .text)
|
||||
t.column("refs", .text)
|
||||
t.column("subject", .text)
|
||||
t.column("fromAddress", .text)
|
||||
t.column("fromName", .text)
|
||||
t.column("toAddresses", .text)
|
||||
t.column("ccAddresses", .text)
|
||||
t.column("date", .text).notNull()
|
||||
t.column("snippet", .text)
|
||||
t.column("bodyText", .text)
|
||||
t.column("bodyHtml", .text)
|
||||
t.column("isRead", .boolean).notNull().defaults(to: false)
|
||||
t.column("isFlagged", .boolean).notNull().defaults(to: false)
|
||||
t.column("size", .integer).notNull().defaults(to: 0)
|
||||
t.uniqueKey(["mailboxId", "uid"])
|
||||
}
|
||||
|
||||
try db.create(table: "thread") { t in
|
||||
t.primaryKey("id", .text)
|
||||
t.belongsTo("account", onDelete: .cascade).notNull()
|
||||
t.column("subject", .text)
|
||||
t.column("lastDate", .text).notNull()
|
||||
t.column("messageCount", .integer).notNull().defaults(to: 0)
|
||||
}
|
||||
|
||||
try db.create(table: "threadMessage") { t in
|
||||
t.belongsTo("thread", onDelete: .cascade).notNull()
|
||||
t.belongsTo("message", onDelete: .cascade).notNull()
|
||||
t.primaryKey(["threadId", "messageId"])
|
||||
}
|
||||
|
||||
try db.create(table: "attachment") { t in
|
||||
t.primaryKey("id", .text)
|
||||
t.belongsTo("message", onDelete: .cascade).notNull()
|
||||
t.column("filename", .text)
|
||||
t.column("mimeType", .text).notNull()
|
||||
t.column("size", .integer).notNull().defaults(to: 0)
|
||||
t.column("contentId", .text)
|
||||
t.column("cachePath", .text)
|
||||
}
|
||||
|
||||
try db.create(index: "idx_message_mailbox_uid", on: "message", columns: ["mailboxId", "uid"])
|
||||
try db.create(index: "idx_message_messageId", on: "message", columns: ["messageId"])
|
||||
try db.create(index: "idx_thread_lastDate", on: "thread", columns: ["lastDate"])
|
||||
}
|
||||
|
||||
migrator.registerMigration("v1_fts5") { db in
|
||||
try db.create(virtualTable: "messageFts", using: FTS5()) { t in
|
||||
t.synchronize(withTable: "message")
|
||||
t.tokenizer = .porter(wrapping: .unicode61())
|
||||
t.column("subject")
|
||||
t.column("fromName")
|
||||
t.column("fromAddress")
|
||||
t.column("bodyText")
|
||||
}
|
||||
}
|
||||
|
||||
return migrator
|
||||
}
|
||||
|
||||
public static func openDatabase(atPath path: String) throws -> DatabasePool {
|
||||
let pool = try DatabasePool(path: path)
|
||||
try migrator().migrate(pool)
|
||||
return pool
|
||||
}
|
||||
|
||||
public static func openInMemoryDatabase() throws -> DatabaseQueue {
|
||||
let queue = try DatabaseQueue()
|
||||
try migrator().migrate(queue)
|
||||
return queue
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
enum MailStorePlaceholder {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import GRDB
|
||||
|
||||
public struct AccountRecord: Codable, FetchableRecord, PersistableRecord, Sendable {
|
||||
public static let databaseTableName = "account"
|
||||
|
||||
public var id: String
|
||||
public var name: String
|
||||
public var email: String
|
||||
public var imapHost: String
|
||||
public var imapPort: Int
|
||||
|
||||
public init(id: String, name: String, email: String, imapHost: String, imapPort: Int) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.email = email
|
||||
self.imapHost = imapHost
|
||||
self.imapPort = imapPort
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import GRDB
|
||||
|
||||
public struct AttachmentRecord: Codable, FetchableRecord, PersistableRecord, Sendable {
|
||||
public static let databaseTableName = "attachment"
|
||||
|
||||
public var id: String
|
||||
public var messageId: String
|
||||
public var filename: String?
|
||||
public var mimeType: String
|
||||
public var size: Int
|
||||
public var contentId: String?
|
||||
public var cachePath: String?
|
||||
|
||||
public init(
|
||||
id: String, messageId: String, filename: String?, mimeType: String,
|
||||
size: Int, contentId: String?, cachePath: String?
|
||||
) {
|
||||
self.id = id
|
||||
self.messageId = messageId
|
||||
self.filename = filename
|
||||
self.mimeType = mimeType
|
||||
self.size = size
|
||||
self.contentId = contentId
|
||||
self.cachePath = cachePath
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import GRDB
|
||||
|
||||
public struct MailboxRecord: Codable, FetchableRecord, PersistableRecord, Sendable {
|
||||
public static let databaseTableName = "mailbox"
|
||||
|
||||
public var id: String
|
||||
public var accountId: String
|
||||
public var name: String
|
||||
public var uidValidity: Int
|
||||
public var uidNext: Int
|
||||
|
||||
public init(id: String, accountId: String, name: String, uidValidity: Int, uidNext: Int) {
|
||||
self.id = id
|
||||
self.accountId = accountId
|
||||
self.name = name
|
||||
self.uidValidity = uidValidity
|
||||
self.uidNext = uidNext
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import GRDB
|
||||
|
||||
public struct MessageRecord: Codable, FetchableRecord, PersistableRecord, Sendable {
|
||||
public static let databaseTableName = "message"
|
||||
|
||||
public var id: String
|
||||
public var accountId: String
|
||||
public var mailboxId: String
|
||||
public var uid: Int
|
||||
public var messageId: String?
|
||||
public var inReplyTo: String?
|
||||
public var refs: String?
|
||||
public var subject: String?
|
||||
public var fromAddress: String?
|
||||
public var fromName: String?
|
||||
public var toAddresses: String?
|
||||
public var ccAddresses: String?
|
||||
public var date: String
|
||||
public var snippet: String?
|
||||
public var bodyText: String?
|
||||
public var bodyHtml: String?
|
||||
public var isRead: Bool
|
||||
public var isFlagged: Bool
|
||||
public var size: Int
|
||||
|
||||
public init(
|
||||
id: String, accountId: String, mailboxId: String, uid: Int,
|
||||
messageId: String?, inReplyTo: String?, refs: String?,
|
||||
subject: String?, fromAddress: String?, fromName: String?,
|
||||
toAddresses: String?, ccAddresses: String?,
|
||||
date: String, snippet: String?, bodyText: String?, bodyHtml: String?,
|
||||
isRead: Bool, isFlagged: Bool, size: Int
|
||||
) {
|
||||
self.id = id
|
||||
self.accountId = accountId
|
||||
self.mailboxId = mailboxId
|
||||
self.uid = uid
|
||||
self.messageId = messageId
|
||||
self.inReplyTo = inReplyTo
|
||||
self.refs = refs
|
||||
self.subject = subject
|
||||
self.fromAddress = fromAddress
|
||||
self.fromName = fromName
|
||||
self.toAddresses = toAddresses
|
||||
self.ccAddresses = ccAddresses
|
||||
self.date = date
|
||||
self.snippet = snippet
|
||||
self.bodyText = bodyText
|
||||
self.bodyHtml = bodyHtml
|
||||
self.isRead = isRead
|
||||
self.isFlagged = isFlagged
|
||||
self.size = size
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import GRDB
|
||||
|
||||
public struct ThreadMessageRecord: Codable, FetchableRecord, PersistableRecord, Sendable {
|
||||
public static let databaseTableName = "threadMessage"
|
||||
|
||||
public var threadId: String
|
||||
public var messageId: String
|
||||
|
||||
public init(threadId: String, messageId: String) {
|
||||
self.threadId = threadId
|
||||
self.messageId = messageId
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import GRDB
|
||||
|
||||
public struct ThreadRecord: Codable, FetchableRecord, PersistableRecord, Sendable {
|
||||
public static let databaseTableName = "thread"
|
||||
|
||||
public var id: String
|
||||
public var accountId: String
|
||||
public var subject: String?
|
||||
public var lastDate: String
|
||||
public var messageCount: Int
|
||||
|
||||
public init(id: String, accountId: String, subject: String?, lastDate: String, messageCount: Int) {
|
||||
self.id = id
|
||||
self.accountId = accountId
|
||||
self.subject = subject
|
||||
self.lastDate = lastDate
|
||||
self.messageCount = messageCount
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user