1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-07-19 13:19:42 +02:00

Added ability to use custom favicon (#202)

* Added ability to change favicon in config

* Turned back version of icon in index.twig

* Added opportunity to upload favicon and route to get saved favicon

* Removed favicon from .codexdocsrc.sample

* Added docs to favicon route

* Replaced uploadFavicon to initiating /favicon route, updated function, added catching errors from uploadFile

* Updated Readme, added info about setting up app

* Updated Readme.md

* Some changes

* Favicon data saves to app.locals, replaced uploading favicon to app.ts

* Changed naming in config, from faviconURL to favicon, changed using app.locals variables

* Renamed uploadFavicon to downLoadFavicon, removed log in locals.ts

* Renamed favicon variable in app.ts

* Added checking favicon before uploading function, removed passing locals to views

* Added timeout for uploading favicon request and writeFileSync changed to writeFile

* Removed passing favicon locals and turned back removed variables

* Turned back variables

* Fixed duplicating os.tmpdir

* Fixed braces in objects, added new lines

* Added default favicon path, if favicon does not exists in config

* Updated docs, fixed using local favicon
This commit is contained in:
slaveeks 2022-07-10 15:21:32 +03:00 committed by GitHub
parent ac40723c16
commit 5c4183b717
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 23484 additions and 8 deletions

View file

@ -83,6 +83,21 @@ yarn lint
yarn test yarn test
``` ```
### Setup
You can configure application using configs in <code>/config</code> directory.
| Property | Role |
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <code>port</code> | to set port of application |
| <code>database</code> | to name directory with data |
| <code>rcFile</code> | to set destination of codexdocsrc config file |
| <code>uploads</code> | to set destination of directory to save uploads |
| <code>secret</code> | to set secret |
| <code>favicon</code> | to set url or favicon path (favicon need to be in /public directory), like `/myFavicon.png`, to get favicon. Server uploads file by url and saves it to temporary directory. And you can get favicon by /favicon static route of application |
You can configure application using configs in <code>/config</code> directory.
### Authentication ### Authentication
To manage pages you need to authorize (available on `/auth`). To manage pages you need to authorize (available on `/auth`).

View file

@ -3,5 +3,6 @@
"database": ".db", "database": ".db",
"rcFile": "./.codexdocsrc", "rcFile": "./.codexdocsrc",
"uploads": "public/uploads", "uploads": "public/uploads",
"secret": "iamasecretstring" "secret": "iamasecretstring",
"favicon": ""
} }

View file

@ -3,5 +3,6 @@
"database": ".db", "database": ".db",
"rcFile": "./.codexdocsrc", "rcFile": "./.codexdocsrc",
"uploads": "/uploads", "uploads": "/uploads",
"secret": "iamasecretstring" "secret": "iamasecretstring",
"favicon": ""
} }

View file

@ -3,5 +3,6 @@
"database": ".testdb", "database": ".testdb",
"rcFile": "./src/test/.codexdocsrc", "rcFile": "./src/test/.codexdocsrc",
"uploads": "public/uploads_test", "uploads": "public/uploads_test",
"secret": "iamasecretstring" "secret": "iamasecretstring",
"favicon": ""
} }

23343
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,23 +7,51 @@ import routes from './routes';
import HttpException from './exceptions/httpException'; import HttpException from './exceptions/httpException';
import * as dotenv from 'dotenv'; import * as dotenv from 'dotenv';
import config from 'config'; import config from 'config';
import os from 'os';
import appConfig from 'config';
import { downloadFavicon, FaviconData } from './utils/downloadFavicon';
dotenv.config(); dotenv.config();
const app = express(); const app = express();
const localConfig = rcParser.getConfiguration(); const localConfig = rcParser.getConfiguration();
// Get url to upload favicon from config
const favicon: string = appConfig.get('favicon');
app.locals.config = localConfig; app.locals.config = localConfig;
// view engine setup // view engine setup
app.set('views', path.join(__dirname, './', 'views')); app.set('views', path.join(__dirname, './', 'views'));
app.set('view engine', 'twig'); app.set('view engine', 'twig');
require('./utils/twig'); require('./utils/twig');
const downloadedFaviconFolder = os.tmpdir();
// Check if favicon is not empty
if (favicon) {
// Upload favicon by url, it's path on server is '/temp/favicon.{format}'
downloadFavicon(favicon, downloadedFaviconFolder).then((res) => {
app.locals.favicon = res;
console.log('Favicon successfully uploaded');
})
.catch( (err) => {
console.log(err);
console.log('Favicon has not uploaded');
});
} else {
console.log('Favicon is empty, using default path');
app.locals.favicon = {
destination: '/favicon.png',
type: 'image/png',
} as FaviconData;
}
app.use(morgan('dev')); app.use(morgan('dev'));
app.use(express.json()); app.use(express.json());
app.use(express.urlencoded({ extended: true })); app.use(express.urlencoded({ extended: true }));
app.use(cookieParser()); app.use(cookieParser());
app.use(express.static(path.join(__dirname, '../../public'))); app.use(express.static(path.join(__dirname, '../../public')));
app.use('/uploads', express.static(config.get('uploads'))); app.use('/uploads', express.static(config.get('uploads')));
app.use('/favicon', express.static(downloadedFaviconFolder));
app.use('/', routes); app.use('/', routes);

