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:
parent
ac40723c16
commit
5c4183b717
9 changed files with 23484 additions and 8 deletions
15
README.md
15
README.md
|
@ -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`).
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
"database": ".db",
|
"database": ".db",
|
||||||
"rcFile": "./.codexdocsrc",
|
"rcFile": "./.codexdocsrc",
|
||||||
"uploads": "public/uploads",
|
"uploads": "public/uploads",
|
||||||
"secret": "iamasecretstring"
|
"secret": "iamasecretstring",
|
||||||
|
"favicon": ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
"database": ".db",
|
"database": ".db",
|
||||||
"rcFile": "./.codexdocsrc",
|
"rcFile": "./.codexdocsrc",
|
||||||
"uploads": "/uploads",
|
"uploads": "/uploads",
|
||||||
"secret": "iamasecretstring"
|
"secret": "iamasecretstring",
|
||||||
|
"favicon": ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
23343
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||||
|
|
||||||
|
|
85
src/backend/utils/downloadFavicon.ts
Normal file
85
src/backend/utils/downloadFavicon.ts
Normal 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;
|
||||||
|
}
|
|
@ -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 = {
|
||||||
|
|
|
@ -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">
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue