feat(sync): GitHub en plus de Gitea, et refus d'un depot public
GitHub parle la meme API des contenus (base64, sha, corps du PUT) : meme chemin, reconnu a api.github.com/repos/. User-Agent pose partout (GitHub refuse sans). Un depot public est refuse avant toute lecture ou ecriture, Gitea comme GitHub : l'historique garde tous les instantanes chiffres. Telephone : github.com/O/R se convertit en URL d'API et inversement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+28
-3
@@ -65,8 +65,8 @@ Bridge.onSyncQr = function (size, bits) {
|
||||
'background:#fff;display:block;border-radius:6px">' +
|
||||
'<path d="' + d + '" fill="#000"/></svg>' +
|
||||
'<p style="font-size:11px;color:var(--text-faint);margin:6px 0 0">' +
|
||||
'Scan it with the phone camera and open it with Coffre: the URL and the ' +
|
||||
'token fill in, then type the sync password (never in the QR) and Save. ' +
|
||||
'In the phone app: Settings → Scan the QR shown on the PC. The URL and ' +
|
||||
'the token fill in; type the sync password (never in the QR), then Save. ' +
|
||||
'Hidden again in 60 s.</p>';
|
||||
box.hidden = false;
|
||||
clearTimeout(syncQrTimer);
|
||||
@@ -83,8 +83,27 @@ Bridge.onWebdavResult = function(reqId, status, payload, etag) {
|
||||
// discriminant the Android client uses, so one URL tells both ends which
|
||||
// transport to speak. Everything below that differs is confined to the two
|
||||
// call sites in runSyncNow(); the bridge itself is shared.
|
||||
// GitHub parle la MEME API des contenus (base64, sha, meme corps de PUT) : un
|
||||
// seul chemin pour les deux, reconnu a l'URL.
|
||||
function syncIsGitea(url) {
|
||||
return (url || '').toLowerCase().includes('/api/v1/repos/');
|
||||
const u = (url || '').toLowerCase();
|
||||
return u.includes('/api/v1/repos/') || u.includes('api.github.com/repos/');
|
||||
}
|
||||
|
||||
// UN DEPOT PUBLIC EST REFUSE. Chaque synchro est un commit : l'historique garde
|
||||
// tous les instantanes, chacun attaquable hors ligne par qui peut les lire.
|
||||
// '' = prive ou impossible a savoir (on ne bloque pas sur un doute reseau).
|
||||
async function syncRepoPublicWarning(cfg) {
|
||||
if (!syncIsGitea(cfg.url)) return '';
|
||||
const repoUrl = cfg.url.split('/contents/')[0];
|
||||
const r = await _webdavCall('get', repoUrl, cfg.user, cfg.pwd);
|
||||
if (r.status < 200 || r.status >= 300 || !r.payload) return '';
|
||||
try {
|
||||
const o = JSON.parse(new TextDecoder().decode(base64ToBytes(r.payload)));
|
||||
if (o && o.private === false)
|
||||
return 'This repository is PUBLIC - make it private before syncing: anyone could download your encrypted vault and all its history.';
|
||||
} catch (e) { /* forme inattendue : pas de verdict */ }
|
||||
return '';
|
||||
}
|
||||
|
||||
// Unwraps a Gitea contents response into { container, sha }. The API answers
|
||||
@@ -278,6 +297,8 @@ async function syncTestConnection() {
|
||||
try {
|
||||
un = giteaUnwrap(new TextDecoder().decode(base64ToBytes(r.payload || '')));
|
||||
} catch (e) { /* traite comme une forme inattendue */ }
|
||||
const pub = await syncRepoPublicWarning(cfg);
|
||||
if (pub) return toast(pub, 'error');
|
||||
return un
|
||||
? toast('Connection OK · file found in the repository')
|
||||
: toast('Reached Gitea, but this URL does not return a file — ' +
|
||||
@@ -634,6 +655,10 @@ async function runSyncNow(_attempt) {
|
||||
// and buildSyncSnapshot are expensive on large vaults — no point
|
||||
// running them if we can't reach the server).
|
||||
syncStatus('Connecting…');
|
||||
if (_attempt === 0) {
|
||||
const pub = await syncRepoPublicWarning(cfg);
|
||||
if (pub) { syncStatus(''); return toast(pub, 'error'); }
|
||||
}
|
||||
let pullResp;
|
||||
try {
|
||||
pullResp = await _webdavCall('get', cfg.url, cfg.user, cfg.pwd);
|
||||
|
||||
Reference in New Issue
Block a user