forked from selfhst/icons
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
88 lines (73 loc) · 2.55 KB
/
server.js
File metadata and controls
88 lines (73 loc) · 2.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import fs from 'fs'
import express from 'express'
import path from 'path'
const app = express()
const PORT = 4050
const APP_DIR = process.cwd()
async function readFile(filePath, res) {
const readStream = fs.createReadStream(filePath);
readStream.on("open", () => {
res.type(path.extname(filePath).slice(1));
readStream.pipe(res);
});
readStream.on("error", () => {
res.set("Content-Type", "text/plain");
res.status(404).end("Not found");
});
}
app.get('/', (_req, res) => {
res.send('Self-hosted icon server');
});
app.get('/*', async (req, res) => {
const urlPath = req.path;
const extMatch = urlPath.match(/\.(\w+)$/);
if (!extMatch)
return res.status(404).send('File extension missing');
const ext = extMatch[1].toLowerCase();
if (!['png', 'webp', 'svg'].includes(ext))
return res.status(404).send('Format not supported');
const filename = urlPath.slice(1);
const lowerFilename = filename.toLowerCase();
const isSuffix = lowerFilename.endsWith('-light.svg') || lowerFilename.endsWith('-dark.svg');
if (isSuffix) {
return readFile(`${APP_DIR}/svg/${filename}`, res);
}
let filePath;
if (ext === 'png') {
filePath = `${APP_DIR}/png/${filename}`;
} else if (ext === 'webp') {
filePath = `${APP_DIR}/webp/${filename}`;
} else if (ext === 'svg') {
filePath = `${APP_DIR}/svg/${filename}`;
} else {
filePath = null;
}
const hasColor = !!req.query['color'] && req.query['color'].trim() !== '';
if (ext === 'svg') {
if (hasColor) {
const baseName = filename.replace(/\.(png|webp|svg)$/, '');
const baseFile = `${APP_DIR}/svg/${baseName}-light.svg`;
if (fs.existsSync(baseFile)) {
let svgContent = fs.readFileSync(baseFile, { encoding: 'utf-8'});
const color = req.query['color'].startsWith('#') ? req.query['color'] : `#${req.query['color']}`;
svgContent = svgContent
.replace(/style="[^"]*fill:\s*#fff[^"]*"/gi, (match) => {
console.log('Replacing style fill:', match);
return match.replace(/fill:\s*#fff/gi, `fill:${color}`);
})
.replace(/fill="#fff"/gi, `fill="${color}"`);
return res.type('image/svg+xml').send(svgContent);
} else {
return res.status(404).end("Not found");
}
} else {
return readFile(filePath, res);
}
} else {
// PNG/WebP: serve directly
return readFile(filePath, res);
}
});
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});