🎨 Optimize storage logic
This commit is contained in:
@@ -33,6 +33,11 @@ export class AppConfig {
|
||||
*/
|
||||
"updates": UpdatesConfig;
|
||||
|
||||
/**
|
||||
* Git同步设置
|
||||
*/
|
||||
"sync": GitSyncConfig;
|
||||
|
||||
/**
|
||||
* 配置元数据
|
||||
*/
|
||||
@@ -52,6 +57,9 @@ export class AppConfig {
|
||||
if (!("updates" in $$source)) {
|
||||
this["updates"] = (new UpdatesConfig());
|
||||
}
|
||||
if (!("sync" in $$source)) {
|
||||
this["sync"] = (new GitSyncConfig());
|
||||
}
|
||||
if (!("metadata" in $$source)) {
|
||||
this["metadata"] = (new ConfigMetadata());
|
||||
}
|
||||
@@ -68,6 +76,7 @@ export class AppConfig {
|
||||
const $$createField2_0 = $$createType2;
|
||||
const $$createField3_0 = $$createType3;
|
||||
const $$createField4_0 = $$createType4;
|
||||
const $$createField5_0 = $$createType5;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("general" in $$parsedSource) {
|
||||
$$parsedSource["general"] = $$createField0_0($$parsedSource["general"]);
|
||||
@@ -81,8 +90,11 @@ export class AppConfig {
|
||||
if ("updates" in $$parsedSource) {
|
||||
$$parsedSource["updates"] = $$createField3_0($$parsedSource["updates"]);
|
||||
}
|
||||
if ("sync" in $$parsedSource) {
|
||||
$$parsedSource["sync"] = $$createField4_0($$parsedSource["sync"]);
|
||||
}
|
||||
if ("metadata" in $$parsedSource) {
|
||||
$$parsedSource["metadata"] = $$createField4_0($$parsedSource["metadata"]);
|
||||
$$parsedSource["metadata"] = $$createField5_0($$parsedSource["metadata"]);
|
||||
}
|
||||
return new AppConfig($$parsedSource as Partial<AppConfig>);
|
||||
}
|
||||
@@ -126,7 +138,7 @@ export class AppearanceConfig {
|
||||
* Creates a new AppearanceConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): AppearanceConfig {
|
||||
const $$createField2_0 = $$createType5;
|
||||
const $$createField2_0 = $$createType6;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("customTheme" in $$parsedSource) {
|
||||
$$parsedSource["customTheme"] = $$createField2_0($$parsedSource["customTheme"]);
|
||||
@@ -135,6 +147,31 @@ export class AppearanceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthMethod 定义Git认证方式
|
||||
*/
|
||||
export enum AuthMethod {
|
||||
/**
|
||||
* The Go zero value for the underlying type of the enum.
|
||||
*/
|
||||
$zero = "",
|
||||
|
||||
/**
|
||||
* 个人访问令牌
|
||||
*/
|
||||
Token = "token",
|
||||
|
||||
/**
|
||||
* SSH密钥
|
||||
*/
|
||||
SSHKey = "ssh_key",
|
||||
|
||||
/**
|
||||
* 用户名密码
|
||||
*/
|
||||
UserPass = "user_pass",
|
||||
};
|
||||
|
||||
/**
|
||||
* ConfigMetadata 配置元数据
|
||||
*/
|
||||
@@ -200,8 +237,8 @@ export class CustomThemeConfig {
|
||||
* Creates a new CustomThemeConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): CustomThemeConfig {
|
||||
const $$createField0_0 = $$createType6;
|
||||
const $$createField1_0 = $$createType6;
|
||||
const $$createField0_0 = $$createType7;
|
||||
const $$createField1_0 = $$createType7;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("darkTheme" in $$parsedSource) {
|
||||
$$parsedSource["darkTheme"] = $$createField0_0($$parsedSource["darkTheme"]);
|
||||
@@ -224,6 +261,11 @@ export class Document {
|
||||
"updatedAt": time$0.Time;
|
||||
"is_deleted": boolean;
|
||||
|
||||
/**
|
||||
* 锁定标志,锁定的文档无法被删除
|
||||
*/
|
||||
"is_locked": boolean;
|
||||
|
||||
/** Creates a new Document instance. */
|
||||
constructor($$source: Partial<Document> = {}) {
|
||||
if (!("id" in $$source)) {
|
||||
@@ -244,6 +286,9 @@ export class Document {
|
||||
if (!("is_deleted" in $$source)) {
|
||||
this["is_deleted"] = false;
|
||||
}
|
||||
if (!("is_locked" in $$source)) {
|
||||
this["is_locked"] = false;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
@@ -389,7 +434,7 @@ export class Extension {
|
||||
* Creates a new Extension instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): Extension {
|
||||
const $$createField3_0 = $$createType7;
|
||||
const $$createField3_0 = $$createType8;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("config" in $$parsedSource) {
|
||||
$$parsedSource["config"] = $$createField3_0($$parsedSource["config"]);
|
||||
@@ -522,7 +567,7 @@ export class GeneralConfig {
|
||||
* Creates a new GeneralConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): GeneralConfig {
|
||||
const $$createField5_0 = $$createType9;
|
||||
const $$createField5_0 = $$createType10;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("globalHotkey" in $$parsedSource) {
|
||||
$$parsedSource["globalHotkey"] = $$createField5_0($$parsedSource["globalHotkey"]);
|
||||
@@ -531,6 +576,91 @@ export class GeneralConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GitSyncConfig 保存Git同步的配置信息
|
||||
*/
|
||||
export class GitSyncConfig {
|
||||
"enabled": boolean;
|
||||
"repo_url": string;
|
||||
"branch": string;
|
||||
"auth_method": AuthMethod;
|
||||
"username"?: string;
|
||||
"password"?: string;
|
||||
"token"?: string;
|
||||
"ssh_key_path"?: string;
|
||||
"ssh_key_passphrase"?: string;
|
||||
|
||||
/**
|
||||
* 同步间隔(分钟)
|
||||
*/
|
||||
"sync_interval": number;
|
||||
"last_sync_time": time$0.Time;
|
||||
|
||||
/**
|
||||
* 是否启用自动同步
|
||||
*/
|
||||
"auto_sync": boolean;
|
||||
"local_repo_path": string;
|
||||
|
||||
/**
|
||||
* 合并冲突策略
|
||||
*/
|
||||
"sync_strategy": SyncStrategy;
|
||||
|
||||
/**
|
||||
* 要同步的文件列表,默认为数据库文件
|
||||
*/
|
||||
"files_to_sync": string[];
|
||||
|
||||
/** Creates a new GitSyncConfig instance. */
|
||||
constructor($$source: Partial<GitSyncConfig> = {}) {
|
||||
if (!("enabled" in $$source)) {
|
||||
this["enabled"] = false;
|
||||
}
|
||||
if (!("repo_url" in $$source)) {
|
||||
this["repo_url"] = "";
|
||||
}
|
||||
if (!("branch" in $$source)) {
|
||||
this["branch"] = "";
|
||||
}
|
||||
if (!("auth_method" in $$source)) {
|
||||
this["auth_method"] = ("" as AuthMethod);
|
||||
}
|
||||
if (!("sync_interval" in $$source)) {
|
||||
this["sync_interval"] = 0;
|
||||
}
|
||||
if (!("last_sync_time" in $$source)) {
|
||||
this["last_sync_time"] = null;
|
||||
}
|
||||
if (!("auto_sync" in $$source)) {
|
||||
this["auto_sync"] = false;
|
||||
}
|
||||
if (!("local_repo_path" in $$source)) {
|
||||
this["local_repo_path"] = "";
|
||||
}
|
||||
if (!("sync_strategy" in $$source)) {
|
||||
this["sync_strategy"] = ("" as SyncStrategy);
|
||||
}
|
||||
if (!("files_to_sync" in $$source)) {
|
||||
this["files_to_sync"] = [];
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GitSyncConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): GitSyncConfig {
|
||||
const $$createField14_0 = $$createType11;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("files_to_sync" in $$parsedSource) {
|
||||
$$parsedSource["files_to_sync"] = $$createField14_0($$parsedSource["files_to_sync"]);
|
||||
}
|
||||
return new GitSyncConfig($$parsedSource as Partial<GitSyncConfig>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GiteaConfig Gitea配置
|
||||
*/
|
||||
@@ -1038,6 +1168,26 @@ export enum LanguageType {
|
||||
LangEnUS = "en-US",
|
||||
};
|
||||
|
||||
/**
|
||||
* SyncStrategy 定义同步策略
|
||||
*/
|
||||
export enum SyncStrategy {
|
||||
/**
|
||||
* The Go zero value for the underlying type of the enum.
|
||||
*/
|
||||
$zero = "",
|
||||
|
||||
/**
|
||||
* LocalFirst 本地优先:如有冲突,保留本地修改
|
||||
*/
|
||||
LocalFirst = "local_first",
|
||||
|
||||
/**
|
||||
* RemoteFirst 远程优先:如有冲突,采用远程版本
|
||||
*/
|
||||
RemoteFirst = "remote_first",
|
||||
};
|
||||
|
||||
/**
|
||||
* SystemThemeType 系统主题类型定义
|
||||
*/
|
||||
@@ -1389,8 +1539,8 @@ export class UpdatesConfig {
|
||||
* Creates a new UpdatesConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): UpdatesConfig {
|
||||
const $$createField6_0 = $$createType10;
|
||||
const $$createField7_0 = $$createType11;
|
||||
const $$createField6_0 = $$createType12;
|
||||
const $$createField7_0 = $$createType13;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("github" in $$parsedSource) {
|
||||
$$parsedSource["github"] = $$createField6_0($$parsedSource["github"]);
|
||||
@@ -1407,16 +1557,18 @@ const $$createType0 = GeneralConfig.createFrom;
|
||||
const $$createType1 = EditingConfig.createFrom;
|
||||
const $$createType2 = AppearanceConfig.createFrom;
|
||||
const $$createType3 = UpdatesConfig.createFrom;
|
||||
const $$createType4 = ConfigMetadata.createFrom;
|
||||
const $$createType5 = CustomThemeConfig.createFrom;
|
||||
const $$createType6 = ThemeColorConfig.createFrom;
|
||||
var $$createType7 = (function $$initCreateType7(...args): any {
|
||||
if ($$createType7 === $$initCreateType7) {
|
||||
$$createType7 = $$createType8;
|
||||
const $$createType4 = GitSyncConfig.createFrom;
|
||||
const $$createType5 = ConfigMetadata.createFrom;
|
||||
const $$createType6 = CustomThemeConfig.createFrom;
|
||||
const $$createType7 = ThemeColorConfig.createFrom;
|
||||
var $$createType8 = (function $$initCreateType8(...args): any {
|
||||
if ($$createType8 === $$initCreateType8) {
|
||||
$$createType8 = $$createType9;
|
||||
}
|
||||
return $$createType7(...args);
|
||||
return $$createType8(...args);
|
||||
});
|
||||
const $$createType8 = $Create.Map($Create.Any, $Create.Any);
|
||||
const $$createType9 = HotkeyCombo.createFrom;
|
||||
const $$createType10 = GithubConfig.createFrom;
|
||||
const $$createType11 = GiteaConfig.createFrom;
|
||||
const $$createType9 = $Create.Map($Create.Any, $Create.Any);
|
||||
const $$createType10 = HotkeyCombo.createFrom;
|
||||
const $$createType11 = $Create.Array($Create.Any);
|
||||
const $$createType12 = GithubConfig.createFrom;
|
||||
const $$createType13 = GiteaConfig.createFrom;
|
||||
|
@@ -22,6 +22,14 @@ export function OnDataPathChanged(): Promise<void> & { cancel(): void } {
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* RegisterModel 注册模型与表的映射关系
|
||||
*/
|
||||
export function RegisterModel(tableName: string, model: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(175397515, tableName, model) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown shuts down the service when the application closes
|
||||
*/
|
||||
|
@@ -81,6 +81,14 @@ export function ListDeletedDocumentsMeta(): Promise<(models$0.Document | null)[]
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* LockDocument 锁定文档,防止删除
|
||||
*/
|
||||
export function LockDocument(id: number): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1889494473, id) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* RestoreDocument restores a deleted document
|
||||
*/
|
||||
@@ -97,6 +105,14 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UnlockDocument 解锁文档
|
||||
*/
|
||||
export function UnlockDocument(id: number): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(222307930, id) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateDocumentContent updates the content of a document
|
||||
*/
|
||||
|
@@ -11,6 +11,9 @@ import {
|
||||
TabType,
|
||||
UpdatesConfig,
|
||||
UpdateSourceType,
|
||||
GitSyncConfig,
|
||||
AuthMethod,
|
||||
SyncStrategy
|
||||
} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {ConfigUtils} from '@/utils/configUtils';
|
||||
@@ -48,6 +51,10 @@ type UpdatesConfigKeyMap = {
|
||||
readonly [K in keyof UpdatesConfig]: string;
|
||||
};
|
||||
|
||||
type SyncConfigKeyMap = {
|
||||
readonly [K in keyof GitSyncConfig]: string;
|
||||
};
|
||||
|
||||
type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
|
||||
|
||||
// 配置键映射
|
||||
@@ -88,6 +95,24 @@ const UPDATES_CONFIG_KEY_MAP: UpdatesConfigKeyMap = {
|
||||
gitea: 'updates.gitea'
|
||||
} as const;
|
||||
|
||||
const SYNC_CONFIG_KEY_MAP: SyncConfigKeyMap = {
|
||||
enabled: 'sync.enabled',
|
||||
repo_url: 'sync.repo_url',
|
||||
branch: 'sync.branch',
|
||||
auth_method: 'sync.auth_method',
|
||||
username: 'sync.username',
|
||||
password: 'sync.password',
|
||||
token: 'sync.token',
|
||||
ssh_key_path: 'sync.ssh_key_path',
|
||||
ssh_key_passphrase: 'sync.ssh_key_passphrase',
|
||||
sync_interval: 'sync.sync_interval',
|
||||
last_sync_time: 'sync.last_sync_time',
|
||||
auto_sync: 'sync.auto_sync',
|
||||
local_repo_path: 'sync.local_repo_path',
|
||||
sync_strategy: 'sync.sync_strategy',
|
||||
files_to_sync: 'sync.files_to_sync'
|
||||
} as const;
|
||||
|
||||
// 配置限制
|
||||
const CONFIG_LIMITS = {
|
||||
fontSize: {min: 12, max: 28, default: 13},
|
||||
@@ -261,6 +286,23 @@ const DEFAULT_CONFIG: AppConfig = {
|
||||
repo: "voidraft",
|
||||
}
|
||||
},
|
||||
sync: {
|
||||
enabled: false,
|
||||
repo_url: "",
|
||||
branch: "main",
|
||||
auth_method: AuthMethod.Token,
|
||||
username: "",
|
||||
password: "",
|
||||
token: "",
|
||||
ssh_key_path: "",
|
||||
ssh_key_passphrase: "",
|
||||
sync_interval: 60,
|
||||
last_sync_time: null,
|
||||
auto_sync: true,
|
||||
local_repo_path: "",
|
||||
sync_strategy: SyncStrategy.LocalFirst,
|
||||
files_to_sync: ["voidraft.db"]
|
||||
},
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
lastUpdated: new Date().toString(),
|
||||
@@ -348,6 +390,21 @@ export const useConfigStore = defineStore('config', () => {
|
||||
state.config.updates[key] = value;
|
||||
};
|
||||
|
||||
const updateSyncConfig = async <K extends keyof GitSyncConfig>(key: K, value: GitSyncConfig[K]): Promise<void> => {
|
||||
// 确保配置已加载
|
||||
if (!state.configLoaded && !state.isLoading) {
|
||||
await initConfig();
|
||||
}
|
||||
|
||||
const backendKey = SYNC_CONFIG_KEY_MAP[key];
|
||||
if (!backendKey) {
|
||||
throw new Error(`No backend key mapping found for sync.${key.toString()}`);
|
||||
}
|
||||
|
||||
await ConfigService.Set(backendKey, value);
|
||||
state.config.sync[key] = value;
|
||||
};
|
||||
|
||||
// 加载配置
|
||||
const initConfig = async (): Promise<void> => {
|
||||
if (state.isLoading) return;
|
||||
@@ -362,6 +419,7 @@ export const useConfigStore = defineStore('config', () => {
|
||||
if (appConfig.editing) Object.assign(state.config.editing, appConfig.editing);
|
||||
if (appConfig.appearance) Object.assign(state.config.appearance, appConfig.appearance);
|
||||
if (appConfig.updates) Object.assign(state.config.updates, appConfig.updates);
|
||||
if (appConfig.sync) Object.assign(state.config.sync, appConfig.sync);
|
||||
if (appConfig.metadata) Object.assign(state.config.metadata, appConfig.metadata);
|
||||
}
|
||||
|
||||
@@ -594,6 +652,20 @@ export const useConfigStore = defineStore('config', () => {
|
||||
},
|
||||
|
||||
// 更新配置相关方法
|
||||
setAutoUpdate: async (value: boolean) => await updateUpdatesConfig('autoUpdate', value)
|
||||
setAutoUpdate: async (value: boolean) => await updateUpdatesConfig('autoUpdate', value),
|
||||
|
||||
// Git同步配置相关方法
|
||||
setGitSyncEnabled: (value: boolean) => updateSyncConfig('enabled', value),
|
||||
setGitRepoUrl: (value: string) => updateSyncConfig('repo_url', value),
|
||||
setGitBranch: (value: string) => updateSyncConfig('branch', value),
|
||||
setAuthMethod: (value: AuthMethod) => updateSyncConfig('auth_method', value),
|
||||
setGitUsername: (value: string) => updateSyncConfig('username', value),
|
||||
setGitPassword: (value: string) => updateSyncConfig('password', value),
|
||||
setGitToken: (value: string) => updateSyncConfig('token', value),
|
||||
setSSHKeyPath: (value: string) => updateSyncConfig('ssh_key_path', value),
|
||||
setSyncInterval: (value: number) => updateSyncConfig('sync_interval', value),
|
||||
setAutoSync: (value: boolean) => updateSyncConfig('auto_sync', value),
|
||||
setSyncStrategy: (value: SyncStrategy) => updateSyncConfig('sync_strategy', value),
|
||||
setFilesToSync: (value: string[]) => updateSyncConfig('files_to_sync', value),
|
||||
};
|
||||
});
|
@@ -124,6 +124,7 @@ type AppConfig struct {
|
||||
Editing EditingConfig `json:"editing"` // 编辑设置
|
||||
Appearance AppearanceConfig `json:"appearance"` // 外观设置
|
||||
Updates UpdatesConfig `json:"updates"` // 更新设置
|
||||
Sync GitSyncConfig `json:"sync"` // Git同步设置
|
||||
Metadata ConfigMetadata `json:"metadata"` // 配置元数据
|
||||
}
|
||||
|
||||
@@ -138,6 +139,7 @@ func NewDefaultAppConfig() *AppConfig {
|
||||
|
||||
currentDir, _ := os.UserHomeDir()
|
||||
dataDir := filepath.Join(currentDir, ".voidraft", "data")
|
||||
repoPath := filepath.Join(currentDir, ".voidraft", "sync-repo")
|
||||
|
||||
return &AppConfig{
|
||||
General: GeneralConfig{
|
||||
@@ -173,7 +175,7 @@ func NewDefaultAppConfig() *AppConfig {
|
||||
CustomTheme: *NewDefaultCustomThemeConfig(),
|
||||
},
|
||||
Updates: UpdatesConfig{
|
||||
Version: "1.0.0",
|
||||
Version: "1.2.0",
|
||||
AutoUpdate: true,
|
||||
PrimarySource: UpdateSourceGithub,
|
||||
BackupSource: UpdateSourceGitea,
|
||||
@@ -189,9 +191,25 @@ func NewDefaultAppConfig() *AppConfig {
|
||||
Repo: "voidraft",
|
||||
},
|
||||
},
|
||||
Sync: GitSyncConfig{
|
||||
Enabled: false,
|
||||
RepoURL: "",
|
||||
Branch: "main",
|
||||
AuthMethod: Token,
|
||||
Username: "",
|
||||
Password: "",
|
||||
Token: "",
|
||||
SSHKeyPath: "",
|
||||
SyncInterval: 60,
|
||||
LastSyncTime: time.Time{},
|
||||
AutoSync: true,
|
||||
LocalRepoPath: repoPath,
|
||||
SyncStrategy: LocalFirst,
|
||||
FilesToSync: []string{"voidraft.db"},
|
||||
},
|
||||
Metadata: ConfigMetadata{
|
||||
LastUpdated: time.Now().Format(time.RFC3339),
|
||||
Version: "1.0.0",
|
||||
Version: "1.2.0",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -12,9 +12,10 @@ type Document struct {
|
||||
CreatedAt time.Time `json:"createdAt" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
IsLocked bool `json:"is_locked" db:"is_locked"` // 锁定标志,锁定的文档无法被删除
|
||||
}
|
||||
|
||||
// NewDocument 创建新文档(不需要传ID,由数据库自增)
|
||||
// NewDocument 创建新文档
|
||||
func NewDocument(title, content string) *Document {
|
||||
now := time.Now()
|
||||
return &Document{
|
||||
@@ -23,6 +24,7 @@ func NewDocument(title, content string) *Document {
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
IsDeleted: false,
|
||||
IsLocked: false, // 默认不锁定
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,10 +4,10 @@ import "time"
|
||||
|
||||
// Extension 单个扩展配置
|
||||
type Extension struct {
|
||||
ID ExtensionID `json:"id"` // 扩展唯一标识
|
||||
Enabled bool `json:"enabled"` // 是否启用
|
||||
IsDefault bool `json:"isDefault"` // 是否为默认扩展
|
||||
Config ExtensionConfig `json:"config"` // 扩展配置项
|
||||
ID ExtensionID `json:"id" db:"id"` // 扩展唯一标识
|
||||
Enabled bool `json:"enabled" db:"enabled"` // 是否启用
|
||||
IsDefault bool `json:"isDefault" db:"is_default"` // 是否为默认扩展
|
||||
Config ExtensionConfig `json:"config" db:"config"` // 扩展配置项
|
||||
}
|
||||
|
||||
// ExtensionID 扩展标识符
|
||||
|
@@ -4,11 +4,11 @@ import "time"
|
||||
|
||||
// KeyBinding 单个快捷键绑定
|
||||
type KeyBinding struct {
|
||||
Command KeyBindingCommand `json:"command"` // 快捷键动作
|
||||
Extension ExtensionID `json:"extension"` // 所属扩展
|
||||
Key string `json:"key"` // 快捷键组合(如 "Mod-f", "Ctrl-Shift-p")
|
||||
Enabled bool `json:"enabled"` // 是否启用
|
||||
IsDefault bool `json:"isDefault"` // 是否为默认快捷键
|
||||
Command KeyBindingCommand `json:"command" db:"command"` // 快捷键动作
|
||||
Extension ExtensionID `json:"extension" db:"extension"` // 所属扩展
|
||||
Key string `json:"key" db:"key"` // 快捷键组合(如 "Mod-f", "Ctrl-Shift-p")
|
||||
Enabled bool `json:"enabled" db:"enabled"` // 是否启用
|
||||
IsDefault bool `json:"isDefault" db:"is_default"` // 是否为默认快捷键
|
||||
}
|
||||
|
||||
// KeyBindingCommand 快捷键命令
|
||||
|
63
internal/models/sync.go
Normal file
63
internal/models/sync.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// AuthMethod 定义Git认证方式
|
||||
type AuthMethod string
|
||||
|
||||
const (
|
||||
Token AuthMethod = "token" // 个人访问令牌
|
||||
SSHKey AuthMethod = "ssh_key" // SSH密钥
|
||||
UserPass AuthMethod = "user_pass" // 用户名密码
|
||||
)
|
||||
|
||||
// SyncStrategy 定义同步策略
|
||||
type SyncStrategy string
|
||||
|
||||
const (
|
||||
// LocalFirst 本地优先:如有冲突,保留本地修改
|
||||
LocalFirst SyncStrategy = "local_first"
|
||||
// RemoteFirst 远程优先:如有冲突,采用远程版本
|
||||
RemoteFirst SyncStrategy = "remote_first"
|
||||
)
|
||||
|
||||
// GitSyncConfig 保存Git同步的配置信息
|
||||
type GitSyncConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
RepoURL string `json:"repo_url"`
|
||||
Branch string `json:"branch"`
|
||||
AuthMethod AuthMethod `json:"auth_method"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
SSHKeyPath string `json:"ssh_key_path,omitempty"`
|
||||
SSHKeyPassphrase string `json:"ssh_key_passphrase,omitempty"`
|
||||
SyncInterval int `json:"sync_interval"` // 同步间隔(分钟)
|
||||
LastSyncTime time.Time `json:"last_sync_time"`
|
||||
AutoSync bool `json:"auto_sync"` // 是否启用自动同步
|
||||
LocalRepoPath string `json:"local_repo_path"`
|
||||
SyncStrategy SyncStrategy `json:"sync_strategy"` // 合并冲突策略
|
||||
FilesToSync []string `json:"files_to_sync"` // 要同步的文件列表,默认为数据库文件
|
||||
}
|
||||
|
||||
// GitSyncStatus 保存同步状态信息
|
||||
type GitSyncStatus struct {
|
||||
IsSyncing bool `json:"is_syncing"`
|
||||
LastSyncTime time.Time `json:"last_sync_time"`
|
||||
LastSyncStatus string `json:"last_sync_status"` // success, failed, conflict
|
||||
LastErrorMsg string `json:"last_error_msg,omitempty"`
|
||||
LastCommitID string `json:"last_commit_id,omitempty"`
|
||||
RemoteCommitID string `json:"remote_commit_id,omitempty"`
|
||||
CommitAhead int `json:"commit_ahead"` // 本地领先远程的提交数
|
||||
CommitBehind int `json:"commit_behind"` // 本地落后远程的提交数
|
||||
}
|
||||
|
||||
// SyncLogEntry 记录每次同步操作的日志
|
||||
type SyncLogEntry struct {
|
||||
ID int64 `json:"id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Action string `json:"action"` // push, pull, reset
|
||||
Status string `json:"status"` // success, failed
|
||||
Message string `json:"message,omitempty"`
|
||||
ChangedFiles int `json:"changed_files"`
|
||||
}
|
@@ -5,7 +5,11 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"voidraft/internal/models"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
@@ -31,7 +35,8 @@ CREATE TABLE IF NOT EXISTS documents (
|
||||
content TEXT DEFAULT '∞∞∞text-a',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
is_deleted INTEGER DEFAULT 0
|
||||
is_deleted INTEGER DEFAULT 0,
|
||||
is_locked INTEGER DEFAULT 0
|
||||
)`
|
||||
|
||||
// Extensions table
|
||||
@@ -58,8 +63,34 @@ CREATE TABLE IF NOT EXISTS key_bindings (
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(command, extension)
|
||||
)`
|
||||
|
||||
// Git sync logs table
|
||||
sqlCreateSyncLogsTable = `
|
||||
CREATE TABLE IF NOT EXISTS sync_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
action TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
message TEXT,
|
||||
commit_id TEXT,
|
||||
changed_files INTEGER DEFAULT 0,
|
||||
repo_url TEXT NOT NULL,
|
||||
branch TEXT NOT NULL
|
||||
)`
|
||||
)
|
||||
|
||||
// ColumnInfo 存储列的信息
|
||||
type ColumnInfo struct {
|
||||
SQLType string
|
||||
DefaultValue string
|
||||
}
|
||||
|
||||
// TableModel 表示表与模型之间的映射关系
|
||||
type TableModel struct {
|
||||
TableName string
|
||||
Model interface{}
|
||||
}
|
||||
|
||||
// DatabaseService provides shared database functionality
|
||||
type DatabaseService struct {
|
||||
configService *ConfigService
|
||||
@@ -67,6 +98,7 @@ type DatabaseService struct {
|
||||
SQLite *sqlite.Service
|
||||
mu sync.RWMutex
|
||||
ctx context.Context
|
||||
tableModels []TableModel // 注册的表模型
|
||||
}
|
||||
|
||||
// NewDatabaseService creates a new database service
|
||||
@@ -75,11 +107,28 @@ func NewDatabaseService(configService *ConfigService, logger *log.Service) *Data
|
||||
logger = log.New()
|
||||
}
|
||||
|
||||
return &DatabaseService{
|
||||
ds := &DatabaseService{
|
||||
configService: configService,
|
||||
logger: logger,
|
||||
SQLite: sqlite.New(),
|
||||
}
|
||||
|
||||
// 注册所有模型
|
||||
ds.registerAllModels()
|
||||
|
||||
return ds
|
||||
}
|
||||
|
||||
// registerAllModels 注册所有数据模型,集中管理表-模型映射
|
||||
func (ds *DatabaseService) registerAllModels() {
|
||||
// 文档表
|
||||
ds.RegisterModel("documents", &models.Document{})
|
||||
// 扩展表
|
||||
ds.RegisterModel("extensions", &models.Extension{})
|
||||
// 快捷键表
|
||||
ds.RegisterModel("key_bindings", &models.KeyBinding{})
|
||||
// 同步日志表
|
||||
ds.RegisterModel("sync_logs", &models.SyncLogEntry{})
|
||||
}
|
||||
|
||||
// ServiceStartup initializes the service when the application starts
|
||||
@@ -135,6 +184,11 @@ func (ds *DatabaseService) initDatabase() error {
|
||||
return fmt.Errorf("failed to create indexes: %w", err)
|
||||
}
|
||||
|
||||
// 执行模型与表结构同步
|
||||
if err := ds.syncAllModelTables(); err != nil {
|
||||
return fmt.Errorf("failed to sync model tables: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -153,6 +207,7 @@ func (ds *DatabaseService) createTables() error {
|
||||
sqlCreateDocumentsTable,
|
||||
sqlCreateExtensionsTable,
|
||||
sqlCreateKeyBindingsTable,
|
||||
sqlCreateSyncLogsTable,
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
@@ -176,6 +231,10 @@ func (ds *DatabaseService) createIndexes() error {
|
||||
`CREATE INDEX IF NOT EXISTS idx_key_bindings_command ON key_bindings(command)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_key_bindings_extension ON key_bindings(extension)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_key_bindings_enabled ON key_bindings(enabled)`,
|
||||
// Sync logs indexes
|
||||
`CREATE INDEX IF NOT EXISTS idx_sync_logs_timestamp ON sync_logs(timestamp DESC)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_sync_logs_action ON sync_logs(action)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_sync_logs_status ON sync_logs(status)`,
|
||||
}
|
||||
|
||||
for _, index := range indexes {
|
||||
@@ -186,6 +245,149 @@ func (ds *DatabaseService) createIndexes() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterModel 注册模型与表的映射关系
|
||||
func (ds *DatabaseService) RegisterModel(tableName string, model interface{}) {
|
||||
ds.mu.Lock()
|
||||
defer ds.mu.Unlock()
|
||||
|
||||
ds.tableModels = append(ds.tableModels, TableModel{
|
||||
TableName: tableName,
|
||||
Model: model,
|
||||
})
|
||||
}
|
||||
|
||||
// syncAllModelTables 同步所有注册的模型与表结构
|
||||
func (ds *DatabaseService) syncAllModelTables() error {
|
||||
for _, tm := range ds.tableModels {
|
||||
if err := ds.syncModelTable(tm.TableName, tm.Model); err != nil {
|
||||
return fmt.Errorf("failed to sync table %s: %w", tm.TableName, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncModelTable 同步模型与表结构
|
||||
func (ds *DatabaseService) syncModelTable(tableName string, model interface{}) error {
|
||||
// 获取表结构元数据
|
||||
columns, err := ds.getTableColumns(tableName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get table columns: %w", err)
|
||||
}
|
||||
|
||||
// 使用反射从模型中提取字段信息
|
||||
expectedColumns, err := ds.getModelColumns(model)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get model columns: %w", err)
|
||||
}
|
||||
|
||||
// 检查缺失的列并添加
|
||||
for colName, colInfo := range expectedColumns {
|
||||
if _, exists := columns[colName]; !exists {
|
||||
// 执行添加列的SQL
|
||||
alterSQL := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s %s DEFAULT %s",
|
||||
tableName, colName, colInfo.SQLType, colInfo.DefaultValue)
|
||||
if err := ds.SQLite.Execute(alterSQL); err != nil {
|
||||
return fmt.Errorf("failed to add column %s: %w", colName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTableColumns 获取表的列信息
|
||||
func (ds *DatabaseService) getTableColumns(table string) (map[string]string, error) {
|
||||
query := fmt.Sprintf("PRAGMA table_info(%s)", table)
|
||||
rows, err := ds.SQLite.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make(map[string]string)
|
||||
for _, row := range rows {
|
||||
name, ok1 := row["name"].(string)
|
||||
typeName, ok2 := row["type"].(string)
|
||||
|
||||
if !ok1 || !ok2 {
|
||||
continue
|
||||
}
|
||||
|
||||
columns[name] = typeName
|
||||
}
|
||||
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// getModelColumns 从模型结构体中提取数据库列信息
|
||||
func (ds *DatabaseService) getModelColumns(model interface{}) (map[string]ColumnInfo, error) {
|
||||
columns := make(map[string]ColumnInfo)
|
||||
|
||||
// 使用反射获取结构体的类型信息
|
||||
t := reflect.TypeOf(model)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("model must be a struct or a pointer to struct")
|
||||
}
|
||||
|
||||
// 遍历所有字段
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
// 获取数据库字段名
|
||||
dbTag := field.Tag.Get("db")
|
||||
if dbTag == "" {
|
||||
// 如果没有db标签,则使用字段名的蛇形命名方式
|
||||
dbTag = toSnakeCase(field.Name)
|
||||
}
|
||||
|
||||
// 获取字段类型对应的SQL类型和默认值
|
||||
sqlType, defaultVal := getSQLTypeAndDefault(field.Type)
|
||||
|
||||
columns[dbTag] = ColumnInfo{
|
||||
SQLType: sqlType,
|
||||
DefaultValue: defaultVal,
|
||||
}
|
||||
}
|
||||
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// toSnakeCase 将驼峰命名转换为蛇形命名
|
||||
func toSnakeCase(s string) string {
|
||||
var result strings.Builder
|
||||
for i, r := range s {
|
||||
if i > 0 && 'A' <= r && r <= 'Z' {
|
||||
result.WriteRune('_')
|
||||
}
|
||||
result.WriteRune(r)
|
||||
}
|
||||
return strings.ToLower(result.String())
|
||||
}
|
||||
|
||||
// getSQLTypeAndDefault 根据Go类型获取对应的SQL类型和默认值
|
||||
func getSQLTypeAndDefault(t reflect.Type) (string, string) {
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return "INTEGER", "0"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return "INTEGER", "0"
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return "REAL", "0.0"
|
||||
case reflect.String:
|
||||
return "TEXT", "''"
|
||||
default:
|
||||
// 处理特殊类型
|
||||
if t == reflect.TypeOf(time.Time{}) {
|
||||
return "DATETIME", "CURRENT_TIMESTAMP"
|
||||
}
|
||||
return "TEXT", "NULL"
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceShutdown shuts down the service when the application closes
|
||||
func (ds *DatabaseService) ServiceShutdown() error {
|
||||
return ds.SQLite.Close()
|
||||
|
@@ -18,28 +18,28 @@ const (
|
||||
|
||||
// Document operations
|
||||
sqlGetDocumentByID = `
|
||||
SELECT id, title, content, created_at, updated_at, is_deleted
|
||||
SELECT id, title, content, created_at, updated_at, is_deleted, is_locked
|
||||
FROM documents
|
||||
WHERE id = ?`
|
||||
|
||||
sqlInsertDocument = `
|
||||
INSERT INTO documents (title, content, created_at, updated_at, is_deleted)
|
||||
VALUES (?, ?, ?, ?, 0)`
|
||||
INSERT INTO documents (title, content, created_at, updated_at, is_deleted, is_locked)
|
||||
VALUES (?, ?, ?, ?, 0, 0)`
|
||||
|
||||
sqlUpdateDocumentContent = `
|
||||
UPDATE documents
|
||||
SET content = ?, updated_at = ?
|
||||
WHERE id = ?`
|
||||
WHERE id = ? AND is_deleted = 0`
|
||||
|
||||
sqlUpdateDocumentTitle = `
|
||||
UPDATE documents
|
||||
SET title = ?, updated_at = ?
|
||||
WHERE id = ?`
|
||||
WHERE id = ? AND is_deleted = 0`
|
||||
|
||||
sqlMarkDocumentAsDeleted = `
|
||||
UPDATE documents
|
||||
SET is_deleted = 1, updated_at = ?
|
||||
WHERE id = ?`
|
||||
WHERE id = ? AND is_locked = 0`
|
||||
|
||||
sqlRestoreDocument = `
|
||||
UPDATE documents
|
||||
@@ -47,13 +47,13 @@ SET is_deleted = 0, updated_at = ?
|
||||
WHERE id = ?`
|
||||
|
||||
sqlListAllDocumentsMeta = `
|
||||
SELECT id, title, created_at, updated_at
|
||||
SELECT id, title, created_at, updated_at, is_locked
|
||||
FROM documents
|
||||
WHERE is_deleted = 0
|
||||
ORDER BY updated_at DESC`
|
||||
|
||||
sqlListDeletedDocumentsMeta = `
|
||||
SELECT id, title, created_at, updated_at
|
||||
SELECT id, title, created_at, updated_at, is_locked
|
||||
FROM documents
|
||||
WHERE is_deleted = 1
|
||||
ORDER BY updated_at DESC`
|
||||
@@ -63,6 +63,16 @@ SELECT id FROM documents WHERE is_deleted = 0 ORDER BY id LIMIT 1`
|
||||
|
||||
sqlCountDocuments = `SELECT COUNT(*) FROM documents WHERE is_deleted = 0`
|
||||
|
||||
sqlSetDocumentLocked = `
|
||||
UPDATE documents
|
||||
SET is_locked = 1, updated_at = ?
|
||||
WHERE id = ?`
|
||||
|
||||
sqlSetDocumentUnlocked = `
|
||||
UPDATE documents
|
||||
SET is_locked = 0, updated_at = ?
|
||||
WHERE id = ?`
|
||||
|
||||
sqlDefaultDocumentID = 1 // 默认文档的ID
|
||||
)
|
||||
|
||||
@@ -80,16 +90,19 @@ func NewDocumentService(databaseService *DatabaseService, logger *log.Service) *
|
||||
logger = log.New()
|
||||
}
|
||||
|
||||
return &DocumentService{
|
||||
ds := &DocumentService{
|
||||
databaseService: databaseService,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return ds
|
||||
}
|
||||
|
||||
// ServiceStartup initializes the service when the application starts
|
||||
func (ds *DocumentService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||
ds.ctx = ctx
|
||||
// Ensure default document exists
|
||||
|
||||
// 确保默认文档存在
|
||||
if err := ds.ensureDefaultDocument(); err != nil {
|
||||
return fmt.Errorf("failed to ensure default document: %w", err)
|
||||
}
|
||||
@@ -170,6 +183,10 @@ func (ds *DocumentService) GetDocumentByID(id int64) (*models.Document, error) {
|
||||
doc.IsDeleted = isDeletedInt == 1
|
||||
}
|
||||
|
||||
if isLockedInt, ok := row["is_locked"].(int64); ok {
|
||||
doc.IsLocked = isLockedInt == 1
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
@@ -186,6 +203,7 @@ func (ds *DocumentService) CreateDocument(title string) (*models.Document, error
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
IsDeleted: false,
|
||||
IsLocked: false,
|
||||
}
|
||||
|
||||
// 执行插入操作
|
||||
@@ -215,6 +233,61 @@ func (ds *DocumentService) CreateDocument(title string) (*models.Document, error
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
// LockDocument 锁定文档,防止删除
|
||||
func (ds *DocumentService) LockDocument(id int64) error {
|
||||
ds.mu.Lock()
|
||||
defer ds.mu.Unlock()
|
||||
|
||||
// 检查文档是否存在且未删除
|
||||
doc, err := ds.GetDocumentByID(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get document: %w", err)
|
||||
}
|
||||
if doc == nil {
|
||||
return fmt.Errorf("document not found: %d", id)
|
||||
}
|
||||
if doc.IsDeleted {
|
||||
return fmt.Errorf("cannot lock deleted document: %d", id)
|
||||
}
|
||||
|
||||
// 如果已经锁定,无需操作
|
||||
if doc.IsLocked {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = ds.databaseService.SQLite.Execute(sqlSetDocumentLocked, time.Now(), id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lock document: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnlockDocument 解锁文档
|
||||
func (ds *DocumentService) UnlockDocument(id int64) error {
|
||||
ds.mu.Lock()
|
||||
defer ds.mu.Unlock()
|
||||
|
||||
// 检查文档是否存在
|
||||
doc, err := ds.GetDocumentByID(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get document: %w", err)
|
||||
}
|
||||
if doc == nil {
|
||||
return fmt.Errorf("document not found: %d", id)
|
||||
}
|
||||
|
||||
// 如果未锁定,无需操作
|
||||
if !doc.IsLocked {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = ds.databaseService.SQLite.Execute(sqlSetDocumentUnlocked, time.Now(), id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unlock document: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateDocumentContent updates the content of a document
|
||||
func (ds *DocumentService) UpdateDocumentContent(id int64, content string) error {
|
||||
ds.mu.Lock()
|
||||
@@ -249,7 +322,19 @@ func (ds *DocumentService) DeleteDocument(id int64) error {
|
||||
return fmt.Errorf("cannot delete the default document")
|
||||
}
|
||||
|
||||
err := ds.databaseService.SQLite.Execute(sqlMarkDocumentAsDeleted, time.Now(), id)
|
||||
// 检查文档是否锁定
|
||||
doc, err := ds.GetDocumentByID(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get document: %w", err)
|
||||
}
|
||||
if doc == nil {
|
||||
return fmt.Errorf("document not found: %d", id)
|
||||
}
|
||||
if doc.IsLocked {
|
||||
return fmt.Errorf("cannot delete locked document: %d", id)
|
||||
}
|
||||
|
||||
err = ds.databaseService.SQLite.Execute(sqlMarkDocumentAsDeleted, time.Now(), id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to mark document as deleted: %w", err)
|
||||
}
|
||||
@@ -304,6 +389,10 @@ func (ds *DocumentService) ListAllDocumentsMeta() ([]*models.Document, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if isLockedInt, ok := row["is_locked"].(int64); ok {
|
||||
doc.IsLocked = isLockedInt == 1
|
||||
}
|
||||
|
||||
documents = append(documents, doc)
|
||||
}
|
||||
|
||||
@@ -346,6 +435,10 @@ func (ds *DocumentService) ListDeletedDocumentsMeta() ([]*models.Document, error
|
||||
}
|
||||
}
|
||||
|
||||
if isLockedInt, ok := row["is_locked"].(int64); ok {
|
||||
doc.IsLocked = isLockedInt == 1
|
||||
}
|
||||
|
||||
documents = append(documents, doc)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user