Docker, Token-Refresh mit Puppeteer, 401-Hinweise

- Dockerfile + .dockerignore
- token-refresh: Chromium öffnet drive.internxt.com, extrahiert Tokens
- 401-Antworten: Link zu drive.internxt.com
- Docs: Token-Erneuerung Option A (automatisch) / B (manuell)

Made-with: Cursor
This commit is contained in:
2026-02-28 12:32:03 +01:00
parent 7dbc6c8fe4
commit 6ca6132cf8
8 changed files with 1503 additions and 10 deletions

7
.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
node_modules
.git
.env
.env.*
*.log
docs
drive-web

13
Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY src ./src
ENV NODE_ENV=production
EXPOSE 3005
CMD ["node", "src/server.js"]

View File

@@ -19,6 +19,25 @@ npm start
Server läuft auf `http://127.0.0.1:3005`.
## Docker
```bash
# Image bauen
docker build -t internxt-webdav .
# Container starten (Umgebungsvariablen aus .env)
docker run -d --name internxt-webdav -p 3005:3005 --env-file .env internxt-webdav
# Oder einzelne Variablen übergeben
docker run -d -p 3005:3005 \
-e INXT_TOKEN="..." \
-e INXT_MNEMONIC="..." \
-e CRYPTO_SECRET="6KYQBP847D4ATSFA" \
internxt-webdav
```
WebDAV erreichbar unter `http://localhost:3005`.
## WebDAV-Funktionen
- **PROPFIND** Verzeichnis auflisten
@@ -50,5 +69,5 @@ Server läuft auf `http://127.0.0.1:3005`.
|-------|--------------|
| `npm start` | WebDAV-Server starten |
| `npm run token-test` | Token prüfen |
| `npm run auth-test` | API-Login testen (E-Mail/Passwort) |
| `npm run token-refresh` | Browser öffnen, einloggen → Tokens automatisch extrahieren |
| `npm run debug-names` | Namensentschlüsselung testen |

View File

@@ -87,9 +87,27 @@ robocopy "i:\" "." /NFL /NDL
# Variante 3: Explorer Datei per Drag & Drop kopieren
``` Windows Explorer: Netzlaufwerk verbinden → `http://127.0.0.1:3005`.
## Token erneuern (bei 401 / abgelaufen)
Tokens laufen nach einiger Zeit ab (typisch Stunden). Bei 401-Fehlern oder „Nicht autorisiert“:
### Option A: Automatisch (Chromium)
```bash
npm run token-refresh
```
Öffnet einen Browser mit drive.internxt.com. Einloggen die Tokens werden automatisch extrahiert und in der Konsole ausgegeben. In `.env` eintragen, Server neu starten.
### Option B: Manuell
1. **[https://drive.internxt.com](https://drive.internxt.com)** öffnen und erneut einloggen
2. Token und Mnemonic wie oben (Schritt 2) aus der Console auslesen
3. `.env` mit den neuen Werten aktualisieren
4. WebDAV-Server neu starten
## Hinweise
- **Bridge-API**: Der Download nutzt die Internxt Bridge mit `x-api-version: 2` und den Headern `internxt-version`/`internxt-client`. Ohne diese liefert die Bridge 400.
- **Token-Ablauf**: Tokens laufen nach einiger Zeit ab (typisch Stunden). Bei 401-Fehlern erneut einloggen und Token aktualisieren.
- **Sicherheit**: Mnemonic und Token sind hochsensibel. Nicht in Git committen, `.env` in `.gitignore` belassen.
- **Nur für Sie**: Die Tokens sind an Ihre Session gebunden. Für andere Nutzer funktioniert dieser Ansatz nicht.

1378
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@
"scripts": {
"auth-test": "node src/auth-poc.js",
"token-test": "node src/token-test.js",
"token-refresh": "node src/token-refresh.js",
"debug-names": "node src/debug-name-decrypt.js",
"start": "node src/server.js"
},
@@ -15,6 +16,7 @@
"bip39": "^3.1.0",
"crypto-js": "^4.1.1",
"dotenv": "^16.0.0",
"express": "^4.18.0"
"express": "^4.18.0",
"puppeteer": "^24.0.0"
}
}

View File

