Compare commits

...

4 Commits

Author SHA1 Message Date
Leendert de Borst
20c9f08616 Tweak migration to prevent double records (#1404) 2025-12-03 19:27:16 +01:00
Leendert de Borst
7f898a2073 Recreate migrations with updated system field definition structure (#1404) 2025-12-03 19:10:06 +01:00
Leendert de Borst
dbc42fdea4 Refactor system fields to be defined in code (#1404) 2025-12-03 17:19:29 +01:00
Leendert de Borst
a3db88ee42 Add item render type models (#1404) 2025-12-03 16:38:55 +01:00
38 changed files with 2792 additions and 4436 deletions

View File

@@ -321,4 +321,135 @@ type ItemTag = {
IsDeleted: number;
};
export { type Alias, type Attachment, type Credential, type EncryptionKey, FieldKey, type FieldKeyValue, type ItemTag, type Passkey, type PasswordSettings, type Tag, type TotpCode };
/**
* Item types supported by the vault
*/
type ItemType = 'Login' | 'CreditCard' | 'Identity' | 'Note';
/**
* Item type representing vault entries in the new field-based data model.
* Replaces the old Credential type.
*/
type Item = {
Id: string;
Name: string | null;
ItemType: ItemType;
Logo?: Uint8Array | number[];
FolderId?: string | null;
FolderPath?: string | null;
Tags?: ItemTagRef[];
Fields: ItemField[];
HasPasskey?: boolean;
HasAttachment?: boolean;
HasTotp?: boolean;
CreatedAt: string;
UpdatedAt: string;
};
/**
* Field value within an item
*/
type ItemField = {
FieldKey: string;
Label: string;
FieldType: FieldType;
Value: string | string[];
IsHidden: boolean;
DisplayOrder: number;
};
/**
* Field types for rendering and validation
*/
type FieldType = 'Text' | 'Password' | 'Email' | 'URL' | 'Date' | 'Number' | 'Phone' | 'TextArea';
/**
* Tag reference for display within an item
*/
type ItemTagRef = {
Id: string;
Name: string;
Color?: string;
};
/**
* Helper functions for working with Item model
*/
/**
* Get a single field value by FieldKey
*/
declare function getFieldValue(item: Item, fieldKey: string): string | undefined;
/**
* Get all values for a multi-value field
*/
declare function getFieldValues(item: Item, fieldKey: string): string[];
/**
* Check if a field exists and has a value
*/
declare function hasField(item: Item, fieldKey: string): boolean;
/**
* Group fields by a categorization function
*/
declare function groupFields(item: Item, grouper: (field: ItemField) => string): Record<string, ItemField[]>;
/**
* Group fields by standard categories (Login, Alias, Custom)
*/
declare function groupFieldsByCategory(item: Item): Record<string, ItemField[]>;
/**
* Convert new Item model to legacy Credential model for backward compatibility.
* @deprecated Use Item model directly. This is a temporary compatibility layer.
*/
declare function itemToCredential(item: Item): Credential;
/**
* System field definition with metadata.
* System fields are predefined fields with immutable keys like 'login.username'.
* Their metadata (label, type, etc.) is defined here in code, not in the database.
*/
type SystemFieldDefinition = {
/** Unique system field key (e.g., 'login.username') */
FieldKey: string;
/** Display label for the field */
Label: string;
/** Field type for rendering/validation */
FieldType: FieldType;
/** Whether field is hidden/masked by default */
IsHidden: boolean;
/** Whether field supports multiple values */
IsMultiValue: boolean;
/** Item types this field applies to */
ApplicableToTypes: ItemType[];
/** Whether to track field value history */
EnableHistory: boolean;
/** Category for grouping in UI */
Category: 'Login' | 'Alias' | 'Card' | 'Identity' | 'API' | 'Note';
/** Default display order within category (lower = first) */
DefaultDisplayOrder: number;
};
/**
* Registry of all system-defined fields.
* These fields are immutable and their metadata is defined in code.
* DO NOT modify these definitions without careful consideration of backwards compatibility.
*/
declare const SystemFieldRegistry: Record<string, SystemFieldDefinition>;
/**
* Get system field definition by key.
* Returns undefined if the field key is not a system field.
*/
declare function getSystemField(fieldKey: string): SystemFieldDefinition | undefined;
/**
* Check if a field key represents a system field.
*/
declare function isSystemField(fieldKey: string): boolean;
/**
* Get all system fields applicable to a specific item type.
* Results are sorted by DefaultDisplayOrder.
*/
declare function getSystemFieldsForItemType(itemType: ItemType): SystemFieldDefinition[];
/**
* Get all system field keys.
*/
declare function getAllSystemFieldKeys(): string[];
/**
* Check if a field key matches a known system field prefix.
* This is useful for validation even before a specific field is registered.
*/
declare function isSystemFieldPrefix(fieldKey: string): boolean;
export { type Alias, type Attachment, type Credential, type EncryptionKey, FieldKey, type FieldKeyValue, type FieldType, type Item, type ItemField, type ItemTag, type ItemTagRef, type ItemType, type Passkey, type PasswordSettings, type SystemFieldDefinition, SystemFieldRegistry, type Tag, type TotpCode, getAllSystemFieldKeys, getFieldValue, getFieldValues, getSystemField, getSystemFieldsForItemType, groupFields, groupFieldsByCategory, hasField, isSystemField, isSystemFieldPrefix, itemToCredential };

View File

@@ -151,6 +151,219 @@ var FieldKey = {
AliasBirthdate: "alias.birthdate"
};
export { FieldKey };
// src/vault/ItemMethods.ts
function getFieldValue(item, fieldKey) {
const field = item.Fields.find((f) => f.FieldKey === fieldKey);
if (!field) {
return void 0;
}
return Array.isArray(field.Value) ? field.Value[0] : field.Value;
}
function getFieldValues(item, fieldKey) {
const field = item.Fields.find((f) => f.FieldKey === fieldKey);
if (!field) {
return [];
}
return Array.isArray(field.Value) ? field.Value : [field.Value];
}
function hasField(item, fieldKey) {
const value = getFieldValue(item, fieldKey);
return value !== void 0 && value !== "";
}
function groupFields(item, grouper) {
const groups = {};
item.Fields.forEach((field) => {
const group = grouper(field);
if (!groups[group]) {
groups[group] = [];
}
groups[group].push(field);
});
return groups;
}
function groupFieldsByCategory(item) {
return groupFields(item, (field) => {
if (field.FieldKey === FieldKey.AliasEmail) {
return "Login";
}
if (field.FieldKey.startsWith("login.")) {
return "Login";
}
if (field.FieldKey.startsWith("alias.")) {
return "Alias";
}
if (field.FieldKey.startsWith("card.")) {
return "Card";
}
if (field.FieldKey.startsWith("identity.")) {
return "Identity";
}
if (field.FieldKey.startsWith("api.")) {
return "API";
}
return "Custom";
});
}
function itemToCredential(item) {
return {
Id: item.Id,
Username: getFieldValue(item, FieldKey.LoginUsername),
Password: getFieldValue(item, FieldKey.LoginPassword) || "",
ServiceName: item.Name || "",
ServiceUrl: getFieldValue(item, FieldKey.LoginUrl),
Logo: item.Logo,
Notes: getFieldValue(item, FieldKey.LoginNotes),
Alias: {
FirstName: getFieldValue(item, FieldKey.AliasFirstName),
LastName: getFieldValue(item, FieldKey.AliasLastName),
NickName: getFieldValue(item, FieldKey.AliasNickname),
BirthDate: getFieldValue(item, FieldKey.AliasBirthdate) || "",
Gender: getFieldValue(item, FieldKey.AliasGender),
Email: getFieldValue(item, FieldKey.AliasEmail)
},
HasPasskey: item.HasPasskey,
HasAttachment: item.HasAttachment
};
}
// src/vault/SystemFieldRegistry.ts
var SystemFieldRegistry = {
// Login Fields
"login.username": {
FieldKey: "login.username",
Label: "Username",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: true,
Category: "Login",
DefaultDisplayOrder: 10
},
"login.password": {
FieldKey: "login.password",
Label: "Password",
FieldType: "Password",
IsHidden: true,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: true,
Category: "Login",
DefaultDisplayOrder: 20
},
"login.url": {
FieldKey: "login.url",
Label: "Website",
FieldType: "URL",
IsHidden: false,
IsMultiValue: true,
ApplicableToTypes: ["Login"],
EnableHistory: false,
Category: "Login",
DefaultDisplayOrder: 30
},
"login.notes": {
FieldKey: "login.notes",
Label: "Notes",
FieldType: "TextArea",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "CreditCard", "Identity", "Note"],
EnableHistory: false,
Category: "Login",
DefaultDisplayOrder: 100
},
// Alias Fields
"alias.email": {
FieldKey: "alias.email",
Label: "Email",
FieldType: "Email",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: true,
Category: "Alias",
DefaultDisplayOrder: 10
},
"alias.first_name": {
FieldKey: "alias.first_name",
Label: "First Name",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 20
},
"alias.last_name": {
FieldKey: "alias.last_name",
Label: "Last Name",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 30
},
"alias.nickname": {
FieldKey: "alias.nickname",
Label: "Nickname",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 40
},
"alias.gender": {
FieldKey: "alias.gender",
Label: "Gender",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 50
},
"alias.birthdate": {
FieldKey: "alias.birthdate",
Label: "Birth Date",
FieldType: "Date",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 60
}
/*
* Note: Card, Identity, and API fields can be added here when those item types are implemented
* Example:
* 'card.number': { ... },
* 'card.cardholder_name': { ... },
* 'identity.phone_number': { ... },
*/
};
function getSystemField(fieldKey) {
return SystemFieldRegistry[fieldKey];
}
function isSystemField(fieldKey) {
return fieldKey in SystemFieldRegistry;
}
function getSystemFieldsForItemType(itemType) {
return Object.values(SystemFieldRegistry).filter((field) => field.ApplicableToTypes.includes(itemType)).sort((a, b) => a.DefaultDisplayOrder - b.DefaultDisplayOrder);
}
function getAllSystemFieldKeys() {
return Object.keys(SystemFieldRegistry);
}
function isSystemFieldPrefix(fieldKey) {
return fieldKey.startsWith("login.") || fieldKey.startsWith("alias.") || fieldKey.startsWith("card.") || fieldKey.startsWith("identity.") || fieldKey.startsWith("api.") || fieldKey.startsWith("note.");
}
export { FieldKey, SystemFieldRegistry, getAllSystemFieldKeys, getFieldValue, getFieldValues, getSystemField, getSystemFieldsForItemType, groupFields, groupFieldsByCategory, hasField, isSystemField, isSystemFieldPrefix, itemToCredential };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -728,14 +728,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -746,7 +744,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -764,6 +762,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -793,9 +801,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -803,7 +812,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -811,9 +829,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -821,27 +843,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -891,13 +901,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -907,13 +918,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -922,13 +934,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -937,130 +950,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1160,95 +1164,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
`;
var MIGRATION_SCRIPTS = {
1: `\uFEFFBEGIN TRANSACTION;
@@ -1878,14 +1794,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -1896,7 +1810,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1914,6 +1828,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -1943,9 +1867,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1953,7 +1878,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -1961,9 +1895,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -1971,27 +1909,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -2041,13 +1967,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2057,13 +1984,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2072,13 +2000,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2087,130 +2016,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2310,93 +2230,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
12: `\uFEFFBEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');`,
13: `\uFEFFBEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;`
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`
};
// src/sql/VaultVersions.ts
@@ -2484,20 +2318,6 @@ var VAULT_VERSIONS = [
description: "Update to Field-Based Data Model",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 13,
version: "1.7.1",
description: "Rename Columns",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 14,
version: "1.8.0",
description: "Add Tags Tables",
releaseVersion: "0.27.0",
compatibleUpToVersion: "0.27.0"
}
];

View File

@@ -696,14 +696,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -714,7 +712,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -732,6 +730,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -761,9 +769,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -771,7 +780,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -779,9 +797,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -789,27 +811,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -859,13 +869,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -875,13 +886,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -890,13 +902,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -905,130 +918,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1128,95 +1132,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
`;
var MIGRATION_SCRIPTS = {
1: `\uFEFFBEGIN TRANSACTION;
@@ -1846,14 +1762,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -1864,7 +1778,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1882,6 +1796,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -1911,9 +1835,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1921,7 +1846,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -1929,9 +1863,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -1939,27 +1877,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -2009,13 +1935,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2025,13 +1952,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2040,13 +1968,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2055,130 +1984,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2278,93 +2198,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
12: `\uFEFFBEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');`,
13: `\uFEFFBEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;`
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`
};
// src/sql/VaultVersions.ts
@@ -2452,20 +2286,6 @@ var VAULT_VERSIONS = [
description: "Update to Field-Based Data Model",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 13,
version: "1.7.1",
description: "Rename Columns",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 14,
version: "1.8.0",
description: "Add Tags Tables",
releaseVersion: "0.27.0",
compatibleUpToVersion: "0.27.0"
}
];

View File

@@ -321,4 +321,135 @@ type ItemTag = {
IsDeleted: number;
};
export { type Alias, type Attachment, type Credential, type EncryptionKey, FieldKey, type FieldKeyValue, type ItemTag, type Passkey, type PasswordSettings, type Tag, type TotpCode };
/**
* Item types supported by the vault
*/
type ItemType = 'Login' | 'CreditCard' | 'Identity' | 'Note';
/**
* Item type representing vault entries in the new field-based data model.
* Replaces the old Credential type.
*/
type Item = {
Id: string;
Name: string | null;
ItemType: ItemType;
Logo?: Uint8Array | number[];
FolderId?: string | null;
FolderPath?: string | null;
Tags?: ItemTagRef[];
Fields: ItemField[];
HasPasskey?: boolean;
HasAttachment?: boolean;
HasTotp?: boolean;
CreatedAt: string;
UpdatedAt: string;
};
/**
* Field value within an item
*/
type ItemField = {
FieldKey: string;
Label: string;
FieldType: FieldType;
Value: string | string[];
IsHidden: boolean;
DisplayOrder: number;
};
/**
* Field types for rendering and validation
*/
type FieldType = 'Text' | 'Password' | 'Email' | 'URL' | 'Date' | 'Number' | 'Phone' | 'TextArea';
/**
* Tag reference for display within an item
*/
type ItemTagRef = {
Id: string;
Name: string;
Color?: string;
};
/**
* Helper functions for working with Item model
*/
/**
* Get a single field value by FieldKey
*/
declare function getFieldValue(item: Item, fieldKey: string): string | undefined;
/**
* Get all values for a multi-value field
*/
declare function getFieldValues(item: Item, fieldKey: string): string[];
/**
* Check if a field exists and has a value
*/
declare function hasField(item: Item, fieldKey: string): boolean;
/**
* Group fields by a categorization function
*/
declare function groupFields(item: Item, grouper: (field: ItemField) => string): Record<string, ItemField[]>;
/**
* Group fields by standard categories (Login, Alias, Custom)
*/
declare function groupFieldsByCategory(item: Item): Record<string, ItemField[]>;
/**
* Convert new Item model to legacy Credential model for backward compatibility.
* @deprecated Use Item model directly. This is a temporary compatibility layer.
*/
declare function itemToCredential(item: Item): Credential;
/**
* System field definition with metadata.
* System fields are predefined fields with immutable keys like 'login.username'.
* Their metadata (label, type, etc.) is defined here in code, not in the database.
*/
type SystemFieldDefinition = {
/** Unique system field key (e.g., 'login.username') */
FieldKey: string;
/** Display label for the field */
Label: string;
/** Field type for rendering/validation */
FieldType: FieldType;
/** Whether field is hidden/masked by default */
IsHidden: boolean;
/** Whether field supports multiple values */
IsMultiValue: boolean;
/** Item types this field applies to */
ApplicableToTypes: ItemType[];
/** Whether to track field value history */
EnableHistory: boolean;
/** Category for grouping in UI */
Category: 'Login' | 'Alias' | 'Card' | 'Identity' | 'API' | 'Note';
/** Default display order within category (lower = first) */
DefaultDisplayOrder: number;
};
/**
* Registry of all system-defined fields.
* These fields are immutable and their metadata is defined in code.
* DO NOT modify these definitions without careful consideration of backwards compatibility.
*/
declare const SystemFieldRegistry: Record<string, SystemFieldDefinition>;
/**
* Get system field definition by key.
* Returns undefined if the field key is not a system field.
*/
declare function getSystemField(fieldKey: string): SystemFieldDefinition | undefined;
/**
* Check if a field key represents a system field.
*/
declare function isSystemField(fieldKey: string): boolean;
/**
* Get all system fields applicable to a specific item type.
* Results are sorted by DefaultDisplayOrder.
*/
declare function getSystemFieldsForItemType(itemType: ItemType): SystemFieldDefinition[];
/**
* Get all system field keys.
*/
declare function getAllSystemFieldKeys(): string[];
/**
* Check if a field key matches a known system field prefix.
* This is useful for validation even before a specific field is registered.
*/
declare function isSystemFieldPrefix(fieldKey: string): boolean;
export { type Alias, type Attachment, type Credential, type EncryptionKey, FieldKey, type FieldKeyValue, type FieldType, type Item, type ItemField, type ItemTag, type ItemTagRef, type ItemType, type Passkey, type PasswordSettings, type SystemFieldDefinition, SystemFieldRegistry, type Tag, type TotpCode, getAllSystemFieldKeys, getFieldValue, getFieldValues, getSystemField, getSystemFieldsForItemType, groupFields, groupFieldsByCategory, hasField, isSystemField, isSystemFieldPrefix, itemToCredential };

View File

@@ -151,6 +151,219 @@ var FieldKey = {
AliasBirthdate: "alias.birthdate"
};
export { FieldKey };
// src/vault/ItemMethods.ts
function getFieldValue(item, fieldKey) {
const field = item.Fields.find((f) => f.FieldKey === fieldKey);
if (!field) {
return void 0;
}
return Array.isArray(field.Value) ? field.Value[0] : field.Value;
}
function getFieldValues(item, fieldKey) {
const field = item.Fields.find((f) => f.FieldKey === fieldKey);
if (!field) {
return [];
}
return Array.isArray(field.Value) ? field.Value : [field.Value];
}
function hasField(item, fieldKey) {
const value = getFieldValue(item, fieldKey);
return value !== void 0 && value !== "";
}
function groupFields(item, grouper) {
const groups = {};
item.Fields.forEach((field) => {
const group = grouper(field);
if (!groups[group]) {
groups[group] = [];
}
groups[group].push(field);
});
return groups;
}
function groupFieldsByCategory(item) {
return groupFields(item, (field) => {
if (field.FieldKey === FieldKey.AliasEmail) {
return "Login";
}
if (field.FieldKey.startsWith("login.")) {
return "Login";
}
if (field.FieldKey.startsWith("alias.")) {
return "Alias";
}
if (field.FieldKey.startsWith("card.")) {
return "Card";
}
if (field.FieldKey.startsWith("identity.")) {
return "Identity";
}
if (field.FieldKey.startsWith("api.")) {
return "API";
}
return "Custom";
});
}
function itemToCredential(item) {
return {
Id: item.Id,
Username: getFieldValue(item, FieldKey.LoginUsername),
Password: getFieldValue(item, FieldKey.LoginPassword) || "",
ServiceName: item.Name || "",
ServiceUrl: getFieldValue(item, FieldKey.LoginUrl),
Logo: item.Logo,
Notes: getFieldValue(item, FieldKey.LoginNotes),
Alias: {
FirstName: getFieldValue(item, FieldKey.AliasFirstName),
LastName: getFieldValue(item, FieldKey.AliasLastName),
NickName: getFieldValue(item, FieldKey.AliasNickname),
BirthDate: getFieldValue(item, FieldKey.AliasBirthdate) || "",
Gender: getFieldValue(item, FieldKey.AliasGender),
Email: getFieldValue(item, FieldKey.AliasEmail)
},
HasPasskey: item.HasPasskey,
HasAttachment: item.HasAttachment
};
}
// src/vault/SystemFieldRegistry.ts
var SystemFieldRegistry = {
// Login Fields
"login.username": {
FieldKey: "login.username",
Label: "Username",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: true,
Category: "Login",
DefaultDisplayOrder: 10
},
"login.password": {
FieldKey: "login.password",
Label: "Password",
FieldType: "Password",
IsHidden: true,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: true,
Category: "Login",
DefaultDisplayOrder: 20
},
"login.url": {
FieldKey: "login.url",
Label: "Website",
FieldType: "URL",
IsHidden: false,
IsMultiValue: true,
ApplicableToTypes: ["Login"],
EnableHistory: false,
Category: "Login",
DefaultDisplayOrder: 30
},
"login.notes": {
FieldKey: "login.notes",
Label: "Notes",
FieldType: "TextArea",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "CreditCard", "Identity", "Note"],
EnableHistory: false,
Category: "Login",
DefaultDisplayOrder: 100
},
// Alias Fields
"alias.email": {
FieldKey: "alias.email",
Label: "Email",
FieldType: "Email",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: true,
Category: "Alias",
DefaultDisplayOrder: 10
},
"alias.first_name": {
FieldKey: "alias.first_name",
Label: "First Name",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 20
},
"alias.last_name": {
FieldKey: "alias.last_name",
Label: "Last Name",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 30
},
"alias.nickname": {
FieldKey: "alias.nickname",
Label: "Nickname",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 40
},
"alias.gender": {
FieldKey: "alias.gender",
Label: "Gender",
FieldType: "Text",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 50
},
"alias.birthdate": {
FieldKey: "alias.birthdate",
Label: "Birth Date",
FieldType: "Date",
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ["Login", "Identity"],
EnableHistory: false,
Category: "Alias",
DefaultDisplayOrder: 60
}
/*
* Note: Card, Identity, and API fields can be added here when those item types are implemented
* Example:
* 'card.number': { ... },
* 'card.cardholder_name': { ... },
* 'identity.phone_number': { ... },
*/
};
function getSystemField(fieldKey) {
return SystemFieldRegistry[fieldKey];
}
function isSystemField(fieldKey) {
return fieldKey in SystemFieldRegistry;
}
function getSystemFieldsForItemType(itemType) {
return Object.values(SystemFieldRegistry).filter((field) => field.ApplicableToTypes.includes(itemType)).sort((a, b) => a.DefaultDisplayOrder - b.DefaultDisplayOrder);
}
function getAllSystemFieldKeys() {
return Object.keys(SystemFieldRegistry);
}
function isSystemFieldPrefix(fieldKey) {
return fieldKey.startsWith("login.") || fieldKey.startsWith("alias.") || fieldKey.startsWith("card.") || fieldKey.startsWith("identity.") || fieldKey.startsWith("api.") || fieldKey.startsWith("note.");
}
export { FieldKey, SystemFieldRegistry, getAllSystemFieldKeys, getFieldValue, getFieldValues, getSystemField, getSystemFieldsForItemType, groupFields, groupFieldsByCategory, hasField, isSystemField, isSystemFieldPrefix, itemToCredential };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -728,14 +728,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -746,7 +744,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -764,6 +762,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -793,9 +801,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -803,7 +812,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -811,9 +829,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -821,27 +843,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -891,13 +901,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -907,13 +918,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -922,13 +934,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -937,130 +950,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1160,95 +1164,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
`;
var MIGRATION_SCRIPTS = {
1: `\uFEFFBEGIN TRANSACTION;
@@ -1878,14 +1794,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -1896,7 +1810,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1914,6 +1828,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -1943,9 +1867,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1953,7 +1878,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -1961,9 +1895,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -1971,27 +1909,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -2041,13 +1967,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2057,13 +1984,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2072,13 +2000,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2087,130 +2016,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2310,93 +2230,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
12: `\uFEFFBEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');`,
13: `\uFEFFBEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;`
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`
};
// src/sql/VaultVersions.ts
@@ -2484,20 +2318,6 @@ var VAULT_VERSIONS = [
description: "Update to Field-Based Data Model",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 13,
version: "1.7.1",
description: "Rename Columns",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 14,
version: "1.8.0",
description: "Add Tags Tables",
releaseVersion: "0.27.0",
compatibleUpToVersion: "0.27.0"
}
];

View File

@@ -696,14 +696,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -714,7 +712,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -732,6 +730,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -761,9 +769,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -771,7 +780,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -779,9 +797,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -789,27 +811,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -859,13 +869,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -875,13 +886,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -890,13 +902,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -905,130 +918,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1128,95 +1132,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
`;
var MIGRATION_SCRIPTS = {
1: `\uFEFFBEGIN TRANSACTION;
@@ -1846,14 +1762,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -1864,7 +1778,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1882,6 +1796,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -1911,9 +1835,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1921,7 +1846,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -1929,9 +1863,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -1939,27 +1877,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -2009,13 +1935,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2025,13 +1952,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2040,13 +1968,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2055,130 +1984,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2278,93 +2198,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
12: `\uFEFFBEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');`,
13: `\uFEFFBEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;`
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`
};
// src/sql/VaultVersions.ts
@@ -2452,20 +2286,6 @@ var VAULT_VERSIONS = [
description: "Update to Field-Based Data Model",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 13,
version: "1.7.1",
description: "Rename Columns",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 14,
version: "1.8.0",
description: "Add Tags Tables",
releaseVersion: "0.27.0",
compatibleUpToVersion: "0.27.0"
}
];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -728,14 +728,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -746,7 +744,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -764,6 +762,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -793,9 +801,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -803,7 +812,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -811,9 +829,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -821,27 +843,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -891,13 +901,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -907,13 +918,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -922,13 +934,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -937,130 +950,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1160,95 +1164,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
`;
var MIGRATION_SCRIPTS = {
1: `\uFEFFBEGIN TRANSACTION;
@@ -1878,14 +1794,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -1896,7 +1810,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1914,6 +1828,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -1943,9 +1867,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1953,7 +1878,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -1961,9 +1895,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -1971,27 +1909,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -2041,13 +1967,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2057,13 +1984,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2072,13 +2000,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2087,130 +2016,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2310,93 +2230,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
12: `\uFEFFBEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');`,
13: `\uFEFFBEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;`
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`
};
// src/sql/VaultVersions.ts
@@ -2484,20 +2318,6 @@ var VAULT_VERSIONS = [
description: "Update to Field-Based Data Model",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 13,
version: "1.7.1",
description: "Rename Columns",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 14,
version: "1.8.0",
description: "Add Tags Tables",
releaseVersion: "0.27.0",
compatibleUpToVersion: "0.27.0"
}
];

View File

@@ -696,14 +696,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -714,7 +712,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -732,6 +730,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -761,9 +769,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -771,7 +780,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -779,9 +797,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -789,27 +811,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -859,13 +869,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -875,13 +886,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -890,13 +902,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -905,130 +918,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1128,95 +1132,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
`;
var MIGRATION_SCRIPTS = {
1: `\uFEFFBEGIN TRANSACTION;
@@ -1846,14 +1762,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -1864,7 +1778,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1882,6 +1796,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -1911,9 +1835,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1921,7 +1846,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -1929,9 +1863,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -1939,27 +1877,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -2009,13 +1935,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2025,13 +1952,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2040,13 +1968,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2055,130 +1984,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2278,93 +2198,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
12: `\uFEFFBEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');`,
13: `\uFEFFBEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;`
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`
};
// src/sql/VaultVersions.ts
@@ -2452,20 +2286,6 @@ var VAULT_VERSIONS = [
description: "Update to Field-Based Data Model",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 13,
version: "1.7.1",
description: "Rename Columns",
releaseVersion: "0.26.0",
compatibleUpToVersion: "0.26.0"
},
{
revision: 14,
version: "1.8.0",
description: "Add Tags Tables",
releaseVersion: "0.27.0",
compatibleUpToVersion: "0.27.0"
}
];

View File

@@ -191,12 +191,13 @@ public class AliasClientDbContext : DbContext
.HasForeignKey(fv => fv.ItemId)
.OnDelete(DeleteBehavior.Cascade);
// Configure FieldValue - FieldDefinition relationship
// Configure FieldValue - FieldDefinition relationship (nullable for system fields)
modelBuilder.Entity<FieldValue>()
.HasOne(fv => fv.FieldDefinition)
.WithMany(fd => fd.FieldValues)
.HasForeignKey(fv => fv.FieldDefinitionId)
.OnDelete(DeleteBehavior.Cascade);
.OnDelete(DeleteBehavior.Cascade)
.IsRequired(false); // Nullable for system fields
// Configure FieldHistory - FieldDefinition relationship
modelBuilder.Entity<FieldHistory>()
@@ -212,9 +213,15 @@ public class AliasClientDbContext : DbContext
modelBuilder.Entity<FieldValue>()
.HasIndex(fv => fv.FieldDefinitionId);
modelBuilder.Entity<FieldValue>()
.HasIndex(fv => fv.FieldKey); // Index for system field lookups
modelBuilder.Entity<FieldValue>()
.HasIndex(fv => new { fv.ItemId, fv.FieldDefinitionId, fv.Weight });
modelBuilder.Entity<FieldValue>()
.HasIndex(fv => new { fv.ItemId, fv.FieldKey }); // Composite index for system field queries
// Configure indexes for FieldHistory
modelBuilder.Entity<FieldHistory>()
.HasIndex(fh => fh.ItemId);
@@ -222,9 +229,7 @@ public class AliasClientDbContext : DbContext
modelBuilder.Entity<FieldHistory>()
.HasIndex(fh => fh.FieldDefinitionId);
// Configure indexes for FieldDefinition
modelBuilder.Entity<FieldDefinition>()
.HasIndex(fd => fd.FieldKey);
// FieldDefinition indexes (FieldKey removed - custom fields use GUID only)
// Configure indexes for Folder
modelBuilder.Entity<Folder>()

View File

@@ -11,7 +11,11 @@ using System.ComponentModel.DataAnnotations;
using AliasClientDb.Abstracts;
/// <summary>
/// FieldDefinition entity that defines the schema for fields.
/// FieldDefinition entity that defines the schema for custom (user-defined) fields.
/// NOTE: System fields (login.username, alias.email, etc.) do NOT have FieldDefinition rows.
/// System field metadata is defined in code (SystemFieldRegistry) and is immutable.
/// This table is ONLY for custom fields that users create themselves.
/// Custom fields are always referenced by their GUID (Id), never by FieldKey.
/// </summary>
public class FieldDefinition : SyncableEntity
{
@@ -21,19 +25,6 @@ public class FieldDefinition : SyncableEntity
[Key]
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the field key for system fields (e.g., 'login.username', 'card.number').
/// NULL for custom (user-defined) fields.
/// System fields use predefined keys like:
/// - login.username, login.password, login.notes, login.url
/// - card.number, card.cardholder_name, card.cvv
/// - identity.first_name, identity.email, identity.phone_numbers
/// - alias.email, alias.first_name (legacy)
/// Custom fields have FieldKey = NULL and are identified by their GUID and Label.
/// </summary>
[StringLength(100)]
public string? FieldKey { get; set; }
/// <summary>
/// Gets or sets the field type (Text, Password, Email, URL, Date, etc.).
/// </summary>

View File

@@ -13,6 +13,7 @@ using AliasClientDb.Abstracts;
/// <summary>
/// FieldValue entity that stores encrypted field values.
/// Supports both system fields (with FieldKey) and custom fields (with FieldDefinitionId).
/// </summary>
public class FieldValue : SyncableEntity
{
@@ -35,16 +36,27 @@ public class FieldValue : SyncableEntity
public virtual Item Item { get; set; } = null!;
/// <summary>
/// Gets or sets the field definition ID.
/// Gets or sets the field definition ID for custom (user-defined) fields.
/// NULL for system fields (which use FieldKey instead).
/// </summary>
[Required]
public Guid FieldDefinitionId { get; set; }
public Guid? FieldDefinitionId { get; set; }
/// <summary>
/// Gets or sets the field definition object.
/// Gets or sets the field definition object for custom fields.
/// NULL for system fields.
/// </summary>
[ForeignKey("FieldDefinitionId")]
public virtual FieldDefinition FieldDefinition { get; set; } = null!;
public virtual FieldDefinition? FieldDefinition { get; set; }
/// <summary>
/// Gets or sets the system field key for predefined fields (e.g., 'login.username').
/// NULL for custom (user-defined) fields (which use FieldDefinitionId instead).
/// System field metadata (label, type, etc.) is defined in code (SystemFieldRegistry),
/// not in the database.
/// Note: Exactly one of FieldKey or FieldDefinitionId must be non-null.
/// </summary>
[StringLength(100)]
public string? FieldKey { get; set; }
/// <summary>
/// Gets or sets the encrypted value.

View File

@@ -1,580 +0,0 @@
// <auto-generated />
using System;
using AliasClientDb;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace AliasClientDb.Migrations
{
[DbContext(typeof(AliasClientDbContext))]
[Migration("20251126211441_1.7.0-FieldBasedDataModelUpdate")]
partial class _170FieldBasedDataModelUpdate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.4")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true);
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("Blob")
.IsRequired()
.HasColumnType("BLOB");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Filename")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ItemId");
b.ToTable("Attachments");
});
modelBuilder.Entity("AliasClientDb.EncryptionKey", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<bool>("IsPrimary")
.HasColumnType("INTEGER");
b.Property<string>("PrivateKey")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("TEXT");
b.Property<string>("PublicKey")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("EncryptionKeys");
});
modelBuilder.Entity("AliasClientDb.FieldDefinition", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ApplicableToTypes")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("DefaultVisibility")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<int>("DisplayOrder")
.HasColumnType("INTEGER");
b.Property<bool>("EnableHistory")
.HasColumnType("INTEGER");
b.Property<string>("EntityType")
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<string>("FieldKey")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("FieldType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<bool>("IsMultiValue")
.HasColumnType("INTEGER");
b.Property<string>("Label")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("FieldKey");
b.ToTable("FieldDefinitions");
});
modelBuilder.Entity("AliasClientDb.FieldHistory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("ChangedAt")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("FieldDefinitionId")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("ValueSnapshot")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("FieldDefinitionId");
b.HasIndex("ItemId");
b.ToTable("FieldHistories");
});
modelBuilder.Entity("AliasClientDb.FieldValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("FieldDefinitionId")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.Property<int>("ValueIndex")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("FieldDefinitionId");
b.HasIndex("ItemId");
b.HasIndex("ItemId", "FieldDefinitionId", "ValueIndex");
b.ToTable("FieldValues");
});
modelBuilder.Entity("AliasClientDb.Folder", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<int>("DisplayOrder")
.HasColumnType("INTEGER");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<Guid?>("ParentFolderId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ParentFolderId");
b.ToTable("Folders");
});
modelBuilder.Entity("AliasClientDb.Item", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid?>("FolderId")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<string>("ItemType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<Guid?>("LogoId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("FolderId");
b.HasIndex("LogoId");
b.ToTable("Items");
});
modelBuilder.Entity("AliasClientDb.Logo", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<DateTime?>("FetchedAt")
.HasColumnType("TEXT");
b.Property<byte[]>("FileData")
.HasColumnType("BLOB");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<string>("MimeType")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("Source")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Source")
.IsUnique();
b.ToTable("Logos");
});
modelBuilder.Entity("AliasClientDb.Passkey", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("AdditionalData")
.HasColumnType("BLOB");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<byte[]>("PrfKey")
.HasMaxLength(64)
.HasColumnType("BLOB");
b.Property<string>("PrivateKey")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("PublicKey")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RpId")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT")
.UseCollation("NOCASE");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<byte[]>("UserHandle")
.IsRequired()
.HasColumnType("BLOB");
b.HasKey("Id");
b.HasIndex("ItemId");
b.HasIndex("RpId");
b.ToTable("Passkeys");
});
modelBuilder.Entity("AliasClientDb.Setting", b =>
{
b.Property<string>("Key")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Key");
b.ToTable("Settings");
});
modelBuilder.Entity("AliasClientDb.TotpCode", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<string>("SecretKey")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ItemId");
b.ToTable("TotpCodes");
});
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("Attachments")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.FieldHistory", b =>
{
b.HasOne("AliasClientDb.FieldDefinition", "FieldDefinition")
.WithMany("FieldHistories")
.HasForeignKey("FieldDefinitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AliasClientDb.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("FieldDefinition");
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.FieldValue", b =>
{
b.HasOne("AliasClientDb.FieldDefinition", "FieldDefinition")
.WithMany("FieldValues")
.HasForeignKey("FieldDefinitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("FieldValues")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("FieldDefinition");
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.Folder", b =>
{
b.HasOne("AliasClientDb.Folder", "ParentFolder")
.WithMany("ChildFolders")
.HasForeignKey("ParentFolderId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentFolder");
});
modelBuilder.Entity("AliasClientDb.Item", b =>
{
b.HasOne("AliasClientDb.Folder", "Folder")
.WithMany("Items")
.HasForeignKey("FolderId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("AliasClientDb.Logo", "Logo")
.WithMany("Items")
.HasForeignKey("LogoId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Folder");
b.Navigation("Logo");
});
modelBuilder.Entity("AliasClientDb.Passkey", b =>
{
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("Passkeys")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.TotpCode", b =>
{
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("TotpCodes")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.FieldDefinition", b =>
{
b.Navigation("FieldHistories");
b.Navigation("FieldValues");
});
modelBuilder.Entity("AliasClientDb.Folder", b =>
{
b.Navigation("ChildFolders");
b.Navigation("Items");
});
modelBuilder.Entity("AliasClientDb.Item", b =>
{
b.Navigation("Attachments");
b.Navigation("FieldValues");
b.Navigation("Passkeys");
b.Navigation("TotpCodes");
});
modelBuilder.Entity("AliasClientDb.Logo", b =>
{
b.Navigation("Items");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -1,575 +0,0 @@
// <auto-generated />
using System;
using AliasClientDb;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace AliasClientDb.Migrations
{
[DbContext(typeof(AliasClientDbContext))]
[Migration("20251126221717_1.7.1-RenameColumns")]
partial class _171RenameColumns
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.4")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true);
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("Blob")
.IsRequired()
.HasColumnType("BLOB");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Filename")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ItemId");
b.ToTable("Attachments");
});
modelBuilder.Entity("AliasClientDb.EncryptionKey", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<bool>("IsPrimary")
.HasColumnType("INTEGER");
b.Property<string>("PrivateKey")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("TEXT");
b.Property<string>("PublicKey")
.IsRequired()
.HasMaxLength(2000)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("EncryptionKeys");
});
modelBuilder.Entity("AliasClientDb.FieldDefinition", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("ApplicableToTypes")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("EnableHistory")
.HasColumnType("INTEGER");
b.Property<string>("FieldKey")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("FieldType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<bool>("IsHidden")
.HasColumnType("INTEGER");
b.Property<bool>("IsMultiValue")
.HasColumnType("INTEGER");
b.Property<string>("Label")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<int>("Weight")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("FieldKey");
b.ToTable("FieldDefinitions");
});
modelBuilder.Entity("AliasClientDb.FieldHistory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("ChangedAt")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("FieldDefinitionId")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("ValueSnapshot")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("FieldDefinitionId");
b.HasIndex("ItemId");
b.ToTable("FieldHistories");
});
modelBuilder.Entity("AliasClientDb.FieldValue", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("FieldDefinitionId")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.Property<int>("Weight")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("FieldDefinitionId");
b.HasIndex("ItemId");
b.HasIndex("ItemId", "FieldDefinitionId", "Weight");
b.ToTable("FieldValues");
});
modelBuilder.Entity("AliasClientDb.Folder", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<Guid?>("ParentFolderId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<int>("Weight")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ParentFolderId");
b.ToTable("Folders");
});
modelBuilder.Entity("AliasClientDb.Item", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid?>("FolderId")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<string>("ItemType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<Guid?>("LogoId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("FolderId");
b.HasIndex("LogoId");
b.ToTable("Items");
});
modelBuilder.Entity("AliasClientDb.Logo", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<DateTime?>("FetchedAt")
.HasColumnType("TEXT");
b.Property<byte[]>("FileData")
.HasColumnType("BLOB");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<string>("MimeType")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("Source")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("Source")
.IsUnique();
b.ToTable("Logos");
});
modelBuilder.Entity("AliasClientDb.Passkey", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("AdditionalData")
.HasColumnType("BLOB");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<byte[]>("PrfKey")
.HasMaxLength(64)
.HasColumnType("BLOB");
b.Property<string>("PrivateKey")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("PublicKey")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RpId")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT")
.UseCollation("NOCASE");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<byte[]>("UserHandle")
.IsRequired()
.HasColumnType("BLOB");
b.HasKey("Id");
b.HasIndex("ItemId");
b.HasIndex("RpId");
b.ToTable("Passkeys");
});
modelBuilder.Entity("AliasClientDb.Setting", b =>
{
b.Property<string>("Key")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("Key");
b.ToTable("Settings");
});
modelBuilder.Entity("AliasClientDb.TotpCode", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
.HasColumnType("INTEGER");
b.Property<Guid>("ItemId")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<string>("SecretKey")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ItemId");
b.ToTable("TotpCodes");
});
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("Attachments")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.FieldHistory", b =>
{
b.HasOne("AliasClientDb.FieldDefinition", "FieldDefinition")
.WithMany("FieldHistories")
.HasForeignKey("FieldDefinitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AliasClientDb.Item", "Item")
.WithMany()
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("FieldDefinition");
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.FieldValue", b =>
{
b.HasOne("AliasClientDb.FieldDefinition", "FieldDefinition")
.WithMany("FieldValues")
.HasForeignKey("FieldDefinitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("FieldValues")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("FieldDefinition");
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.Folder", b =>
{
b.HasOne("AliasClientDb.Folder", "ParentFolder")
.WithMany("ChildFolders")
.HasForeignKey("ParentFolderId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentFolder");
});
modelBuilder.Entity("AliasClientDb.Item", b =>
{
b.HasOne("AliasClientDb.Folder", "Folder")
.WithMany("Items")
.HasForeignKey("FolderId")
.OnDelete(DeleteBehavior.SetNull);
b.HasOne("AliasClientDb.Logo", "Logo")
.WithMany("Items")
.HasForeignKey("LogoId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Folder");
b.Navigation("Logo");
});
modelBuilder.Entity("AliasClientDb.Passkey", b =>
{
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("Passkeys")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.TotpCode", b =>
{
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("TotpCodes")
.HasForeignKey("ItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Item");
});
modelBuilder.Entity("AliasClientDb.FieldDefinition", b =>
{
b.Navigation("FieldHistories");
b.Navigation("FieldValues");
});
modelBuilder.Entity("AliasClientDb.Folder", b =>
{
b.Navigation("ChildFolders");
b.Navigation("Items");
});
modelBuilder.Entity("AliasClientDb.Item", b =>
{
b.Navigation("Attachments");
b.Navigation("FieldValues");
b.Navigation("Passkeys");
b.Navigation("TotpCodes");
});
modelBuilder.Entity("AliasClientDb.Logo", b =>
{
b.Navigation("Items");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -1,93 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AliasClientDb.Migrations
{
/// <inheritdoc />
public partial class _171RenameColumns : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DefaultVisibility",
table: "FieldDefinitions");
migrationBuilder.DropColumn(
name: "EntityType",
table: "FieldDefinitions");
migrationBuilder.RenameColumn(
name: "DisplayOrder",
table: "Folders",
newName: "Weight");
migrationBuilder.RenameColumn(
name: "ValueIndex",
table: "FieldValues",
newName: "Weight");
migrationBuilder.RenameIndex(
name: "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex",
table: "FieldValues",
newName: "IX_FieldValues_ItemId_FieldDefinitionId_Weight");
migrationBuilder.RenameColumn(
name: "DisplayOrder",
table: "FieldDefinitions",
newName: "Weight");
migrationBuilder.AddColumn<bool>(
name: "IsHidden",
table: "FieldDefinitions",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsHidden",
table: "FieldDefinitions");
migrationBuilder.RenameColumn(
name: "Weight",
table: "Folders",
newName: "DisplayOrder");
migrationBuilder.RenameColumn(
name: "Weight",
table: "FieldValues",
newName: "ValueIndex");
migrationBuilder.RenameIndex(
name: "IX_FieldValues_ItemId_FieldDefinitionId_Weight",
table: "FieldValues",
newName: "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex");
migrationBuilder.RenameColumn(
name: "Weight",
table: "FieldDefinitions",
newName: "DisplayOrder");
migrationBuilder.AddColumn<string>(
name: "DefaultVisibility",
table: "FieldDefinitions",
type: "TEXT",
maxLength: 50,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "EntityType",
table: "FieldDefinitions",
type: "TEXT",
maxLength: 50,
nullable: true);
}
}
}

View File

@@ -1,92 +0,0 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AliasClientDb.Migrations
{
/// <inheritdoc />
public partial class _172AddTagTables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
Color = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
DisplayOrder = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ItemTags",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
ItemId = table.Column<Guid>(type: "TEXT", nullable: false),
TagId = table.Column<Guid>(type: "TEXT", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ItemTags", x => x.Id);
table.ForeignKey(
name: "FK_ItemTags_Items_ItemId",
column: x => x.ItemId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ItemTags_Tags_TagId",
column: x => x.TagId,
principalTable: "Tags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ItemTags_ItemId",
table: "ItemTags",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_ItemTags_ItemId_TagId",
table: "ItemTags",
columns: new[] { "ItemId", "TagId" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ItemTags_TagId",
table: "ItemTags",
column: "TagId");
migrationBuilder.CreateIndex(
name: "IX_Tags_Name",
table: "Tags",
column: "Name");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ItemTags");
migrationBuilder.DropTable(
name: "Tags");
}
}
}

View File

@@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace AliasClientDb.Migrations
{
[DbContext(typeof(AliasClientDbContext))]
[Migration("20251202211204_1.7.2-AddTagTables")]
partial class _172AddTagTables
[Migration("20251203162345_1.7.0-FieldBasedDataModelUpdate")]
partial class _170FieldBasedDataModelUpdate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -106,10 +106,6 @@ namespace AliasClientDb.Migrations
b.Property<bool>("EnableHistory")
.HasColumnType("INTEGER");
b.Property<string>("FieldKey")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("FieldType")
.IsRequired()
.HasMaxLength(50)
@@ -137,8 +133,6 @@ namespace AliasClientDb.Migrations
b.HasKey("Id");
b.HasIndex("FieldKey");
b.ToTable("FieldDefinitions");
});
@@ -188,7 +182,11 @@ namespace AliasClientDb.Migrations
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("FieldDefinitionId")
b.Property<Guid?>("FieldDefinitionId")
.HasColumnType("TEXT");
b.Property<string>("FieldKey")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
@@ -210,8 +208,12 @@ namespace AliasClientDb.Migrations
b.HasIndex("FieldDefinitionId");
b.HasIndex("FieldKey");
b.HasIndex("ItemId");
b.HasIndex("ItemId", "FieldKey");
b.HasIndex("ItemId", "FieldDefinitionId", "Weight");
b.ToTable("FieldValues");
@@ -544,8 +546,7 @@ namespace AliasClientDb.Migrations
b.HasOne("AliasClientDb.FieldDefinition", "FieldDefinition")
.WithMany("FieldValues")
.HasForeignKey("FieldDefinitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("FieldValues")

View File

@@ -1,11 +1,11 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AliasClientDb.Migrations
{
using System;
using Microsoft.EntityFrameworkCore.Migrations;
/// <inheritdoc />
public partial class _170FieldBasedDataModelUpdate : Migration
{
@@ -56,18 +56,16 @@ namespace AliasClientDb.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
FieldKey = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
EntityType = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
FieldType = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
Label = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
IsMultiValue = table.Column<bool>(type: "INTEGER", nullable: false),
DefaultVisibility = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
IsHidden = table.Column<bool>(type: "INTEGER", nullable: false),
EnableHistory = table.Column<bool>(type: "INTEGER", nullable: false),
DisplayOrder = table.Column<int>(type: "INTEGER", nullable: false),
Weight = table.Column<int>(type: "INTEGER", nullable: false),
ApplicableToTypes = table.Column<string>(type: "TEXT", nullable: true),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
@@ -81,10 +79,10 @@ namespace AliasClientDb.Migrations
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
ParentFolderId = table.Column<Guid>(type: "TEXT", nullable: true),
DisplayOrder = table.Column<int>(type: "INTEGER", nullable: false),
Weight = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
@@ -108,13 +106,30 @@ namespace AliasClientDb.Migrations
FetchedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_Logos", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
Color = table.Column<string>(type: "TEXT", maxLength: 50, nullable: true),
DisplayOrder = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Items",
columns: table => new
@@ -126,7 +141,7 @@ namespace AliasClientDb.Migrations
FolderId = table.Column<Guid>(type: "TEXT", nullable: true),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
@@ -156,7 +171,7 @@ namespace AliasClientDb.Migrations
ChangedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
@@ -181,12 +196,13 @@ namespace AliasClientDb.Migrations
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
ItemId = table.Column<Guid>(type: "TEXT", nullable: false),
FieldDefinitionId = table.Column<Guid>(type: "TEXT", nullable: false),
FieldDefinitionId = table.Column<Guid>(type: "TEXT", nullable: true),
FieldKey = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true),
Value = table.Column<string>(type: "TEXT", nullable: true),
ValueIndex = table.Column<int>(type: "INTEGER", nullable: false),
Weight = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false)
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
@@ -205,10 +221,33 @@ namespace AliasClientDb.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_FieldDefinitions_FieldKey",
table: "FieldDefinitions",
column: "FieldKey");
migrationBuilder.CreateTable(
name: "ItemTags",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
ItemId = table.Column<Guid>(type: "TEXT", nullable: false),
TagId = table.Column<Guid>(type: "TEXT", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
IsDeleted = table.Column<bool>(type: "INTEGER", nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_ItemTags", x => x.Id);
table.ForeignKey(
name: "FK_ItemTags_Items_ItemId",
column: x => x.ItemId,
principalTable: "Items",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ItemTags_Tags_TagId",
column: x => x.TagId,
principalTable: "Tags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_FieldHistories_FieldDefinitionId",
@@ -225,15 +264,25 @@ namespace AliasClientDb.Migrations
table: "FieldValues",
column: "FieldDefinitionId");
migrationBuilder.CreateIndex(
name: "IX_FieldValues_FieldKey",
table: "FieldValues",
column: "FieldKey");
migrationBuilder.CreateIndex(
name: "IX_FieldValues_ItemId",
table: "FieldValues",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex",
name: "IX_FieldValues_ItemId_FieldDefinitionId_Weight",
table: "FieldValues",
columns: new[] { "ItemId", "FieldDefinitionId", "ValueIndex" });
columns: new[] { "ItemId", "FieldDefinitionId", "Weight" });
migrationBuilder.CreateIndex(
name: "IX_FieldValues_ItemId_FieldKey",
table: "FieldValues",
columns: new[] { "ItemId", "FieldKey" });
migrationBuilder.CreateIndex(
name: "IX_Folders_ParentFolderId",
@@ -250,12 +299,33 @@ namespace AliasClientDb.Migrations
table: "Items",
column: "LogoId");
migrationBuilder.CreateIndex(
name: "IX_ItemTags_ItemId",
table: "ItemTags",
column: "ItemId");
migrationBuilder.CreateIndex(
name: "IX_ItemTags_ItemId_TagId",
table: "ItemTags",
columns: new[] { "ItemId", "TagId" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ItemTags_TagId",
table: "ItemTags",
column: "TagId");
migrationBuilder.CreateIndex(
name: "IX_Logos_Source",
table: "Logos",
column: "Source",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Tags_Name",
table: "Tags",
column: "Name");
migrationBuilder.AddForeignKey(
name: "FK_Attachments_Items_ItemId",
table: "Attachments",
@@ -280,33 +350,10 @@ namespace AliasClientDb.Migrations
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
// ==============================================================
// CUSTOM DATA MIGRATION SCRIPT
// Migrates existing data from old schema to V5 field-based schema
// ==============================================================
// Data migration: Migrate from old Credentials/Services/Aliases structure to new Items/FieldValues structure
// System fields now use FieldKey directly in FieldValues (no FieldDefinitions needed)
// Step 1: Populate core FieldDefinitions (system fields)
migrationBuilder.Sql(@"
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '[""Login""]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '[""Login""]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '[""Login""]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '[""Login""]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '[""Login""]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '[""Login""]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '[""Login""]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '[""Login""]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '[""Login""]', datetime('now'), datetime('now'), 0);
");
// Step 2: Migrate Credentials to Items
// Migrate Items from Credentials
migrationBuilder.Sql(@"
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
SELECT
@@ -322,7 +369,7 @@ namespace AliasClientDb.Migrations
LEFT JOIN Services s ON s.Id = c.ServiceId;
");
// Step 3: Create Logo entries from Services (deduplicated)
// Migrate Logos from Services
migrationBuilder.Sql(@"
INSERT INTO Logos (Id, Source, FileData, MimeType, FetchedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
@@ -339,7 +386,7 @@ namespace AliasClientDb.Migrations
GROUP BY s.Url;
");
// Step 4: Link Items to Logos
// Update Items with LogoId
migrationBuilder.Sql(@"
UPDATE Items
SET LogoId = (
@@ -356,15 +403,16 @@ namespace AliasClientDb.Migrations
);
");
// Step 5: Migrate Service.Url → FieldValue (multi-value URLs)
// Migrate login.url field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -373,15 +421,16 @@ namespace AliasClientDb.Migrations
WHERE s.Url IS NOT NULL AND s.Url != '';
");
// Step 6: Migrate Credential.Username → FieldValue
// Migrate login.username field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -389,15 +438,16 @@ namespace AliasClientDb.Migrations
WHERE c.Username IS NOT NULL AND c.Username != '';
");
// Step 7: Migrate Credential.Notes → FieldValue
// Migrate login.notes field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -405,135 +455,129 @@ namespace AliasClientDb.Migrations
WHERE c.Notes IS NOT NULL AND c.Notes != '';
");
// Step 8: Migrate Passwords → FieldValue (most recent password)
// Password table stores plain text as whole client DB is encrypted
// Migrate login.password field (system field using FieldKey) - only most recent password
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
");
// Step 9: Migrate historical passwords → FieldHistory
// Migrate alias.email field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{""values"":[""' || p.Value || '""]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
");
// Step 10: Migrate Alias fields → FieldValues
migrationBuilder.Sql(@"
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
");
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
// Migrate alias.first_name field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
");
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
// Migrate alias.last_name field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
");
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
// Migrate alias.nickname field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
");
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
// Migrate alias.gender field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
");
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
// Migrate alias.birthdate field (system field using FieldKey)
migrationBuilder.Sql(@"
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -542,14 +586,6 @@ namespace AliasClientDb.Migrations
WHERE a.BirthDate IS NOT NULL AND a.BirthDate != '' AND a.BirthDate != '0001-01-01 00:00:00.000';
");
// Step 11: Update foreign keys to reference Items instead of Credentials
// NOTE: This step is not needed because RenameColumn already renamed CredentialId to ItemId
// at the beginning of the migration, so the data is already in the correct column.
// ==============================================================
// END OF CUSTOM DATA MIGRATION SCRIPT
// ==============================================================
migrationBuilder.DropTable(
name: "Passwords");
@@ -584,12 +620,18 @@ namespace AliasClientDb.Migrations
migrationBuilder.DropTable(
name: "FieldValues");
migrationBuilder.DropTable(
name: "ItemTags");
migrationBuilder.DropTable(
name: "FieldDefinitions");
migrationBuilder.DropTable(
name: "Items");
migrationBuilder.DropTable(
name: "Tags");
migrationBuilder.DropTable(
name: "Folders");

View File

@@ -103,10 +103,6 @@ namespace AliasClientDb.Migrations
b.Property<bool>("EnableHistory")
.HasColumnType("INTEGER");
b.Property<string>("FieldKey")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("FieldType")
.IsRequired()
.HasMaxLength(50)
@@ -134,8 +130,6 @@ namespace AliasClientDb.Migrations
b.HasKey("Id");
b.HasIndex("FieldKey");
b.ToTable("FieldDefinitions");
});
@@ -185,7 +179,11 @@ namespace AliasClientDb.Migrations
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("FieldDefinitionId")
b.Property<Guid?>("FieldDefinitionId")
.HasColumnType("TEXT");
b.Property<string>("FieldKey")
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<bool>("IsDeleted")
@@ -207,8 +205,12 @@ namespace AliasClientDb.Migrations
b.HasIndex("FieldDefinitionId");
b.HasIndex("FieldKey");
b.HasIndex("ItemId");
b.HasIndex("ItemId", "FieldKey");
b.HasIndex("ItemId", "FieldDefinitionId", "Weight");
b.ToTable("FieldValues");
@@ -541,8 +543,7 @@ namespace AliasClientDb.Migrations
b.HasOne("AliasClientDb.FieldDefinition", "FieldDefinition")
.WithMany("FieldValues")
.HasForeignKey("FieldDefinitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("AliasClientDb.Item", "Item")
.WithMany("FieldValues")

View File

@@ -690,14 +690,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -708,7 +706,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -726,6 +724,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -755,9 +763,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -765,7 +774,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -773,9 +791,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -783,27 +805,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -853,13 +863,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -869,13 +880,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -884,13 +896,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -899,130 +912,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1122,93 +1126,5 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');

View File

@@ -19,14 +19,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -37,7 +35,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -55,6 +53,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -84,9 +92,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -94,7 +103,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -102,9 +120,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -112,27 +134,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -182,13 +192,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -198,13 +209,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -213,13 +225,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -228,130 +241,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -451,5 +455,5 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');

View File

@@ -1,53 +0,0 @@
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');

View File

@@ -1,35 +0,0 @@
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;

View File

@@ -6,7 +6,6 @@
# Configurable settings
SQL_DIR="MigrationSql"
OUTPUT_FILE="MigrationTs/SqlConstants.ts"
VERSIONS_FILE="MigrationTs/VaultVersions.ts"
TEMP_DIR="/tmp/sql_to_ts"
# Path to the shared vault-sql package

View File

@@ -0,0 +1,5 @@
/**
* @deprecated This file is deprecated. Use ItemMethods.ts instead.
* Kept for backward compatibility during migration.
*/
export { itemToCredential } from './ItemMethods';

View File

@@ -0,0 +1,62 @@
/**
* Item types supported by the vault
*/
export type ItemType =
| 'Login'
| 'CreditCard'
| 'Identity'
| 'Note';
/**
* Item type representing vault entries in the new field-based data model.
* Replaces the old Credential type.
*/
export type Item = {
Id: string;
Name: string | null;
ItemType: ItemType;
Logo?: Uint8Array | number[];
FolderId?: string | null;
FolderPath?: string | null;
Tags?: ItemTagRef[];
Fields: ItemField[];
HasPasskey?: boolean;
HasAttachment?: boolean;
HasTotp?: boolean;
CreatedAt: string;
UpdatedAt: string;
}
/**
* Field value within an item
*/
export type ItemField = {
FieldKey: string;
Label: string;
FieldType: FieldType;
Value: string | string[];
IsHidden: boolean;
DisplayOrder: number;
}
/**
* Field types for rendering and validation
*/
export type FieldType =
| 'Text'
| 'Password'
| 'Email'
| 'URL'
| 'Date'
| 'Number'
| 'Phone'
| 'TextArea';
/**
* Tag reference for display within an item
*/
export type ItemTagRef = {
Id: string;
Name: string;
Color?: string;
}

View File

@@ -0,0 +1,113 @@
import type { Item, ItemField } from './Item';
import type { Credential } from './Credential';
import { FieldKey } from './FieldKey';
/**
* Helper functions for working with Item model
*/
/**
* Get a single field value by FieldKey
*/
export function getFieldValue(item: Item, fieldKey: string): string | undefined {
const field = item.Fields.find(f => f.FieldKey === fieldKey);
if (!field) {
return undefined;
}
return Array.isArray(field.Value) ? field.Value[0] : field.Value;
}
/**
* Get all values for a multi-value field
*/
export function getFieldValues(item: Item, fieldKey: string): string[] {
const field = item.Fields.find(f => f.FieldKey === fieldKey);
if (!field) {
return [];
}
return Array.isArray(field.Value) ? field.Value : [field.Value];
}
/**
* Check if a field exists and has a value
*/
export function hasField(item: Item, fieldKey: string): boolean {
const value = getFieldValue(item, fieldKey);
return value !== undefined && value !== '';
}
/**
* Group fields by a categorization function
*/
export function groupFields(
item: Item,
grouper: (field: ItemField) => string
): Record<string, ItemField[]> {
const groups: Record<string, ItemField[]> = {};
item.Fields.forEach(field => {
const group = grouper(field);
if (!groups[group]) {
groups[group] = [];
}
groups[group].push(field);
});
return groups;
}
/**
* Group fields by standard categories (Login, Alias, Custom)
*/
export function groupFieldsByCategory(item: Item): Record<string, ItemField[]> {
return groupFields(item, (field) => {
// Render alias.email as login
if (field.FieldKey === FieldKey.AliasEmail) {
return 'Login';
}
if (field.FieldKey.startsWith('login.')) {
return 'Login';
}
if (field.FieldKey.startsWith('alias.')) {
return 'Alias';
}
if (field.FieldKey.startsWith('card.')) {
return 'Card';
}
if (field.FieldKey.startsWith('identity.')) {
return 'Identity';
}
if (field.FieldKey.startsWith('api.')) {
return 'API';
}
return 'Custom';
});
}
/**
* Convert new Item model to legacy Credential model for backward compatibility.
* @deprecated Use Item model directly. This is a temporary compatibility layer.
*/
export function itemToCredential(item: Item): Credential {
return {
Id: item.Id,
Username: getFieldValue(item, FieldKey.LoginUsername),
Password: getFieldValue(item, FieldKey.LoginPassword) || '',
ServiceName: item.Name || '',
ServiceUrl: getFieldValue(item, FieldKey.LoginUrl),
Logo: item.Logo,
Notes: getFieldValue(item, FieldKey.LoginNotes),
Alias: {
FirstName: getFieldValue(item, FieldKey.AliasFirstName),
LastName: getFieldValue(item, FieldKey.AliasLastName),
NickName: getFieldValue(item, FieldKey.AliasNickname),
BirthDate: getFieldValue(item, FieldKey.AliasBirthdate) || '',
Gender: getFieldValue(item, FieldKey.AliasGender),
Email: getFieldValue(item, FieldKey.AliasEmail)
},
HasPasskey: item.HasPasskey,
HasAttachment: item.HasAttachment
};
}

View File

@@ -0,0 +1,201 @@
import type { FieldType, ItemType } from './Item';
/**
* System field definition with metadata.
* System fields are predefined fields with immutable keys like 'login.username'.
* Their metadata (label, type, etc.) is defined here in code, not in the database.
*/
export type SystemFieldDefinition = {
/** Unique system field key (e.g., 'login.username') */
FieldKey: string;
/** Display label for the field */
Label: string;
/** Field type for rendering/validation */
FieldType: FieldType;
/** Whether field is hidden/masked by default */
IsHidden: boolean;
/** Whether field supports multiple values */
IsMultiValue: boolean;
/** Item types this field applies to */
ApplicableToTypes: ItemType[];
/** Whether to track field value history */
EnableHistory: boolean;
/** Category for grouping in UI */
Category: 'Login' | 'Alias' | 'Card' | 'Identity' | 'API' | 'Note';
/** Default display order within category (lower = first) */
DefaultDisplayOrder: number;
};
/**
* Registry of all system-defined fields.
* These fields are immutable and their metadata is defined in code.
* DO NOT modify these definitions without careful consideration of backwards compatibility.
*/
export const SystemFieldRegistry: Record<string, SystemFieldDefinition> = {
// Login Fields
'login.username': {
FieldKey: 'login.username',
Label: 'Username',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login'],
EnableHistory: true,
Category: 'Login',
DefaultDisplayOrder: 10
},
'login.password': {
FieldKey: 'login.password',
Label: 'Password',
FieldType: 'Password',
IsHidden: true,
IsMultiValue: false,
ApplicableToTypes: ['Login'],
EnableHistory: true,
Category: 'Login',
DefaultDisplayOrder: 20
},
'login.url': {
FieldKey: 'login.url',
Label: 'Website',
FieldType: 'URL',
IsHidden: false,
IsMultiValue: true,
ApplicableToTypes: ['Login'],
EnableHistory: false,
Category: 'Login',
DefaultDisplayOrder: 30
},
'login.notes': {
FieldKey: 'login.notes',
Label: 'Notes',
FieldType: 'TextArea',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login', 'CreditCard', 'Identity', 'Note'],
EnableHistory: false,
Category: 'Login',
DefaultDisplayOrder: 100
},
// Alias Fields
'alias.email': {
FieldKey: 'alias.email',
Label: 'Email',
FieldType: 'Email',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login'],
EnableHistory: true,
Category: 'Alias',
DefaultDisplayOrder: 10
},
'alias.first_name': {
FieldKey: 'alias.first_name',
Label: 'First Name',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login', 'Identity'],
EnableHistory: false,
Category: 'Alias',
DefaultDisplayOrder: 20
},
'alias.last_name': {
FieldKey: 'alias.last_name',
Label: 'Last Name',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login', 'Identity'],
EnableHistory: false,
Category: 'Alias',
DefaultDisplayOrder: 30
},
'alias.nickname': {
FieldKey: 'alias.nickname',
Label: 'Nickname',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login'],
EnableHistory: false,
Category: 'Alias',
DefaultDisplayOrder: 40
},
'alias.gender': {
FieldKey: 'alias.gender',
Label: 'Gender',
FieldType: 'Text',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login', 'Identity'],
EnableHistory: false,
Category: 'Alias',
DefaultDisplayOrder: 50
},
'alias.birthdate': {
FieldKey: 'alias.birthdate',
Label: 'Birth Date',
FieldType: 'Date',
IsHidden: false,
IsMultiValue: false,
ApplicableToTypes: ['Login', 'Identity'],
EnableHistory: false,
Category: 'Alias',
DefaultDisplayOrder: 60
},
/*
* Note: Card, Identity, and API fields can be added here when those item types are implemented
* Example:
* 'card.number': { ... },
* 'card.cardholder_name': { ... },
* 'identity.phone_number': { ... },
*/
};
/**
* Get system field definition by key.
* Returns undefined if the field key is not a system field.
*/
export function getSystemField(fieldKey: string): SystemFieldDefinition | undefined {
return SystemFieldRegistry[fieldKey];
}
/**
* Check if a field key represents a system field.
*/
export function isSystemField(fieldKey: string): boolean {
return fieldKey in SystemFieldRegistry;
}
/**
* Get all system fields applicable to a specific item type.
* Results are sorted by DefaultDisplayOrder.
*/
export function getSystemFieldsForItemType(itemType: ItemType): SystemFieldDefinition[] {
return Object.values(SystemFieldRegistry)
.filter(field => field.ApplicableToTypes.includes(itemType))
.sort((a, b) => a.DefaultDisplayOrder - b.DefaultDisplayOrder);
}
/**
* Get all system field keys.
*/
export function getAllSystemFieldKeys(): string[] {
return Object.keys(SystemFieldRegistry);
}
/**
* Check if a field key matches a known system field prefix.
* This is useful for validation even before a specific field is registered.
*/
export function isSystemFieldPrefix(fieldKey: string): boolean {
return fieldKey.startsWith('login.') ||
fieldKey.startsWith('alias.') ||
fieldKey.startsWith('card.') ||
fieldKey.startsWith('identity.') ||
fieldKey.startsWith('api.') ||
fieldKey.startsWith('note.');
}

View File

@@ -10,3 +10,7 @@ export * from './Passkey';
export * from './FieldKey';
export * from './Tag';
export * from './ItemTag';
export * from './Item';
export * from './CredentialCompat';
export * from './ItemMethods';
export * from './SystemFieldRegistry';

View File

@@ -697,14 +697,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -715,7 +713,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -733,6 +731,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -762,9 +770,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -772,7 +781,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -780,9 +798,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -790,27 +812,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -860,13 +870,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -876,13 +887,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -891,13 +903,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -906,130 +919,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -1129,95 +1133,7 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');
BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');
`;
/**
* Individual migration SQL scripts
@@ -1851,14 +1767,12 @@ CREATE INDEX IF NOT EXISTS "IX_Attachments_ItemId" ON "Attachments" ("ItemId");
CREATE TABLE "FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"FieldKey" TEXT NULL,
"EntityType" TEXT NULL,
"FieldType" TEXT NOT NULL,
"Label" TEXT NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"DefaultVisibility" TEXT NULL,
"IsHidden" INTEGER NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
@@ -1869,7 +1783,7 @@ CREATE TABLE "Folders" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Folders" PRIMARY KEY,
"Name" TEXT NOT NULL,
"ParentFolderId" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1887,6 +1801,16 @@ CREATE TABLE "Logos" (
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "Items" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY,
"Name" TEXT NULL,
@@ -1916,9 +1840,10 @@ CREATE TABLE "FieldHistories" (
CREATE TABLE "FieldValues" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldValues" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NOT NULL,
"FieldDefinitionId" TEXT NULL,
"FieldKey" TEXT NULL,
"Value" TEXT NULL,
"ValueIndex" INTEGER NOT NULL,
"Weight" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
@@ -1926,7 +1851,16 @@ CREATE TABLE "FieldValues" (
CONSTRAINT "FK_FieldValues_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_FieldHistories_FieldDefinitionId" ON "FieldHistories" ("FieldDefinitionId");
@@ -1934,9 +1868,13 @@ CREATE INDEX "IX_FieldHistories_ItemId" ON "FieldHistories" ("ItemId");
CREATE INDEX "IX_FieldValues_FieldDefinitionId" ON "FieldValues" ("FieldDefinitionId");
CREATE INDEX "IX_FieldValues_FieldKey" ON "FieldValues" ("FieldKey");
CREATE INDEX "IX_FieldValues_ItemId" ON "FieldValues" ("ItemId");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex" ON "FieldValues" ("ItemId", "FieldDefinitionId", "ValueIndex");
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
CREATE INDEX "IX_FieldValues_ItemId_FieldKey" ON "FieldValues" ("ItemId", "FieldKey");
CREATE INDEX "IX_Folders_ParentFolderId" ON "Folders" ("ParentFolderId");
@@ -1944,27 +1882,15 @@ CREATE INDEX "IX_Items_FolderId" ON "Items" ("FolderId");
CREATE INDEX "IX_Items_LogoId" ON "Items" ("LogoId");
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
-- Login fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'login.username', 'Item', 'Text', 'Username', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.password', 'Item', 'Password', 'Password', 0, 'Hidden', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.notes', 'Item', 'Text', 'Notes', 0, 'Collapsed', 0, 0, NULL, datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'login.url', 'Item', 'URL', 'Website URLs', 1, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
-- Alias fields
INSERT INTO FieldDefinitions (Id, FieldKey, EntityType, FieldType, Label, IsMultiValue, DefaultVisibility, EnableHistory, DisplayOrder, ApplicableToTypes, CreatedAt, UpdatedAt, IsDeleted)
VALUES
(lower(hex(randomblob(16))), 'alias.email', 'Item', 'Email', 'Alias Email', 0, 'Visible', 1, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.first_name', 'Item', 'Text', 'First Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.last_name', 'Item', 'Text', 'Last Name', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.nickname', 'Item', 'Text', 'Nickname', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.gender', 'Item', 'Text', 'Gender', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0),
(lower(hex(randomblob(16))), 'alias.birthdate', 'Item', 'Date', 'Birth Date', 0, 'Visible', 0, 0, '["Login"]', datetime('now'), datetime('now'), 0);
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO Items (Id, Name, ItemType, LogoId, FolderId, CreatedAt, UpdatedAt, IsDeleted)
@@ -2014,13 +1940,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.url' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.url' AS FieldKey,
s.Url AS Value,
0 AS ValueIndex,
0 AS Weight,
s.UpdatedAt AS CreatedAt,
s.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2030,13 +1957,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.username' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.username' AS FieldKey,
c.Username AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2045,13 +1973,14 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.notes' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.notes' AS FieldKey,
c.Notes AS Value,
0 AS ValueIndex,
0 AS Weight,
c.UpdatedAt AS CreatedAt,
c.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2060,130 +1989,121 @@ CREATE UNIQUE INDEX "IX_Logos_Source" ON "Logos" ("Source");
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'login.password' AS FieldKey,
p.Value AS Value,
0 AS ValueIndex,
0 AS Weight,
p.UpdatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated, MAX(Id) AS MaxId
FROM Passwords
WHERE IsDeleted = 0
GROUP BY CredentialId
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated;
) pm ON p.CredentialId = pm.CredentialId AND p.UpdatedAt = pm.MaxUpdated AND p.Id = pm.MaxId
WHERE p.IsDeleted = 0;
INSERT INTO FieldHistories (Id, ItemId, FieldDefinitionId, ValueSnapshot, ChangedAt, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
p.CredentialId AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'login.password' LIMIT 1) AS FieldDefinitionId,
'{"values":["' || p.Value || '"]}' AS ValueSnapshot,
p.UpdatedAt AS ChangedAt,
p.CreatedAt AS CreatedAt,
p.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Passwords p
WHERE p.Id NOT IN (
SELECT p2.Id FROM Passwords p2
INNER JOIN (
SELECT CredentialId, MAX(UpdatedAt) AS MaxUpdated
FROM Passwords
GROUP BY CredentialId
) pm ON p2.CredentialId = pm.CredentialId AND p2.UpdatedAt = pm.MaxUpdated
);
-- Migrate Alias.Email
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.email' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.email' AS FieldKey,
a.Email AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Email IS NOT NULL AND a.Email != '';
-- Migrate Alias.FirstName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.first_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.first_name' AS FieldKey,
a.FirstName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.FirstName IS NOT NULL AND a.FirstName != '';
-- Migrate Alias.LastName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.last_name' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.last_name' AS FieldKey,
a.LastName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.LastName IS NOT NULL AND a.LastName != '';
-- Migrate Alias.NickName
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.nickname' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.nickname' AS FieldKey,
a.NickName AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.NickName IS NOT NULL AND a.NickName != '';
-- Migrate Alias.Gender
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.gender' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.gender' AS FieldKey,
a.Gender AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
FROM Credentials c
INNER JOIN Aliases a ON a.Id = c.AliasId
WHERE a.Gender IS NOT NULL AND a.Gender != '';
-- Migrate Alias.BirthDate
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, Value, ValueIndex, CreatedAt, UpdatedAt, IsDeleted)
INSERT INTO FieldValues (Id, ItemId, FieldDefinitionId, FieldKey, Value, Weight, CreatedAt, UpdatedAt, IsDeleted)
SELECT
lower(hex(randomblob(16))) AS Id,
c.Id AS ItemId,
(SELECT Id FROM FieldDefinitions WHERE FieldKey = 'alias.birthdate' LIMIT 1) AS FieldDefinitionId,
NULL AS FieldDefinitionId,
'alias.birthdate' AS FieldKey,
a.BirthDate AS Value,
0 AS ValueIndex,
0 AS Weight,
a.UpdatedAt AS CreatedAt,
a.UpdatedAt AS UpdatedAt,
0 AS IsDeleted
@@ -2283,91 +2203,5 @@ CREATE INDEX "IX_TotpCodes_ItemId" ON "TotpCodes" ("ItemId");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126211441_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
12: `BEGIN TRANSACTION;
ALTER TABLE "Folders" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldValues" RENAME COLUMN "ValueIndex" TO "Weight";
DROP INDEX "IX_FieldValues_ItemId_FieldDefinitionId_ValueIndex";
CREATE INDEX "IX_FieldValues_ItemId_FieldDefinitionId_Weight" ON "FieldValues" ("ItemId", "FieldDefinitionId", "Weight");
ALTER TABLE "FieldDefinitions" RENAME COLUMN "DisplayOrder" TO "Weight";
ALTER TABLE "FieldDefinitions" ADD "IsHidden" INTEGER NOT NULL DEFAULT 0;
CREATE TABLE "ef_temp_FieldDefinitions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_FieldDefinitions" PRIMARY KEY,
"ApplicableToTypes" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"EnableHistory" INTEGER NOT NULL,
"FieldKey" TEXT NULL,
"FieldType" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
"IsHidden" INTEGER NOT NULL,
"IsMultiValue" INTEGER NOT NULL,
"Label" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"Weight" INTEGER NOT NULL
);
INSERT INTO "ef_temp_FieldDefinitions" ("Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight")
SELECT "Id", "ApplicableToTypes", "CreatedAt", "EnableHistory", "FieldKey", "FieldType", "IsDeleted", "IsHidden", "IsMultiValue", "Label", "UpdatedAt", "Weight"
FROM "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "FieldDefinitions";
ALTER TABLE "ef_temp_FieldDefinitions" RENAME TO "FieldDefinitions";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE INDEX "IX_FieldDefinitions_FieldKey" ON "FieldDefinitions" ("FieldKey");
COMMIT;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251126221717_1.7.1-RenameColumns', '9.0.4');`,
13: `BEGIN TRANSACTION;
CREATE TABLE "Tags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_Tags" PRIMARY KEY,
"Name" TEXT NOT NULL,
"Color" TEXT NULL,
"DisplayOrder" INTEGER NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL
);
CREATE TABLE "ItemTags" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ItemTags" PRIMARY KEY,
"ItemId" TEXT NOT NULL,
"TagId" TEXT NOT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
"IsDeleted" INTEGER NOT NULL,
CONSTRAINT "FK_ItemTags_Items_ItemId" FOREIGN KEY ("ItemId") REFERENCES "Items" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_ItemTags_Tags_TagId" FOREIGN KEY ("TagId") REFERENCES "Tags" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_ItemTags_ItemId" ON "ItemTags" ("ItemId");
CREATE UNIQUE INDEX "IX_ItemTags_ItemId_TagId" ON "ItemTags" ("ItemId", "TagId");
CREATE INDEX "IX_ItemTags_TagId" ON "ItemTags" ("TagId");
CREATE INDEX "IX_Tags_Name" ON "Tags" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20251202211204_1.7.2-AddTagTables', '9.0.4');
COMMIT;`,
VALUES ('20251203162345_1.7.0-FieldBasedDataModelUpdate', '9.0.4');`,
};

View File

@@ -95,18 +95,4 @@ export const VAULT_VERSIONS: VaultVersion[] = [
releaseVersion: '0.26.0',
compatibleUpToVersion: '0.26.0',
},
{
revision: 13,
version: '1.7.1',
description: 'Rename Columns',
releaseVersion: '0.26.0',
compatibleUpToVersion: '0.26.0',
},
{
revision: 14,
version: '1.8.0',
description: 'Add Tags Tables',
releaseVersion: '0.27.0',
compatibleUpToVersion: '0.27.0',
},
];