feat: upload site favicon

pull/5698/head
Nicolas Giard 2 years ago
parent 33547ddce9
commit 9a4ac686ca
No known key found for this signature in database
GPG Key ID: 85061B8F9D55B7C8

@ -51,6 +51,14 @@ router.get('/_site/:siteId?/:resource', async (req, res, next) => {
}
break
}
case 'favicon': {
if (site.config.assets.favicon) {
res.sendFile(path.join(siteAssetsPath, `favicon-${site.id}.${site.config.assets.faviconExt}`))
} else {
res.sendFile(path.join(WIKI.ROOTPATH, 'assets/_assets/logo-wikijs.svg'))
}
break
}
default: {
return res.status(404).send('Invalid Site Resource')
}

@ -488,7 +488,8 @@ exports.up = async knex => {
assets: {
logo: false,
logoExt: 'svg',
favicon: false
favicon: false,
faviconExt: 'svg'
},
theme: {
dark: false,

@ -156,6 +156,9 @@ module.exports = {
if (!WIKI.extensions.ext.sharp.isInstalled) {
throw new Error('This feature requires the Sharp extension but it is not installed.')
}
if (!['.svg', '.png', '.jpg', 'webp', '.gif'].some(s => filename.endsWith(s))) {
throw new Error('Invalid File Extension. Must be svg, png, jpg, webp or gif.')
}
const destFormat = mimetype.startsWith('image/svg') ? 'svg' : 'png'
const destFolder = path.resolve(
process.cwd(),
@ -198,10 +201,52 @@ module.exports = {
* UPLOAD FAVICON
*/
async uploadSiteFavicon (obj, args) {
const { filename, mimetype, createReadStream } = await args.image
console.info(filename, mimetype)
return {
operation: graphHelper.generateSuccess('Site favicon uploaded successfully')
try {
const { filename, mimetype, createReadStream } = await args.image
WIKI.logger.info(`Processing site favicon ${filename} of type ${mimetype}...`)
if (!WIKI.extensions.ext.sharp.isInstalled) {
throw new Error('This feature requires the Sharp extension but it is not installed.')
}
if (!['.svg', '.png', '.jpg', '.webp', '.gif'].some(s => filename.endsWith(s))) {
throw new Error('Invalid File Extension. Must be svg, png, jpg, webp or gif.')
}
const destFormat = mimetype.startsWith('image/svg') ? 'svg' : 'png'
const destFolder = path.resolve(
process.cwd(),
WIKI.config.dataPath,
`assets`
)
const destPath = path.join(destFolder, `favicon-${args.id}.${destFormat}`)
await fs.ensureDir(destFolder)
// -> Resize
await WIKI.extensions.ext.sharp.resize({
format: destFormat,
inputStream: createReadStream(),
outputPath: destPath,
width: 64,
height: 64
})
// -> Save favicon meta to DB
const site = await WIKI.models.sites.query().findById(args.id)
if (!site.config.assets.favicon) {
site.config.assets.favicon = uuid()
}
site.config.assets.faviconExt = destFormat
await WIKI.models.sites.query().findById(args.id).patch({ config: site.config })
await WIKI.models.sites.reloadCache()
// -> Save image data to DB
const imgBuffer = await fs.readFile(destPath)
await WIKI.models.knex('assetData').insert({
id: site.config.assets.favicon,
data: imgBuffer
}).onConflict('id').merge()
WIKI.logger.info('New site favicon processed successfully.')
return {
operation: graphHelper.generateSuccess('Site favicon uploaded successfully')
}
} catch (err) {
WIKI.logger.warn(err)
return graphHelper.generateError(err)
}
}
}

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" href="/_site/favicon" />
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">

@ -77,6 +77,12 @@ module.exports = configure(function (/* ctx */) {
extendViteConf (viteConf) {
viteConf.build.assetsDir = '_assets'
viteConf.build.rollupOptions = {
...viteConf.build.rollupOptions ?? {},
external: [
/^\/_site\//
]
}
},
// viteVuePluginOptions: {},

@ -173,14 +173,14 @@
"admin.general.displaySiteTitle": "Display Site Title",
"admin.general.displaySiteTitleHint": "Should the site title be displayed next to the logo? If your logo isn't square and contain your brand name, turn this option off.",
"admin.general.favicon": "Favicon",
"admin.general.faviconHint": "Favicon image file, in SVG, PNG, ICO or GIF format. Must be a square image.",
"admin.general.faviconHint": "Favicon image file, in SVG, PNG, JPG, WEBP or GIF format. Must be a square image.",
"admin.general.faviconUploadSuccess": "Site Favicon uploaded successfully.",
"admin.general.features": "Features",
"admin.general.footerCopyright": "Footer / Copyright",
"admin.general.general": "General",
"admin.general.logo": "Logo",
"admin.general.logoUpl": "Site Logo",
"admin.general.logoUplHint": "Logo image file, in SVG, PNG, JPG or GIF format.",
"admin.general.logoUplHint": "Logo image file, in SVG, PNG, JPG, WEBP or GIF format.",
"admin.general.logoUploadSuccess": "Site logo uploaded successfully.",
"admin.general.ratingsOff": "Off",
"admin.general.ratingsStars": "Stars",

@ -302,10 +302,11 @@ q-page.admin-general
.admin-general-favicontabs.q-mt-md
div
q-avatar(
v-if='adminStore.currentSiteId'
size='24px'
square
)
img(src='/_assets/logo-wikijs.svg')
img(:src='`/_site/` + adminStore.currentSiteId + `/favicon?` + state.assetTimestamp')
.text-caption.q-ml-sm {{state.config.title}}
div
q-icon(name='las la-otter', size='24px', color='grey')
@ -669,7 +670,7 @@ async function uploadLogo () {
input.onchange = async e => {
state.loading++
try {
await APOLLO_CLIENT.mutate({
const resp = await APOLLO_CLIENT.mutate({
mutation: gql`
mutation uploadLogo (
$id: UUID!
@ -681,7 +682,6 @@ async function uploadLogo () {
) {
operation {
succeeded
slug
message
}
}
@ -692,11 +692,15 @@ async function uploadLogo () {
image: e.target.files[0]
}
})
$q.notify({
type: 'positive',
message: t('admin.general.logoUploadSuccess')
})
state.assetTimestamp = (new Date()).toISOString()
if (resp?.data?.uploadSiteLogo?.operation?.succeeded) {
$q.notify({
type: 'positive',
message: t('admin.general.logoUploadSuccess')
})
state.assetTimestamp = (new Date()).toISOString()
} else {
throw new Error(resp?.data?.uploadSiteLogo?.operation?.message || 'An unexpected error occured.')
}
} catch (err) {
$q.notify({
type: 'negative',
@ -717,7 +721,7 @@ async function uploadFavicon () {
input.onchange = async e => {
state.loading++
try {
await APOLLO_CLIENT.mutate({
const resp = await APOLLO_CLIENT.mutate({
mutation: gql`
mutation uploadFavicon (
$id: UUID!
@ -727,9 +731,8 @@ async function uploadFavicon () {
id: $id
image: $image
) {
status {
operation {
succeeded
slug
message
}
}
@ -740,10 +743,15 @@ async function uploadFavicon () {
image: e.target.files[0]
}
})
$q.notify({
type: 'positive',
message: t('admin.general.faviconUploadSuccess')
})
if (resp?.data?.uploadSiteFavicon?.operation?.succeeded) {
$q.notify({
type: 'positive',
message: t('admin.general.faviconUploadSuccess')
})
state.assetTimestamp = (new Date()).toISOString()
} else {
throw new Error(resp?.data?.uploadSiteFavicon?.operation?.message || 'An unexpected error occured.')
}
} catch (err) {
$q.notify({
type: 'negative',

@ -119,7 +119,7 @@ onMounted(() => {
state.connecting = true
// socket = io(window.location.host, {
socket = io('localhost:3000', {
socket = io(window.location.host, {
path: '/_ws/',
auth: {
token: 'TEST' // TODO: Use active token

Loading…
Cancel
Save