@@ -234,7 +234,7 @@ async function handlePropfind(req, res) {
} catch (err) {
console.error('PROPFIND Fehler:', err.message);
if (err.message?.includes('Token') || err.response?.status === 401) {
res.status(401).send('Nicht autorisiert Token erneuern');
res.status(401).send('Nicht autorisiert Token abgelaufen. Neu einloggen: https://drive.internxt.com');
return;
}
res.status(500).send(err.message || 'Interner Fehler');
@@ -304,7 +304,7 @@ async function handleMkcol(req, res) {
} catch (err) {
console.error('MKCOL Fehler:', err.message);
if (err.message?.includes('Token') || err.response?.status === 401) {
res.status(401).send('Nicht autorisiert');
res.status(401).send('Nicht autorisiert Token erneuern: https://drive.internxt.com');
return;
}
res.status(500).send(err.message || 'Interner Fehler');
@@ -343,7 +343,7 @@ async function handleDelete(req, res) {
} catch (err) {
console.error('DELETE Fehler:', err.message);
if (err.message?.includes('Token') || err.response?.status === 401) {
res.status(401).send('Nicht autorisiert');
res.status(401).send('Nicht autorisiert Token erneuern: https://drive.internxt.com');
return;
}
res.status(500).send(err.message || 'Interner Fehler');
@@ -429,7 +429,7 @@ async function handleMove(req, res) {
} catch (err) {
console.error('MOVE Fehler:', err.message);
if (err.message?.includes('Token') || err.response?.status === 401) {
res.status(401).send('Nicht autorisiert');
res.status(401).send('Nicht autorisiert Token erneuern: https://drive.internxt.com');
return;
}
res.status(500).send(err.message || 'Interner Fehler');
@@ -499,7 +499,7 @@ async function handleGet(req, res) {
} catch (err) {
console.error('GET Fehler:', err.message);
if (err.message?.includes('Token') || err.response?.status === 401) {
res.status(401).send('Nicht autorisiert');
res.status(401).send('Nicht autorisiert Token erneuern: https://drive.internxt.com');
return;
}
if (!res.headersSent) res.status(500).send(err.message || 'Interner Fehler');
@@ -666,7 +666,7 @@ async function handlePut(req, res) {
} catch (err) {
console.error('PUT Fehler:', err.message);
if (err.message?.includes('Token') || err.response?.status === 401) {
res.status(401).send('Nicht autorisiert');
res.status(401).send('Nicht autorisiert Token erneuern: https://drive.internxt.com');
return;
}
if (!res.headersSent) res.status(500).send(err.message || 'Interner Fehler');

58
src/token-refresh.js Normal file
View File

@@ -0,0 +1,58 @@
/**
* Token-Refresh: Öffnet drive.internxt.com im Browser, wartet auf Login,
* extrahiert Token und Mnemonic aus localStorage.
*
* Aufruf: npm run token-refresh
*
* Browser öffnet sich (headed). Einloggen, dann werden die Tokens ausgegeben.
*/
import puppeteer from 'puppeteer';
const DRIVE_URL = 'https://drive.internxt.com';
const POLL_MS = 2000;
const TIMEOUT_MS = 5 * 60 * 1000; // 5 Min
async function getTokens(page) {
return page.evaluate(() => {
const token = localStorage.getItem('xNewToken') || localStorage.getItem('xToken');
const mnemonic = localStorage.getItem('xMnemonic');
return { token, mnemonic };
});
}
async function main() {
console.log('Starte Browser bitte auf', DRIVE_URL, 'einloggen.\n');
const browser = await puppeteer.launch({
headless: false,
defaultViewport: null,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = (await browser.pages())[0] || (await browser.newPage());
await page.goto(DRIVE_URL, { waitUntil: 'networkidle2' });
const start = Date.now();
while (Date.now() - start < TIMEOUT_MS) {
const { token, mnemonic } = await getTokens(page);
if (token && mnemonic) {
console.log('\n=== Tokens gefunden in .env eintragen ===\n');
console.log('INXT_TOKEN=' + token);
console.log('INXT_MNEMONIC=' + mnemonic);
console.log('\n===========================================\n');
await browser.close();
return;
}
await new Promise((r) => setTimeout(r, POLL_MS));
}
console.log('Timeout keine Tokens gefunden. Bitte einloggen und erneut ausführen.');
await browser.close();
process.exit(1);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});