mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-06-23 05:11:32 +02:00
Merge commit 'c1d8b9bb5a9697c8d86ac518456ab1ae6fe45234' into v1.14.x
This commit is contained in:
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Преглед на профила",
|
||||
"start-new-chat": "Започване на нов разговор",
|
||||
"go-to-target": "Преглед на целта на доклада",
|
||||
"delete-post": "Изтриване на публикацията",
|
||||
"purge-post": "Изчистване на публикацията",
|
||||
"restore-post": "Възстановяване на публикацията",
|
||||
|
||||
"user-view": "Преглед на профила",
|
||||
"user-edit": "Редактиране на профила",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Zobrazit profil",
|
||||
"start-new-chat": "Začít novou konverzaci",
|
||||
"go-to-target": "Zobrazit cílové označení",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Zobrazit profil",
|
||||
"user-edit": "Upravit profil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Profil ansehen",
|
||||
"start-new-chat": "Neuen Chat beginnen",
|
||||
"go-to-target": "Meldungsziel ansehen",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Profil ansehen",
|
||||
"user-edit": "Profil bearbeiten",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Ver perfil",
|
||||
"start-new-chat": "Empezar nuevo chat",
|
||||
"go-to-target": "Ver objetivo marcado",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Ver perfil",
|
||||
"user-edit": "Editar perfil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "نمایش پروفایل",
|
||||
"start-new-chat": "شروع چت جدید",
|
||||
"go-to-target": "مشاهده محتوای گزارش شده",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "نمایش پروفایل",
|
||||
"user-edit": "ویرایش پروفایل",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Voir le profil",
|
||||
"start-new-chat": "Démarrer un nouveau Chat",
|
||||
"go-to-target": "Voir le signalement cible",
|
||||
"delete-post": "Supprimer les messages",
|
||||
"purge-post": "Supprimer définitivement",
|
||||
"restore-post": "Restaurer les messages",
|
||||
|
||||
"user-view": "Voir le profil",
|
||||
"user-edit": "Éditer le profil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Ver perfil",
|
||||
"start-new-chat": "Comezar novo chat",
|
||||
"go-to-target": "Ver contido marcado",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Ver perfil",
|
||||
"user-edit": "Editar perfil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "צפה בפרופיל",
|
||||
"start-new-chat": "התחל שיחה חדשה",
|
||||
"go-to-target": "צפה במטרת הסימון",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "צפה בפרופיל",
|
||||
"user-edit": "ערוך פרופיל",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Pogledaj profil",
|
||||
"start-new-chat": "Pokreni novi razgovor",
|
||||
"go-to-target": "Pogledaj metu zastave",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Pogledaj profil",
|
||||
"user-edit": "Uredi profil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Profil megtekintése",
|
||||
"start-new-chat": "Új chat indítása",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Vedi Profilo",
|
||||
"start-new-chat": "Inizia Nuova Chat",
|
||||
"go-to-target": "Visualizza oggetto segnalazione",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Vedi Profilo",
|
||||
"user-edit": "Modifica Profilo",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "プロフィールを見る",
|
||||
"start-new-chat": "新しいチャットを開始",
|
||||
"go-to-target": "フラグのターゲットを表示",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "プロフィールを見る",
|
||||
"user-edit": "プロフィールを編集",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "프로필 보기",
|
||||
"start-new-chat": "새로운 채팅 시작",
|
||||
"go-to-target": "신고된 게시물 바로가기",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "프로필 보기",
|
||||
"user-edit": "프로필 수정",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Skatīt profilu",
|
||||
"start-new-chat": "Sākt jaunu sarunu",
|
||||
"go-to-target": "Skatīt atzīmēto rakstu",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Skatīt profilu",
|
||||
"user-edit": "Rediģēt profilu",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Profiel bekijken",
|
||||
"start-new-chat": "Begin een nieuwe chat",
|
||||
"go-to-target": "Bekijk markering doel",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Profiel bekijken",
|
||||
"user-edit": "Profiel wijzigen",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Zobacz profil",
|
||||
"start-new-chat": "Rozpocznij nowy czat",
|
||||
"go-to-target": "Zobacz cel flagowania",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Zobacz profil",
|
||||
"user-edit": "Edytuj profil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Ver Perfil",
|
||||
"start-new-chat": "Iniciar Novo Chat",
|
||||
"go-to-target": "Ver o Sinalizado",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Ver Perfil",
|
||||
"user-edit": "Editar Perfil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Ver Perfil",
|
||||
"start-new-chat": "Iniciar Nova Conversa",
|
||||
"go-to-target": "Ver Alvo da Denúncia",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Ver Perfil",
|
||||
"user-edit": "Editar Perfil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Просмотреть профиль",
|
||||
"start-new-chat": "Начать новый чат",
|
||||
"go-to-target": "Показать предмет жалобы",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Открыть профиль",
|
||||
"user-edit": "Изменить профиль",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Zobraziť profil",
|
||||
"start-new-chat": "Začať novú konverzáciu",
|
||||
"go-to-target": "Zobraziť cieľové označenie",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Zobraziť profil",
|
||||
"user-edit": "Upraviť profil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Погледај профил",
|
||||
"start-new-chat": "Започни ново ћаскање",
|
||||
"go-to-target": "Погледај циљ означавања",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Погледај профил",
|
||||
"user-edit": "Уреди профил",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Visa profil",
|
||||
"start-new-chat": "Påbörja ny chatt",
|
||||
"go-to-target": "Visa flaggans ämne",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Visa profil",
|
||||
"user-edit": "Redigera profil",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "ดูโปรไฟล์",
|
||||
"start-new-chat": "เริ่มแชทใหม่",
|
||||
"go-to-target": "ดูเป้าหมายการปักธง",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "ดูโปรไฟล์",
|
||||
"user-edit": "แก้ไขโปรไฟล์",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "Profili Gör",
|
||||
"start-new-chat": "Yeni Sohbet Başlat",
|
||||
"go-to-target": "Şikayet Edilen İçeriği Gör",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "Profili Gör",
|
||||
"user-edit": "Profili Düzenle",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "View Profile",
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "查看个人资料",
|
||||
"start-new-chat": "开始新会话",
|
||||
"go-to-target": "查看举报目标",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "查看资料",
|
||||
"user-edit": "编辑资料",
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"view-profile": "查看個人資料",
|
||||
"start-new-chat": "開始新聊天對話",
|
||||
"go-to-target": "查看舉報目標",
|
||||
"delete-post": "Delete Post",
|
||||
"purge-post": "Purge Post",
|
||||
"restore-post": "Restore Post",
|
||||
|
||||
"user-view": "查看資料",
|
||||
"user-edit": "編輯資料",
|
||||
|
||||
@@ -36,7 +36,7 @@ define('search', ['navigator', 'translator', 'storage'], function (nav, translat
|
||||
Search.quick = function (query, options, callback) {
|
||||
callback = callback || function () {};
|
||||
var template = options.template || 'partials/quick-search-results';
|
||||
$(window).trigger('action:search.quick', { data: query });
|
||||
$(window).trigger('action:search.quick.start', { data: query });
|
||||
query.searchOnly = 1;
|
||||
Search.api(query, function (data) {
|
||||
if (options.hideOnNoMatches && !data.posts.length) {
|
||||
@@ -52,7 +52,7 @@ define('search', ['navigator', 'translator', 'storage'], function (nav, translat
|
||||
} else {
|
||||
options.resultEl.addClass('hidden').find('#quick-search-results-container').html('');
|
||||
}
|
||||
$(window).trigger('action:search.quick.complete', { });
|
||||
$(window).trigger('action:search.quick.complete', { data: data });
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ Analytics.increment = function (keys, callback) {
|
||||
}
|
||||
};
|
||||
|
||||
Analytics.pageView = function (payload) {
|
||||
Analytics.pageView = async function (payload) {
|
||||
pageViews += 1;
|
||||
|
||||
if (payload.uid > 0) {
|
||||
@@ -71,20 +71,16 @@ Analytics.pageView = function (payload) {
|
||||
ipCache.set(payload.ip + nconf.get('secret'), hash);
|
||||
}
|
||||
|
||||
db.sortedSetScore('ip:recent', hash, function (err, score) {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
if (!score) {
|
||||
uniqueIPCount += 1;
|
||||
}
|
||||
var today = new Date();
|
||||
today.setHours(today.getHours(), 0, 0, 0);
|
||||
if (!score || score < today.getTime()) {
|
||||
uniquevisitors += 1;
|
||||
db.sortedSetAdd('ip:recent', Date.now(), hash);
|
||||
}
|
||||
});
|
||||
const score = await db.sortedSetScore('ip:recent', hash);
|
||||
if (!score) {
|
||||
uniqueIPCount += 1;
|
||||
}
|
||||
const today = new Date();
|
||||
today.setHours(today.getHours(), 0, 0, 0);
|
||||
if (!score || score < today.getTime()) {
|
||||
uniquevisitors += 1;
|
||||
await db.sortedSetAdd('ip:recent', Date.now(), hash);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ const validator = require('validator');
|
||||
|
||||
const meta = require('../meta');
|
||||
const plugins = require('../plugins');
|
||||
const middleware = require('../middleware');
|
||||
|
||||
exports.handle404 = function handle404(req, res) {
|
||||
const relativePath = nconf.get('relative_path');
|
||||
@@ -36,14 +37,12 @@ exports.handle404 = function handle404(req, res) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.send404 = function (req, res) {
|
||||
exports.send404 = async function (req, res) {
|
||||
res.status(404);
|
||||
const path = String(req.path || '');
|
||||
if (res.locals.isAPI) {
|
||||
return res.json({ path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]' });
|
||||
}
|
||||
const middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
|
||||
});
|
||||
await middleware.buildHeaderAsync(req, res);
|
||||
res.render('404', { path: validator.escape(path), title: '[[global:404.title]]' });
|
||||
};
|
||||
|
||||
@@ -4,8 +4,9 @@ var nconf = require('nconf');
|
||||
var winston = require('winston');
|
||||
var validator = require('validator');
|
||||
var plugins = require('../plugins');
|
||||
var middleware = require('../middleware');
|
||||
|
||||
exports.handleURIErrors = function handleURIErrors(err, req, res, next) {
|
||||
exports.handleURIErrors = async function handleURIErrors(err, req, res, next) {
|
||||
// Handle cases where malformed URIs are passed in
|
||||
if (err instanceof URIError) {
|
||||
const cleanPath = req.path.replace(new RegExp('^' + nconf.get('relative_path')), '');
|
||||
@@ -23,10 +24,8 @@ exports.handleURIErrors = function handleURIErrors(err, req, res, next) {
|
||||
error: '[[global:400.title]]',
|
||||
});
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.status(400).render('400', { error: validator.escape(String(err.message)) });
|
||||
});
|
||||
await middleware.buildHeaderAsync(req, res);
|
||||
res.status(400).render('400', { error: validator.escape(String(err.message)) });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -46,7 +45,7 @@ exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-di
|
||||
res.status(403).type('text/plain').send(err.message);
|
||||
},
|
||||
};
|
||||
var defaultHandler = function () {
|
||||
var defaultHandler = async function () {
|
||||
// Display NodeBB error page
|
||||
var status = parseInt(err.status, 10);
|
||||
if ((status === 302 || status === 308) && err.path) {
|
||||
@@ -61,10 +60,8 @@ exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-di
|
||||
if (res.locals.isAPI) {
|
||||
res.json({ path: validator.escape(path), error: err.message });
|
||||
} else {
|
||||
var middleware = require('../middleware');
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
|
||||
});
|
||||
await middleware.buildHeaderAsync(req, res);
|
||||
res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
const nconf = require('nconf');
|
||||
const validator = require('validator');
|
||||
const winston = require('winston');
|
||||
const querystring = require('querystring');
|
||||
const _ = require('lodash');
|
||||
|
||||
@@ -15,21 +14,19 @@ const middleware = require('../middleware');
|
||||
|
||||
const helpers = module.exports;
|
||||
|
||||
helpers.noScriptErrors = function (req, res, error, httpStatus) {
|
||||
helpers.noScriptErrors = async function (req, res, error, httpStatus) {
|
||||
if (req.body.noscript !== 'true') {
|
||||
return res.status(httpStatus).send(error);
|
||||
}
|
||||
|
||||
const middleware = require('../middleware');
|
||||
const httpStatusString = httpStatus.toString();
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.status(httpStatus).render(httpStatusString, {
|
||||
path: req.path,
|
||||
loggedIn: req.loggedIn,
|
||||
error: error,
|
||||
returnLink: true,
|
||||
title: '[[global:' + httpStatusString + '.title]]',
|
||||
});
|
||||
await middleware.buildHeaderAsync(req, res);
|
||||
res.status(httpStatus).render(httpStatusString, {
|
||||
path: req.path,
|
||||
loggedIn: req.loggedIn,
|
||||
error: error,
|
||||
returnLink: true,
|
||||
title: '[[global:' + httpStatusString + '.title]]',
|
||||
});
|
||||
};
|
||||
|
||||
@@ -104,41 +101,37 @@ helpers.buildTerms = function (url, term, query) {
|
||||
}];
|
||||
};
|
||||
|
||||
helpers.notAllowed = function (req, res, error) {
|
||||
plugins.fireHook('filter:helpers.notAllowed', {
|
||||
helpers.notAllowed = async function (req, res, error) {
|
||||
const data = await plugins.fireHook('filter:helpers.notAllowed', {
|
||||
req: req,
|
||||
res: res,
|
||||
error: error,
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return winston.error(err);
|
||||
}
|
||||
if (req.loggedIn || req.uid === -1) {
|
||||
if (res.locals.isAPI) {
|
||||
res.status(403).json({
|
||||
path: req.path.replace(/^\/api/, ''),
|
||||
loggedIn: req.loggedIn,
|
||||
error: error,
|
||||
title: '[[global:403.title]]',
|
||||
});
|
||||
} else {
|
||||
middleware.buildHeader(req, res, function () {
|
||||
res.status(403).render('403', {
|
||||
path: req.path,
|
||||
loggedIn: req.loggedIn,
|
||||
error: error,
|
||||
title: '[[global:403.title]]',
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if (res.locals.isAPI) {
|
||||
req.session.returnTo = req.url.replace(/^\/api/, '');
|
||||
res.status(401).json('not-authorized');
|
||||
} else {
|
||||
req.session.returnTo = req.url;
|
||||
res.redirect(nconf.get('relative_path') + '/login');
|
||||
}
|
||||
});
|
||||
|
||||
if (req.loggedIn || req.uid === -1) {
|
||||
if (res.locals.isAPI) {
|
||||
res.status(403).json({
|
||||
path: req.path.replace(/^\/api/, ''),
|
||||
loggedIn: req.loggedIn,
|
||||
error: data.error,
|
||||
title: '[[global:403.title]]',
|
||||
});
|
||||
} else {
|
||||
await middleware.buildHeaderAsync(req, res);
|
||||
res.status(403).render('403', {
|
||||
path: req.path,
|
||||
loggedIn: req.loggedIn,
|
||||
error: data.error,
|
||||
title: '[[global:403.title]]',
|
||||
});
|
||||
}
|
||||
} else if (res.locals.isAPI) {
|
||||
req.session.returnTo = req.url.replace(/^\/api/, '');
|
||||
res.status(401).json('not-authorized');
|
||||
} else {
|
||||
req.session.returnTo = req.url;
|
||||
res.redirect(nconf.get('relative_path') + '/login');
|
||||
}
|
||||
};
|
||||
|
||||
helpers.redirect = function (res, url) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
var jsesc = require('jsesc');
|
||||
var nconf = require('nconf');
|
||||
@@ -9,7 +8,9 @@ var semver = require('semver');
|
||||
var user = require('../user');
|
||||
var meta = require('../meta');
|
||||
var plugins = require('../plugins');
|
||||
var utils = require('../../public/src/utils');
|
||||
var versions = require('../admin/versions');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
var controllers = {
|
||||
api: require('../controllers/api'),
|
||||
@@ -18,112 +19,88 @@ var controllers = {
|
||||
|
||||
module.exports = function (middleware) {
|
||||
middleware.admin = {};
|
||||
middleware.admin.isAdmin = function (req, res, next) {
|
||||
middleware.admin.isAdmin = helpers.try(async function (req, res, next) {
|
||||
winston.warn('[middleware.admin.isAdmin] deprecation warning, no need to use this from plugins!');
|
||||
middleware.isAdmin(req, res, next);
|
||||
};
|
||||
await middleware.isAdmin(req, res, next);
|
||||
});
|
||||
|
||||
middleware.admin.buildHeader = function (req, res, next) {
|
||||
middleware.admin.buildHeader = helpers.try(async function (req, res, next) {
|
||||
res.locals.renderAdminHeader = true;
|
||||
res.locals.config = await controllers.api.loadConfig(req);
|
||||
next();
|
||||
});
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
controllers.api.loadConfig(req, next);
|
||||
},
|
||||
function (config, next) {
|
||||
res.locals.config = config;
|
||||
next();
|
||||
},
|
||||
], next);
|
||||
};
|
||||
|
||||
middleware.admin.renderHeader = function (req, res, data, next) {
|
||||
middleware.admin.renderHeader = async (req, res, data) => {
|
||||
var custom_header = {
|
||||
plugins: [],
|
||||
authentication: [],
|
||||
};
|
||||
res.locals.config = res.locals.config || {};
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
userData: function (next) {
|
||||
user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], next);
|
||||
},
|
||||
scripts: function (next) {
|
||||
getAdminScripts(next);
|
||||
},
|
||||
custom_header: function (next) {
|
||||
plugins.fireHook('filter:admin.header.build', custom_header, next);
|
||||
},
|
||||
configs: function (next) {
|
||||
meta.configs.list(next);
|
||||
},
|
||||
latestVersion: function (next) {
|
||||
versions.getLatestVersion(function (err, result) {
|
||||
if (err) {
|
||||
winston.error('[acp] Failed to fetch latest version', err);
|
||||
}
|
||||
|
||||
next(null, err ? null : result);
|
||||
});
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
var userData = results.userData;
|
||||
userData.uid = req.uid;
|
||||
userData['email:confirmed'] = userData['email:confirmed'] === 1;
|
||||
const results = await utils.promiseParallel({
|
||||
userData: user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed']),
|
||||
scripts: getAdminScripts(),
|
||||
custom_header: plugins.fireHook('filter:admin.header.build', custom_header),
|
||||
configs: meta.configs.list(),
|
||||
latestVersion: getLatestVersion(),
|
||||
});
|
||||
|
||||
var acpPath = req.path.slice(1).split('/');
|
||||
acpPath.forEach(function (path, i) {
|
||||
acpPath[i] = path.charAt(0).toUpperCase() + path.slice(1);
|
||||
});
|
||||
acpPath = acpPath.join(' > ');
|
||||
var userData = results.userData;
|
||||
userData.uid = req.uid;
|
||||
userData['email:confirmed'] = userData['email:confirmed'] === 1;
|
||||
|
||||
var version = nconf.get('version');
|
||||
var acpPath = req.path.slice(1).split('/');
|
||||
acpPath.forEach(function (path, i) {
|
||||
acpPath[i] = path.charAt(0).toUpperCase() + path.slice(1);
|
||||
});
|
||||
acpPath = acpPath.join(' > ');
|
||||
|
||||
res.locals.config.userLang = res.locals.config.acpLang || res.locals.config.userLang;
|
||||
var templateValues = {
|
||||
config: res.locals.config,
|
||||
configJSON: jsesc(JSON.stringify(res.locals.config), { isScriptContext: true }),
|
||||
relative_path: res.locals.config.relative_path,
|
||||
adminConfigJSON: encodeURIComponent(JSON.stringify(results.configs)),
|
||||
user: userData,
|
||||
userJSON: jsesc(JSON.stringify(userData), { isScriptContext: true }),
|
||||
plugins: results.custom_header.plugins,
|
||||
authentication: results.custom_header.authentication,
|
||||
scripts: results.scripts,
|
||||
'cache-buster': meta.config['cache-buster'] || '',
|
||||
env: !!process.env.NODE_ENV,
|
||||
title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel',
|
||||
bodyClass: data.bodyClass,
|
||||
version: version,
|
||||
latestVersion: results.latestVersion,
|
||||
upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version),
|
||||
};
|
||||
var version = nconf.get('version');
|
||||
|
||||
templateValues.template = { name: res.locals.template };
|
||||
templateValues.template[res.locals.template] = true;
|
||||
res.locals.config.userLang = res.locals.config.acpLang || res.locals.config.userLang;
|
||||
var templateValues = {
|
||||
config: res.locals.config,
|
||||
configJSON: jsesc(JSON.stringify(res.locals.config), { isScriptContext: true }),
|
||||
relative_path: res.locals.config.relative_path,
|
||||
adminConfigJSON: encodeURIComponent(JSON.stringify(results.configs)),
|
||||
user: userData,
|
||||
userJSON: jsesc(JSON.stringify(userData), { isScriptContext: true }),
|
||||
plugins: results.custom_header.plugins,
|
||||
authentication: results.custom_header.authentication,
|
||||
scripts: results.scripts,
|
||||
'cache-buster': meta.config['cache-buster'] || '',
|
||||
env: !!process.env.NODE_ENV,
|
||||
title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel',
|
||||
bodyClass: data.bodyClass,
|
||||
version: version,
|
||||
latestVersion: results.latestVersion,
|
||||
upgradeAvailable: results.latestVersion && semver.gt(results.latestVersion, version),
|
||||
};
|
||||
|
||||
req.app.render('admin/header', templateValues, next);
|
||||
},
|
||||
], next);
|
||||
templateValues.template = { name: res.locals.template };
|
||||
templateValues.template[res.locals.template] = true;
|
||||
|
||||
return await req.app.renderAsync('admin/header', templateValues);
|
||||
};
|
||||
|
||||
function getAdminScripts(callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
plugins.fireHook('filter:admin.scripts.get', [], next);
|
||||
},
|
||||
function (scripts, next) {
|
||||
next(null, scripts.map(function (script) {
|
||||
return { src: script };
|
||||
}));
|
||||
},
|
||||
], callback);
|
||||
async function getAdminScripts() {
|
||||
const scripts = await plugins.fireHook('filter:admin.scripts.get', []);
|
||||
return scripts.map(function (script) {
|
||||
return { src: script };
|
||||
});
|
||||
}
|
||||
|
||||
middleware.admin.renderFooter = function (req, res, data, next) {
|
||||
req.app.render('admin/footer', data, next);
|
||||
async function getLatestVersion() {
|
||||
try {
|
||||
const result = await versions.getLatestVersion();
|
||||
return result;
|
||||
} catch (err) {
|
||||
winston.error('[acp] Failed to fetch latest version' + err.stack);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
middleware.admin.renderFooter = async function (req, res, data) {
|
||||
return await req.app.renderAsync('admin/footer', data);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var nconf = require('nconf');
|
||||
var jsesc = require('jsesc');
|
||||
var _ = require('lodash');
|
||||
var util = require('util');
|
||||
|
||||
var db = require('../database');
|
||||
var user = require('../user');
|
||||
@@ -16,6 +16,7 @@ var translator = require('../translator');
|
||||
var privileges = require('../privileges');
|
||||
var languages = require('../languages');
|
||||
var utils = require('../utils');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
var controllers = {
|
||||
api: require('../controllers/api'),
|
||||
@@ -23,36 +24,20 @@ var controllers = {
|
||||
};
|
||||
|
||||
module.exports = function (middleware) {
|
||||
middleware.buildHeader = function buildHeader(req, res, next) {
|
||||
middleware.buildHeader = helpers.try(async function buildHeader(req, res, next) {
|
||||
res.locals.renderHeader = true;
|
||||
res.locals.isAPI = false;
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
if (req.uid >= 0) {
|
||||
middleware.applyCSRF(req, res, next);
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
},
|
||||
function (next) {
|
||||
async.parallel({
|
||||
config: function (next) {
|
||||
controllers.api.loadConfig(req, next);
|
||||
},
|
||||
plugins: function (next) {
|
||||
plugins.fireHook('filter:middleware.buildHeader', { req: req, locals: res.locals }, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
res.locals.config = results.config;
|
||||
// Return no arguments
|
||||
setImmediate(next);
|
||||
},
|
||||
], next);
|
||||
};
|
||||
const [config] = await Promise.all([
|
||||
controllers.api.loadConfig(req),
|
||||
plugins.fireHook('filter:middleware.buildHeader', { req: req, locals: res.locals }),
|
||||
]);
|
||||
res.locals.config = config;
|
||||
next();
|
||||
});
|
||||
|
||||
middleware.generateHeader = function generateHeader(req, res, data, callback) {
|
||||
middleware.buildHeaderAsync = util.promisify(middleware.buildHeader);
|
||||
|
||||
async function generateHeader(req, res, data) {
|
||||
var registrationType = meta.config.registrationType || 'normal';
|
||||
res.locals.config = res.locals.config || {};
|
||||
var templateValues = {
|
||||
@@ -73,207 +58,162 @@ module.exports = function (middleware) {
|
||||
|
||||
templateValues.configJSON = jsesc(JSON.stringify(res.locals.config), { isScriptContext: true });
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
isAdmin: function (next) {
|
||||
user.isAdministrator(req.uid, next);
|
||||
},
|
||||
isGlobalMod: function (next) {
|
||||
user.isGlobalModerator(req.uid, next);
|
||||
},
|
||||
isModerator: function (next) {
|
||||
user.isModeratorOfAnyCategory(req.uid, next);
|
||||
},
|
||||
privileges: function (next) {
|
||||
privileges.global.get(req.uid, next);
|
||||
},
|
||||
user: function (next) {
|
||||
user.getUserData(req.uid, next);
|
||||
},
|
||||
isEmailConfirmSent: function (next) {
|
||||
if (!meta.config.requireEmailConfirmation || req.uid <= 0) {
|
||||
return next(null, false);
|
||||
}
|
||||
db.get('uid:' + req.uid + ':confirm:email:sent', next);
|
||||
},
|
||||
languageDirection: function (next) {
|
||||
translator.translate('[[language:dir]]', res.locals.config.userLang, function (translated) {
|
||||
next(null, translated);
|
||||
});
|
||||
},
|
||||
browserTitle: function (next) {
|
||||
translator.translate(controllers.helpers.buildTitle(translator.unescape(data.title)), function (translated) {
|
||||
next(null, translated);
|
||||
});
|
||||
},
|
||||
navigation: async.apply(navigation.get, req.uid),
|
||||
banned: async.apply(user.bans.isBanned, req.uid),
|
||||
banReason: async.apply(user.bans.getReason, req.uid),
|
||||
const results = await utils.promiseParallel({
|
||||
isAdmin: user.isAdministrator(req.uid),
|
||||
isGlobalMod: user.isGlobalModerator(req.uid),
|
||||
isModerator: user.isModeratorOfAnyCategory(req.uid),
|
||||
privileges: privileges.global.get(req.uid),
|
||||
user: user.getUserData(req.uid),
|
||||
isEmailConfirmSent: (!meta.config.requireEmailConfirmation || req.uid <= 0) ? false : await db.get('uid:' + req.uid + ':confirm:email:sent'),
|
||||
languageDirection: translator.translate('[[language:dir]]', res.locals.config.userLang),
|
||||
browserTitle: translator.translate(controllers.helpers.buildTitle(translator.unescape(data.title))),
|
||||
navigation: navigation.get(req.uid),
|
||||
banned: user.bans.isBanned(req.uid),
|
||||
banReason: user.bans.getReason(req.uid),
|
||||
|
||||
unreadData: async.apply(topics.getUnreadData, { uid: req.uid }),
|
||||
unreadChatCount: async.apply(messaging.getUnreadCount, req.uid),
|
||||
unreadNotificationCount: async.apply(user.notifications.getUnreadCount, req.uid),
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
if (results.banned) {
|
||||
req.logout();
|
||||
return res.redirect('/');
|
||||
}
|
||||
|
||||
const unreadData = {
|
||||
'': {},
|
||||
new: {},
|
||||
watched: {},
|
||||
unreplied: {},
|
||||
};
|
||||
|
||||
results.user.unreadData = unreadData;
|
||||
results.user.isAdmin = results.isAdmin;
|
||||
results.user.isGlobalMod = results.isGlobalMod;
|
||||
results.user.isMod = !!results.isModerator;
|
||||
results.user.privileges = results.privileges;
|
||||
results.user[results.user.status] = true;
|
||||
|
||||
results.user.email = String(results.user.email);
|
||||
results.user['email:confirmed'] = results.user['email:confirmed'] === 1;
|
||||
results.user.isEmailConfirmSent = !!results.isEmailConfirmSent;
|
||||
|
||||
templateValues.bootswatchSkin = (parseInt(meta.config.disableCustomUserSkins, 10) !== 1 ? res.locals.config.bootswatchSkin : '') || meta.config.bootswatchSkin || '';
|
||||
templateValues.config.bootswatchSkin = templateValues.bootswatchSkin || 'noskin'; // TODO remove in v1.12.0+
|
||||
|
||||
const unreadCounts = results.unreadData.counts;
|
||||
var unreadCount = {
|
||||
topic: unreadCounts[''] || 0,
|
||||
newTopic: unreadCounts.new || 0,
|
||||
watchedTopic: unreadCounts.watched || 0,
|
||||
unrepliedTopic: unreadCounts.unreplied || 0,
|
||||
chat: results.unreadChatCount || 0,
|
||||
notification: results.unreadNotificationCount || 0,
|
||||
};
|
||||
|
||||
Object.keys(unreadCount).forEach(function (key) {
|
||||
if (unreadCount[key] > 99) {
|
||||
unreadCount[key] = '99+';
|
||||
}
|
||||
});
|
||||
|
||||
const tidsByFilter = results.unreadData.tidsByFilter;
|
||||
results.navigation = results.navigation.map(function (item) {
|
||||
function modifyNavItem(item, route, filter, content) {
|
||||
if (item && item.originalRoute === route) {
|
||||
unreadData[filter] = _.zipObject(tidsByFilter[filter], tidsByFilter[filter].map(() => true));
|
||||
item.content = content;
|
||||
if (unreadCounts[filter] > 0) {
|
||||
item.iconClass += ' unread-count';
|
||||
}
|
||||
}
|
||||
}
|
||||
modifyNavItem(item, '/unread', '', unreadCount.topic);
|
||||
modifyNavItem(item, '/unread?filter=new', 'new', unreadCount.newTopic);
|
||||
modifyNavItem(item, '/unread?filter=watched', 'watched', unreadCount.watchedTopic);
|
||||
modifyNavItem(item, '/unread?filter=unreplied', 'unreplied', unreadCount.unrepliedTopic);
|
||||
return item;
|
||||
});
|
||||
|
||||
templateValues.browserTitle = results.browserTitle;
|
||||
templateValues.navigation = results.navigation;
|
||||
templateValues.unreadCount = unreadCount;
|
||||
templateValues.isAdmin = results.user.isAdmin;
|
||||
templateValues.isGlobalMod = results.user.isGlobalMod;
|
||||
templateValues.showModMenu = results.user.isAdmin || results.user.isGlobalMod || results.user.isMod;
|
||||
templateValues.canChat = results.canChat && meta.config.disableChat !== 1;
|
||||
templateValues.user = results.user;
|
||||
templateValues.userJSON = jsesc(JSON.stringify(results.user), { isScriptContext: true });
|
||||
templateValues.useCustomCSS = meta.config.useCustomCSS && meta.config.customCSS;
|
||||
templateValues.customCSS = templateValues.useCustomCSS ? (meta.config.renderedCustomCSS || '') : '';
|
||||
templateValues.useCustomHTML = meta.config.useCustomHTML;
|
||||
templateValues.customHTML = templateValues.useCustomHTML ? meta.config.customHTML : '';
|
||||
templateValues.maintenanceHeader = meta.config.maintenanceMode && !results.isAdmin;
|
||||
templateValues.defaultLang = meta.config.defaultLang || 'en-GB';
|
||||
templateValues.userLang = res.locals.config.userLang;
|
||||
templateValues.languageDirection = results.languageDirection;
|
||||
|
||||
templateValues.template = { name: res.locals.template };
|
||||
templateValues.template[res.locals.template] = true;
|
||||
|
||||
if (data.hasOwnProperty('_header')) {
|
||||
templateValues.metaTags = data._header.tags.meta;
|
||||
templateValues.linkTags = data._header.tags.link;
|
||||
}
|
||||
|
||||
if (req.route && req.route.path === '/') {
|
||||
modifyTitle(templateValues);
|
||||
}
|
||||
|
||||
plugins.fireHook('filter:middleware.renderHeader', {
|
||||
req: req,
|
||||
res: res,
|
||||
templateValues: templateValues,
|
||||
data: data,
|
||||
}, next);
|
||||
},
|
||||
], function (err, data) {
|
||||
callback(err, data.templateValues);
|
||||
unreadData: topics.getUnreadData({ uid: req.uid }),
|
||||
unreadChatCount: messaging.getUnreadCount(req.uid),
|
||||
unreadNotificationCount: user.notifications.getUnreadCount(req.uid),
|
||||
});
|
||||
};
|
||||
|
||||
middleware.renderHeader = function renderHeader(req, res, data, callback) {
|
||||
async.waterfall([
|
||||
async.apply(middleware.generateHeader, req, res, data),
|
||||
function (templateValues, next) {
|
||||
req.app.render('header', templateValues, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
if (results.banned) {
|
||||
req.logout();
|
||||
return res.redirect('/');
|
||||
}
|
||||
|
||||
middleware.renderFooter = function renderFooter(req, res, data, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
plugins.fireHook('filter:middleware.renderFooter', {
|
||||
req: req,
|
||||
res: res,
|
||||
templateValues: data,
|
||||
}, next);
|
||||
},
|
||||
function (data, next) {
|
||||
async.parallel({
|
||||
scripts: async.apply(plugins.fireHook, 'filter:scripts.get', []),
|
||||
timeagoLocale: (next) => {
|
||||
async.waterfall([
|
||||
async.apply(languages.listCodes),
|
||||
(languageCodes, next) => {
|
||||
const userLang = res.locals.config.userLang;
|
||||
const timeagoCode = utils.userLangToTimeagoCode(userLang);
|
||||
const unreadData = {
|
||||
'': {},
|
||||
new: {},
|
||||
watched: {},
|
||||
unreplied: {},
|
||||
};
|
||||
|
||||
if (languageCodes.includes(userLang) && languages.timeagoCodes.includes(timeagoCode)) {
|
||||
const pathToLocaleFile = '/vendor/jquery/timeago/locales/jquery.timeago.' + timeagoCode + '.js';
|
||||
next(null, (nconf.get('relative_path') + '/assets' + pathToLocaleFile));
|
||||
} else {
|
||||
next(null, false);
|
||||
}
|
||||
},
|
||||
], next);
|
||||
},
|
||||
}, function (err, results) {
|
||||
next(err, data, results);
|
||||
});
|
||||
},
|
||||
function (data, results, next) {
|
||||
if (results.timeagoLocale) {
|
||||
results.scripts.push(results.timeagoLocale);
|
||||
results.user.unreadData = unreadData;
|
||||
results.user.isAdmin = results.isAdmin;
|
||||
results.user.isGlobalMod = results.isGlobalMod;
|
||||
results.user.isMod = !!results.isModerator;
|
||||
results.user.privileges = results.privileges;
|
||||
results.user[results.user.status] = true;
|
||||
|
||||
results.user.email = String(results.user.email);
|
||||
results.user['email:confirmed'] = results.user['email:confirmed'] === 1;
|
||||
results.user.isEmailConfirmSent = !!results.isEmailConfirmSent;
|
||||
|
||||
templateValues.bootswatchSkin = (parseInt(meta.config.disableCustomUserSkins, 10) !== 1 ? res.locals.config.bootswatchSkin : '') || meta.config.bootswatchSkin || '';
|
||||
templateValues.config.bootswatchSkin = templateValues.bootswatchSkin || 'noskin'; // TODO remove in v1.12.0+
|
||||
|
||||
const unreadCounts = results.unreadData.counts;
|
||||
const unreadCount = {
|
||||
topic: unreadCounts[''] || 0,
|
||||
newTopic: unreadCounts.new || 0,
|
||||
watchedTopic: unreadCounts.watched || 0,
|
||||
unrepliedTopic: unreadCounts.unreplied || 0,
|
||||
chat: results.unreadChatCount || 0,
|
||||
notification: results.unreadNotificationCount || 0,
|
||||
};
|
||||
|
||||
Object.keys(unreadCount).forEach(function (key) {
|
||||
if (unreadCount[key] > 99) {
|
||||
unreadCount[key] = '99+';
|
||||
}
|
||||
});
|
||||
|
||||
const tidsByFilter = results.unreadData.tidsByFilter;
|
||||
results.navigation = results.navigation.map(function (item) {
|
||||
function modifyNavItem(item, route, filter, content) {
|
||||
if (item && item.originalRoute === route) {
|
||||
unreadData[filter] = _.zipObject(tidsByFilter[filter], tidsByFilter[filter].map(() => true));
|
||||
item.content = content;
|
||||
if (unreadCounts[filter] > 0) {
|
||||
item.iconClass += ' unread-count';
|
||||
}
|
||||
}
|
||||
data.templateValues.scripts = results.scripts.map(function (script) {
|
||||
return { src: script };
|
||||
});
|
||||
}
|
||||
modifyNavItem(item, '/unread', '', unreadCount.topic);
|
||||
modifyNavItem(item, '/unread?filter=new', 'new', unreadCount.newTopic);
|
||||
modifyNavItem(item, '/unread?filter=watched', 'watched', unreadCount.watchedTopic);
|
||||
modifyNavItem(item, '/unread?filter=unreplied', 'unreplied', unreadCount.unrepliedTopic);
|
||||
return item;
|
||||
});
|
||||
|
||||
data.templateValues.useCustomJS = meta.config.useCustomJS;
|
||||
data.templateValues.customJS = data.templateValues.useCustomJS ? meta.config.customJS : '';
|
||||
data.templateValues.isSpider = req.uid === -1;
|
||||
req.app.render('footer', data.templateValues, next);
|
||||
},
|
||||
], callback);
|
||||
templateValues.browserTitle = results.browserTitle;
|
||||
templateValues.navigation = results.navigation;
|
||||
templateValues.unreadCount = unreadCount;
|
||||
templateValues.isAdmin = results.user.isAdmin;
|
||||
templateValues.isGlobalMod = results.user.isGlobalMod;
|
||||
templateValues.showModMenu = results.user.isAdmin || results.user.isGlobalMod || results.user.isMod;
|
||||
templateValues.canChat = results.canChat && meta.config.disableChat !== 1;
|
||||
templateValues.user = results.user;
|
||||
templateValues.userJSON = jsesc(JSON.stringify(results.user), { isScriptContext: true });
|
||||
templateValues.useCustomCSS = meta.config.useCustomCSS && meta.config.customCSS;
|
||||
templateValues.customCSS = templateValues.useCustomCSS ? (meta.config.renderedCustomCSS || '') : '';
|
||||
templateValues.useCustomHTML = meta.config.useCustomHTML;
|
||||
templateValues.customHTML = templateValues.useCustomHTML ? meta.config.customHTML : '';
|
||||
templateValues.maintenanceHeader = meta.config.maintenanceMode && !results.isAdmin;
|
||||
templateValues.defaultLang = meta.config.defaultLang || 'en-GB';
|
||||
templateValues.userLang = res.locals.config.userLang;
|
||||
templateValues.languageDirection = results.languageDirection;
|
||||
|
||||
templateValues.template = { name: res.locals.template };
|
||||
templateValues.template[res.locals.template] = true;
|
||||
|
||||
if (data.hasOwnProperty('_header')) {
|
||||
templateValues.metaTags = data._header.tags.meta;
|
||||
templateValues.linkTags = data._header.tags.link;
|
||||
}
|
||||
|
||||
if (req.route && req.route.path === '/') {
|
||||
modifyTitle(templateValues);
|
||||
}
|
||||
|
||||
const hookReturn = await plugins.fireHook('filter:middleware.renderHeader', {
|
||||
req: req,
|
||||
res: res,
|
||||
templateValues: templateValues,
|
||||
data: data,
|
||||
});
|
||||
|
||||
return hookReturn.templateValues;
|
||||
}
|
||||
|
||||
middleware.renderHeader = async function renderHeader(req, res, data) {
|
||||
return await req.app.renderAsync('header', await generateHeader(req, res, data));
|
||||
};
|
||||
|
||||
middleware.renderFooter = async function renderFooter(req, res, templateValues) {
|
||||
const data = await plugins.fireHook('filter:middleware.renderFooter', {
|
||||
req: req,
|
||||
res: res,
|
||||
templateValues: templateValues,
|
||||
});
|
||||
|
||||
const results = await utils.promiseParallel({
|
||||
scripts: plugins.fireHook('filter:scripts.get', []),
|
||||
timeagoLocale: (async () => {
|
||||
const languageCodes = await languages.listCodes();
|
||||
const userLang = res.locals.config.userLang;
|
||||
const timeagoCode = utils.userLangToTimeagoCode(userLang);
|
||||
|
||||
if (languageCodes.includes(userLang) && languages.timeagoCodes.includes(timeagoCode)) {
|
||||
const pathToLocaleFile = '/vendor/jquery/timeago/locales/jquery.timeago.' + timeagoCode + '.js';
|
||||
return nconf.get('relative_path') + '/assets' + pathToLocaleFile;
|
||||
}
|
||||
return false;
|
||||
})(),
|
||||
});
|
||||
|
||||
if (results.timeagoLocale) {
|
||||
results.scripts.push(results.timeagoLocale);
|
||||
}
|
||||
data.templateValues.scripts = results.scripts.map(function (script) {
|
||||
return { src: script };
|
||||
});
|
||||
|
||||
data.templateValues.useCustomJS = meta.config.useCustomJS;
|
||||
data.templateValues.customJS = data.templateValues.useCustomJS ? meta.config.customJS : '';
|
||||
data.templateValues.isSpider = req.uid === -1;
|
||||
|
||||
return await req.app.renderAsync('footer', data.templateValues);
|
||||
};
|
||||
|
||||
function modifyTitle(obj) {
|
||||
|
||||
@@ -6,9 +6,10 @@ var _ = require('lodash');
|
||||
|
||||
var meta = require('../meta');
|
||||
var languages = require('../languages');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
module.exports = function (middleware) {
|
||||
middleware.addHeaders = function addHeaders(req, res, next) {
|
||||
middleware.addHeaders = helpers.try(function addHeaders(req, res, next) {
|
||||
var headers = {
|
||||
'X-Powered-By': encodeURI(meta.config['powered-by'] || 'NodeBB'),
|
||||
'X-Frame-Options': meta.config['allow-from-uri'] ? 'ALLOW-FROM ' + encodeURI(meta.config['allow-from-uri']) : 'SAMEORIGIN',
|
||||
@@ -61,31 +62,30 @@ module.exports = function (middleware) {
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
});
|
||||
|
||||
let langs = [];
|
||||
middleware.autoLocale = function autoLocale(req, res, next) {
|
||||
middleware.autoLocale = helpers.try(async function autoLocale(req, res, next) {
|
||||
if (parseInt(req.uid, 10) > 0 || !meta.config.autoDetectLang || req.query.lang) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const langs = await listCodes();
|
||||
const lang = req.acceptsLanguages(langs);
|
||||
if (!lang) {
|
||||
return next();
|
||||
}
|
||||
req.query.lang = lang;
|
||||
next();
|
||||
};
|
||||
|
||||
languages.listCodes(function (err, codes) {
|
||||
if (err) {
|
||||
winston.error('[middleware/autoLocale] Could not retrieve languages codes list!');
|
||||
codes = [];
|
||||
}
|
||||
|
||||
winston.verbose('[middleware/autoLocale] Retrieves languages list for middleware');
|
||||
var defaultLang = meta.config.defaultLang || 'en-GB';
|
||||
|
||||
langs = _.uniq([defaultLang, ...codes]);
|
||||
});
|
||||
|
||||
async function listCodes() {
|
||||
const defaultLang = meta.config.defaultLang || 'en-GB';
|
||||
try {
|
||||
const codes = await languages.listCodes();
|
||||
winston.verbose('[middleware/autoLocale] Retrieves languages list for middleware');
|
||||
return _.uniq([defaultLang, ...codes]);
|
||||
} catch (err) {
|
||||
winston.error('[middleware/autoLocale] Could not retrieve languages codes list! ' + err.stack);
|
||||
return [defaultLang];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
22
src/middleware/helpers.js
Normal file
22
src/middleware/helpers.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const helpers = module.exports;
|
||||
|
||||
helpers.try = function (middleware) {
|
||||
if (middleware && middleware.constructor && middleware.constructor.name === 'AsyncFunction') {
|
||||
return async function (req, res, next) {
|
||||
try {
|
||||
await middleware(req, res, next);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
return function (req, res, next) {
|
||||
try {
|
||||
middleware(req, res, next);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -8,6 +8,7 @@ var nconf = require('nconf');
|
||||
var ensureLoggedIn = require('connect-ensure-login');
|
||||
var toobusy = require('toobusy-js');
|
||||
var LRU = require('lru-cache');
|
||||
var util = require('util');
|
||||
|
||||
var plugins = require('../plugins');
|
||||
var meta = require('../meta');
|
||||
@@ -15,6 +16,7 @@ var user = require('../user');
|
||||
var groups = require('../groups');
|
||||
var analytics = require('../analytics');
|
||||
var privileges = require('../privileges');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
var controllers = {
|
||||
api: require('../controllers/api'),
|
||||
@@ -31,7 +33,7 @@ middleware.regexes = {
|
||||
timestampedUpload: /^\d+-.+$/,
|
||||
};
|
||||
|
||||
middleware.applyCSRF = csrf({
|
||||
const csurfMiddleware = csrf({
|
||||
cookie: nconf.get('url_parsed').protocol === 'https:' ? {
|
||||
secure: true,
|
||||
sameSite: 'Strict',
|
||||
@@ -39,6 +41,14 @@ middleware.applyCSRF = csrf({
|
||||
} : true,
|
||||
});
|
||||
|
||||
middleware.applyCSRF = function (req, res, next) {
|
||||
if (req.uid >= 0) {
|
||||
csurfMiddleware(req, res, next);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login');
|
||||
|
||||
require('./admin')(middleware);
|
||||
@@ -51,56 +61,39 @@ require('./headers')(middleware);
|
||||
middleware.stripLeadingSlashes = function stripLeadingSlashes(req, res, next) {
|
||||
var target = req.originalUrl.replace(nconf.get('relative_path'), '');
|
||||
if (target.startsWith('//')) {
|
||||
res.redirect(nconf.get('relative_path') + target.replace(/^\/+/, '/'));
|
||||
} else {
|
||||
setImmediate(next);
|
||||
return res.redirect(nconf.get('relative_path') + target.replace(/^\/+/, '/'));
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
middleware.pageView = function pageView(req, res, next) {
|
||||
analytics.pageView({
|
||||
ip: req.ip,
|
||||
uid: req.uid,
|
||||
middleware.pageView = helpers.try(async function pageView(req, res, next) {
|
||||
const promises = [
|
||||
analytics.pageView({ ip: req.ip, uid: req.uid }),
|
||||
];
|
||||
if (req.loggedIn) {
|
||||
promises.push(user.updateOnlineUsers(req.uid));
|
||||
promises.push(user.updateLastOnlineTime(req.uid));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
plugins.fireHook('action:middleware.pageView', { req: req });
|
||||
next();
|
||||
});
|
||||
|
||||
middleware.pluginHooks = helpers.try(async function pluginHooks(req, res, next) {
|
||||
// TODO: Deprecate in v2.0
|
||||
await async.each(plugins.loadedHooks['filter:router.page'] || [], function (hookObj, next) {
|
||||
hookObj.method(req, res, next);
|
||||
});
|
||||
|
||||
plugins.fireHook('action:middleware.pageView', { req: req });
|
||||
|
||||
if (req.loggedIn) {
|
||||
if (req.path.startsWith('/api/users') || req.path.startsWith('/users')) {
|
||||
async.parallel([
|
||||
async.apply(user.updateOnlineUsers, req.uid),
|
||||
async.apply(user.updateLastOnlineTime, req.uid),
|
||||
], next);
|
||||
} else {
|
||||
user.updateOnlineUsers(req.uid);
|
||||
user.updateLastOnlineTime(req.uid);
|
||||
setImmediate(next);
|
||||
}
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
middleware.pluginHooks = async function pluginHooks(req, res, next) {
|
||||
// TODO: Deprecate in v2.0
|
||||
try {
|
||||
await async.each(plugins.loadedHooks['filter:router.page'] || [], function (hookObj, next) {
|
||||
hookObj.method(req, res, next);
|
||||
});
|
||||
|
||||
await plugins.fireHook('response:router.page', {
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
await plugins.fireHook('response:router.page', {
|
||||
req: req,
|
||||
res: res,
|
||||
});
|
||||
|
||||
if (!res.headersSent) {
|
||||
next();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
middleware.validateFiles = function validateFiles(req, res, next) {
|
||||
if (!Array.isArray(req.files.files) || !req.files.files.length) {
|
||||
@@ -131,36 +124,28 @@ middleware.routeTouchIcon = function routeTouchIcon(req, res) {
|
||||
});
|
||||
};
|
||||
|
||||
middleware.privateTagListing = function privateTagListing(req, res, next) {
|
||||
privileges.global.can('view:tags', req.uid, function (err, canView) {
|
||||
if (err || canView) {
|
||||
return next(err);
|
||||
}
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
});
|
||||
};
|
||||
middleware.privateTagListing = helpers.try(async function privateTagListing(req, res, next) {
|
||||
const canView = await privileges.global.can('view:tags', req.uid);
|
||||
if (!canView) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
middleware.exposeGroupName = function exposeGroupName(req, res, next) {
|
||||
expose('groupName', groups.getGroupNameByGroupSlug, 'slug', req, res, next);
|
||||
};
|
||||
middleware.exposeGroupName = helpers.try(async function exposeGroupName(req, res, next) {
|
||||
await expose('groupName', groups.getGroupNameByGroupSlug, 'slug', req, res, next);
|
||||
});
|
||||
|
||||
middleware.exposeUid = function exposeUid(req, res, next) {
|
||||
expose('uid', user.getUidByUserslug, 'userslug', req, res, next);
|
||||
};
|
||||
middleware.exposeUid = helpers.try(async function exposeUid(req, res, next) {
|
||||
await expose('uid', user.getUidByUserslug, 'userslug', req, res, next);
|
||||
});
|
||||
|
||||
function expose(exposedField, method, field, req, res, next) {
|
||||
async function expose(exposedField, method, field, req, res, next) {
|
||||
if (!req.params.hasOwnProperty(field)) {
|
||||
return next();
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
method(req.params[field], next);
|
||||
},
|
||||
function (id, next) {
|
||||
res.locals[exposedField] = id;
|
||||
next();
|
||||
},
|
||||
], next);
|
||||
res.locals[exposedField] = await method(req.params[field]);
|
||||
next();
|
||||
}
|
||||
|
||||
middleware.privateUploads = function privateUploads(req, res, next) {
|
||||
@@ -188,10 +173,13 @@ middleware.busyCheck = function busyCheck(req, res, next) {
|
||||
}
|
||||
};
|
||||
|
||||
middleware.applyBlacklist = function applyBlacklist(req, res, next) {
|
||||
meta.blacklist.test(req.ip, function (err) {
|
||||
middleware.applyBlacklist = async function applyBlacklist(req, res, next) {
|
||||
try {
|
||||
await meta.blacklist.test(req.ip);
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
middleware.delayLoading = function delayLoading(req, res, next) {
|
||||
@@ -207,25 +195,19 @@ middleware.delayLoading = function delayLoading(req, res, next) {
|
||||
setTimeout(next, 1000);
|
||||
};
|
||||
|
||||
middleware.buildSkinAsset = function buildSkinAsset(req, res, next) {
|
||||
middleware.buildSkinAsset = helpers.try(async function buildSkinAsset(req, res, next) {
|
||||
// If this middleware is reached, a skin was requested, so it is built on-demand
|
||||
var target = path.basename(req.originalUrl).match(/(client-[a-z]+)/);
|
||||
if (target) {
|
||||
async.waterfall([
|
||||
async.apply(plugins.prepareForBuild, ['client side styles']),
|
||||
async.apply(meta.css.buildBundle, target[0], true),
|
||||
], function (err, css) {
|
||||
if (err) {
|
||||
return next();
|
||||
}
|
||||
|
||||
require('../meta/minifier').killAll();
|
||||
res.status(200).type('text/css').send(css);
|
||||
});
|
||||
} else {
|
||||
setImmediate(next);
|
||||
const target = path.basename(req.originalUrl).match(/(client-[a-z]+)/);
|
||||
if (!target) {
|
||||
return next();
|
||||
}
|
||||
};
|
||||
|
||||
await plugins.prepareForBuild(['client side styles']);
|
||||
const buildBundle = util.promisify(meta.css.buildBundle);
|
||||
const css = await buildBundle(target[0], true);
|
||||
require('../meta/minifier').killAll();
|
||||
res.status(200).type('text/css').send(css);
|
||||
});
|
||||
|
||||
middleware.trimUploadTimestamps = function trimUploadTimestamps(req, res, next) {
|
||||
// Check match
|
||||
@@ -238,19 +220,18 @@ middleware.trimUploadTimestamps = function trimUploadTimestamps(req, res, next)
|
||||
next();
|
||||
};
|
||||
|
||||
middleware.validateAuth = function validateAuth(req, res, next) {
|
||||
plugins.fireHook('static:auth.validate', {
|
||||
user: res.locals.user,
|
||||
strategy: res.locals.strategy,
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
return req.session.regenerate(function () {
|
||||
req.uid = 0;
|
||||
req.loggedIn = false;
|
||||
next(err);
|
||||
});
|
||||
}
|
||||
|
||||
middleware.validateAuth = helpers.try(async function validateAuth(req, res, next) {
|
||||
try {
|
||||
await plugins.fireHook('static:auth.validate', {
|
||||
user: res.locals.user,
|
||||
strategy: res.locals.strategy,
|
||||
});
|
||||
next();
|
||||
});
|
||||
};
|
||||
} catch (err) {
|
||||
const regenerateSession = util.promisify(cb => req.session.regenerate(cb));
|
||||
await regenerateSession();
|
||||
req.uid = 0;
|
||||
req.loggedIn = false;
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,11 +4,12 @@ const util = require('util');
|
||||
const nconf = require('nconf');
|
||||
const meta = require('../meta');
|
||||
const user = require('../user');
|
||||
const helpers = require('./helpers');
|
||||
|
||||
module.exports = function (middleware) {
|
||||
middleware.maintenanceMode = async function maintenanceMode(req, res, next) {
|
||||
middleware.maintenanceMode = helpers.try(async function maintenanceMode(req, res, next) {
|
||||
if (!meta.config.maintenanceMode) {
|
||||
return setImmediate(next);
|
||||
return next();
|
||||
}
|
||||
|
||||
const hooksAsync = util.promisify(middleware.pluginHooks);
|
||||
@@ -16,12 +17,12 @@ module.exports = function (middleware) {
|
||||
|
||||
const url = req.url.replace(nconf.get('relative_path'), '');
|
||||
if (url.startsWith('/login') || url.startsWith('/api/login')) {
|
||||
return setImmediate(next);
|
||||
return next();
|
||||
}
|
||||
|
||||
const isAdmin = await user.isAdministrator(req.uid);
|
||||
if (isAdmin) {
|
||||
return setImmediate(next);
|
||||
return next();
|
||||
}
|
||||
|
||||
res.status(meta.config.maintenanceModeStatus);
|
||||
@@ -34,8 +35,7 @@ module.exports = function (middleware) {
|
||||
if (res.locals.isAPI) {
|
||||
return res.json(data);
|
||||
}
|
||||
const buildHeaderAsync = util.promisify(middleware.buildHeader);
|
||||
await buildHeaderAsync(req, res);
|
||||
await middleware.buildHeaderAsync(req, res);
|
||||
res.render('503', data);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -12,8 +12,6 @@ const widgets = require('../widgets');
|
||||
const utils = require('../utils');
|
||||
|
||||
module.exports = function (middleware) {
|
||||
const renderHeaderFooterAsync = util.promisify(renderHeaderFooter);
|
||||
|
||||
middleware.processRender = function processRender(req, res, next) {
|
||||
// res.render post-processing, modified from here: https://gist.github.com/mrlannigan/5051687
|
||||
const render = res.render;
|
||||
@@ -63,9 +61,9 @@ module.exports = function (middleware) {
|
||||
const renderAsync = util.promisify((templateToRender, options, next) => render.call(self, templateToRender, options, next));
|
||||
|
||||
const results = await utils.promiseParallel({
|
||||
header: renderHeaderFooterAsync('renderHeader', req, res, options),
|
||||
header: renderHeaderFooter('renderHeader', req, res, options),
|
||||
content: renderAsync(templateToRender, options),
|
||||
footer: renderHeaderFooterAsync('renderFooter', req, res, options),
|
||||
footer: renderHeaderFooter('renderFooter', req, res, options),
|
||||
});
|
||||
|
||||
const str = results.header +
|
||||
@@ -89,14 +87,13 @@ module.exports = function (middleware) {
|
||||
next();
|
||||
};
|
||||
|
||||
function renderHeaderFooter(method, req, res, options, next) {
|
||||
async function renderHeaderFooter(method, req, res, options) {
|
||||
if (res.locals.renderHeader) {
|
||||
middleware[method](req, res, options, next);
|
||||
return await middleware[method](req, res, options);
|
||||
} else if (res.locals.renderAdminHeader) {
|
||||
middleware.admin[method](req, res, options, next);
|
||||
} else {
|
||||
next(null, '');
|
||||
return await middleware.admin[method](req, res, options);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
async function translate(str, req, res) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
const nconf = require('nconf');
|
||||
const winston = require('winston');
|
||||
|
||||
@@ -8,7 +7,7 @@ const meta = require('../meta');
|
||||
const user = require('../user');
|
||||
const privileges = require('../privileges');
|
||||
const plugins = require('../plugins');
|
||||
|
||||
const helpers = require('./helpers');
|
||||
const auth = require('../routes/authentication');
|
||||
|
||||
const controllers = {
|
||||
@@ -16,9 +15,9 @@ const controllers = {
|
||||
};
|
||||
|
||||
module.exports = function (middleware) {
|
||||
async function authenticate(req, res, next, callback) {
|
||||
async function authenticate(req, res) {
|
||||
if (req.loggedIn) {
|
||||
return next();
|
||||
return true;
|
||||
}
|
||||
|
||||
await plugins.fireHook('response:middleware.authenticate', {
|
||||
@@ -28,35 +27,35 @@ module.exports = function (middleware) {
|
||||
});
|
||||
|
||||
if (!res.headersSent) {
|
||||
auth.setAuthVars(req, res, function () {
|
||||
if (req.loggedIn && req.user && req.user.uid) {
|
||||
return next();
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
auth.setAuthVars(req);
|
||||
}
|
||||
return !res.headersSent;
|
||||
}
|
||||
|
||||
middleware.authenticate = function middlewareAuthenticate(req, res, next) {
|
||||
authenticate(req, res, next, function () {
|
||||
controllers.helpers.notAllowed(req, res, next);
|
||||
});
|
||||
};
|
||||
middleware.authenticate = helpers.try(async function middlewareAuthenticate(req, res, next) {
|
||||
if (!await authenticate(req, res)) {
|
||||
return;
|
||||
}
|
||||
if (!req.loggedIn) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
const authenticateAsync = util.promisify(middleware.authenticate);
|
||||
middleware.authenticateOrGuest = helpers.try(async function authenticateOrGuest(req, res, next) {
|
||||
if (!await authenticate(req, res)) {
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
middleware.authenticateOrGuest = function authenticateOrGuest(req, res, next) {
|
||||
authenticate(req, res, next, next);
|
||||
};
|
||||
middleware.ensureSelfOrGlobalPrivilege = helpers.try(async function ensureSelfOrGlobalPrivilege(req, res, next) {
|
||||
await ensureSelfOrMethod(user.isAdminOrGlobalMod, req, res, next);
|
||||
});
|
||||
|
||||
middleware.ensureSelfOrGlobalPrivilege = function ensureSelfOrGlobalPrivilege(req, res, next) {
|
||||
ensureSelfOrMethod(user.isAdminOrGlobalMod, req, res, next);
|
||||
};
|
||||
|
||||
middleware.ensureSelfOrPrivileged = function ensureSelfOrPrivileged(req, res, next) {
|
||||
ensureSelfOrMethod(user.isPrivileged, req, res, next);
|
||||
};
|
||||
middleware.ensureSelfOrPrivileged = helpers.try(async function ensureSelfOrPrivileged(req, res, next) {
|
||||
await ensureSelfOrMethod(user.isPrivileged, req, res, next);
|
||||
});
|
||||
|
||||
async function ensureSelfOrMethod(method, req, res, next) {
|
||||
/*
|
||||
@@ -67,7 +66,7 @@ module.exports = function (middleware) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
if (req.uid === parseInt(res.locals.uid, 10)) {
|
||||
return setImmediate(next);
|
||||
return next();
|
||||
}
|
||||
const allowed = await method(req.uid);
|
||||
if (!allowed) {
|
||||
@@ -77,12 +76,12 @@ module.exports = function (middleware) {
|
||||
return next();
|
||||
}
|
||||
|
||||
middleware.checkGlobalPrivacySettings = function checkGlobalPrivacySettings(req, res, next) {
|
||||
middleware.checkGlobalPrivacySettings = helpers.try(async function checkGlobalPrivacySettings(req, res, next) {
|
||||
winston.warn('[middleware], checkGlobalPrivacySettings deprecated, use canViewUsers or canViewGroups');
|
||||
middleware.canViewUsers(req, res, next);
|
||||
};
|
||||
await middleware.canViewUsers(req, res, next);
|
||||
});
|
||||
|
||||
middleware.canViewUsers = async function canViewUsers(req, res, next) {
|
||||
middleware.canViewUsers = helpers.try(async function canViewUsers(req, res, next) {
|
||||
if (parseInt(res.locals.uid, 10) === req.uid) {
|
||||
return next();
|
||||
}
|
||||
@@ -91,19 +90,24 @@ module.exports = function (middleware) {
|
||||
return next();
|
||||
}
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
};
|
||||
});
|
||||
|
||||
middleware.canViewGroups = async function canViewGroups(req, res, next) {
|
||||
middleware.canViewGroups = helpers.try(async function canViewGroups(req, res, next) {
|
||||
const canView = await privileges.global.can('view:groups', req.uid);
|
||||
if (canView) {
|
||||
return next();
|
||||
}
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
};
|
||||
});
|
||||
|
||||
middleware.checkAccountPermissions = async function checkAccountPermissions(req, res, next) {
|
||||
middleware.checkAccountPermissions = helpers.try(async function checkAccountPermissions(req, res, next) {
|
||||
// This middleware ensures that only the requested user and admins can pass
|
||||
await authenticateAsync(req, res);
|
||||
if (!await authenticate(req, res)) {
|
||||
return;
|
||||
}
|
||||
if (!req.loggedIn) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
const uid = await user.getUidByUserslug(req.params.userslug);
|
||||
let allowed = await privileges.users.canEdit(req.uid, uid);
|
||||
if (allowed) {
|
||||
@@ -117,17 +121,17 @@ module.exports = function (middleware) {
|
||||
return next();
|
||||
}
|
||||
controllers.helpers.notAllowed(req, res);
|
||||
};
|
||||
});
|
||||
|
||||
middleware.redirectToAccountIfLoggedIn = async function redirectToAccountIfLoggedIn(req, res, next) {
|
||||
middleware.redirectToAccountIfLoggedIn = helpers.try(async function redirectToAccountIfLoggedIn(req, res, next) {
|
||||
if (req.session.forceLogin || req.uid <= 0) {
|
||||
return next();
|
||||
}
|
||||
const userslug = await user.getUserField(req.uid, 'userslug');
|
||||
controllers.helpers.redirect(res, '/user/' + userslug);
|
||||
};
|
||||
});
|
||||
|
||||
middleware.redirectUidToUserslug = async function redirectUidToUserslug(req, res, next) {
|
||||
middleware.redirectUidToUserslug = helpers.try(async function redirectUidToUserslug(req, res, next) {
|
||||
const uid = parseInt(req.params.uid, 10);
|
||||
if (uid <= 0) {
|
||||
return next();
|
||||
@@ -140,18 +144,18 @@ module.exports = function (middleware) {
|
||||
.replace('uid', 'user')
|
||||
.replace(uid, function () { return userslug; });
|
||||
controllers.helpers.redirect(res, path);
|
||||
};
|
||||
});
|
||||
|
||||
middleware.redirectMeToUserslug = async function redirectMeToUserslug(req, res) {
|
||||
middleware.redirectMeToUserslug = helpers.try(async function redirectMeToUserslug(req, res) {
|
||||
const userslug = await user.getUserField(req.uid, 'userslug');
|
||||
if (!userslug) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
}
|
||||
const path = req.path.replace(/^(\/api)?\/me/, '/user/' + userslug);
|
||||
controllers.helpers.redirect(res, path);
|
||||
};
|
||||
});
|
||||
|
||||
middleware.isAdmin = async function isAdmin(req, res, next) {
|
||||
middleware.isAdmin = helpers.try(async function isAdmin(req, res, next) {
|
||||
const isAdmin = await user.isAdministrator(req.uid);
|
||||
if (!isAdmin) {
|
||||
return controllers.helpers.notAllowed(req, res);
|
||||
@@ -186,7 +190,7 @@ module.exports = function (middleware) {
|
||||
} else {
|
||||
res.redirect(nconf.get('relative_path') + '/login?local=1');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
middleware.requireUser = function (req, res, next) {
|
||||
if (req.loggedIn) {
|
||||
|
||||
@@ -8,13 +8,7 @@ module.exports = function (app, middleware, controllers) {
|
||||
var router = express.Router();
|
||||
app.use('/api', router);
|
||||
|
||||
router.get('/config', function (req, res, next) {
|
||||
if (req.uid >= 0) {
|
||||
middleware.applyCSRF(req, res, next);
|
||||
} else {
|
||||
setImmediate(next);
|
||||
}
|
||||
}, controllers.api.getConfig);
|
||||
router.get('/config', middleware.applyCSRF, controllers.api.getConfig);
|
||||
|
||||
router.get('/me', controllers.user.getCurrentUser);
|
||||
router.get('/user/uid/:uid', middleware.canViewUsers, controllers.user.getUserByUID);
|
||||
|
||||
@@ -23,14 +23,17 @@ Auth.initialize = function (app, middleware) {
|
||||
passportSessionMiddleware(req, res, next);
|
||||
});
|
||||
|
||||
app.use(Auth.setAuthVars);
|
||||
app.use(function (req, res, next) {
|
||||
Auth.setAuthVars(req, res);
|
||||
next();
|
||||
});
|
||||
|
||||
Auth.app = app;
|
||||
Auth.middleware = middleware;
|
||||
};
|
||||
|
||||
Auth.setAuthVars = function setAuthVars(req, res, next) {
|
||||
var isSpider = req.isSpider();
|
||||
Auth.setAuthVars = function setAuthVars(req) {
|
||||
const isSpider = req.isSpider();
|
||||
req.loggedIn = !isSpider && !!req.user;
|
||||
if (req.user) {
|
||||
req.uid = parseInt(req.user.uid, 10);
|
||||
@@ -39,7 +42,6 @@ Auth.setAuthVars = function setAuthVars(req, res, next) {
|
||||
} else {
|
||||
req.uid = 0;
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
Auth.getLoginStrategies = function () {
|
||||
|
||||
@@ -5,7 +5,7 @@ var helpers = module.exports;
|
||||
helpers.setupPageRoute = function (router, name, middleware, middlewares, controller) {
|
||||
middlewares = [middleware.maintenanceMode, middleware.registrationComplete, middleware.pageView, middleware.pluginHooks].concat(middlewares);
|
||||
|
||||
router.get(name, middleware.busyCheck, middleware.buildHeader, middlewares, helpers.tryRoute(controller));
|
||||
router.get(name, middleware.busyCheck, middleware.applyCSRF, middleware.buildHeader, middlewares, helpers.tryRoute(controller));
|
||||
router.get('/api' + name, middlewares, helpers.tryRoute(controller));
|
||||
};
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ function topicRoutes(app, middleware, controllers) {
|
||||
|
||||
function postRoutes(app, middleware, controllers) {
|
||||
const middlewares = [middleware.maintenanceMode, middleware.registrationComplete, middleware.pluginHooks];
|
||||
app.get('/post/:pid', middleware.busyCheck, middleware.buildHeader, middlewares, controllers.posts.redirectToPost);
|
||||
app.get('/post/:pid', middleware.busyCheck, middlewares, controllers.posts.redirectToPost);
|
||||
app.get('/api/post/:pid', middlewares, controllers.posts.redirectToPost);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user