Files
zn-ai/electron/service/script-store-service/index.ts
duanshuwen 721344883f refactor: reorganize imports and update type references across services
- Updated import paths for types and utilities in config-service, menu-service, script-execution-service, script-store-service, and window-service to reflect new structure.
- Moved utility functions like debounce and cloneDeep to shared utilities.
- Created new runtime and script types files to centralize type definitions.
- Removed deprecated task-types and locale messages files.
- Updated constants to use shared definitions and added new placeholders for providers.
- Enhanced provider type information with additional placeholders for different languages.
2026-04-21 23:25:51 +08:00

242 lines
6.4 KiB
TypeScript

import { app } from 'electron';
import fs from 'fs';
import path from 'path';
import log from 'electron-log';
import type {
AutomationScript,
ScriptMetaItem,
ScriptsMeta,
ScriptSaveInput,
} from '../../types/script-types';
const META_FILENAME = 'scripts.meta.json';
function getScriptsDir(): string {
return app.isPackaged
? path.join(__dirname, 'scripts')
: path.join(process.cwd(), 'electron/scripts');
}
function ensureScriptsDir(): void {
const dir = getScriptsDir();
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
function getMetaPath(): string {
return path.join(getScriptsDir(), META_FILENAME);
}
function readMeta(): ScriptsMeta {
const metaPath = getMetaPath();
if (!fs.existsSync(metaPath)) {
return { scripts: [] };
}
try {
const raw = fs.readFileSync(metaPath, 'utf-8');
const parsed = JSON.parse(raw);
if (parsed && Array.isArray(parsed.scripts)) {
return parsed as ScriptsMeta;
}
} catch (err) {
log.warn('[script-store-service] Failed to read meta:', err);
}
return { scripts: [] };
}
function writeMeta(meta: ScriptsMeta): void {
ensureScriptsDir();
const metaPath = getMetaPath();
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2), 'utf-8');
}
function sanitizeFilename(name: string): string {
return name
.toLowerCase()
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
.replace(/^-+|-+$/g, '')
|| 'script';
}
function generateUniqueFilename(name: string, existingNames: Set<string>): string {
const base = sanitizeFilename(name);
let filename = `${base}.mjs`;
let counter = 1;
while (existingNames.has(filename)) {
filename = `${base}-${counter}.mjs`;
counter++;
}
return filename;
}
function seedScripts(): void {
const scriptsDir = getScriptsDir();
const metaPath = getMetaPath();
if (fs.existsSync(metaPath)) {
return;
}
if (!fs.existsSync(scriptsDir)) {
log.info('[script-store-service] Scripts directory does not exist, skipping seed.');
return;
}
const meta: ScriptsMeta = { scripts: [] };
const scriptFiles = fs.readdirSync(scriptsDir).filter((f) => f.endsWith('.mjs'));
for (const file of scriptFiles) {
try {
const name = file.replace(/\.mjs$/, '');
const now = new Date().toISOString();
meta.scripts.push({
id: `seed-${name}`,
name,
description: '',
filename: file,
enabled: true,
channel: '',
createdAt: now,
updatedAt: now,
});
} catch (err) {
log.warn('[script-store-service] Failed to seed script', file, err);
}
}
writeMeta(meta);
log.info('[script-store-service] Seeded scripts:', meta.scripts.length);
}
export function initScriptStoreService(): void {
ensureScriptsDir();
seedScripts();
}
export function listScripts(): AutomationScript[] {
const meta = readMeta();
return meta.scripts
.map((item) => enrichWithCode(item))
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
}
export function getScript(id: string): AutomationScript | null {
const meta = readMeta();
const item = meta.scripts.find((s) => s.id === id);
if (!item) return null;
return enrichWithCode(item);
}
export function getScriptPathById(id: string): string | null {
const meta = readMeta();
const item = meta.scripts.find((s) => s.id === id);
if (!item) return null;
return path.join(getScriptsDir(), item.filename);
}
export function saveScript(input: ScriptSaveInput): AutomationScript {
const meta = readMeta();
const scriptsDir = getScriptsDir();
const existingNames = new Set(meta.scripts.map((s) => s.filename));
const now = new Date().toISOString();
if (input.id) {
const index = meta.scripts.findIndex((s) => s.id === input.id);
if (index >= 0) {
const existing = meta.scripts[index];
const filePath = path.join(scriptsDir, existing.filename);
fs.writeFileSync(filePath, input.code, 'utf-8');
meta.scripts[index] = {
...existing,
name: input.name,
description: input.description,
channel: input.channel,
enabled: input.enabled,
updatedAt: now,
};
writeMeta(meta);
return enrichWithCode(meta.scripts[index]);
}
}
const filename = generateUniqueFilename(input.name, existingNames);
const filePath = path.join(scriptsDir, filename);
fs.writeFileSync(filePath, input.code, 'utf-8');
const id = `script-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
const item: ScriptMetaItem = {
id,
name: input.name,
description: input.description,
filename,
enabled: input.enabled,
channel: input.channel,
createdAt: now,
updatedAt: now,
};
meta.scripts.push(item);
writeMeta(meta);
return enrichWithCode(item);
}
export function deleteScript(id: string): boolean {
const meta = readMeta();
const index = meta.scripts.findIndex((s) => s.id === id);
if (index === -1) return false;
const item = meta.scripts[index];
const filePath = path.join(getScriptsDir(), item.filename);
if (fs.existsSync(filePath)) {
try {
fs.unlinkSync(filePath);
} catch (err) {
log.warn('[script-store-service] Failed to delete script file:', err);
}
}
meta.scripts.splice(index, 1);
writeMeta(meta);
return true;
}
export function toggleScript(id: string, enabled: boolean): boolean {
const meta = readMeta();
const index = meta.scripts.findIndex((s) => s.id === id);
if (index === -1) return false;
meta.scripts[index].enabled = enabled;
meta.scripts[index].updatedAt = new Date().toISOString();
writeMeta(meta);
return true;
}
export function updateLastRun(id: string, lastRun: NonNullable<AutomationScript['lastRun']>): boolean {
const meta = readMeta();
const index = meta.scripts.findIndex((s) => s.id === id);
if (index === -1) return false;
meta.scripts[index].lastRun = lastRun;
meta.scripts[index].updatedAt = new Date().toISOString();
writeMeta(meta);
return true;
}
function enrichWithCode(item: ScriptMetaItem): AutomationScript {
const scriptsDir = getScriptsDir();
const filePath = path.join(scriptsDir, item.filename);
let code = '';
try {
if (fs.existsSync(filePath)) {
code = fs.readFileSync(filePath, 'utf-8');
}
} catch (err) {
log.warn('[script-store-service] Failed to read script file:', err);
}
return {
...item,
code,
} as AutomationScript;
}