feat(rclone): akıllı cache yönetimi ve streaming performans ayarları ekle
Disk doluluk oranını izleyen ve otomatik temizleme yapan akıllı cache sistemi eklendi. Streaming performansı için buffer size, VFS read ahead ve chunk size ayarları yapılandırılabilir hale getirildi. Rclone crash durumunda otomatik yeniden başlatma mekanizması eklendi. UI'da disk kullanım bilgileri ve VFS cache modu görüntülenmeye başlandı.
This commit is contained in:
24
.env.example
24
.env.example
@@ -60,6 +60,8 @@ RCLONE_POLL_INTERVAL=1m
|
|||||||
# Rclone dizin cache süresi
|
# Rclone dizin cache süresi
|
||||||
RCLONE_DIR_CACHE_TIME=1m
|
RCLONE_DIR_CACHE_TIME=1m
|
||||||
# Rclone VFS cache modu (off, minimal, writes, full)
|
# Rclone VFS cache modu (off, minimal, writes, full)
|
||||||
|
# full: Hızlı streaming için okumalar ve yazmalar cache'lenir
|
||||||
|
# Disk doluluğu threshold'ı geçince otomatik temizlenir
|
||||||
RCLONE_VFS_CACHE_MODE=full
|
RCLONE_VFS_CACHE_MODE=full
|
||||||
# Rclone VFS cache dizini
|
# Rclone VFS cache dizini
|
||||||
RCLONE_VFS_CACHE_DIR=/app/server/cache/rclone-vfs
|
RCLONE_VFS_CACHE_DIR=/app/server/cache/rclone-vfs
|
||||||
@@ -73,3 +75,25 @@ RCLONE_RC_ADDR=127.0.0.1:5572
|
|||||||
RCLONE_DEBUG_MODE_LOG=0
|
RCLONE_DEBUG_MODE_LOG=0
|
||||||
# Media stream debug log (akış kaynağını loglamak için kullanılır)
|
# Media stream debug log (akış kaynağını loglamak için kullanılır)
|
||||||
MEDIA_DEBUG_LOG=0
|
MEDIA_DEBUG_LOG=0
|
||||||
|
|
||||||
|
# --- Rclone Streaming Performans Ayarları ---
|
||||||
|
# Buffer size - streaming performansı için (varsayılan: 16M, VPS için 8M yeterli)
|
||||||
|
RCLONE_BUFFER_SIZE=8M
|
||||||
|
# VFS read ahead - streaming için önbellek (varsayılan: off)
|
||||||
|
RCLONE_VFS_READ_AHEAD=128M
|
||||||
|
# VFS read chunk size - büyük dosyalar için (varsayılan: 128M)
|
||||||
|
RCLONE_VFS_READ_CHUNK_SIZE=32M
|
||||||
|
# VFS read chunk size limit - seek performansı için (varsayılan: off)
|
||||||
|
RCLONE_VFS_READ_CHUNK_SIZE_LIMIT=64M
|
||||||
|
|
||||||
|
# --- Rclone Akıllı Cache Yönetimi ---
|
||||||
|
# Disk doluluk oranı eşik değeri (百分比) - Bu oran aşıldığında otomatik cache temizlenir
|
||||||
|
RCLONE_CACHE_CLEAN_THRESHOLD=85
|
||||||
|
# Cache temizleme sırasında korunacak minimum boş alan (GB)
|
||||||
|
RCLONE_MIN_FREE_SPACE_GB=5
|
||||||
|
# Rclone crash olursa otomatik yeniden başlatma (1 = aç, 0 = kapa)
|
||||||
|
RCLONE_AUTO_RESTART=1
|
||||||
|
# Maksimum yeniden başlatma deneme sayısı
|
||||||
|
RCLONE_AUTO_RESTART_MAX_RETRIES=5
|
||||||
|
# Yeniden başlatma arasındaki bekleme süresi (milisaniye)
|
||||||
|
RCLONE_AUTO_RESTART_DELAY_MS=5000
|
||||||
|
|||||||
@@ -236,6 +236,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkAndCleanCache() {
|
||||||
|
error = null;
|
||||||
|
success = null;
|
||||||
|
try {
|
||||||
|
const resp = await apiFetch("/api/rclone/cache/check-and-clean", { method: "POST" });
|
||||||
|
const data = await resp.json().catch(() => ({}));
|
||||||
|
if (!resp.ok) {
|
||||||
|
throw new Error(data?.error || data?.message || `HTTP ${resp.status}`);
|
||||||
|
}
|
||||||
|
if (data.cleaned) {
|
||||||
|
success = data.message || "Cache temizlendi.";
|
||||||
|
} else {
|
||||||
|
success = data.message || "Disk durumu iyi, temizleme gerekmedi.";
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
error = err?.message || "Cache kontrolü başarısız.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
loadCookies();
|
loadCookies();
|
||||||
loadYoutubeSettings();
|
loadYoutubeSettings();
|
||||||
@@ -404,7 +423,10 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn" on:click={cleanRcloneCache}>
|
<button class="btn" on:click={cleanRcloneCache}>
|
||||||
<i class="fa-solid fa-broom"></i> Clean Cache
|
<i class="fa-solid fa-broom"></i> Temizle
|
||||||
|
</button>
|
||||||
|
<button class="btn primary" on:click={checkAndCleanCache}>
|
||||||
|
<i class="fa-solid fa-wand-magic-sparkles"></i> Akıllı Temizle
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -444,11 +466,30 @@
|
|||||||
|
|
||||||
{#if rcloneStatus}
|
{#if rcloneStatus}
|
||||||
<div class="card muted" style="margin-top:10px;">
|
<div class="card muted" style="margin-top:10px;">
|
||||||
|
<div><strong>Durum:</strong></div>
|
||||||
<div>Enabled: {rcloneStatus.enabled ? "Evet" : "Hayır"}</div>
|
<div>Enabled: {rcloneStatus.enabled ? "Evet" : "Hayır"}</div>
|
||||||
<div>Mounted: {rcloneStatus.mounted ? "Evet" : "Hayır"}</div>
|
<div>Mounted: {rcloneStatus.mounted ? "Evet" : "Hayır"}</div>
|
||||||
<div>Remote: {rcloneStatus.remoteConfigured ? "Hazır" : "Eksik"}</div>
|
<div>Remote: {rcloneStatus.remoteConfigured ? "Hazır" : "Eksik"}</div>
|
||||||
|
{#if rcloneStatus.vfsCacheMode}
|
||||||
|
<div>VFS Cache Mode: <code>{rcloneStatus.vfsCacheMode}</code></div>
|
||||||
|
{/if}
|
||||||
|
{#if rcloneStatus.diskUsage}
|
||||||
|
<div style="margin-top: 8px;">
|
||||||
|
<div style="font-size: 12px; color: #666;">Disk Kullanımı:</div>
|
||||||
|
<div style="font-size: 13px;">
|
||||||
|
Kullanım: %{rcloneStatus.diskUsage.usedPercent} |
|
||||||
|
Boş: {rcloneStatus.diskUsage.availableGB.toFixed(1)}GB /
|
||||||
|
{rcloneStatus.diskUsage.totalGB.toFixed(1)}GB
|
||||||
|
</div>
|
||||||
|
{#if rcloneStatus.cacheCleanThreshold}
|
||||||
|
<div style="font-size: 11px; color: #888;">
|
||||||
|
Temizleme eşiği: %{rcloneStatus.cacheCleanThreshold}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{#if rcloneStatus.lastError}
|
{#if rcloneStatus.lastError}
|
||||||
<div>Son hata: {rcloneStatus.lastError}</div>
|
<div style="margin-top: 8px; color: #d32f2f;">Son hata: {rcloneStatus.lastError}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -668,6 +709,14 @@
|
|||||||
color: #0f7a1f;
|
color: #0f7a1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(code) {
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.password-field {
|
.password-field {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,3 +41,18 @@ services:
|
|||||||
RCLONE_DIR_CACHE_TIME: ${RCLONE_DIR_CACHE_TIME}
|
RCLONE_DIR_CACHE_TIME: ${RCLONE_DIR_CACHE_TIME}
|
||||||
RCLONE_VFS_CACHE_MODE: ${RCLONE_VFS_CACHE_MODE}
|
RCLONE_VFS_CACHE_MODE: ${RCLONE_VFS_CACHE_MODE}
|
||||||
RCLONE_VFS_CACHE_DIR: ${RCLONE_VFS_CACHE_DIR}
|
RCLONE_VFS_CACHE_DIR: ${RCLONE_VFS_CACHE_DIR}
|
||||||
|
RCLONE_VFS_CACHE_MAX_SIZE: ${RCLONE_VFS_CACHE_MAX_SIZE}
|
||||||
|
RCLONE_VFS_CACHE_MAX_AGE: ${RCLONE_VFS_CACHE_MAX_AGE}
|
||||||
|
RCLONE_RC_ENABLED: ${RCLONE_RC_ENABLED}
|
||||||
|
RCLONE_RC_ADDR: ${RCLONE_RC_ADDR}
|
||||||
|
RCLONE_BUFFER_SIZE: ${RCLONE_BUFFER_SIZE}
|
||||||
|
RCLONE_VFS_READ_AHEAD: ${RCLONE_VFS_READ_AHEAD}
|
||||||
|
RCLONE_VFS_READ_CHUNK_SIZE: ${RCLONE_VFS_READ_CHUNK_SIZE}
|
||||||
|
RCLONE_VFS_READ_CHUNK_SIZE_LIMIT: ${RCLONE_VFS_READ_CHUNK_SIZE_LIMIT}
|
||||||
|
RCLONE_DEBUG_MODE_LOG: ${RCLONE_DEBUG_MODE_LOG}
|
||||||
|
MEDIA_DEBUG_LOG: ${MEDIA_DEBUG_LOG}
|
||||||
|
RCLONE_CACHE_CLEAN_THRESHOLD: ${RCLONE_CACHE_CLEAN_THRESHOLD}
|
||||||
|
RCLONE_MIN_FREE_SPACE_GB: ${RCLONE_MIN_FREE_SPACE_GB}
|
||||||
|
RCLONE_AUTO_RESTART: ${RCLONE_AUTO_RESTART}
|
||||||
|
RCLONE_AUTO_RESTART_MAX_RETRIES: ${RCLONE_AUTO_RESTART_MAX_RETRIES}
|
||||||
|
RCLONE_AUTO_RESTART_DELAY_MS: ${RCLONE_AUTO_RESTART_DELAY_MS}
|
||||||
|
|||||||
218
server/server.js
218
server/server.js
@@ -64,6 +64,26 @@ const RCLONE_VFS_CACHE_MAX_SIZE =
|
|||||||
process.env.RCLONE_VFS_CACHE_MAX_SIZE || "20G";
|
process.env.RCLONE_VFS_CACHE_MAX_SIZE || "20G";
|
||||||
const RCLONE_VFS_CACHE_MAX_AGE =
|
const RCLONE_VFS_CACHE_MAX_AGE =
|
||||||
process.env.RCLONE_VFS_CACHE_MAX_AGE || "24h";
|
process.env.RCLONE_VFS_CACHE_MAX_AGE || "24h";
|
||||||
|
// --- Streaming performans ayarları ---
|
||||||
|
const RCLONE_BUFFER_SIZE = process.env.RCLONE_BUFFER_SIZE || "8M";
|
||||||
|
const RCLONE_VFS_READ_AHEAD = process.env.RCLONE_VFS_READ_AHEAD || "128M";
|
||||||
|
const RCLONE_VFS_READ_CHUNK_SIZE = process.env.RCLONE_VFS_READ_CHUNK_SIZE || "32M";
|
||||||
|
const RCLONE_VFS_READ_CHUNK_SIZE_LIMIT = process.env.RCLONE_VFS_READ_CHUNK_SIZE_LIMIT || "64M";
|
||||||
|
// Disk doluluk oranı eşik değeri (百分比) - Bu oran aşıldığında cache temizlenir
|
||||||
|
const RCLONE_CACHE_CLEAN_THRESHOLD =
|
||||||
|
Number(process.env.RCLONE_CACHE_CLEAN_THRESHOLD) || 85;
|
||||||
|
// Cache temizleme sırasında korunacak minimum boş alan (GB)
|
||||||
|
const RCLONE_MIN_FREE_SPACE_GB =
|
||||||
|
Number(process.env.RCLONE_MIN_FREE_SPACE_GB) || 5;
|
||||||
|
// Auto-restart enable/disable
|
||||||
|
const RCLONE_AUTO_RESTART = ["1", "true", "yes", "on"].includes(
|
||||||
|
String(process.env.RCLONE_AUTO_RESTART || "1").toLowerCase()
|
||||||
|
);
|
||||||
|
// Auto-restart için retry sayısı ve delay
|
||||||
|
const RCLONE_AUTO_RESTART_MAX_RETRIES =
|
||||||
|
Number(process.env.RCLONE_AUTO_RESTART_MAX_RETRIES) || 5;
|
||||||
|
const RCLONE_AUTO_RESTART_DELAY_MS =
|
||||||
|
Number(process.env.RCLONE_AUTO_RESTART_DELAY_MS) || 5000;
|
||||||
const MEDIA_DEBUG_LOG = ["1", "true", "yes", "on"].includes(
|
const MEDIA_DEBUG_LOG = ["1", "true", "yes", "on"].includes(
|
||||||
String(process.env.MEDIA_DEBUG_LOG || "").toLowerCase()
|
String(process.env.MEDIA_DEBUG_LOG || "").toLowerCase()
|
||||||
);
|
);
|
||||||
@@ -762,6 +782,9 @@ let rcloneProcess = null;
|
|||||||
let rcloneLastError = null;
|
let rcloneLastError = null;
|
||||||
const rcloneAuthSessions = new Map();
|
const rcloneAuthSessions = new Map();
|
||||||
let rcloneCacheCleanTimer = null;
|
let rcloneCacheCleanTimer = null;
|
||||||
|
// Auto-restart sayaçları
|
||||||
|
let rcloneRestartCount = 0;
|
||||||
|
let rcloneRestartInProgress = false;
|
||||||
|
|
||||||
function logRcloneMoveError(context, error) {
|
function logRcloneMoveError(context, error) {
|
||||||
if (!error) return;
|
if (!error) return;
|
||||||
@@ -1029,17 +1052,17 @@ function startRcloneCacheCleanSchedule(minutes) {
|
|||||||
if (!interval || interval <= 0) return;
|
if (!interval || interval <= 0) return;
|
||||||
rcloneCacheCleanTimer = setInterval(() => {
|
rcloneCacheCleanTimer = setInterval(() => {
|
||||||
if (!RCLONE_RC_ENABLED) return;
|
if (!RCLONE_RC_ENABLED) return;
|
||||||
fetch(`http://${RCLONE_RC_ADDR}/vfs/refresh`, {
|
// Query string format kullan
|
||||||
method: "POST",
|
const params = new URLSearchParams();
|
||||||
headers: { "Content-Type": "application/json" },
|
params.append("recursive", "true");
|
||||||
body: JSON.stringify({ recursive: true })
|
|
||||||
|
fetch(`http://${RCLONE_RC_ADDR}/vfs/refresh?${params.toString()}`, {
|
||||||
|
method: "POST"
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.status === 404) {
|
if (resp.status === 404) {
|
||||||
return fetch(`http://${RCLONE_RC_ADDR}/rc/vfs/refresh`, {
|
return fetch(`http://${RCLONE_RC_ADDR}/rc/vfs/refresh?${params.toString()}`, {
|
||||||
method: "POST",
|
method: "POST"
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ recursive: true })
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return resp;
|
return resp;
|
||||||
@@ -1057,16 +1080,21 @@ async function runRcloneCacheClean() {
|
|||||||
const wasRunning = Boolean(rcloneProcess && !rcloneProcess.killed);
|
const wasRunning = Boolean(rcloneProcess && !rcloneProcess.killed);
|
||||||
try {
|
try {
|
||||||
if (wasRunning && RCLONE_RC_ENABLED) {
|
if (wasRunning && RCLONE_RC_ENABLED) {
|
||||||
const resp = await fetch(`http://${RCLONE_RC_ADDR}/vfs/refresh`, {
|
// Rclone RC API doğru format: Query string kullanılır
|
||||||
method: "POST",
|
// POST /vfs/refresh with form data: recursive=true
|
||||||
headers: { "Content-Type": "application/json" },
|
const params = new URLSearchParams();
|
||||||
body: JSON.stringify({ recursive: true })
|
params.append("recursive", "true");
|
||||||
|
|
||||||
|
const resp = await fetch(`http://${RCLONE_RC_ADDR}/vfs/refresh?${params.toString()}`, {
|
||||||
|
method: "POST"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (resp.status === 404) {
|
if (resp.status === 404) {
|
||||||
const fallback = await fetch(`http://${RCLONE_RC_ADDR}/rc/vfs/refresh`, {
|
// Fallback for older rclone versions
|
||||||
method: "POST",
|
const fallbackParams = new URLSearchParams();
|
||||||
headers: { "Content-Type": "application/json" },
|
fallbackParams.append("recursive", "true");
|
||||||
body: JSON.stringify({ recursive: true })
|
const fallback = await fetch(`http://${RCLONE_RC_ADDR}/rc/vfs/refresh?${fallbackParams.toString()}`, {
|
||||||
|
method: "POST"
|
||||||
});
|
});
|
||||||
if (!fallback.ok) {
|
if (!fallback.ok) {
|
||||||
const body = await fallback.text();
|
const body = await fallback.text();
|
||||||
@@ -1083,6 +1111,7 @@ async function runRcloneCacheClean() {
|
|||||||
return { ok: false, error: "Rclone RC kapalıyken mount durdurulmadan cache temizlenemez." };
|
return { ok: false, error: "Rclone RC kapalıyken mount durdurulmadan cache temizlenemez." };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RC kapalıysa dosya sisteminden temizle
|
||||||
fs.rmSync(RCLONE_VFS_CACHE_DIR, { recursive: true, force: true });
|
fs.rmSync(RCLONE_VFS_CACHE_DIR, { recursive: true, force: true });
|
||||||
fs.mkdirSync(RCLONE_VFS_CACHE_DIR, { recursive: true });
|
fs.mkdirSync(RCLONE_VFS_CACHE_DIR, { recursive: true });
|
||||||
return { ok: true, method: "fs", restarted: false };
|
return { ok: true, method: "fs", restarted: false };
|
||||||
@@ -1091,6 +1120,64 @@ async function runRcloneCacheClean() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Akıllı cache yönetimi ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disk alanını kontrol eder ve gerekirse cache temizler
|
||||||
|
* @returns {Promise<{ok: boolean, cleaned: boolean, diskUsage: object, message: string}>}
|
||||||
|
*/
|
||||||
|
async function checkAndCleanCacheIfNeeded() {
|
||||||
|
try {
|
||||||
|
const diskInfo = await getDiskSpace(RCLONE_VFS_CACHE_DIR);
|
||||||
|
const usedPercent = diskInfo.usedPercent || 0;
|
||||||
|
const availableGB = parseFloat(diskInfo.availableGB) || 0;
|
||||||
|
|
||||||
|
const shouldClean = usedPercent >= RCLONE_CACHE_CLEAN_THRESHOLD || availableGB < RCLONE_MIN_FREE_SPACE_GB;
|
||||||
|
|
||||||
|
if (!shouldClean) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
cleaned: false,
|
||||||
|
diskUsage: { usedPercent, availableGB, threshold: RCLONE_CACHE_CLEAN_THRESHOLD, minFreeGB: RCLONE_MIN_FREE_SPACE_GB },
|
||||||
|
message: `Disk durumu iyi (${usedPercent}% kullanılıyor, ${availableGB}GB boş)`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(`⚠️ Disk doluluk oranı yüksek (${usedPercent}%) veya boş alan az (${availableGB}GB). Cache temizleniyor...`);
|
||||||
|
|
||||||
|
const result = await runRcloneCacheClean();
|
||||||
|
|
||||||
|
if (result.ok) {
|
||||||
|
// Temizleme sonrası disk durumunu tekrar kontrol et
|
||||||
|
const newDiskInfo = await getDiskSpace(RCLONE_VFS_CACHE_DIR);
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
cleaned: true,
|
||||||
|
diskUsage: {
|
||||||
|
before: { usedPercent, availableGB },
|
||||||
|
after: { usedPercent: newDiskInfo.usedPercent, availableGB: parseFloat(newDiskInfo.availableGB) }
|
||||||
|
},
|
||||||
|
message: `Cache temizlendi. Öncesi: ${usedPercent}%, Sonrası: ${newDiskInfo.usedPercent}%`,
|
||||||
|
method: result.method
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
cleaned: false,
|
||||||
|
error: result.error,
|
||||||
|
message: `Cache temizleme başarısız: ${result.error}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
cleaned: false,
|
||||||
|
error: err?.message || String(err),
|
||||||
|
message: `Cache kontrolü başarısız: ${err?.message}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isRcloneMounted(mountDir) {
|
function isRcloneMounted(mountDir) {
|
||||||
if (!mountDir) return false;
|
if (!mountDir) return false;
|
||||||
try {
|
try {
|
||||||
@@ -1157,6 +1244,14 @@ function startRcloneMount(settings) {
|
|||||||
RCLONE_VFS_CACHE_MAX_SIZE,
|
RCLONE_VFS_CACHE_MAX_SIZE,
|
||||||
"--vfs-cache-max-age",
|
"--vfs-cache-max-age",
|
||||||
RCLONE_VFS_CACHE_MAX_AGE,
|
RCLONE_VFS_CACHE_MAX_AGE,
|
||||||
|
"--buffer-size",
|
||||||
|
RCLONE_BUFFER_SIZE,
|
||||||
|
"--vfs-read-ahead",
|
||||||
|
RCLONE_VFS_READ_AHEAD,
|
||||||
|
"--vfs-read-chunk-size",
|
||||||
|
RCLONE_VFS_READ_CHUNK_SIZE,
|
||||||
|
"--vfs-read-chunk-size-limit",
|
||||||
|
RCLONE_VFS_READ_CHUNK_SIZE_LIMIT,
|
||||||
"--dir-cache-time",
|
"--dir-cache-time",
|
||||||
RCLONE_DIR_CACHE_TIME,
|
RCLONE_DIR_CACHE_TIME,
|
||||||
"--poll-interval",
|
"--poll-interval",
|
||||||
@@ -1190,10 +1285,39 @@ function startRcloneMount(settings) {
|
|||||||
console.warn(`⚠️ rclone: ${msg}`);
|
console.warn(`⚠️ rclone: ${msg}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
rcloneProcess.on("exit", (code) => {
|
rcloneProcess.on("exit", async (code) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
rcloneLastError = `rclone exit: ${code}`;
|
rcloneLastError = `rclone exit: ${code}`;
|
||||||
console.warn(`⚠️ rclone mount durdu (code ${code})`);
|
console.warn(`⚠️ rclone mount durdu (code ${code})`);
|
||||||
|
|
||||||
|
// Auto-restart mekanizması
|
||||||
|
if (RCLONE_AUTO_RESTART && !rcloneRestartInProgress) {
|
||||||
|
const settings = loadRcloneSettings();
|
||||||
|
|
||||||
|
if (settings.autoMount && rcloneRestartCount < RCLONE_AUTO_RESTART_MAX_RETRIES) {
|
||||||
|
rcloneRestartInProgress = true;
|
||||||
|
rcloneRestartCount++;
|
||||||
|
|
||||||
|
console.warn(`🔄 Rclone otomatik yeniden başlatılıyor (${rcloneRestartCount}/${RCLONE_AUTO_RESTART_MAX_RETRIES})...`);
|
||||||
|
|
||||||
|
// Bekle ve yeniden başlat
|
||||||
|
setTimeout(async () => {
|
||||||
|
const result = startRcloneMount(settings);
|
||||||
|
if (result.ok) {
|
||||||
|
console.log(`✅ Rclone başarıyla yeniden başlatıldı.`);
|
||||||
|
rcloneRestartCount = 0; // Başarılı olunca sayacı sıfırla
|
||||||
|
} else {
|
||||||
|
console.error(`❌ Rclone yeniden başlatılamadı: ${result.error}`);
|
||||||
|
}
|
||||||
|
rcloneRestartInProgress = false;
|
||||||
|
}, RCLONE_AUTO_RESTART_DELAY_MS);
|
||||||
|
} else if (rcloneRestartCount >= RCLONE_AUTO_RESTART_MAX_RETRIES) {
|
||||||
|
console.error(`❌ Rclone yeniden başlatma sayısı aşıldı (${RCLONE_AUTO_RESTART_MAX_RETRIES}). Otomatik yeniden başlatma devre dışı.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal exit (code 0) - sayacı sıfırla
|
||||||
|
rcloneRestartCount = 0;
|
||||||
}
|
}
|
||||||
rcloneProcess = null;
|
rcloneProcess = null;
|
||||||
});
|
});
|
||||||
@@ -9068,6 +9192,20 @@ app.post("/api/youtube/settings", requireAuth, (req, res) => {
|
|||||||
app.get("/api/rclone/status", requireAuth, async (req, res) => {
|
app.get("/api/rclone/status", requireAuth, async (req, res) => {
|
||||||
const settings = loadRcloneSettings();
|
const settings = loadRcloneSettings();
|
||||||
const mounted = isRcloneMounted(settings.mountDir);
|
const mounted = isRcloneMounted(settings.mountDir);
|
||||||
|
|
||||||
|
// Disk durumunu da ekle
|
||||||
|
let diskUsage = null;
|
||||||
|
try {
|
||||||
|
const diskInfo = await getDiskSpace(RCLONE_VFS_CACHE_DIR);
|
||||||
|
diskUsage = {
|
||||||
|
usedPercent: diskInfo.usedPercent || 0,
|
||||||
|
availableGB: parseFloat(diskInfo.availableGB) || 0,
|
||||||
|
totalGB: parseFloat(diskInfo.totalGB) || 0
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
// Disk bilgisi alınamazsa null kalsın
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
enabled: RCLONE_ENABLED,
|
enabled: RCLONE_ENABLED,
|
||||||
mounted,
|
mounted,
|
||||||
@@ -9081,7 +9219,18 @@ app.get("/api/rclone/status", requireAuth, async (req, res) => {
|
|||||||
cacheCleanMinutes: settings.cacheCleanMinutes || 0,
|
cacheCleanMinutes: settings.cacheCleanMinutes || 0,
|
||||||
configExists: fs.existsSync(settings.configPath),
|
configExists: fs.existsSync(settings.configPath),
|
||||||
remoteConfigured: rcloneConfigHasRemote(settings.remoteName),
|
remoteConfigured: rcloneConfigHasRemote(settings.remoteName),
|
||||||
lastError: rcloneLastError || null
|
lastError: rcloneLastError || null,
|
||||||
|
// Performans ayarları
|
||||||
|
vfsCacheMode: RCLONE_VFS_CACHE_MODE,
|
||||||
|
bufferSize: RCLONE_BUFFER_SIZE,
|
||||||
|
vfsReadAhead: RCLONE_VFS_READ_AHEAD,
|
||||||
|
vfsReadChunkSize: RCLONE_VFS_READ_CHUNK_SIZE,
|
||||||
|
vfsReadChunkSizeLimit: RCLONE_VFS_READ_CHUNK_SIZE_LIMIT,
|
||||||
|
// Disk kullanımı
|
||||||
|
diskUsage,
|
||||||
|
// Cache temizleme threshold
|
||||||
|
cacheCleanThreshold: RCLONE_CACHE_CLEAN_THRESHOLD,
|
||||||
|
minFreeSpaceGB: RCLONE_MIN_FREE_SPACE_GB
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -9223,6 +9372,15 @@ app.post("/api/rclone/cache/clean", requireAuth, async (req, res) => {
|
|||||||
return res.json({ ok: true, ...result });
|
return res.json({ ok: true, ...result });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Akıllı cache kontrolü - disk durumunu kontrol eder ve gerekirse temizler
|
||||||
|
app.post("/api/rclone/cache/check-and-clean", requireAuth, async (req, res) => {
|
||||||
|
const result = await checkAndCleanCacheIfNeeded();
|
||||||
|
if (!result.ok) {
|
||||||
|
return res.status(500).json({ ok: false, error: result.error, message: result.message });
|
||||||
|
}
|
||||||
|
return res.json({ ok: true, ...result });
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/api/rclone/conf", requireAuth, (req, res) => {
|
app.get("/api/rclone/conf", requireAuth, (req, res) => {
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(RCLONE_CONFIG_PATH)) {
|
if (!fs.existsSync(RCLONE_CONFIG_PATH)) {
|
||||||
@@ -10526,6 +10684,20 @@ if (WEBDAV_ENABLED) {
|
|||||||
|
|
||||||
// --- ☁️ Rclone auto mount ---
|
// --- ☁️ Rclone auto mount ---
|
||||||
const initialRcloneSettings = loadRcloneSettings();
|
const initialRcloneSettings = loadRcloneSettings();
|
||||||
|
|
||||||
|
// Başlangıçta disk kontrolü yap - cache temizleme gerekirse yap
|
||||||
|
if (RCLONE_ENABLED) {
|
||||||
|
checkAndCleanCacheIfNeeded().then(result => {
|
||||||
|
if (result.cleaned) {
|
||||||
|
console.log(`🧹 Başlangıç cache temizlemesi: ${result.message}`);
|
||||||
|
} else {
|
||||||
|
console.log(`✅ Disk durumu: ${result.message}`);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.warn(`⚠️ Başlangıç cache kontrolü başarısız: ${err.message}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (RCLONE_ENABLED && initialRcloneSettings.autoMount) {
|
if (RCLONE_ENABLED && initialRcloneSettings.autoMount) {
|
||||||
const result = startRcloneMount(initialRcloneSettings);
|
const result = startRcloneMount(initialRcloneSettings);
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
@@ -10534,6 +10706,16 @@ if (RCLONE_ENABLED && initialRcloneSettings.autoMount) {
|
|||||||
}
|
}
|
||||||
startRcloneCacheCleanSchedule(initialRcloneSettings.cacheCleanMinutes || 0);
|
startRcloneCacheCleanSchedule(initialRcloneSettings.cacheCleanMinutes || 0);
|
||||||
|
|
||||||
|
// --- Disk alanı izleme - periyodik kontrol (her 5 dakikada bir) ---
|
||||||
|
setInterval(async () => {
|
||||||
|
if (RCLONE_ENABLED) {
|
||||||
|
const result = await checkAndCleanCacheIfNeeded();
|
||||||
|
if (result.cleaned) {
|
||||||
|
console.log(`🧹 Otomatik cache temizlemesi: ${result.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
|
||||||
// --- ✅ Client build (frontend) dosyalarını sun ---
|
// --- ✅ Client build (frontend) dosyalarını sun ---
|
||||||
const publicDir = path.join(__dirname, "public");
|
const publicDir = path.join(__dirname, "public");
|
||||||
if (fs.existsSync(publicDir)) {
|
if (fs.existsSync(publicDir)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user