Files
opsxsql/frontend/src/lib/storage.ts
T
Logan.Li f6d636fa44 feat(query): sql workspace conveniences per opsxdb patterns
- generic ContextMenu component (viewport flip, esc/outside close)
- SchemaSidebar table right-click menu: preview data, generate
  SELECT/INSERT/UPDATE/DELETE templates with pk-aware WHERE, copy table
  name; double-click table runs preview SELECT; column right-click
  copy/insert; dialect-aware quoting; preview SELECT carries no LIMIT
- SqlEditor selection-first execution (Mod-Enter runs selection else
  statement at cursor via quote/comment-aware splitter); pagination
  replays last executed statement
- query history persisted server-side: drawer fetches query-histories
  with keyword/mine/favorites filters, star toggle (new PUT favorite +
  mine filter endpoints), insert and insert-and-run
- custom templates in localStorage alongside built-ins
- unit tests: genSql + statement splitter (28 tests green)
2026-10-26 18:53:02 +08:00

81 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 统一的 localStorage 命名空间与迁移层。
*
* 所有持久化 key 一律从 STORAGE_KEYS 取值(`<app>.<域>.<名>` 命名空间),
* 禁止在业务代码里手写字符串 key。历史无命名空间 key 仅在此处做
* 「读旧写新」迁移,迁移期结束后删除 LEGACY_KEYS。
*/
const read = (key: string): string | null => {
try {
return localStorage.getItem(key);
} catch {
return null;
}
};
const write = (key: string, value: string): void => {
try {
localStorage.setItem(key, value);
} catch {
/* 存储不可用(隐私模式等)时静默降级为内存态 */
}
};
const remove = (key: string): void => {
try {
localStorage.removeItem(key);
} catch {
/* noop */
}
};
export const STORAGE_KEYS = {
token: 'opsxsql.auth.token',
refreshToken: 'opsxsql.auth.refresh',
theme: 'opsxsql.theme.mode',
loginStyle: 'opsxsql.ui.loginStyle',
queryFavorites: 'opsxsql.query.favorites',
queryCustomTemplates: 'opsxsql.query.customTemplates',
querySideWidth: 'opsxsql.query.sideWidth',
chunkReloaded: 'opsxsql.app.chunkReloaded',
} as const;
/** 历史无命名空间 key → 新 key(仅用于一次性迁移读取) */
const LEGACY_KEYS: Partial<Record<keyof typeof STORAGE_KEYS, string>> = {
token: 'token',
theme: 'dg-theme',
loginStyle: 'login-style',
queryFavorites: 'query_favorites',
querySideWidth: 'query-side-width',
};
/** 读取(新 key 优先,回退旧 key 并完成迁移写入) */
export function readStorage(key: keyof typeof STORAGE_KEYS): string | null {
const current = read(STORAGE_KEYS[key]);
if (current !== null) return current;
const legacyKey = LEGACY_KEYS[key];
if (legacyKey) {
const legacy = read(legacyKey);
if (legacy !== null) {
write(STORAGE_KEYS[key], legacy);
remove(legacyKey);
return legacy;
}
}
return null;
}
/** 写入(只写新 key) */
export function writeStorage(key: keyof typeof STORAGE_KEYS, value: string): void {
write(STORAGE_KEYS[key], value);
}
/** 删除(新旧 key 一起清理) */
export function removeStorage(key: keyof typeof STORAGE_KEYS): void {
remove(STORAGE_KEYS[key]);
const legacyKey = LEGACY_KEYS[key];
if (legacyKey) remove(legacyKey);
}