View file

@ -0,0 +1,85 @@
import path from 'path';
import fs from 'fs';
import fetch from 'node-fetch';
/**
* Uploaded favicon data
*/
export interface FaviconData {
// Uploaded favicon path
destination: string;
// File type
type: string;
}
// Initiate controller for aborting request
const controller = new AbortController();
/**
* Check if string is url
*
* @param str - string to check
*/
function checkIsUrl(str: string): boolean {
const re = new RegExp('https?://');
return re.test(str);
}
/**
* Upload favicon by url, or get it by path
*
* @param destination - url or path of favicon
* @param faviconFolder - folder to save favicon
* @returns { Promise<FaviconData> } - Promise with data about favicon
*/
export async function downloadFavicon(destination: string, faviconFolder: string): Promise<FaviconData> {
// Check of destination is empty
if (!destination) {
throw Error('Favicon destination is empty');
}
// Get file name by destination
const filename = destination.substring(destination.lastIndexOf('/')+1);
// Get file format
const format = filename.split('.')[1];
// Check if string is url
if (!checkIsUrl(destination)) {
return {
destination: `/${filename}`,
type: `image/${format}`,
} as FaviconData;
}
// Create timeout to abort request
const timeoutId = setTimeout(() => {
controller.abort();
console.log('Favicon request has timed out.');
}, 5000);
// Make get request to url
const res = await fetch(destination, { signal: controller.signal });
// Get buffer data from response
const fileData = await res.buffer();
// Clear timeout, if data was got
clearTimeout(timeoutId);
// Get file path in temporary directory
const filePath = path.join(faviconFolder, `favicon.${format}`);
// Save file
await fs.writeFile(filePath, fileData, (err) => {
if (err) {
console.log(err);
}
});
return {
destination: `/favicon/favicon.${format}`,
type: `image/${format}`,
} as FaviconData;
}

View file

@ -7,7 +7,7 @@
<meta property="og:title" content="{{ page.title | striptags }}" /> <meta property="og:title" content="{{ page.title | striptags }}" />
<meta property="article:modified_time" content="{{ (page.body.time / 1000) | date("c") }}" /> <meta property="article:modified_time" content="{{ (page.body.time / 1000) | date("c") }}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="icon" type="image/png" href="/favicon.png"> <link rel="icon" type="{{ favicon.type }}" href="{{ favicon.destination }}">
</head> </head>
<script> <script>
window.config = { window.config = {

View file

@ -4,12 +4,14 @@
<title>{{ config.title }}</title> <title>{{ config.title }}</title>
<link rel="stylesheet" href="/dist/main.css" /> <link rel="stylesheet" href="/dist/main.css" />
<link rel="preload" href="{{ config.landingFrameSrc }}" as="document"> <link rel="preload" href="{{ config.landingFrameSrc }}" as="document">
<link rel="icon" type="image/png" href="/favicon.png?v=2"> <link rel="icon" type="{{ favicon.type }}" href="{{ favicon.destination }}">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta property="og:title" content="{{ config.title }}" /> <meta property="og:title" content="{{ config.title }}" />
<meta property="og:site_name" content="{{ config.title }}" /> <meta property="og:site_name" content="{{ config.title }}" />
<meta name="description" property="og:description" content="{{ config.description }}"> <meta name="description" property="og:description" content="{{ config.description }}">
</head> </head>
<script>
</script>
<body class="landing-body"> <body class="landing-body">
{% include "components/header.twig" %} {% include "components/header.twig" %}
<div class="landing-loader" id="frame-loader"> <div class="landing-loader" id="frame-loader">