From 468898690146164334f69563b747986ce759de5d Mon Sep 17 00:00:00 2001 From: Stanislav Khromov Date: Sun, 9 Oct 2022 04:07:45 +0200 Subject: [PATCH] Resolve favicon locally (WIP) --- components/common/Favicon.js | 21 ++------ components/metrics/WebsiteChart.js | 4 +- components/metrics/WebsiteHeader.js | 6 +-- components/pages/TestConsole.js | 2 +- components/pages/WebsiteDetails.js | 2 +- components/pages/WebsiteList.js | 4 +- components/settings/WebsiteSettings.js | 4 +- .../04_add_favicon_column/migration.sql | 2 + db/postgresql/schema.prisma | 1 + package.json | 1 + pages/api/website/index.js | 28 ++++++++++- public/default-favicon.png | Bin 0 -> 1478 bytes yarn.lock | 46 ++++++++++++++++++ 13 files changed, 91 insertions(+), 30 deletions(-) create mode 100644 db/postgresql/migrations/04_add_favicon_column/migration.sql create mode 100644 public/default-favicon.png diff --git a/components/common/Favicon.js b/components/common/Favicon.js index d72cd3c7..00376b05 100644 --- a/components/common/Favicon.js +++ b/components/common/Favicon.js @@ -2,27 +2,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import styles from './Favicon.module.css'; -function getHostName(url) { - const match = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:/\n?=]+)/im); - return match && match.length > 1 ? match[1] : null; -} +function Favicon({ url, ...props }) { + const faviconUrl = url ? url : '/default-favicon.png'; -function Favicon({ domain, ...props }) { - const hostName = domain ? getHostName(domain) : null; - - return hostName ? ( - - ) : null; + return ; } Favicon.propTypes = { - domain: PropTypes.string, + url: PropTypes.string, }; export default Favicon; diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js index fd74538f..5e3c09e6 100644 --- a/components/metrics/WebsiteChart.js +++ b/components/metrics/WebsiteChart.js @@ -18,7 +18,7 @@ import styles from './WebsiteChart.module.css'; export default function WebsiteChart({ websiteId, title, - domain, + favicon, stickyHeader = false, showLink = false, showChart = true, @@ -81,7 +81,7 @@ export default function WebsiteChart({ return (
- +
- + ) : ( <> - + {title} ); diff --git a/components/pages/TestConsole.js b/components/pages/TestConsole.js index efeb0264..1f36c0b5 100644 --- a/components/pages/TestConsole.js +++ b/components/pages/TestConsole.js @@ -106,7 +106,7 @@ export default function TestConsole() { Events diff --git a/components/pages/WebsiteDetails.js b/components/pages/WebsiteDetails.js index 78c5f752..a35d02a0 100644 --- a/components/pages/WebsiteDetails.js +++ b/components/pages/WebsiteDetails.js @@ -141,7 +141,7 @@ export default function WebsiteDetails({ websiteId }) { - {ordered.map(({ website_id, name, domain }, index) => + {ordered.map(({ website_id, name, favicon }, index) => index < limit ? (
diff --git a/components/settings/WebsiteSettings.js b/components/settings/WebsiteSettings.js index 451be47f..22dfc1ad 100644 --- a/components/settings/WebsiteSettings.js +++ b/components/settings/WebsiteSettings.js @@ -83,13 +83,13 @@ export default function WebsiteSettings() { ); - const DetailsLink = ({ website_id, name, domain }) => ( + const DetailsLink = ({ website_id, name, favicon }) => ( - + {name} ); diff --git a/db/postgresql/migrations/04_add_favicon_column/migration.sql b/db/postgresql/migrations/04_add_favicon_column/migration.sql new file mode 100644 index 00000000..a1f113ca --- /dev/null +++ b/db/postgresql/migrations/04_add_favicon_column/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "website" ADD COLUMN "favicon" VARCHAR(500); diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma index a76a3da4..7c661d2b 100644 --- a/db/postgresql/schema.prisma +++ b/db/postgresql/schema.prisma @@ -85,6 +85,7 @@ model website { domain String? @db.VarChar(500) share_id String? @unique @db.VarChar(64) created_at DateTime? @default(now()) @db.Timestamptz(6) + favicon String? @db.VarChar(500) account account @relation(fields: [user_id], references: [user_id]) event event[] pageview pageview[] diff --git a/package.json b/package.json index be7fd7d1..0019e2e0 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "del": "^6.0.0", "detect-browser": "^5.2.0", "dotenv": "^10.0.0", + "favecon": "^1.0.2", "formik": "^2.2.9", "fs-extra": "^10.0.1", "immer": "^9.0.12", diff --git a/pages/api/website/index.js b/pages/api/website/index.js index ac02de85..9f57b9a8 100644 --- a/pages/api/website/index.js +++ b/pages/api/website/index.js @@ -2,6 +2,22 @@ import { ok, unauthorized, methodNotAllowed, getRandomChars } from 'next-basics' import { updateWebsite, createWebsite, getWebsiteById } from 'queries'; import { useAuth } from 'lib/middleware'; import { uuid } from 'lib/crypto'; +import favecon from 'favecon'; + +const getFavicon = async domain => { + try { + const icons = await favecon.getIcons(`https://${domain}`); + + if (icons.length && icons.length > 0) { + return icons[0]?.href; + } + } catch (e) { + // eslint-disable-next-line no-console + console.log(`Could not fetch favicon for domain ${domain}`, e); + } + + return null; +}; export default async (req, res) => { await useAuth(req, res); @@ -13,6 +29,8 @@ export default async (req, res) => { const { name, domain, owner } = req.body; const website_owner = parseInt(owner); + const favicon = await getFavicon(domain); + if (website_id) { const website = await getWebsiteById(website_id); @@ -28,13 +46,19 @@ export default async (req, res) => { share_id = null; } - await updateWebsite(website_id, { name, domain, share_id, user_id: website_owner }); + await updateWebsite(website_id, { name, domain, share_id, user_id: website_owner, favicon }); return ok(res); } else { const website_uuid = uuid(); const share_id = enable_share_url ? getRandomChars(8) : null; - const website = await createWebsite(website_owner, { website_uuid, name, domain, share_id }); + const website = await createWebsite(website_owner, { + website_uuid, + name, + domain, + share_id, + favicon, + }); return ok(res, website); } diff --git a/public/default-favicon.png b/public/default-favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..b018b02cce8f6f854ecf9721e172411914a05f8c GIT binary patch literal 1478 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%o>>?5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s0*lEl2^R8JRMC7?NanVBh8PEHngVhK8;tPHu)4PHs*X zrUn+KmPTf7FugAM$)&lec_lEtDG0q5IQ4=OL~a4lW|!2W%(B!Jx1#)91+d4hGI6`b z6sLJmy(zfeVun+%KF~4xpy)-4ZkP}-4S|^O#0%uWlYeR+FwGYM6Sv{2XCVv>OeUT# zjv*DduFSISJ7gfxx>roLxQj5;vQJ9{ojRV>eoL|O{8MT-pS3YbR{Kwn!K#pi6sw{?GE0woT#lCYI=<;~J=s zS4L;sOkc#*5NQAHM6u(PD0iFh8V${Tb2*mFR!OiF9O%~GV&*HbNila>!o9W|<*WgX zmzQbI4Uu-ZGOf6=v>?1Pa-!8~PG+6EA(PH=d51oaw|dv}+9hVIkb29r#3}c#v3jgM zvPI_7OSP3}=g&wxRI*RLLASZ`AkW6Fj3R;uc0V)c<49#lT6Ai2rslyb0uL;wx$KnZ zN>Y70$;7$izhp;^c|wbTpm>9RhX+^OM&|=hdg=7ySO&m4EoY%FSf=55Tpfc+q2eMwEV(f^M*40$dLnXD|=h4;$3mZVT6V z(_&Xj