Skip to content

Commit 764c38c

Browse files
committed
Add logo API comparison page
1 parent b9441f3 commit 764c38c

File tree

7 files changed

+220
-20
lines changed

7 files changed

+220
-20
lines changed

.github/workflows/deploy-heroku.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
uses: actions/checkout@v4
2121

2222
- name: deploy to Heroku
23-
run: git push -f https://heroku:${{ secrets.HEROKU_API_KEY }}@git.heroku.com/${{ secrets.HEROKU_APP_NAME }}.git origin/main:main
23+
run: git push https://heroku:${{ secrets.HEROKU_API_KEY }}@git.heroku.com/${{ secrets.HEROKU_APP_NAME }}.git origin/main:main
2424

2525
- name: update env vars
2626
run: heroku config:set --app ${{ secrets.HEROKU_APP_NAME }} "COMMIT=${GITHUB_SHA:0:7}" "LASTMOD=$(date -u +%Y-%m-%dT%H:%M:%SZ)"

src/config.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ const config = convict({
2323
format: String,
2424
sensitive: true,
2525
},
26+
brandfetchApiKey: {
27+
default: null,
28+
doc: 'API key for brandfetch.com',
29+
env: 'BRANDFETCH_API_KEY',
30+
format: String,
31+
sensitive: true,
32+
},
2633
buildId: {
2734
default: process.env.COMMIT || `local@${new Date().getTime()}`,
2835
doc: 'Unique Build ID (commit hash or timestamp) for cache busting',
@@ -118,6 +125,13 @@ const config = convict({
118125
env: 'LOG_LEVEL',
119126
format: ['fatal', 'error', 'warn', 'info', 'debug', 'trace'],
120127
},
128+
logodevToken: {
129+
default: null,
130+
doc: 'API token for logodev.com',
131+
env: 'LOGODEV_TOKEN',
132+
format: String,
133+
sensitive: true,
134+
},
121135
maxmindUrlBase: {
122136
default: null,
123137
doc: 'Base URL for encrypted MaxMind data files',

src/routers/infoRouter.ts

Lines changed: 115 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,134 @@
11
//import { promises as fsPromises } from 'fs';
2-
import Router from '@koa/router';
2+
import handlebars from "handlebars";
3+
import Router from "@koa/router";
34
//import * as path from 'path';
45

6+
import * as util from "../util.js";
7+
import config from "../config.js";
8+
59
const infoRouter = new Router();
610

7-
infoRouter.get('/info/', async (ctx) => {
8-
ctx.redirect('/info/index.html');
11+
infoRouter.get("/info/", async (ctx) => {
12+
ctx.redirect("/info/index.html");
13+
});
14+
15+
infoRouter.get("/info/index.html", async (ctx) => {
16+
ctx.redirect("/tools.html#info");
917
});
1018

11-
infoRouter.get('/info/index.html', async (ctx) => {
12-
ctx.redirect('/tools.html#info');
19+
infoRouter.get("/info/glossary.html", async (ctx: any) => {
20+
ctx.body = await ctx.render("info/glossary.hbs", {
21+
title: "Glossary",
22+
});
1323
});
1424

15-
infoRouter.get('/info/glossary.html', async (ctx:any) => {
16-
ctx.body = await ctx.render('info/glossary.hbs', {
17-
title: 'Glossary',
18-
});
25+
infoRouter.get("/info/steps.html", async (ctx: any) => {
26+
ctx.body = await ctx.render("info/steps.hbs", {
27+
title: "Steps to get a web page",
28+
});
29+
});
30+
31+
infoRouter.get("/info/random-logo.html", async (ctx: any) => {
32+
33+
const resp = await fetch("https://siterank.redirect2.me/api/random.json");
34+
const json = await resp.json();
35+
const domain = json.domain;
36+
ctx.redirect(`/info/logo-api-comparison.html?domain=${encodeURIComponent(domain)}`);
1937
});
2038

21-
infoRouter.get('/info/steps.html', async (ctx:any) => {
22-
ctx.body = await ctx.render('info/steps.hbs', {
23-
title: 'Steps to get a web page',
24-
});
39+
type LogoResult = {
40+
provider: string;
41+
providerUrl: string;
42+
logoUrl: string;
43+
}
44+
45+
infoRouter.get("/info/logo-api-comparison.html", async (ctx: any) => {
46+
let domain = ctx.request.query.domain;
47+
let loadLogos = true;
48+
if (!domain) {
49+
ctx.flash("info", new handlebars.SafeString(`This is a comparison of <a href="https://andrew.marcuse.info/blog/2024/2024-12-11-logo-apis.html">Logo APIs</a> that have free/public tiers.`));
50+
loadLogos = false;
51+
}
52+
53+
if (loadLogos && !util.hasValidPublicSuffix(domain)) {
54+
try {
55+
const url = new URL(domain);
56+
domain = url.hostname;
57+
} catch (err) {
58+
ctx.flash(
59+
"error",
60+
`Unable to extract a domain from "${handlebars.escapeExpression(
61+
domain
62+
)}"!`
63+
);
64+
loadLogos = false;
65+
}
66+
if (!util.hasValidPublicSuffix(domain)) {
67+
ctx.flash(
68+
"error",
69+
`${handlebars.escapeExpression(
70+
domain
71+
)} is not a valid domain!`
72+
);
73+
loadLogos = false;
74+
}
75+
}
76+
77+
const results: LogoResult[] = [];
78+
if (loadLogos) {
79+
if (config.get("brandfetchApiKey")) {
80+
results.push({
81+
provider: "Brandfetch",
82+
providerUrl: "https://brandfetch.com/developers",
83+
logoUrl: `https://cdn.brandfetch.io/${domain}/w/256/h/256?c=${config.get(
84+
"brandfetchApiKey"
85+
)}`,
86+
});
87+
}
88+
results.push({
89+
provider: "Clearbit",
90+
providerUrl: "https://clearbit.com/",
91+
logoUrl: `https://logo.clearbit.com/${domain}`,
92+
});
93+
results.push({
94+
provider: "CUFinder",
95+
providerUrl: "https://cufinder.io/enrichment-engine/logo-api",
96+
logoUrl: `https://api.cufinder.io/logo/${domain}`,
97+
});
98+
if (config.get("logodevToken")) {
99+
results.push({
100+
provider: "logo.dev",
101+
providerUrl: "https://logo.dev",
102+
logoUrl: `https://img.logo.dev/${domain}?token=${config.get(
103+
"logodevToken"
104+
)}`,
105+
});
106+
}
107+
results.push({
108+
provider: "Lucky Logo",
109+
providerUrl: "https://lucky.logosear.ch/",
110+
logoUrl: `https://lucky.logosear.ch/logo?url=${domain}`,
111+
});
112+
results.push({
113+
provider: "Uplead",
114+
providerUrl: "https://www.uplead.com/free-company-logo-api/",
115+
logoUrl: `https://logo.uplead.com/${domain}`,
116+
});
117+
}
118+
119+
ctx.body = await ctx.render("info/logo-api-comparison.hbs", {
120+
title: "Logo API comparision",
121+
domain,
122+
results,
123+
});
25124
});
26125

27-
function getUrls():string[] {
126+
function getUrls(): string[] {
28127
return [
29128
"/info/glossary.html",
30129
"/info/steps.html",
130+
"/info/logo-api-comparison.html",
31131
];
32132
}
33133

34-
export {
35-
getUrls,
36-
infoRouter
37-
}
134+
export { getUrls, infoRouter };

static/images/broken-image.svg

Lines changed: 35 additions & 0 deletions
Loading

static/images/memphis-mini.png

39.9 KB
Loading

views/info/logo-api-comparison.hbs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{{>above}}
2+
3+
<script>
4+
function onImageError(event) {
5+
event.target.src = '/images/broken-image.svg';
6+
}
7+
</script>
8+
9+
{{#if results}}
10+
<h2>Results for {{domain}}</h2>
11+
12+
<div class="row mt-3">
13+
<div class="col d-flex flex-wrap justify-content-center">
14+
{{#each results}}
15+
<div class="card m-3 d-inline-block" style="width:18rem;background-image:url('/images/memphis-mini.png');">
16+
<img src="{{logoUrl}}" class="card-img-top p-3" alt="logo for {{../domain}} from {{provider}}" onerror="onImageError(event)">
17+
<h5 class="card-footer border-top bg-body-tertiary mb-0"><a href="{{providerUrl}}">{{provider}}</a></h5>
18+
</div>
19+
{{/each}}
20+
</div>
21+
</div>
22+
23+
<hr>
24+
<h2>Try another</h2>
25+
{{/if}}
26+
27+
<div class="container d-flex flex-column">
28+
<div class="row align-items-center justify-content-center no-gutters">
29+
<div class="col-12 col-md-5 col-lg-4 py-8 py-md-11">
30+
<form action="logo-api-comparison.html" class="mb-6" method="GET">
31+
<div class="mb-3">
32+
<label class="form-label" for="domain">
33+
Domain name
34+
</label>
35+
<input type="text" class="form-control" id="domain" name="domain" placeholder=""
36+
value="{{domain}}" />
37+
</div>
38+
<div class="d-grid mb-3">
39+
<button class="btn btn-primary" type="submit">
40+
Test
41+
</button>
42+
</div>
43+
<div class="d-grid mb-3">
44+
<a class="btn btn-outline-primary" href="random-logo.html">
45+
Try a random domain
46+
</a>
47+
</div>
48+
</form>
49+
</div>
50+
</div> <!-- / .row -->
51+
</div> <!-- / .container -->
52+
53+
{{>below}}

views/tools.hbs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<h2 id="experimental">Experimental</h2>
6060
<ul>
6161
<li id="crypto"><a href="/crypto/hash.html">Hash bytes/strings/files with multiple algorithms</a></li>
62-
<li id="datagen"><a href="/datagen/haikunator.html">Heroku-style (Haikunator) names</a> </li>
62+
<li id="datagen"><a href="/datagen/haikunator.html">Heroku-style (Haikunator) names</a></li>
63+
<li id="logoapi"><a href="/info/logo-api-comparison.html">Logo API comparision</a></li>
6364
</ul>
6465
{{>below}}

0 commit comments

Comments
 (0)