Added archive support! (Beta) + UI change
This commit is contained in:
parent
f3f36a9b96
commit
d010e351b5
|
@ -0,0 +1,35 @@
|
|||
const puppeteer = require('puppeteer');
|
||||
const { PuppeteerBlocker } = require('@cliqz/adblocker-puppeteer');
|
||||
const fetch = require('cross-fetch');
|
||||
const config = require('../../src/config.json');
|
||||
const fs = require('fs');
|
||||
|
||||
const screenshotDirectory = config.api.storage_location + '/Webmarker/screenshot\'s/';
|
||||
const pdfDirectory = config.api.storage_location + '/Webmarker/pdf\'s/';
|
||||
|
||||
if (!fs.existsSync(screenshotDirectory)){
|
||||
fs.mkdirSync(screenshotDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(pdfDirectory)){
|
||||
fs.mkdirSync(pdfDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
module.exports = async (link, id) => {
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
|
||||
await PuppeteerBlocker.fromPrebuiltAdsAndTracking(fetch).then((blocker) => {
|
||||
blocker.enableBlockingInPage(page);
|
||||
});
|
||||
|
||||
await page.goto(link, { waitUntil: 'load', timeout: 0 });
|
||||
|
||||
const title = await page.title();
|
||||
await page.screenshot({ path: screenshotDirectory + id + '.png', fullPage: true});
|
||||
await page.pdf({ path: pdfDirectory + id + '.pdf', format: 'a4' });
|
||||
|
||||
await browser.close();
|
||||
|
||||
return title;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -9,12 +9,13 @@
|
|||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"@cliqz/adblocker-puppeteer": "^1.23.8",
|
||||
"cors": "^2.8.5",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"express": "^4.17.3",
|
||||
"mongodb": "^4.5.0",
|
||||
"phantom": "^6.3.0",
|
||||
"url-parse": "^1.5.10"
|
||||
"puppeteer": "^14.1.1",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.15"
|
||||
|
|
|
@ -1,58 +1,48 @@
|
|||
const express = require('express');
|
||||
const app = express();
|
||||
const { MongoClient, ObjectId } = require('mongodb');
|
||||
const request = require('request');
|
||||
const cheerio = require('cheerio');
|
||||
const URL = require('url-parse');
|
||||
const { MongoClient } = require('mongodb');
|
||||
const cors = require('cors');
|
||||
const config = require('../src/config.json')
|
||||
const config = require('../src/config.json');
|
||||
const getData = require('./modules/getData.js')
|
||||
|
||||
const port = config.server.port;
|
||||
const port = config.api.port;
|
||||
|
||||
const uri = config.server.mongodb_full_address;
|
||||
const database = config.server.database_name;
|
||||
const collection = config.server.collection_name;
|
||||
const URI = config.api.mongodb_URI;
|
||||
const database = config.api.database_name;
|
||||
const collection = config.api.collection_name;
|
||||
|
||||
const client = new MongoClient(uri);
|
||||
const client = new MongoClient(URI);
|
||||
|
||||
app.use(cors());
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.get('/get', async (req, res) => {
|
||||
app.get('/api', async (req, res) => {
|
||||
const data = await getDoc();
|
||||
res.send(data);
|
||||
});
|
||||
|
||||
app.post('/post', (req, res) => {
|
||||
let title;
|
||||
app.post('/api', async (req, res) => {
|
||||
const pageToVisit = req.body.link;
|
||||
request(pageToVisit, (error, response, body) => {
|
||||
try {
|
||||
if(response.statusCode === 200) {
|
||||
// Parse the document body
|
||||
const $ = cheerio.load(body);
|
||||
|
||||
req.body.title = $('title').text();
|
||||
insertDoc(req.body);
|
||||
} else {
|
||||
req.body.title = null;
|
||||
insertDoc(req.body);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
req.body.title = null;
|
||||
insertDoc(req.body);
|
||||
}
|
||||
});
|
||||
try {
|
||||
const dataResult = await getData(pageToVisit, req.body._id);
|
||||
req.body.title = dataResult;
|
||||
insertDoc(req.body);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
insertDoc(req.body);
|
||||
} finally {
|
||||
res.send('DONE!');
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/delete', async (req, res) => {
|
||||
app.delete('/api', async (req, res) => {
|
||||
const id = req.body.id.toString();
|
||||
|
||||
await deleteDoc(id);
|
||||
|
||||
res.send(`Bookmark with ObjectId "${id}" deleted.`);
|
||||
res.send(`Bookmark with _id:${id} deleted.`);
|
||||
});
|
||||
|
||||
async function insertDoc(doc) {
|
||||
|
@ -85,7 +75,7 @@ async function deleteDoc(doc) {
|
|||
try {
|
||||
const db = client.db(database);
|
||||
const list = db.collection(collection);
|
||||
const result = await list.deleteOne({"_id": ObjectId(doc)});
|
||||
const result = await list.deleteOne({"_id": doc});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^12.1.4",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"nanoid": "^3.3.4",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-scripts": "5.0.0",
|
||||
|
@ -8098,9 +8099,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
|
||||
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
|
@ -11013,9 +11014,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz",
|
||||
"integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==",
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
|
@ -21721,9 +21722,9 @@
|
|||
}
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
|
||||
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"requires": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
|
@ -23813,9 +23814,9 @@
|
|||
}
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz",
|
||||
"integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA=="
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^12.1.4",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"nanoid": "^3.3.4",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-scripts": "5.0.0",
|
||||
|
|
22
src/App.js
22
src/App.js
|
@ -21,17 +21,17 @@ function App() {
|
|||
return (e.name.toLowerCase().includes(searchQuery.toLowerCase()) || e.title.toLowerCase().includes(searchQuery.toLowerCase()) || e.tag.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
const address = config.client.api_address + ":" + config.server.port;
|
||||
const res = await fetch(address + '/get');
|
||||
const resJSON = await res.json();
|
||||
const Data = resJSON.sort((a, b) => { return b-a });
|
||||
setData(Data);
|
||||
}
|
||||
async function fetchData() {
|
||||
const address = config.api.address + ":" + config.api.port;
|
||||
const res = await fetch(address + '/api');
|
||||
const resJSON = await res.json();
|
||||
const Data = resJSON.sort((a, b) => { return b-a });
|
||||
setData(Data);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [data]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
|
@ -39,8 +39,8 @@ function App() {
|
|||
<input className="search" type="search" placeholder=" Search for Name / Title / Tag" onChange={search}/>
|
||||
<button className="add-btn" onClick={() => setIsAdding(true)}></button>
|
||||
</div>
|
||||
<List data={filteredData} />
|
||||
{isAdding ? <AddModal onExit={exitAdding} /> : null}
|
||||
<List data={filteredData} reFetch={fetchData} />
|
||||
{isAdding ? <AddModal onExit={exitAdding} reFetch={fetchData} /> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { useState } from 'react';
|
||||
import { nanoid } from 'nanoid'
|
||||
import '../styles/Modal.css';
|
||||
import config from '../config.json';
|
||||
|
||||
const AddModal = ({onExit}) => {
|
||||
const AddModal = ({onExit, reFetch}) => {
|
||||
const [name, setName] = useState('');
|
||||
const [link, setLink] = useState('');
|
||||
const [tag, setTag] = useState('');
|
||||
|
@ -33,16 +34,17 @@ const AddModal = ({onExit}) => {
|
|||
}
|
||||
|
||||
if(name !== '' && isValidHttpUrl(link) && tag !== '') {
|
||||
const address = config.client.api_address + ":" + config.server.port;
|
||||
fetch(address + "/post", {
|
||||
const address = config.api.address + ":" + config.api.port;
|
||||
fetch(address + "/api", {
|
||||
|
||||
// Adding method type
|
||||
method: "POST",
|
||||
|
||||
// Adding body or contents to send
|
||||
body: JSON.stringify({
|
||||
_id: nanoid(),
|
||||
name: name,
|
||||
title: "foo",
|
||||
title: null,
|
||||
link: link,
|
||||
tag: tag
|
||||
}),
|
||||
|
@ -51,7 +53,10 @@ const AddModal = ({onExit}) => {
|
|||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8"
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(res => res.text())
|
||||
.then(message => {console.log(message)})
|
||||
.then(() => reFetch());
|
||||
|
||||
onExit();
|
||||
} else if(name !== '' && link !== '' && tag !== '') {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import '../styles/List.css';
|
||||
import config from '../config.json';
|
||||
|
||||
const List = ({data}) => {
|
||||
const List = ({data, reFetch}) => {
|
||||
function deleteEntity(id) {
|
||||
const address = config.client.api_address + ":" + config.server.port;
|
||||
fetch(address + "/delete", {
|
||||
const address = config.api.address + ":" + config.api.port;
|
||||
fetch(address + "/api", {
|
||||
|
||||
// Adding method type
|
||||
method: "DELETE",
|
||||
|
@ -19,37 +19,32 @@ const List = ({data}) => {
|
|||
})
|
||||
.then(res => res.text())
|
||||
.then(message => {console.log(message)})
|
||||
.then(() => reFetch())
|
||||
}
|
||||
|
||||
return (
|
||||
<table className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className='number'>#</th>
|
||||
<th>Name</th>
|
||||
<th>Title</th>
|
||||
<th>Link</th>
|
||||
<th>Tag</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div className="list">
|
||||
{data.map((e, i) => {
|
||||
try {
|
||||
const url = new URL(e.link)
|
||||
return <tr key={i}>
|
||||
<td className='number'>{i + 1}</td>
|
||||
<td>{e.name}</td>
|
||||
<td>{e.title}</td>
|
||||
<td><a href={e.link}>{url.hostname}</a></td>
|
||||
<td>{e.tag}</td>
|
||||
<td className="delete" onClick={() => deleteEntity(e._id)}></td>
|
||||
</tr>
|
||||
const url = new URL(e.link);
|
||||
const favicon = 'http://www.google.com/s2/favicons?domain=' + url.hostname;
|
||||
return <div className="list-row">
|
||||
<div className="img-content-grp">
|
||||
<img src={favicon} />
|
||||
<div className="list-entity-content" key={i}>
|
||||
<div className='row-name'><span className="num">{i + 1}.</span> {e.name}</div>
|
||||
<div>{e.title}</div>
|
||||
<div><a href={e.link}>{url.hostname}</a></div>
|
||||
<div className="tag">{e.tag}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="delete" onClick={() => deleteEntity(e._id)}></div>
|
||||
</div>
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
console.log(e);
|
||||
}
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
{
|
||||
"client": {
|
||||
"api_address": "http://localhost"
|
||||
},
|
||||
"server": {
|
||||
"port": 5000,
|
||||
"mongodb_full_address": "mongodb://localhost:27017",
|
||||
"api": {
|
||||
"address": "http://localhost",
|
||||
"port": 5002,
|
||||
"mongodb_URI": "mongodb://localhost:27017",
|
||||
"database_name": "sample_db",
|
||||
"collection_name": "list"
|
||||
"collection_name": "list",
|
||||
"storage_location": "/home/danny/Documents"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
.App {
|
||||
min-height: 96vh;
|
||||
padding: 2vh;
|
||||
min-height: 100vh;
|
||||
background-color: #1f2c38;
|
||||
color: white;
|
||||
}
|
||||
|
@ -10,11 +9,15 @@
|
|||
}
|
||||
|
||||
.search {
|
||||
border-radius: 10px;
|
||||
margin: 20px 20px 0px 20px;
|
||||
padding: 10px;
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-size: 1.5rem;
|
||||
padding-left: 10px;
|
||||
border: none;
|
||||
width: 50%;
|
||||
width: 30%;
|
||||
min-width: 450px;
|
||||
color: white;
|
||||
background-color:#273949;
|
||||
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
|
||||
|
@ -29,6 +32,8 @@
|
|||
}
|
||||
|
||||
.add-btn {
|
||||
border-radius: 10px;
|
||||
margin: 20px 20px 0px 20px;
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
padding: 10px;
|
||||
font-size: 1.5rem;
|
||||
|
@ -38,7 +43,6 @@
|
|||
background-color:#273949;
|
||||
border: none;
|
||||
margin-left: auto;
|
||||
margin-right: 10px;
|
||||
transition: background-color 0.1s;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,47 @@
|
|||
.table {
|
||||
.list {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
padding-top: 20px;
|
||||
border-spacing: 10px 10px;
|
||||
}
|
||||
|
||||
.table td {
|
||||
font-size: 1.3rem;
|
||||
padding: 10px;
|
||||
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
|
||||
.list img {
|
||||
margin: 20px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 10px;
|
||||
box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 28px, rgba(0, 0, 0, 0.22) 0px 10px 10px;
|
||||
}
|
||||
|
||||
.table th {
|
||||
font-size: 1.6rem;
|
||||
.img-content-grp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table tbody tr:nth-of-type(2n-1) {
|
||||
.list-row {
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color:#273949;
|
||||
|
||||
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.table a {
|
||||
.list-entity-content {
|
||||
padding: 20px;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.list a {
|
||||
text-decoration: none;
|
||||
color: rgb(194, 193, 193);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.table a:hover {
|
||||
.list a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
@ -35,6 +50,11 @@
|
|||
cursor: pointer;
|
||||
transition: background-color 0.1s;
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
padding: 10px;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.delete:hover {
|
||||
|
@ -45,6 +65,19 @@
|
|||
box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px;
|
||||
}
|
||||
|
||||
.number {
|
||||
text-align: center;
|
||||
.row-name {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
margin: 10px;
|
||||
border: solid;
|
||||
border-width: 1px;
|
||||
width: fit-content;
|
||||
padding: 10px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 1rem;
|
||||
}
|
|
@ -5,4 +5,5 @@ body {
|
|||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: #1f2c38;
|
||||
}
|
||||
|
|
Ŝarĝante…
Reference in New Issue