kingkont 0.20.48 → 0.20.50
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/lib/providers.js +18 -7
- package/package.json +1 -1
- package/renderer/board.js +1 -8
- package/renderer/cloudProjects.js +48 -7
- package/renderer/generate.js +5 -1
- package/renderer/media.js +5 -0
- package/server.js +6 -1
- package/settings.html +10 -5
package/lib/providers.js
CHANGED
|
@@ -1091,16 +1091,19 @@ function _extFromFilename(name) {
|
|
|
1091
1091
|
/**
|
|
1092
1092
|
* @returns {Promise<{ url, fileName, size, hash?: string, provider }>}
|
|
1093
1093
|
*/
|
|
1094
|
-
async function uploadFile({ buffer, filename = 'upload.bin', mime = 'application/octet-stream', settings: s }) {
|
|
1094
|
+
async function uploadFile({ buffer, filename = 'upload.bin', mime = 'application/octet-stream', kind, settings: s }) {
|
|
1095
1095
|
if (!buffer || !buffer.length) throw new Error('пустой файл');
|
|
1096
1096
|
if (buffer.length > 50 * 1024 * 1024) {
|
|
1097
1097
|
throw new Error(`файл слишком большой (${(buffer.length/1024/1024).toFixed(1)} MB), лимит 50MB`);
|
|
1098
1098
|
}
|
|
1099
|
-
|
|
1100
|
-
//
|
|
1101
|
-
//
|
|
1102
|
-
|
|
1103
|
-
|
|
1099
|
+
// kind: 'ref' (передача референса в gen) | 'project' (default — сохранение
|
|
1100
|
+
// проекта). Юзер: «Cloudflare нельзя использовать для выкладки проектов,
|
|
1101
|
+
// только для передачи reference». Project-save'ы НИКОГДА не идут в R2.
|
|
1102
|
+
const isRef = kind === 'ref';
|
|
1103
|
+
|
|
1104
|
+
// Только для REF-аплоада: явный override useCloudflareR2 → R2 directly.
|
|
1105
|
+
if (isRef && s.useCloudflareR2 && r2Upload.isConfigured()) {
|
|
1106
|
+
console.log('[uploadFile ref] useCloudflareR2=true → R2 directly');
|
|
1104
1107
|
const r2 = await r2Upload.uploadToR2(buffer, mime, _extFromFilename(filename));
|
|
1105
1108
|
return {
|
|
1106
1109
|
url: r2.url, fileName: filename, size: buffer.length, hash: r2.key, provider: 'cloudflare-r2',
|
|
@@ -1108,7 +1111,15 @@ async function uploadFile({ buffer, filename = 'upload.bin', mime = 'application
|
|
|
1108
1111
|
}
|
|
1109
1112
|
|
|
1110
1113
|
if (s.useChatium && s.chatium?.token && s.chatium?.base) {
|
|
1111
|
-
|
|
1114
|
+
// Project-save: ВСЕГДА только chatium, без R2-fallback (даже если
|
|
1115
|
+
// chatium тормозит). Юзер хочет чтобы вся проектная storage была
|
|
1116
|
+
// в chatium-storage, иначе R2-URL'ы попадают в manifest и при
|
|
1117
|
+
// следующем open project возвращаем мусор.
|
|
1118
|
+
// Ref: race chatium с R2-fallback по 5s timeout (для устойчивости).
|
|
1119
|
+
if (isRef) {
|
|
1120
|
+
return await _uploadViaChatiumWithR2Fallback({ buffer, filename, mime, s });
|
|
1121
|
+
}
|
|
1122
|
+
return await _chatiumUploadOnly({ buffer, filename, mime, s });
|
|
1112
1123
|
}
|
|
1113
1124
|
|
|
1114
1125
|
if (!s.useKie) throw new Error('Войдите в KingKont или KIE для загрузки файлов.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kingkont",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.50",
|
|
4
4
|
"description": "KingKont \u00b7 Chatium \u2014 \u043d\u043e\u0434-\u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0441\u0446\u0435\u043d \u0441 AI-\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 (\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438/\u0432\u0438\u0434\u0435\u043e/\u0433\u043e\u043b\u043e\u0441/SFX/\u043c\u0443\u0437\u044b\u043a\u0430/\u0442\u0435\u043a\u0441\u0442)",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"bin": {
|
package/renderer/board.js
CHANGED
|
@@ -1671,14 +1671,7 @@ async function openShareModal(p) {
|
|
|
1671
1671
|
// там приложение живёт на kingkont.ru. В web используем origin.
|
|
1672
1672
|
const isLocal = /^https?:\/\/(localhost|127\.0\.0\.1)/.test(location.origin);
|
|
1673
1673
|
const base = isLocal ? 'https://kingkont.ru' : location.origin;
|
|
1674
|
-
|
|
1675
|
-
// НАВЕЧНО, и юзеры получали старую версию HTML со старыми хешами
|
|
1676
|
-
// скриптов. Старые cloudProjects.js не имеют R2 pub→worker rewrite
|
|
1677
|
-
// → картинки 503 → «Файл не найден».
|
|
1678
|
-
// Сейчас: /app/spaces/client/ — auth-aware stub, который JS'ом
|
|
1679
|
-
// редиректит на свежий /static/web-<sha>.html (новое имя файла =
|
|
1680
|
-
// свежий cache-entry на каждом deploy).
|
|
1681
|
-
return base + '/app/spaces/client/#template=' + proj.id;
|
|
1674
|
+
return base + '/static/web.html#template=' + proj.id;
|
|
1682
1675
|
}
|
|
1683
1676
|
function render() {
|
|
1684
1677
|
const isPub = !!proj.isPublic;
|
|
@@ -82,16 +82,23 @@
|
|
|
82
82
|
// openShareModal живёт в board.js (window.openShareModal). В Electron
|
|
83
83
|
// share-API'ы (/api/proj/setPublic|share|unshare|shares) проксируются
|
|
84
84
|
// через server.js → kingkont.ru/proj_*; в web — через web-shim.
|
|
85
|
+
// ↗ Перейти — рядом, только когда проект публичный (state.cloudIsPublic).
|
|
86
|
+
// Юзер: «на проекте если он публичный сделай кнопку перейти (с иконкой,
|
|
87
|
+
// без надписи) справа от кнопки Расшарить».
|
|
85
88
|
{
|
|
86
|
-
let
|
|
89
|
+
let row = $('shareProjectRow');
|
|
87
90
|
const wantShow = logged && state.cloudProjectId && state.cloudMine !== false;
|
|
88
|
-
if (wantShow && !
|
|
89
|
-
|
|
91
|
+
if (wantShow && !row && saveBtn?.parentNode) {
|
|
92
|
+
row = document.createElement('div');
|
|
93
|
+
row.id = 'shareProjectRow';
|
|
94
|
+
row.style.cssText = 'display:flex; gap:6px; margin-top:6px; align-items:stretch;';
|
|
95
|
+
|
|
96
|
+
const shareBtn = document.createElement('button');
|
|
90
97
|
shareBtn.id = 'shareProjectBtn';
|
|
91
98
|
shareBtn.className = 'kk-edit-only';
|
|
92
99
|
shareBtn.textContent = '🤝 Расшарить';
|
|
93
100
|
shareBtn.title = 'Расшарить проект другому юзеру или сделать публичным';
|
|
94
|
-
shareBtn.style.cssText = '
|
|
101
|
+
shareBtn.style.cssText = 'flex:1; font-size:12px;';
|
|
95
102
|
shareBtn.addEventListener('click', () => {
|
|
96
103
|
if (typeof window.openShareModal === 'function') {
|
|
97
104
|
window.openShareModal({
|
|
@@ -102,9 +109,31 @@
|
|
|
102
109
|
});
|
|
103
110
|
}
|
|
104
111
|
});
|
|
105
|
-
|
|
112
|
+
row.appendChild(shareBtn);
|
|
113
|
+
|
|
114
|
+
// ↗ Перейти — иконка, открывает share-URL в новой вкладке.
|
|
115
|
+
// Видна только если проект публичный (по private-проекту ссылка
|
|
116
|
+
// не работает у незалогиненных, и тут смысла нет).
|
|
117
|
+
const gotoBtn = document.createElement('button');
|
|
118
|
+
gotoBtn.id = 'gotoPublicBtn';
|
|
119
|
+
gotoBtn.className = 'kk-edit-only';
|
|
120
|
+
gotoBtn.textContent = '↗';
|
|
121
|
+
gotoBtn.title = 'Открыть публичную страницу проекта в новой вкладке';
|
|
122
|
+
gotoBtn.style.cssText = 'font-size:14px; padding:0 10px;';
|
|
123
|
+
gotoBtn.addEventListener('click', () => {
|
|
124
|
+
const isLocal = /^https?:\/\/(localhost|127\.0\.0\.1)/.test(location.origin);
|
|
125
|
+
const base = isLocal ? 'https://kingkont.ru' : location.origin;
|
|
126
|
+
const url = base + '/static/web.html#template=' + encodeURIComponent(state.cloudProjectId);
|
|
127
|
+
window.open(url, '_blank', 'noopener');
|
|
128
|
+
});
|
|
129
|
+
row.appendChild(gotoBtn);
|
|
130
|
+
|
|
131
|
+
saveBtn.parentNode.insertBefore(row, saveBtn.nextSibling);
|
|
106
132
|
}
|
|
107
|
-
if (
|
|
133
|
+
if (row) row.style.display = wantShow ? 'flex' : 'none';
|
|
134
|
+
// Видимость ↗ Перейти зависит от isPublic — обновляем каждый тик.
|
|
135
|
+
const gotoBtn = $('gotoPublicBtn');
|
|
136
|
+
if (gotoBtn) gotoBtn.style.display = (wantShow && state.cloudIsPublic) ? '' : 'none';
|
|
108
137
|
}
|
|
109
138
|
}).catch(() => {});
|
|
110
139
|
}
|
|
@@ -935,8 +964,17 @@
|
|
|
935
964
|
// Dedup: сначала быстрая проверка по size, потом content-hash.
|
|
936
965
|
// Mtime НЕ используем — Date.now() в JS не совпадает с OS-stamped
|
|
937
966
|
// mtime, после download/write валидные файлы выглядели как «новые».
|
|
967
|
+
// Юзер: «Cloudflare нельзя использовать для выкладки проектов».
|
|
968
|
+
// Старые R2-URL'ы (pub-…r2.dev / kingkont-r2-upload…workers.dev) в
|
|
969
|
+
// кэше — это легаси от прежней архитектуры. Не переиспользуем их —
|
|
970
|
+
// форсим re-upload в chatium-storage. Иначе manifest проекта так и
|
|
971
|
+
// останется с R2-URL'ами навсегда.
|
|
972
|
+
const _isCachedR2 = cached?.url && (
|
|
973
|
+
cached.url.startsWith('https://pub-cd4114af9f7d44c9bf8c9442bc7dddc2.r2.dev') ||
|
|
974
|
+
cached.url.startsWith('https://kingkont-r2-upload.timur-dd5.workers.dev')
|
|
975
|
+
);
|
|
938
976
|
let fhash = null;
|
|
939
|
-
if (cached && cached.size === file.size) {
|
|
977
|
+
if (cached && !_isCachedR2 && cached.size === file.size) {
|
|
940
978
|
fhash = await _quickFileHash(file);
|
|
941
979
|
if (cached.fhash === fhash) {
|
|
942
980
|
boardFiles[f.relPath] = cached.url;
|
|
@@ -949,6 +987,9 @@
|
|
|
949
987
|
continue;
|
|
950
988
|
}
|
|
951
989
|
}
|
|
990
|
+
if (_isCachedR2) {
|
|
991
|
+
console.log(`[cloudProjects] re-upload (R2→chatium migration) ${f.relPath}`);
|
|
992
|
+
}
|
|
952
993
|
PROGRESS.update(uploaded, total,
|
|
953
994
|
`[${board.kind}/${board.name}] ${f.relPath} (${uploaded + 1}/${total})`);
|
|
954
995
|
const r = await fetch('/api/upload', {
|
package/renderer/generate.js
CHANGED
|
@@ -620,7 +620,11 @@ async function _imageRefToDataUrl(ref) {
|
|
|
620
620
|
const filename = ref.file.split('/').pop() || 'ref.jpg';
|
|
621
621
|
const r = await fetch('/api/upload', {
|
|
622
622
|
method: 'POST',
|
|
623
|
-
headers: {
|
|
623
|
+
headers: {
|
|
624
|
+
'Content-Type': mime,
|
|
625
|
+
'X-File-Name': encodeURIComponent(filename),
|
|
626
|
+
'X-Upload-Kind': 'ref',
|
|
627
|
+
},
|
|
624
628
|
body: buf,
|
|
625
629
|
});
|
|
626
630
|
if (!r.ok) {
|
package/renderer/media.js
CHANGED
|
@@ -1384,11 +1384,16 @@ async function uploadBoardFile(boardHandle, boardKey, filename) {
|
|
|
1384
1384
|
return cached.url;
|
|
1385
1385
|
}
|
|
1386
1386
|
console.log('[uploadBoardFile] cache MISS — uploading', { file: filename, size: file.size, mime: file.type });
|
|
1387
|
+
// X-Upload-Kind: ref — этот upload идёт для передачи референса в gen.
|
|
1388
|
+
// Только ref-аплоады могут уйти в Cloudflare R2 (если useCloudflareR2).
|
|
1389
|
+
// Project-save'ы (cloudProjects.js) не передают этот header → chatium-only.
|
|
1390
|
+
// Юзер: «Cloudflare нельзя использовать для выкладки проектов».
|
|
1387
1391
|
const r = await fetch('/api/upload', {
|
|
1388
1392
|
method: 'POST',
|
|
1389
1393
|
headers: {
|
|
1390
1394
|
'Content-Type': file.type || 'application/octet-stream',
|
|
1391
1395
|
'X-File-Name': encodeURIComponent(file.name),
|
|
1396
|
+
'X-Upload-Kind': 'ref',
|
|
1392
1397
|
},
|
|
1393
1398
|
body: file,
|
|
1394
1399
|
});
|
package/server.js
CHANGED
|
@@ -125,9 +125,14 @@ async function handlePoll(res, url) {
|
|
|
125
125
|
async function handleUpload(req, res) {
|
|
126
126
|
const filename = decodeURIComponent(req.headers['x-file-name'] || 'upload.bin');
|
|
127
127
|
const mime = (req.headers['content-type'] || 'application/octet-stream').split(';')[0].trim();
|
|
128
|
+
// X-Upload-Kind: 'ref' (для передачи референсов в gen) | 'project' (default).
|
|
129
|
+
// Только 'ref' может уйти в R2 (если включено) — project-save'ы ВСЕГДА
|
|
130
|
+
// в chatium-storage. Юзер: «Cloudflare нельзя использовать для выкладки
|
|
131
|
+
// проектов, только для передачи reference».
|
|
132
|
+
const kind = (req.headers['x-upload-kind'] === 'ref') ? 'ref' : 'project';
|
|
128
133
|
const buffer = await readBody(req);
|
|
129
134
|
try {
|
|
130
|
-
const r = await providers.uploadFile({ buffer, filename, mime, settings: getSettings() });
|
|
135
|
+
const r = await providers.uploadFile({ buffer, filename, mime, kind, settings: getSettings() });
|
|
131
136
|
send(res, 200, { url: r.url, fileName: r.fileName, size: r.size, hash: r.hash }, { 'X-Provider': r.provider });
|
|
132
137
|
} catch (e) { sendError(res, e, 502); }
|
|
133
138
|
}
|
package/settings.html
CHANGED
|
@@ -174,15 +174,20 @@
|
|
|
174
174
|
<label class="use-toggle" for="useCloudflareR2">
|
|
175
175
|
<input type="checkbox" id="useCloudflareR2" />
|
|
176
176
|
<span class="label-text">
|
|
177
|
-
Использовать Cloudflare для
|
|
178
|
-
<span class="sub">—
|
|
177
|
+
Использовать Cloudflare для референсов
|
|
178
|
+
<span class="sub">— только для передачи refs в gen; проекты ВСЕГДА в chatium-storage</span>
|
|
179
179
|
</span>
|
|
180
180
|
</label>
|
|
181
181
|
<div class="hint">
|
|
182
|
-
По умолчанию (выключено) —
|
|
182
|
+
По умолчанию (выключено) — реф идёт в <code>fs.chatium.ru</code>, и
|
|
183
183
|
если он медленнее 5 секунд, автоматически уходит в Cloudflare R2.
|
|
184
|
-
Если включить — chatium-storage не
|
|
185
|
-
|
|
184
|
+
Если включить — chatium-storage для рефов не используется, каждый
|
|
185
|
+
реф сразу в R2 (<code>worker/get/<uuid></code>).
|
|
186
|
+
<br><br>
|
|
187
|
+
<strong>Важно</strong>: настройка касается ТОЛЬКО передачи референсов
|
|
188
|
+
в генерацию. Сохранение проектов (☁ Сохранить на сервер) всегда
|
|
189
|
+
пишет файлы в chatium-storage — иначе R2-URL'ы попадают в manifest
|
|
190
|
+
и нарушают переносимость проекта.
|
|
186
191
|
</div>
|
|
187
192
|
</div>
|
|
188
193
|
|