From e25c3c1bb46c320d9176580d0d3afefe461dcca2 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:13:00 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[#1]=20feat:=20AppSchema=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Storage/Local/Models/AppSchema.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift diff --git a/Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift b/Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift new file mode 100644 index 0000000..ed63c4b --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift @@ -0,0 +1,22 @@ +// Copyright © 2026 Devault. All rights reserved + +import SwiftData + +enum SwiftDataModel { } + +extension Schema { + private static let actualVersion: Schema.Version = Version(1, 0, 0) + + static var appSchema: Schema { + Schema([ + SwiftDataModel.Project.self, + SwiftDataModel.Secret.self, + SwiftDataModel.SecretProjectLink.self, + SwiftDataModel.SecretPayload.self, + SwiftDataModel.SecretMetadata.self, + SwiftDataModel.SecretAuditLog.self, + SwiftDataModel.AppAuditLog.self, + SwiftDataModel.BackupRecord.self, + ], version: actualVersion) + } +} From 0c0e53baaf21d64fe6a4a67cdafae918426c9453 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:13:56 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[#1]=20feat:=20swift=20model=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(=ED=85=8C=EC=9D=B4=EB=B8=94=208=EA=B0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Storage/Local/Models/AppAuditLog.swift | 25 +++++++ .../Storage/Local/Models/BackupRecord.swift | 37 ++++++++++ .../Storage/Local/Models/Project.swift | 33 +++++++++ .../Sources/Storage/Local/Models/Secret.swift | 69 +++++++++++++++++++ .../Storage/Local/Models/SecretAuditLog.swift | 33 +++++++++ .../Storage/Local/Models/SecretMetadata.swift | 27 ++++++++ .../Storage/Local/Models/SecretPayload.swift | 30 ++++++++ .../Local/Models/SecretProjectLink.swift | 26 +++++++ 8 files changed, 280 insertions(+) create mode 100644 Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/Project.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/Secret.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift diff --git a/Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift b/Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift new file mode 100644 index 0000000..ad1a92a --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift @@ -0,0 +1,25 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class AppAuditLog { + @Attribute(.unique) var id: UUID + var eventType: String + var actorContext: String + var occurredAt: Date + + init( + id: UUID = UUID(), + eventType: String, + actorContext: String, + occurredAt: Date = Date() + ) { + self.id = id + self.eventType = eventType + self.actorContext = actorContext + self.occurredAt = occurredAt + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift b/Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift new file mode 100644 index 0000000..0a543dd --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift @@ -0,0 +1,37 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class BackupRecord { + @Attribute(.unique) var id: UUID + var fileName: String + var filePath: String + var backupScope: String + var hasIndependentPassword: Bool + var keyTag: String + var totalSecrets: Int + var createdAt: Date + + init( + id: UUID = UUID(), + fileName: String, + filePath: String, + backupScope: String, + hasIndependentPassword: Bool, + keyTag: String, + totalSecrets: Int, + createdAt: Date = Date() + ) { + self.id = id + self.fileName = fileName + self.filePath = filePath + self.backupScope = backupScope + self.hasIndependentPassword = hasIndependentPassword + self.keyTag = keyTag + self.totalSecrets = totalSecrets + self.createdAt = createdAt + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/Project.swift b/Projects/DVData/Sources/Storage/Local/Models/Project.swift new file mode 100644 index 0000000..40d4097 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/Project.swift @@ -0,0 +1,33 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class Project { + @Attribute(.unique) var id: UUID + var name: String + var createdAt: Date + var updatedAt: Date + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretProjectLink.project) + var secretLinks: [SecretProjectLink] + + var secrets: [Secret] { + secretLinks.compactMap(\.secret) + } + + init( + id: UUID = UUID(), + name: String, + createdAt: Date = Date(), + updatedAt: Date = Date() + ) { + self.id = id + self.name = name + self.createdAt = createdAt + self.updatedAt = updatedAt + self.secretLinks = [] + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift new file mode 100644 index 0000000..422a086 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -0,0 +1,69 @@ +// Copyright © 2026 Devault. All rights reserved + +import SwiftData +import Foundation + +extension SwiftDataModel { + @Model final class Secret { + @Attribute(.unique) var secretId: UUID + var name: String + var secretType: String + var subType: String? + var service: String? + var environment: String? + var expiresAt: Date? + var memo: String? + var liked: Bool + var deletedAt: Date? + var createdAt: Date + var updatedAt: Date + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretProjectLink.secret) + var projectLinks: [SecretProjectLink] + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretPayload.secret) + var payload: SecretPayload? + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretMetadata.secret) + var metadata: [SecretMetadata] + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretAuditLog.secret) + var auditLogs: [SecretAuditLog] + + var projects: [Project] { + projectLinks.compactMap(\.project) + } + + init( + secretId: UUID = UUID(), + name: String, + secretType: String, + subType: String? = nil, + service: String? = nil, + environment: String? = nil, + expiresAt: Date? = nil, + memo: String? = nil, + liked: Bool = false, + deletedAt: Date? = nil, + createdAt: Date = Date(), + updatedAt: Date = Date() + ) { + self.secretId = secretId + self.name = name + self.secretType = secretType + self.subType = subType + self.service = service + self.environment = environment + self.expiresAt = expiresAt + self.memo = memo + self.liked = liked + self.deletedAt = deletedAt + self.createdAt = createdAt + self.updatedAt = updatedAt + self.projectLinks = [] + self.payload = nil + self.metadata = [] + self.auditLogs = [] + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift new file mode 100644 index 0000000..0c6c69a --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift @@ -0,0 +1,33 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretAuditLog { + @Attribute(.unique) var id: UUID + var eventType: String + var actorContext: String + var isSuspicious: Bool + var occurredAt: Date + + @Relationship + var secret: Secret? + + init( + id: UUID = UUID(), + eventType: String, + actorContext: String, + isSuspicious: Bool = false, + occurredAt: Date = Date(), + secret: Secret? = nil + ) { + self.id = id + self.eventType = eventType + self.actorContext = actorContext + self.isSuspicious = isSuspicious + self.occurredAt = occurredAt + self.secret = secret + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift new file mode 100644 index 0000000..caab841 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift @@ -0,0 +1,27 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretMetadata { + @Attribute(.unique) var id: UUID + var metadataJSON: Data + var schemaVersion: Int + + @Relationship + var secret: Secret? + + init( + id: UUID = UUID(), + metadataJSON: Data, + schemaVersion: Int, + secret: Secret? = nil + ) { + self.id = id + self.metadataJSON = metadataJSON + self.schemaVersion = schemaVersion + self.secret = secret + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift new file mode 100644 index 0000000..b268218 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift @@ -0,0 +1,30 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretPayload { + @Attribute(.unique) var id: UUID + var encryptedData: Data + var keyTag: String + var schemaVersion: Int + + @Relationship + var secret: Secret? + + init( + id: UUID = UUID(), + encryptedData: Data, + keyTag: String, + schemaVersion: Int, + secret: Secret? = nil + ) { + self.id = id + self.encryptedData = encryptedData + self.keyTag = keyTag + self.schemaVersion = schemaVersion + self.secret = secret + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift new file mode 100644 index 0000000..b30420d --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift @@ -0,0 +1,26 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretProjectLink { + var linkedAt: Date + + @Relationship + var project: Project? + + @Relationship + var secret: Secret? + + init( + project: Project, + secret: Secret, + linkedAt: Date = Date() + ) { + self.linkedAt = linkedAt + self.project = project + self.secret = secret + } + } +} From 9ab3eff08072987b77a1500c0b25ff08b4979079 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:43:45 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[#1]=20fix:=20.coderabbit.yaml=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .coderabbit.yaml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 4035938..0232619 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -13,15 +13,7 @@ enable_free_tier: true # 프로젝트 성격에 맞게 자유롭게 수정 # ============================================================================= tone_instructions: | - 당신은 경험 많은 Apple 플랫폼 개발자이자 코드 리뷰어입니다. - 목표는 팀원들이 더 나은 Swift 코드를 작성하며 함께 성장하도록 돕는 것입니다. - - 리뷰 원칙: - 1. 피드백은 명확하고 구체적이어야 하며, 문제의 원인과 개선 방법을 반드시 함께 제시하세요. - 2. 리뷰는 교육적이어야 하며, Apple Developer Documentation이나 Swift Evolution 제안 등 관련 자료를 함께 안내하세요. - 3. 비판보다는 개선 중심의 제안을 우선하세요. "이렇게 하면 안 돼요" 대신 "이렇게 하면 더 좋아요"로 표현하세요. - 4. 잘된 부분은 짧고 위트 있게 칭찬하세요. - 5. 스타일 지적은 SwiftLint/SwiftFormat으로 자동화할 수 있는 것인지 구분하고, 자동화 가능한 경우 툴 도입을 제안하세요. + 경험 많은 Apple 플랫폼 개발자로서 한국어로 리뷰하세요. 문제의 원인과 개선 방법을 구체적으로 제안하고, Swift/Apple 문서 근거를 덧붙이세요. 비판보다 개선 중심으로 말하며, 자동화 가능한 스타일 지적은 SwiftLint/SwiftFormat 도입을 제안하세요. # ============================================================================= # 리뷰 설정 From b315d6011766b492b7ca6a1f9b42c6d97eebf81c Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:51:47 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[#1]=20fix:=20Secret-SecretAuditLog=20del?= =?UTF-8?q?eteRule=EC=9D=84=20nullify=EB=A1=9C=20=EC=88=98=EC=A0=95,=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9E=90=20=EC=88=98=EC=A0=95=20(createdAt,?= =?UTF-8?q?=20updatedAt)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DVData/Sources/Storage/Local/Models/Secret.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift index 422a086..4529f52 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -27,7 +27,7 @@ extension SwiftDataModel { @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretMetadata.secret) var metadata: [SecretMetadata] - @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretAuditLog.secret) + @Relationship(deleteRule: .nullify, inverse: \SwiftDataModel.SecretAuditLog.secret) var auditLogs: [SecretAuditLog] var projects: [Project] { @@ -46,8 +46,9 @@ extension SwiftDataModel { liked: Bool = false, deletedAt: Date? = nil, createdAt: Date = Date(), - updatedAt: Date = Date() + updatedAt: Date? = nil ) { + let initialCreatedAt = createdAt self.secretId = secretId self.name = name self.secretType = secretType @@ -58,8 +59,8 @@ extension SwiftDataModel { self.memo = memo self.liked = liked self.deletedAt = deletedAt - self.createdAt = createdAt - self.updatedAt = updatedAt + self.createdAt = initialCreatedAt + self.updatedAt = updatedAt ?? initialCreatedAt self.projectLinks = [] self.payload = nil self.metadata = [] From adbede508903f2651f5f9f32b961715f492e7f21 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 16:01:46 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[#1]=20fix:=20=EB=A1=9C=EC=BB=AC=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=EC=86=8C=20=EB=AC=B4=EA=B2=B0=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20key=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=A7=A4=ED=95=91=EA=B4=80?= =?UTF-8?q?=EA=B3=84=20=EC=9E=AC=EC=A0=95=EB=A6=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DVData/Sources/Storage/Local/Models/Project.swift | 2 +- Projects/DVData/Sources/Storage/Local/Models/Secret.swift | 5 ++--- .../Sources/Storage/Local/Models/SecretAuditLog.swift | 4 ++-- .../Sources/Storage/Local/Models/SecretMetadata.swift | 6 ++++-- .../DVData/Sources/Storage/Local/Models/SecretPayload.swift | 6 ++++-- .../Sources/Storage/Local/Models/SecretProjectLink.swift | 6 ++++-- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Projects/DVData/Sources/Storage/Local/Models/Project.swift b/Projects/DVData/Sources/Storage/Local/Models/Project.swift index 40d4097..84706fd 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Project.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Project.swift @@ -14,7 +14,7 @@ extension SwiftDataModel { var secretLinks: [SecretProjectLink] var secrets: [Secret] { - secretLinks.compactMap(\.secret) + secretLinks.map(\.secret) } init( diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift index 4529f52..fe4aaf7 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -25,13 +25,12 @@ extension SwiftDataModel { var payload: SecretPayload? @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretMetadata.secret) - var metadata: [SecretMetadata] + var metadata: SecretMetadata? @Relationship(deleteRule: .nullify, inverse: \SwiftDataModel.SecretAuditLog.secret) var auditLogs: [SecretAuditLog] var projects: [Project] { - projectLinks.compactMap(\.project) } init( @@ -63,7 +62,7 @@ extension SwiftDataModel { self.updatedAt = updatedAt ?? initialCreatedAt self.projectLinks = [] self.payload = nil - self.metadata = [] + self.metadata = nil self.auditLogs = [] } } diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift index 0c6c69a..de232c9 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift @@ -12,7 +12,7 @@ extension SwiftDataModel { var occurredAt: Date @Relationship - var secret: Secret? + var secret: Secret init( id: UUID = UUID(), @@ -20,7 +20,7 @@ extension SwiftDataModel { actorContext: String, isSuspicious: Bool = false, occurredAt: Date = Date(), - secret: Secret? = nil + secret: Secret ) { self.id = id self.eventType = eventType diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift index caab841..abe773d 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift @@ -6,19 +6,21 @@ import SwiftData extension SwiftDataModel { @Model final class SecretMetadata { @Attribute(.unique) var id: UUID + @Attribute(.unique) var secretKey: String var metadataJSON: Data var schemaVersion: Int @Relationship - var secret: Secret? + var secret: Secret init( id: UUID = UUID(), metadataJSON: Data, schemaVersion: Int, - secret: Secret? = nil + secret: Secret ) { self.id = id + self.secretKey = secret.secretId.uuidString self.metadataJSON = metadataJSON self.schemaVersion = schemaVersion self.secret = secret diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift index b268218..195c31d 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift @@ -6,21 +6,23 @@ import SwiftData extension SwiftDataModel { @Model final class SecretPayload { @Attribute(.unique) var id: UUID + @Attribute(.unique) var secretKey: String var encryptedData: Data var keyTag: String var schemaVersion: Int @Relationship - var secret: Secret? + var secret: Secret init( id: UUID = UUID(), encryptedData: Data, keyTag: String, schemaVersion: Int, - secret: Secret? = nil + secret: Secret ) { self.id = id + self.secretKey = secret.secretId.uuidString self.encryptedData = encryptedData self.keyTag = keyTag self.schemaVersion = schemaVersion diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift index b30420d..2b7a5b5 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift @@ -5,19 +5,21 @@ import SwiftData extension SwiftDataModel { @Model final class SecretProjectLink { + @Attribute(.unique) var linkKey: String var linkedAt: Date @Relationship - var project: Project? + var project: Project @Relationship - var secret: Secret? + var secret: Secret init( project: Project, secret: Secret, linkedAt: Date = Date() ) { + self.linkKey = "\(project.id.uuidString):\(secret.secretId.uuidString)" self.linkedAt = linkedAt self.project = project self.secret = secret From 8bfe9b2b43160cdd1f24e0ef59a669407232392c Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 16:03:25 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[#1]=20fix:=20compactMap=EC=97=90?= =?UTF-8?q?=EC=84=9C=20map=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DVData/Sources/Storage/Local/Models/Secret.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift index fe4aaf7..2f0da59 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -31,6 +31,7 @@ extension SwiftDataModel { var auditLogs: [SecretAuditLog] var projects: [Project] { + projectLinks.map(\.project) } init( From 9b4c466955d9a4cf848433904a46bc5492e3e5b7 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:13:00 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[#1]=20feat:=20AppSchema=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Storage/Local/Models/AppSchema.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift diff --git a/Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift b/Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift new file mode 100644 index 0000000..ed63c4b --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/AppSchema.swift @@ -0,0 +1,22 @@ +// Copyright © 2026 Devault. All rights reserved + +import SwiftData + +enum SwiftDataModel { } + +extension Schema { + private static let actualVersion: Schema.Version = Version(1, 0, 0) + + static var appSchema: Schema { + Schema([ + SwiftDataModel.Project.self, + SwiftDataModel.Secret.self, + SwiftDataModel.SecretProjectLink.self, + SwiftDataModel.SecretPayload.self, + SwiftDataModel.SecretMetadata.self, + SwiftDataModel.SecretAuditLog.self, + SwiftDataModel.AppAuditLog.self, + SwiftDataModel.BackupRecord.self, + ], version: actualVersion) + } +} From d9d60817ab42d35b59c9eb0674c9092d9a7e9250 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:13:56 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[#1]=20feat:=20swift=20model=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(=ED=85=8C=EC=9D=B4=EB=B8=94=208=EA=B0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Storage/Local/Models/AppAuditLog.swift | 25 +++++++ .../Storage/Local/Models/BackupRecord.swift | 37 ++++++++++ .../Storage/Local/Models/Project.swift | 33 +++++++++ .../Sources/Storage/Local/Models/Secret.swift | 69 +++++++++++++++++++ .../Storage/Local/Models/SecretAuditLog.swift | 33 +++++++++ .../Storage/Local/Models/SecretMetadata.swift | 27 ++++++++ .../Storage/Local/Models/SecretPayload.swift | 30 ++++++++ .../Local/Models/SecretProjectLink.swift | 26 +++++++ 8 files changed, 280 insertions(+) create mode 100644 Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/Project.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/Secret.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift create mode 100644 Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift diff --git a/Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift b/Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift new file mode 100644 index 0000000..ad1a92a --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/AppAuditLog.swift @@ -0,0 +1,25 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class AppAuditLog { + @Attribute(.unique) var id: UUID + var eventType: String + var actorContext: String + var occurredAt: Date + + init( + id: UUID = UUID(), + eventType: String, + actorContext: String, + occurredAt: Date = Date() + ) { + self.id = id + self.eventType = eventType + self.actorContext = actorContext + self.occurredAt = occurredAt + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift b/Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift new file mode 100644 index 0000000..0a543dd --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/BackupRecord.swift @@ -0,0 +1,37 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class BackupRecord { + @Attribute(.unique) var id: UUID + var fileName: String + var filePath: String + var backupScope: String + var hasIndependentPassword: Bool + var keyTag: String + var totalSecrets: Int + var createdAt: Date + + init( + id: UUID = UUID(), + fileName: String, + filePath: String, + backupScope: String, + hasIndependentPassword: Bool, + keyTag: String, + totalSecrets: Int, + createdAt: Date = Date() + ) { + self.id = id + self.fileName = fileName + self.filePath = filePath + self.backupScope = backupScope + self.hasIndependentPassword = hasIndependentPassword + self.keyTag = keyTag + self.totalSecrets = totalSecrets + self.createdAt = createdAt + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/Project.swift b/Projects/DVData/Sources/Storage/Local/Models/Project.swift new file mode 100644 index 0000000..40d4097 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/Project.swift @@ -0,0 +1,33 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class Project { + @Attribute(.unique) var id: UUID + var name: String + var createdAt: Date + var updatedAt: Date + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretProjectLink.project) + var secretLinks: [SecretProjectLink] + + var secrets: [Secret] { + secretLinks.compactMap(\.secret) + } + + init( + id: UUID = UUID(), + name: String, + createdAt: Date = Date(), + updatedAt: Date = Date() + ) { + self.id = id + self.name = name + self.createdAt = createdAt + self.updatedAt = updatedAt + self.secretLinks = [] + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift new file mode 100644 index 0000000..422a086 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -0,0 +1,69 @@ +// Copyright © 2026 Devault. All rights reserved + +import SwiftData +import Foundation + +extension SwiftDataModel { + @Model final class Secret { + @Attribute(.unique) var secretId: UUID + var name: String + var secretType: String + var subType: String? + var service: String? + var environment: String? + var expiresAt: Date? + var memo: String? + var liked: Bool + var deletedAt: Date? + var createdAt: Date + var updatedAt: Date + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretProjectLink.secret) + var projectLinks: [SecretProjectLink] + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretPayload.secret) + var payload: SecretPayload? + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretMetadata.secret) + var metadata: [SecretMetadata] + + @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretAuditLog.secret) + var auditLogs: [SecretAuditLog] + + var projects: [Project] { + projectLinks.compactMap(\.project) + } + + init( + secretId: UUID = UUID(), + name: String, + secretType: String, + subType: String? = nil, + service: String? = nil, + environment: String? = nil, + expiresAt: Date? = nil, + memo: String? = nil, + liked: Bool = false, + deletedAt: Date? = nil, + createdAt: Date = Date(), + updatedAt: Date = Date() + ) { + self.secretId = secretId + self.name = name + self.secretType = secretType + self.subType = subType + self.service = service + self.environment = environment + self.expiresAt = expiresAt + self.memo = memo + self.liked = liked + self.deletedAt = deletedAt + self.createdAt = createdAt + self.updatedAt = updatedAt + self.projectLinks = [] + self.payload = nil + self.metadata = [] + self.auditLogs = [] + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift new file mode 100644 index 0000000..0c6c69a --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift @@ -0,0 +1,33 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretAuditLog { + @Attribute(.unique) var id: UUID + var eventType: String + var actorContext: String + var isSuspicious: Bool + var occurredAt: Date + + @Relationship + var secret: Secret? + + init( + id: UUID = UUID(), + eventType: String, + actorContext: String, + isSuspicious: Bool = false, + occurredAt: Date = Date(), + secret: Secret? = nil + ) { + self.id = id + self.eventType = eventType + self.actorContext = actorContext + self.isSuspicious = isSuspicious + self.occurredAt = occurredAt + self.secret = secret + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift new file mode 100644 index 0000000..caab841 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift @@ -0,0 +1,27 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretMetadata { + @Attribute(.unique) var id: UUID + var metadataJSON: Data + var schemaVersion: Int + + @Relationship + var secret: Secret? + + init( + id: UUID = UUID(), + metadataJSON: Data, + schemaVersion: Int, + secret: Secret? = nil + ) { + self.id = id + self.metadataJSON = metadataJSON + self.schemaVersion = schemaVersion + self.secret = secret + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift new file mode 100644 index 0000000..b268218 --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift @@ -0,0 +1,30 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretPayload { + @Attribute(.unique) var id: UUID + var encryptedData: Data + var keyTag: String + var schemaVersion: Int + + @Relationship + var secret: Secret? + + init( + id: UUID = UUID(), + encryptedData: Data, + keyTag: String, + schemaVersion: Int, + secret: Secret? = nil + ) { + self.id = id + self.encryptedData = encryptedData + self.keyTag = keyTag + self.schemaVersion = schemaVersion + self.secret = secret + } + } +} diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift new file mode 100644 index 0000000..b30420d --- /dev/null +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift @@ -0,0 +1,26 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation +import SwiftData + +extension SwiftDataModel { + @Model final class SecretProjectLink { + var linkedAt: Date + + @Relationship + var project: Project? + + @Relationship + var secret: Secret? + + init( + project: Project, + secret: Secret, + linkedAt: Date = Date() + ) { + self.linkedAt = linkedAt + self.project = project + self.secret = secret + } + } +} From ece1f0a8ddcff3f7b61183c683f286e8ee06c0c5 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:43:45 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[#1]=20fix:=20.coderabbit.yaml=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .coderabbit.yaml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 4035938..0232619 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -13,15 +13,7 @@ enable_free_tier: true # 프로젝트 성격에 맞게 자유롭게 수정 # ============================================================================= tone_instructions: | - 당신은 경험 많은 Apple 플랫폼 개발자이자 코드 리뷰어입니다. - 목표는 팀원들이 더 나은 Swift 코드를 작성하며 함께 성장하도록 돕는 것입니다. - - 리뷰 원칙: - 1. 피드백은 명확하고 구체적이어야 하며, 문제의 원인과 개선 방법을 반드시 함께 제시하세요. - 2. 리뷰는 교육적이어야 하며, Apple Developer Documentation이나 Swift Evolution 제안 등 관련 자료를 함께 안내하세요. - 3. 비판보다는 개선 중심의 제안을 우선하세요. "이렇게 하면 안 돼요" 대신 "이렇게 하면 더 좋아요"로 표현하세요. - 4. 잘된 부분은 짧고 위트 있게 칭찬하세요. - 5. 스타일 지적은 SwiftLint/SwiftFormat으로 자동화할 수 있는 것인지 구분하고, 자동화 가능한 경우 툴 도입을 제안하세요. + 경험 많은 Apple 플랫폼 개발자로서 한국어로 리뷰하세요. 문제의 원인과 개선 방법을 구체적으로 제안하고, Swift/Apple 문서 근거를 덧붙이세요. 비판보다 개선 중심으로 말하며, 자동화 가능한 스타일 지적은 SwiftLint/SwiftFormat 도입을 제안하세요. # ============================================================================= # 리뷰 설정 From 5a15217f5f2f784f09b76dee443df1dabf2981c4 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 15:51:47 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[#1]=20fix:=20Secret-SecretAuditLog=20del?= =?UTF-8?q?eteRule=EC=9D=84=20nullify=EB=A1=9C=20=EC=88=98=EC=A0=95,=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9E=90=20=EC=88=98=EC=A0=95=20(createdAt,?= =?UTF-8?q?=20updatedAt)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DVData/Sources/Storage/Local/Models/Secret.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift index 422a086..4529f52 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -27,7 +27,7 @@ extension SwiftDataModel { @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretMetadata.secret) var metadata: [SecretMetadata] - @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretAuditLog.secret) + @Relationship(deleteRule: .nullify, inverse: \SwiftDataModel.SecretAuditLog.secret) var auditLogs: [SecretAuditLog] var projects: [Project] { @@ -46,8 +46,9 @@ extension SwiftDataModel { liked: Bool = false, deletedAt: Date? = nil, createdAt: Date = Date(), - updatedAt: Date = Date() + updatedAt: Date? = nil ) { + let initialCreatedAt = createdAt self.secretId = secretId self.name = name self.secretType = secretType @@ -58,8 +59,8 @@ extension SwiftDataModel { self.memo = memo self.liked = liked self.deletedAt = deletedAt - self.createdAt = createdAt - self.updatedAt = updatedAt + self.createdAt = initialCreatedAt + self.updatedAt = updatedAt ?? initialCreatedAt self.projectLinks = [] self.payload = nil self.metadata = [] From e1bbd2dcc8240da96c860e29d82681da59b33bb2 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 16:01:46 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[#1]=20fix:=20=EB=A1=9C=EC=BB=AC=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=EC=86=8C=20=EB=AC=B4=EA=B2=B0=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20key=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=A7=A4=ED=95=91=EA=B4=80?= =?UTF-8?q?=EA=B3=84=20=EC=9E=AC=EC=A0=95=EB=A6=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DVData/Sources/Storage/Local/Models/Project.swift | 2 +- Projects/DVData/Sources/Storage/Local/Models/Secret.swift | 5 ++--- .../Sources/Storage/Local/Models/SecretAuditLog.swift | 4 ++-- .../Sources/Storage/Local/Models/SecretMetadata.swift | 6 ++++-- .../DVData/Sources/Storage/Local/Models/SecretPayload.swift | 6 ++++-- .../Sources/Storage/Local/Models/SecretProjectLink.swift | 6 ++++-- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Projects/DVData/Sources/Storage/Local/Models/Project.swift b/Projects/DVData/Sources/Storage/Local/Models/Project.swift index 40d4097..84706fd 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Project.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Project.swift @@ -14,7 +14,7 @@ extension SwiftDataModel { var secretLinks: [SecretProjectLink] var secrets: [Secret] { - secretLinks.compactMap(\.secret) + secretLinks.map(\.secret) } init( diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift index 4529f52..fe4aaf7 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -25,13 +25,12 @@ extension SwiftDataModel { var payload: SecretPayload? @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretMetadata.secret) - var metadata: [SecretMetadata] + var metadata: SecretMetadata? @Relationship(deleteRule: .nullify, inverse: \SwiftDataModel.SecretAuditLog.secret) var auditLogs: [SecretAuditLog] var projects: [Project] { - projectLinks.compactMap(\.project) } init( @@ -63,7 +62,7 @@ extension SwiftDataModel { self.updatedAt = updatedAt ?? initialCreatedAt self.projectLinks = [] self.payload = nil - self.metadata = [] + self.metadata = nil self.auditLogs = [] } } diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift index 0c6c69a..de232c9 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretAuditLog.swift @@ -12,7 +12,7 @@ extension SwiftDataModel { var occurredAt: Date @Relationship - var secret: Secret? + var secret: Secret init( id: UUID = UUID(), @@ -20,7 +20,7 @@ extension SwiftDataModel { actorContext: String, isSuspicious: Bool = false, occurredAt: Date = Date(), - secret: Secret? = nil + secret: Secret ) { self.id = id self.eventType = eventType diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift index caab841..abe773d 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretMetadata.swift @@ -6,19 +6,21 @@ import SwiftData extension SwiftDataModel { @Model final class SecretMetadata { @Attribute(.unique) var id: UUID + @Attribute(.unique) var secretKey: String var metadataJSON: Data var schemaVersion: Int @Relationship - var secret: Secret? + var secret: Secret init( id: UUID = UUID(), metadataJSON: Data, schemaVersion: Int, - secret: Secret? = nil + secret: Secret ) { self.id = id + self.secretKey = secret.secretId.uuidString self.metadataJSON = metadataJSON self.schemaVersion = schemaVersion self.secret = secret diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift index b268218..195c31d 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretPayload.swift @@ -6,21 +6,23 @@ import SwiftData extension SwiftDataModel { @Model final class SecretPayload { @Attribute(.unique) var id: UUID + @Attribute(.unique) var secretKey: String var encryptedData: Data var keyTag: String var schemaVersion: Int @Relationship - var secret: Secret? + var secret: Secret init( id: UUID = UUID(), encryptedData: Data, keyTag: String, schemaVersion: Int, - secret: Secret? = nil + secret: Secret ) { self.id = id + self.secretKey = secret.secretId.uuidString self.encryptedData = encryptedData self.keyTag = keyTag self.schemaVersion = schemaVersion diff --git a/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift index b30420d..2b7a5b5 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/SecretProjectLink.swift @@ -5,19 +5,21 @@ import SwiftData extension SwiftDataModel { @Model final class SecretProjectLink { + @Attribute(.unique) var linkKey: String var linkedAt: Date @Relationship - var project: Project? + var project: Project @Relationship - var secret: Secret? + var secret: Secret init( project: Project, secret: Secret, linkedAt: Date = Date() ) { + self.linkKey = "\(project.id.uuidString):\(secret.secretId.uuidString)" self.linkedAt = linkedAt self.project = project self.secret = secret From 8874a5348916ab3f2ce67bf0ee787ceb836e8a8c Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 7 May 2026 16:03:25 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[#1]=20fix:=20compactMap=EC=97=90?= =?UTF-8?q?=EC=84=9C=20map=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DVData/Sources/Storage/Local/Models/Secret.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift index fe4aaf7..2f0da59 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -31,6 +31,7 @@ extension SwiftDataModel { var auditLogs: [SecretAuditLog] var projects: [Project] { + projectLinks.map(\.project) } init( From d8a75db05ebb9273af97fb81ab26d253868d83d8 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Mon, 11 May 2026 19:44:30 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[#1]=20feat:=20=ED=8E=B8=EC=9D=98=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=EC=9E=90=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DVData/Sources/Storage/Local/Models/Project.swift | 1 + Projects/DVData/Sources/Storage/Local/Models/Secret.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Projects/DVData/Sources/Storage/Local/Models/Project.swift b/Projects/DVData/Sources/Storage/Local/Models/Project.swift index 84706fd..33887e4 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Project.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Project.swift @@ -13,6 +13,7 @@ extension SwiftDataModel { @Relationship(deleteRule: .cascade, inverse: \SwiftDataModel.SecretProjectLink.project) var secretLinks: [SecretProjectLink] + /// `SecretProjectLink`를 통해 연결된 시크릿 목록을 반환하는 편의 접근자입니다. var secrets: [Secret] { secretLinks.map(\.secret) } diff --git a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift index 2f0da59..1a115ec 100644 --- a/Projects/DVData/Sources/Storage/Local/Models/Secret.swift +++ b/Projects/DVData/Sources/Storage/Local/Models/Secret.swift @@ -30,6 +30,7 @@ extension SwiftDataModel { @Relationship(deleteRule: .nullify, inverse: \SwiftDataModel.SecretAuditLog.secret) var auditLogs: [SecretAuditLog] + /// `SecretProjectLink`를 통해 연결된 프로젝트 목록을 반환하는 편의 접근자입니다. var projects: [Project] { projectLinks.map(\.project) }