Commit bc67b028 authored by yPhil's avatar yPhil
Browse files

Merge branch 'server'

parents 7784d2b2 5a6627ee
Pipeline #421243 passed with stage
in 35 seconds
......@@ -3,14 +3,19 @@
## 1.3.0
- New theming engine / logic
- Full "Light / Dark" UI
- New feed items indicator / badge #96
- New help system #104
- Number of items hard limit #81
- New theming engine / logic #75
- Full "Light / Dark" UI
- New icon set
- Rendering (CSS, transitions, JQ, etc.) optimizations
- Dialog KB control
### Bugfixes
- Better (faster) server (fetch) error handling
- Sister libs Licence change
- Finalized name refactoring
- Explicit CSS vars names
......
{
"name": "petrolette",
"description": "The news reader that doesn't know you",
"version": "1.3.0",
"version": "1.3.1",
"private": true,
"license": "GPL-3.0-only",
"HTTP_PORT": 8000,
......
......@@ -1174,6 +1174,7 @@ video {
#ptlSearch {
display: flex;
flex-direction: row;
padding-left: 12px;
}
#ptlSearch .ptlSearchButton {
......
......@@ -388,7 +388,6 @@ PTL.dialog = {
$('.helpTourDialogItem').each(function() {
const step = $(this).data('step');
console.error('step: %s (%s)', step);
$(this)
.parent()
.prev()
......
This diff is collapsed.
......@@ -86,9 +86,11 @@ PTL.i18n.translations = {
"How many new items should this feed display at a time?" : { fr: "Combien de nouveaux articles dans ce flux ?", ja: "フィードは一度にいくつ表示する必要がありますか?", es: "¿Cuántos elementos nuevos debería mostrar el feed a la vez?" },
"Tabs and feeds saved" : { fr: "Onglets et flux enregistrés" , ja: "タブとソースが保存されました", es: "Pestañas y fuentes guardadas" },
"New icon in cache" : { fr: "Nouvelle icône dans le cache" , ja: "キャッシュの新しいアイコン", es: "Nuevo icono en caché" },
"Tabs and feeds saved" : { fr: "Onglets et flux enregistrés" , ja: "タブとソースが保存されました", es: "Pestañas y fuentes guardadas" },
"Error fetching icon" : { fr: "Erreur au téléchargement de l'icône" , ja: "アイコンのフェッチエラー", es: "Error al buscar icono" },
"Theme" : { fr: "Thème" , ja: "テーマ", es: "Tema" },
"Language" : { fr: "Langue" , ja: "言語", es: "Idioma" },
......
This diff is collapsed.
const fetch = require('node-fetch'),
iconv = require('iconv-lite'),
FeedParser = require('feedparser');
const fetch = require('node-fetch'),
iconv = require('iconv-lite'),
FeedParser = require('feedparser');
exports.getFeed = getFeed;
......@@ -29,14 +29,14 @@ function getParams(str) {
return params;
}
function formatError (error) {
function formatError(error) {
let message = (error.message) ? error.message : 'Can\'t read this feed';
let type = (error.type) ? error.type : 'Unknown type';
let status = (error.status && Number.isInteger(error.status)) ? error.status : 400;
return { type: type, status: status, message: message };
return { type: type, status: status, message: message };
}
function getFeed(feedUrl, lastItem, callback) {
......@@ -46,9 +46,9 @@ function getFeed(feedUrl, lastItem, callback) {
'accept': 'text/html,application/xhtml+xml',
redirect: 'follow'
}).then(function(res) {
if (res.status != 200) {
reject();
callback(formatError({ type: 'Network error', status: res.status, message: 'Bad server response' }));
}
var feedparser = new FeedParser();
......@@ -58,59 +58,51 @@ function getFeed(feedUrl, lastItem, callback) {
responseStream = maybeTranslate(responseStream, charset);
responseStream.pipe(feedparser);
return new Promise((resolve, reject) => {
feedparser.on('error', function(error) {
feedparser.on('error', function(error) {
reject();
callback(formatError(error));
}).on('readable', function() {
try {
var item = this.read();
if (item !== null) {
feedItems.push(item);
}
}
catch (error) {
reject();
callback(formatError(error));
}
}).on('end', function() {
callback(formatError(error));
}).on('readable', function() {
if (feedItems.length === 0) {
reject();
callback(formatError({type:'Empty feed', status:300, message:'Feed OK, but empty'}));
try {
var item = this.read();
if (item !== null) {
feedItems.push(item);
}
resolve();
}
catch (error) {
callback(formatError(error));
}
var newLastItem;
var totalNewItems;
var i = 0;
}).on('end', function() {
feedItems.forEach(countItems);
if (feedItems.length === 0) {
callback(formatError({ type: 'Empty feed', status: 300, message: 'Feed OK, but empty' }));
}
function countItems(item) {
i++;
if (newLastItem == undefined) newLastItem = item.link;
if (item.link == lastItem) totalNewItems = i - 1;
}
var newLastItem;
var totalNewItems;
var i = 0;
feedItems.forEach(countItems);
if (totalNewItems == undefined) totalNewItems = i;
function countItems(item) {
i++;
if (newLastItem == undefined) newLastItem = item.link;
if (item.link == lastItem) totalNewItems = i - 1;
}
const meta = this.meta;
return callback(null, feedItems, meta.title || feedUrl, meta.link || feedUrl, newLastItem, totalNewItems);
if (totalNewItems == undefined) totalNewItems = i;
});
const meta = this.meta;
return callback(null, feedItems, meta.title || feedUrl, meta.link || feedUrl, newLastItem, totalNewItems);
});
}).catch((error) => {
// callback(formatError(error));
callback(formatError({type:'Network problem', status:300, message: error.message || 'Network problem'}));
callback(formatError({ type: 'Network problem', status: 300, message: error.message || 'Network problem' }));
});
......
const express = require('express'),
router = express.Router(),
favrat = require('favrat'),
feeder = require('./feeder'),
fetch = require('node-fetch'),
favrat = require('favrat'),
// favrat = require(__dirname + '/../../favrat/'),
feedrat = require('feedrat'),
// feedrat = require(__dirname + '/../../feedrat/'),
fs = require('fs'),
......@@ -14,58 +15,38 @@ const express = require('express'),
console.error('### (re)START ## Version (%s)', pjson.version);
// var options = {
// object: false,
// reversible: false,
// coerce: true,
// sanitize: false,
// trim: false,
// arrayNotation: false,
// alternateTextNode: false
// };
router.use(sanitize);
router.get('/favicon', function(req, res) {
favrat(req.query.url, function(err, url) {
const downloadFile = (async (url, path) => {
const res = await fetch(url);
const fileStream = fs.createWriteStream(path);
await new Promise((resolve, reject) => {
res.body.pipe(fileStream);
res.body.on("error", reject);
fileStream.on("finish", resolve);
});
});
if (err) console.error('######### err: %s (%s)', err);
router.get('/favicon', function(req, res) {
// console.error('######### favicon: %s (%s)', url);
if (url) {
favrat(req.query.url, function(error, url) {
// console.error('######### url: %s (%s)',url);
if (error) {
res.status(500).send(error);
} else if (url) {
if (!url.startsWith('http') || !url.startsWith('//')) url = 'http://' + url.substring(url.indexOf("/") + 1);
// if (!url.startsWith('http') || !url.startsWith('//')) url = 'http://' + url.substring(url.indexOf("/") + 1);
const hash = crypto.createHash('md5').update(url).digest('hex'),
fileName = hash + '.favicon',
filePath = path.join(pjson.FAVICONS_CACHE_DIR, fileName);
// res.send(hash);
fetch(url)
.then(
res =>
new Promise((resolve, reject) => {
const dest = fs.createWriteStream(filePath, {'Content-Type': 'image/x-icon'});
res.body.pipe(dest);
res.body.on("end", () => {
resolve({fileName, url});
});
dest.on("error", () => {
res.status(500).send(false);
reject('No favicon found');
});
})
);
downloadFile(url, filePath);
} else {
reject('Not a valid URL');
res.send(false);
res.send(hash);
}
});
});
......@@ -124,15 +105,12 @@ router.get('/', function(req, res) {
});
router.use(function(req, res) {
console.error('404 req: %s (%s)', req.url);
res.status(404).send('404: Page not Found');
});
router.use(function(error, req, res, next) {
console.error('500 req: %s (%s)', req.url);
// res.send('500: Internal Server Error', 500);
res.status(500).send('500: Internal Server Error');
});
......
......@@ -218,33 +218,6 @@
</div>
</fieldset>
<div class="sideMenuTitleToggleDiv ui-corner-top hover">
<i class="icon-right rotate unfold"></i>
<span class="translate"
data-content="Pétrolette">
Pétrolette
</span>
</div>
<fieldset class="menuForm">
<div class="flexBox">
<a href="https://framagit.org/yphil/petrolette"
class="grow ui-button ui-corner-all translate left"
data-title="Use the force - read the Source"
title="Use the force - read the Source"
data-content="Source">
Source
</a>
<a href="/about/javascript"
data-jslicense="1"
class="grow ui-button ui-corner-all translate"
data-title="JavaScript licencing information"
title="JavaScript licencing information"
data-content="License">
License
</a>
</div>
</fieldset>
<div class="sideMenuTitleToggleDiv ui-corner-top hover">
<i class="icon-right rotate unfold"></i>
<span class="translate"
......@@ -404,6 +377,33 @@
</div>
</fieldset>
<div class="sideMenuTitleToggleDiv ui-corner-top hover">
<i class="icon-right rotate unfold"></i>
<span class="translate"
data-content="Pétrolette">
Pétrolette
</span>
</div>
<fieldset class="menuForm">
<div class="flexBox">
<a href="https://framagit.org/yphil/petrolette"
class="grow ui-button ui-corner-all translate left"
data-title="Use the force - read the Source"
title="Use the force - read the Source"
data-content="Source">
Source
</a>
<a href="/about/javascript"
data-jslicense="1"
class="grow ui-button ui-corner-all translate"
data-title="JavaScript licencing information"
title="JavaScript licencing information"
data-content="License">
License
</a>
</div>
</fieldset>
<div class="sideMenuTitleToggleDiv ui-corner-top hover">
<i class="icon-right rotate unfold"></i>
<span class="translate"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment