ework-web 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +11 -2
- package/src/static/db-wizard.js +106 -0
- package/src/views/settings.ts +1 -43
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -240,8 +240,16 @@ function daemonEnvPath(): string | null {
|
|
|
240
240
|
const p = join(dataDir, ".env");
|
|
241
241
|
return existsSync(p) ? p : null;
|
|
242
242
|
}
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
// Conventional paths: standalone install, then ework-aio managed layout
|
|
244
|
+
// (~/.local/share/ework-aio/ework-daemon/.env).
|
|
245
|
+
const candidates = [
|
|
246
|
+
join(homedir(), ".local", "share", "ework-daemon", ".env"),
|
|
247
|
+
join(homedir(), ".local", "share", "ework-aio", "ework-daemon", ".env"),
|
|
248
|
+
];
|
|
249
|
+
for (const c of candidates) {
|
|
250
|
+
if (existsSync(c)) return c;
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
function spawnDaemonRestart(): void {
|
|
@@ -370,6 +378,7 @@ function chunkTextTTS(text: string, max = 120): string[] {
|
|
|
370
378
|
|
|
371
379
|
async function handle(req: Request, url: URL, ip: string, ctx: { authed: boolean; user: UserRow | null }): Promise<Response> {
|
|
372
380
|
if (url.pathname === "/static/app.js") return staticAsset("app.js", "text/javascript; charset=utf-8", req);
|
|
381
|
+
if (url.pathname === "/static/db-wizard.js") return staticAsset("db-wizard.js", "text/javascript; charset=utf-8", req);
|
|
373
382
|
if (url.pathname === "/static/session.js") return staticAsset("session.js", "text/javascript; charset=utf-8", req);
|
|
374
383
|
if (url.pathname === "/static/file.js") return staticAsset("file.js", "text/javascript; charset=utf-8", req);
|
|
375
384
|
if (url.pathname === "/static/tts.js") return staticAsset("tts.js", "text/javascript; charset=utf-8", req);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// DB-backend migration wizard (settings page). External file because CSP
|
|
2
|
+
// script-src 'self' blocks inline scripts — see index.ts SEC_HEADERS.
|
|
3
|
+
(function () {
|
|
4
|
+
if (!document.getElementById('db-test')) return;
|
|
5
|
+
var R = document.getElementById('db-result');
|
|
6
|
+
|
|
7
|
+
function get(id) { return document.getElementById(id).value; }
|
|
8
|
+
function gather() {
|
|
9
|
+
return JSON.stringify({
|
|
10
|
+
host: get('db-host').trim(),
|
|
11
|
+
port: Number(get('db-port')),
|
|
12
|
+
user: get('db-user').trim(),
|
|
13
|
+
password: get('db-password'),
|
|
14
|
+
database: get('db-database').trim(),
|
|
15
|
+
prefix: get('db-prefix').trim()
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function setBtns(d) {
|
|
19
|
+
document.querySelectorAll('.db-controls button').forEach(function (b) { b.disabled = d; });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function act(action) {
|
|
23
|
+
if (action === 'enable' && !confirm('确认启用 MySQL 并重启?确保已完成 ① 测试 + ② 迁移。')) return;
|
|
24
|
+
setBtns(true);
|
|
25
|
+
R.className = 'db-result db-loading';
|
|
26
|
+
R.textContent = action === 'enable' ? '正在写入 .env,进程即将重启…' : '处理中…';
|
|
27
|
+
try {
|
|
28
|
+
var res = await fetch('/api/db/' + action, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { 'Content-Type': 'application/json' },
|
|
31
|
+
body: gather()
|
|
32
|
+
});
|
|
33
|
+
var data = await res.json();
|
|
34
|
+
if (action === 'enable' && data.ok) {
|
|
35
|
+
R.className = 'db-result db-ok';
|
|
36
|
+
R.textContent = '✓ .env 已写入。进程重启中,等待恢复…';
|
|
37
|
+
setTimeout(poll, 3000);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (action === 'daemon-config') {
|
|
41
|
+
R.className = 'db-result ' + (data.configured ? 'db-ok' : 'db-err');
|
|
42
|
+
R.textContent = data.configured
|
|
43
|
+
? '✓ daemon 已写入 .env 并重启:\n ' + data.envPath
|
|
44
|
+
: '⚠ daemon 无法自动配置(.env 未找到)。请手动操作:\n\n' + (data.manual || data.error || '');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
R.className = 'db-result ' + (data.ok ? 'db-ok' : 'db-err');
|
|
48
|
+
if (!data.ok) {
|
|
49
|
+
var m = '✗ ' + (data.error || '未知错误');
|
|
50
|
+
if (data.hint) m += '\n💡 ' + data.hint;
|
|
51
|
+
R.textContent = m;
|
|
52
|
+
} else if (data.tables) {
|
|
53
|
+
R.textContent = '✓ 迁移完成(' + data.tables.length + ' 张表):\n' +
|
|
54
|
+
data.tables.map(function (t) { return ' ' + t.table + ': ' + t.rows + ' 行'; }).join('\n');
|
|
55
|
+
} else if (data.serverVersion) {
|
|
56
|
+
R.textContent = '✓ 连接成功 · MySQL ' + data.serverVersion +
|
|
57
|
+
(data.databaseExists ? '' : '(库不存在,迁移时自动创建)');
|
|
58
|
+
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
R.className = 'db-result db-err';
|
|
61
|
+
R.textContent = '✗ ' + (e.message || String(e));
|
|
62
|
+
} finally {
|
|
63
|
+
if (action !== 'enable') setBtns(false);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function poll() {
|
|
68
|
+
try { await fetch('/'); location.href = '/settings?saved=1'; }
|
|
69
|
+
catch (e) { setTimeout(poll, 2000); }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
document.getElementById('db-test').onclick = function () { act('test'); };
|
|
73
|
+
document.getElementById('db-migrate').onclick = function () { act('migrate'); };
|
|
74
|
+
document.getElementById('db-daemon').onclick = function () { act('daemon-config'); };
|
|
75
|
+
document.getElementById('db-enable').onclick = function () { act('enable'); };
|
|
76
|
+
|
|
77
|
+
var revertBtn = document.getElementById('db-revert');
|
|
78
|
+
if (revertBtn) {
|
|
79
|
+
revertBtn.onclick = async function () {
|
|
80
|
+
if (!confirm('确认切回 SQLite?将创建新的 SQLite 文件并迁移数据,然后重启。')) return;
|
|
81
|
+
var Rr = document.getElementById('db-revert-result');
|
|
82
|
+
Rr.className = 'db-result db-loading';
|
|
83
|
+
Rr.textContent = '正在迁移到 SQLite…';
|
|
84
|
+
try {
|
|
85
|
+
var res = await fetch('/api/db/revert', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: '{}'
|
|
89
|
+
});
|
|
90
|
+
var data = await res.json();
|
|
91
|
+
if (data.ok) {
|
|
92
|
+
Rr.className = 'db-result db-ok';
|
|
93
|
+
Rr.textContent = '✓ 已迁移到 ' + data.targetPath + '。进程重启中…';
|
|
94
|
+
setTimeout(poll, 3000);
|
|
95
|
+
} else {
|
|
96
|
+
Rr.className = 'db-result db-err';
|
|
97
|
+
Rr.textContent = '✗ ' + (data.error || '未知错误') +
|
|
98
|
+
(data.partial ? '\n部分完成: ' + data.partial.join(', ') : '');
|
|
99
|
+
}
|
|
100
|
+
} catch (e) {
|
|
101
|
+
Rr.className = 'db-result db-err';
|
|
102
|
+
Rr.textContent = '✗ ' + (e.message || String(e));
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
})();
|
package/src/views/settings.ts
CHANGED
|
@@ -63,49 +63,7 @@ function buildDbSection(viewer: UserRow): string {
|
|
|
63
63
|
</div>
|
|
64
64
|
<div id="db-result" class="db-result"></div>
|
|
65
65
|
${isMysql ? `<hr style="border:0;border-top:1px solid var(--border);margin:1rem 0"><div class="db-controls"><button type="button" id="db-revert" class="secondary">⚠ 切回 SQLite(安全网)</button></div><div id="db-revert-result" class="db-result"></div>` : ""}
|
|
66
|
-
<script>
|
|
67
|
-
(function(){
|
|
68
|
-
var R=document.getElementById('db-result');
|
|
69
|
-
function get(id){return document.getElementById(id).value;}
|
|
70
|
-
function gather(){return JSON.stringify({host:get('db-host').trim(),port:Number(get('db-port')),user:get('db-user').trim(),password:get('db-password'),database:get('db-database').trim(),prefix:get('db-prefix').trim()});}
|
|
71
|
-
function setBtns(d){document.querySelectorAll('.db-controls button').forEach(function(b){b.disabled=d;});}
|
|
72
|
-
async function act(action){
|
|
73
|
-
if(action==='enable'&&!confirm('确认启用 MySQL 并重启?确保已完成 ① 测试 + ② 迁移。'))return;
|
|
74
|
-
setBtns(true);
|
|
75
|
-
R.className='db-result db-loading';
|
|
76
|
-
R.textContent=action==='enable'?'正在写入 .env,进程即将重启…':'处理中…';
|
|
77
|
-
try{
|
|
78
|
-
var res=await fetch('/api/db/'+action,{method:'POST',headers:{'Content-Type':'application/json'},body:gather()});
|
|
79
|
-
var data=await res.json();
|
|
80
|
-
if(action==='enable'&&data.ok){R.className='db-result db-ok';R.textContent='✓ .env 已写入。进程重启中,等待恢复…';setTimeout(poll,3000);return;}
|
|
81
|
-
if(action==='daemon-config'){R.className='db-result '+(data.configured?'db-ok':'db-err');R.textContent=data.configured?'✓ daemon 已写入 .env 并重启:\\n '+data.envPath:'⚠ daemon 无法自动配置(.env 未找到)。请手动操作:\\n\\n'+(data.manual||data.error||'');return;}
|
|
82
|
-
R.className='db-result '+(data.ok?'db-ok':'db-err');
|
|
83
|
-
if(!data.ok){var m='✗ '+(data.error||'未知错误');if(data.hint)m+='\\n💡 '+data.hint;R.textContent=m;}
|
|
84
|
-
else if(data.tables){R.textContent='✓ 迁移完成('+data.tables.length+' 张表):\\n'+data.tables.map(function(t){return' '+t.table+': '+t.rows+' 行';}).join('\\n');}
|
|
85
|
-
else if(data.serverVersion){R.textContent='✓ 连接成功 · MySQL '+data.serverVersion+(data.databaseExists?'':'(库不存在,迁移时自动创建)');}
|
|
86
|
-
}catch(e){R.className='db-result db-err';R.textContent='✗ '+(e.message||String(e));}
|
|
87
|
-
finally{if(action!=='enable')setBtns(false);}
|
|
88
|
-
}
|
|
89
|
-
async function poll(){try{await fetch('/');location.href='/settings?saved=1';}catch(e){setTimeout(poll,2000);}}
|
|
90
|
-
document.getElementById('db-test').onclick=function(){act('test');};
|
|
91
|
-
document.getElementById('db-migrate').onclick=function(){act('migrate');};
|
|
92
|
-
document.getElementById('db-daemon').onclick=function(){act('daemon-config');};
|
|
93
|
-
document.getElementById('db-enable').onclick=function(){act('enable');};
|
|
94
|
-
if(document.getElementById('db-revert')){
|
|
95
|
-
document.getElementById('db-revert').onclick=async function(){
|
|
96
|
-
if(!confirm('确认切回 SQLite?将创建新的 SQLite 文件并迁移数据,然后重启。'))return;
|
|
97
|
-
var R=document.getElementById('db-revert-result');
|
|
98
|
-
R.className='db-result db-loading';R.textContent='正在迁移到 SQLite…';
|
|
99
|
-
try{
|
|
100
|
-
var res=await fetch('/api/db/revert',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'});
|
|
101
|
-
var data=await res.json();
|
|
102
|
-
if(data.ok){R.className='db-result db-ok';R.textContent='✓ 已迁移到 '+data.targetPath+'。进程重启中…';setTimeout(poll,3000);}
|
|
103
|
-
else{R.className='db-result db-err';R.textContent='✗ '+(data.error||'未知错误')+(data.partial?'\\n部分完成: '+data.partial.join(', '):'');}
|
|
104
|
-
}catch(e){R.className='db-result db-err';R.textContent='✗ '+(e.message||String(e));}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
})();
|
|
108
|
-
</script>
|
|
66
|
+
<script src="/static/db-wizard.js"></script>
|
|
109
67
|
</section>`;
|
|
110
68
|
}
|
|
111
69
|
|