Spaces:
Sleeping
Sleeping
File size: 9,126 Bytes
06989bd 7960834 06989bd db3071a ce132f9 06989bd db3071a aee961c 06989bd aee961c db3071a aee961c db3071a aee961c 06989bd db3071a aee961c db3071a aee961c 06989bd ce132f9 db3071a ce132f9 db3071a 06989bd aee961c db3071a aee961c db3071a aee961c db3071a ce132f9 db3071a aee961c db3071a aee961c db3071a ce132f9 db3071a aee961c db3071a aee961c db3071a aee961c db3071a aee961c ce132f9 aee961c ce132f9 aee961c ce132f9 aee961c ce132f9 aee961c db3071a ce132f9 db3071a aee961c db3071a aee961c ce132f9 aee961c ce132f9 aee961c ce132f9 db3071a aee961c db3071a aee961c ce132f9 aee961c ce132f9 aee961c db3071a aee961c db3071a 06989bd aee961c db3071a ce132f9 db3071a 06989bd db3071a 06989bd db3071a aee961c ce132f9 06989bd db3071a aee961c 06989bd aee961c 06989bd db3071a 06989bd aee961c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | const express = require('express');
const cors = require('cors');
const { Octokit } = require('@octokit/rest');
const app = express();
app.use(cors({ origin: '*' }));
app.use(express.json({ limit: '200mb' }));
const PORT = process.env.PORT || 7860;
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const REPO_OWNER = 'Nour774';
const REPO_NAME = 's';
const BRANCH = 'main';
// طابور العمليات
let operationQueue = [];
let novelIndex = [];
// 🎯 الكاش السحري: تخزين ملفات الروايات بالكامل في RAM هجينج فيس لمنع طلبها من جيثب تكراراً
let localNovelsCache = {};
let isIndexLoaded = false;
/**
* دالة مساعدة تجلب الملفات مع كاش محلي صارم
*/
async function fetchFileContentWithCache(path) {
try {
const { data } = await octokit.repos.getContent({ owner: REPO_OWNER, repo: REPO_NAME, path, ref: BRANCH });
return Buffer.from(data.content, 'base64').toString('utf8');
} catch (e) {
return null;
}
}
// مسارات استقبال الطلبات (إضافة رواية / إضافة فصل)
app.post('/api/add-novel', (req, res) => {
operationQueue.push({ type: 'ADD_NOVEL', data: req.body });
res.status(200).json({ status: 'queued' });
});
app.post('/api/add-chapter', (req, res) => {
operationQueue.push({ type: 'ADD_CHAPTER', data: req.body });
res.status(200).json({ status: 'queued' });
});
/**
* 🔄 محرك الضخ الذكي الخالي من استهلاك طلبات الـ API
*/
setInterval(async () => {
if (operationQueue.length === 0) return;
const currentOps = [...operationQueue];
operationQueue = [];
console.log(`[⚡ Engine] بدء تصفية وحقن حزمة تحتوي على ${currentOps.length} عملية بالذاكرة...`);
try {
let finalTreeChanges = [];
const timestamp = Math.floor(Date.now() / 1000);
// 1. جلب الفهرس العام مرة واحدة فقط عند بدء تشغيل السيرفر الفعلي وتخزينه بالذاكرة
if (!isIndexLoaded) {
console.log("[📥 Cache] جلب الفهرس العام لأول مرة من جيثب...");
let searchIndexContent = await fetchFileContentWithCache('novel/search_index.js');
if (searchIndexContent) {
let novelIndexMatch = searchIndexContent.match(/window\.novelIndex\s*=\s*([\s\S]*?);/);
novelIndex = novelIndexMatch ? eval(novelIndexMatch[1]) : [];
}
isIndexLoaded = true;
}
// مجموعة لتتبع الروايات التي تم تعديلها في هذه الدفعة لرفعها
let alteredNovelIds = new Set();
for (const op of currentOps) {
if (op.type === 'ADD_NOVEL') {
const d = op.data;
const nextId = novelIndex.length > 0 ? novelIndex[novelIndex.length - 1][0] + 1 : 1;
// تحديث مصفوفة الفهرس بالذاكرة
novelIndex.push([
nextId, d.englishName, d.arabicName, d.slug, d.genres,
parseInt(d.status), 10.0, 0, timestamp, 0, d.author, d.coverType
]);
// حقن الرواية الجديدة مباشرة في كاش الذاكرة (0 طلبات لجيثب!)
localNovelsCache[nextId] = {
id: nextId,
slug: d.slug,
uploader_id: parseInt(d.uploaderId),
uploader_username: d.uploaderUsername,
description: d.description,
chapters: []
};
alteredNovelIds.add(nextId);
} else if (op.type === 'ADD_CHAPTER') {
const d = op.data;
const novelId = parseInt(d.novelId);
// إذا لم تكن الرواية في كاش الذاكرة، نسحبها من جيثب مرة واحدة فقط ونحفظها أبدياً في الكاش
if (!localNovelsCache[novelId]) {
console.log(`[📥 Cache Missing] جلب وصف الرواية ${novelId} لتخزينه في الذاكرة...`);
let novelDocContent = await fetchFileContentWithCache(`novels/${novelId}.js`);
if (novelDocContent) {
let docMatch = novelDocContent.match(/window\.currentNovelData\s*=\s*([\s\S]*?);/);
if (docMatch) {
localNovelsCache[novelId] = eval(`(${docMatch[1]})`);
}
}
}
let cachedNovel = localNovelsCache[novelId];
if (cachedNovel) {
// حقن الفصل الجديد في كاش الذاكرة (0 طلبات لجيثب!)
const existingCh = cachedNovel.chapters.find(c => c.num === parseFloat(d.chapterNum));
if (!existingCh) {
cachedNovel.chapters.push({
num: parseFloat(d.chapterNum),
title: d.chapterTitle,
type: "text",
contributor: d.contributor
});
cachedNovel.chapters.sort((a, b) => a.num - b.num);
}
alteredNovelIds.add(novelId);
// تحديث عداد الفهرس العام في الذاكرة
let row = novelIndex.find(r => r[0] === novelId);
if (row) {
row[9] = cachedNovel.chapters.length;
row[8] = timestamp;
}
}
// 2. إنشاء كائن الفصل المنفصل وضخه في الشجرة
finalTreeChanges.push({
path: `chapters/${novelId}/${d.chapterNum}.js`,
mode: '100644',
type: 'blob',
content: `window.currentChapterData = {\n "novelId": ${novelId},\n "num": ${d.chapterNum},\n "title": "${d.chapterTitle}",\n "contributor": "${d.contributor}",\n "content": "${d.content.replace(/"/g, '\\"')}"\n};`
});
}
}
// 3. تحويل كافة الروايات المعدلة من الكاش الميموري إلى ملفات شجرة جيثب بضربة واحدة
alteredNovelIds.forEach(id => {
finalTreeChanges.push({
path: `novels/${id}.js`,
mode: '100644',
type: 'blob',
content: `window.currentNovelData = ${JSON.stringify(localNovelsCache[id], null, 2)};`
});
});
// 4. بناء ملف الفهرس المحدث من الذاكرة وضخه
const updatedSearchIndexContent = `window.blackSkyConfig = window.blackSkyConfig || {};\nwindow.novelIndex = ${JSON.stringify(novelIndex, null, 4)};`;
finalTreeChanges.push({
path: 'novel/search_index.js',
mode: '100644',
type: 'blob',
content: updatedSearchIndexContent
});
// 5. عملية دفع الـ Commit المجمع إلى جيثب (تستهلك طلب API واحد فقط للشجرة ككل!)
console.log(`[📤 GitHub Sync] جاري شحن الشجرة المجمعة (${finalTreeChanges.length} ملفاً بالتزامن)...`);
const { data: refData } = await octokit.git.getRef({ owner: REPO_OWNER, repo: REPO_NAME, ref: `heads/${BRANCH}` });
const { data: commitData } = await octokit.git.getCommit({ owner: REPO_OWNER, repo: REPO_NAME, commit_sha: refData.object.sha });
const { data: newTreeData } = await octokit.git.createTree({
owner: REPO_OWNER, repo: REPO_NAME, base_tree: commitData.tree.sha, tree: finalTreeChanges
});
const { data: newCommitData } = await octokit.git.createCommit({
owner: REPO_OWNER, repo: REPO_NAME,
message: `🤖 تحسين الأداء: دمج وضخ حزمة ضخمة (${finalTreeChanges.length} ملفاً) بطلب API واحد`,
tree: newTreeData.sha, parents: [refData.object.sha]
});
await octokit.git.updateRef({ owner: REPO_OWNER, repo: REPO_NAME, ref: `heads/${BRANCH}`, sha: newCommitData.sha });
console.log(`[🟢 Success] تم الدمج الكلي بنجاح تام وحماية خطة جيثب!`);
} catch (error) {
console.error('[🔴 Engine Error] حدث خطأ في حزمة الضخ:', error.message);
// في حال حدوث خطأ، نعيد العمليات للطابور لإعادة المحاولة لاحقاً
operationQueue = [...currentOps, ...operationQueue];
}
}, 15000);
app.listen(PORT, () => console.log(`سيرفر محاكاة الضغط المطور مستعد على المنفذ ${PORT}`)); |