Verified Commit ee872c44 authored by FabioWidmer's avatar FabioWidmer
Browse files

Improve SEO

parent ae20fe50
......@@ -6,16 +6,16 @@ const config = require('./webpack.config.js');
const env = process.env.NODE_ENV || 'development';
const target = process.env.TARGET || 'web';
const isCordova = target === 'cordova'
const isCordova = target === 'cordova';
const spinner = ora(env === 'production' ? 'building for production...' : 'building development version...');
spinner.start();
rm(isCordova ? './cordova/www' : './www/', (removeErr) => {
if (removeErr) throw removeErr;
if(removeErr) throw removeErr;
webpack(config, (err, stats) => {
if (err) throw err;
if(err) throw err;
spinner.stop();
process.stdout.write(`${stats.toString({
......@@ -26,7 +26,7 @@ rm(isCordova ? './cordova/www' : './www/', (removeErr) => {
chunkModules: false,
})}\n\n`);
if (stats.hasErrors()) {
if(stats.hasErrors()) {
console.log(chalk.red('Build failed with errors.\n'));
process.exit(1);
}
......
......@@ -17,6 +17,7 @@ const env = process.env.NODE_ENV || 'development';
const target = process.env.TARGET || 'web';
const isCordova = target === 'cordova';
const isElectronWatch = process.env.ELECTRON_WATCH || false;
const hashName = env === 'production' ? '.[hash:8]' : '';
module.exports = {
mode: env,
......@@ -25,8 +26,8 @@ module.exports = {
},
output: {
path: resolvePath(isCordova ? (isElectronWatch ? 'cordova/platforms/electron/www' : 'cordova/www') : 'www'),
filename: 'js/[name].js',
chunkFilename: 'js/[name].js',
filename: `js/[name]${hashName}.js`,
chunkFilename: `js/[name]${hashName}.js`,
publicPath: '',
hotUpdateChunkFilename: 'hot/hot-update.js',
hotUpdateMainFilename: 'hot/hot-update.json',
......@@ -52,9 +53,11 @@ module.exports = {
},
},
optimization: {
minimizer: [new TerserPlugin({
minimizer: [
new TerserPlugin({
sourceMap: true,
})],
}),
],
},
module: {
rules: [
......@@ -66,14 +69,12 @@ module.exports = {
resolvePath('node_modules/framework7'),
resolvePath('node_modules/framework7-vue'),
resolvePath('node_modules/template7'),
resolvePath('node_modules/dom7'),
resolvePath('node_modules/ssr-window'),
],
},
{
test: /\.vue$/,
use: 'vue-loader',
......@@ -84,8 +85,8 @@ module.exports = {
(env === 'development' ? 'style-loader' : {
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
publicPath: '../',
},
}),
'css-loader',
'postcss-loader',
......@@ -97,8 +98,8 @@ module.exports = {
(env === 'development' ? 'style-loader' : {
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
publicPath: '../',
},
}),
'css-loader',
'postcss-loader',
......@@ -111,8 +112,8 @@ module.exports = {
(env === 'development' ? 'style-loader' : {
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
publicPath: '../',
},
}),
'css-loader',
'postcss-loader',
......@@ -125,8 +126,8 @@ module.exports = {
(env === 'development' ? 'style-loader' : {
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
publicPath: '../',
},
}),
'css-loader',
'postcss-loader',
......@@ -138,7 +139,7 @@ module.exports = {
loader: 'url-loader',
options: {
limit: 10000,
name: 'images/[name].[ext]',
name: `images/[name]${hashName}.[ext]`,
},
},
......@@ -147,7 +148,7 @@ module.exports = {
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name].[ext]',
name: `media/[name]${hashName}.[ext]`,
},
},
......@@ -156,7 +157,7 @@ module.exports = {
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[ext]',
name: `fonts/[name]${hashName}.[ext]`,
},
},
......@@ -172,7 +173,7 @@ module.exports = {
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true,
map: { inline: false },
map: {inline: false},
},
}),
new webpack.optimize.ModuleConcatenationPlugin(),
......@@ -191,11 +192,11 @@ module.exports = {
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
useShortDoctype: true,
} : false,
}),
new MiniCssExtractPlugin({
filename: 'css/[name].css',
filename: `css/[name]${hashName}.css`,
}),
new CopyWebpackPlugin({
patterns: [
......@@ -204,18 +205,37 @@ module.exports = {
from: resolvePath('src/static'),
to: resolvePath(isCordova ? 'cordova/www/static' : 'www/static'),
},
{
noErrorOnMissing: true,
from: resolvePath('src/.htaccess'),
to: resolvePath('www'),
},
{
noErrorOnMissing: true,
from: resolvePath('src/manifest.json'),
to: resolvePath('www/manifest.json'),
},
{
noErrorOnMissing: true,
from: resolvePath('src/robots.txt'),
to: resolvePath('www/robots.txt'),
},
{
noErrorOnMissing: true,
from: resolvePath('src/sitemap.xml'),
to: resolvePath('www/sitemap.xml'),
},
{
noErrorOnMissing: true,
from: resolvePath('src/assetlinks.json'),
to: resolvePath('www/.well-known'),
},
],
}),
...(!isCordova ? [
new WorkboxPlugin.InjectManifest({
swSrc: resolvePath('src/service-worker.js'),
})
}),
] : []),
],
};
......@@ -5,7 +5,7 @@
<author email="info@ablota.com" href="https://ablota.com">Ablota (StarApps GmbH)</author>
<content src="index.html"/>
<access origin="https://store.ablota.com"/>
<access origin="https://api.store.ablota.com"/>
<allow-intent href="http://*/*"/>
<allow-intent href="https://*/*"/>
<allow-intent href="mailto:*"/>
......
......@@ -11,9 +11,9 @@
"dev": true
},
"@ablota/store-cordova-plugin": {
"version": "1.0.0",
"resolved": "https://gitlab.starapps-network.com/api/v4/projects/34/packages/npm/@ablota/store-cordova-plugin/-/@ablota/store-cordova-plugin-1.0.0.tgz",
"integrity": "sha1-A9Y1N/0ABxuSYo1g3cTu0P1oTsw=",
"version": "1.0.1",
"resolved": "https://gitlab.starapps-network.com/api/v4/projects/34/packages/npm/@ablota/store-cordova-plugin/-/@ablota/store-cordova-plugin-1.0.1.tgz",
"integrity": "sha1-U9REcXaMdp5+/Q56mHtMpV3ollk=",
"dev": true
},
"@develar/schema-utils": {
......
......@@ -27,7 +27,7 @@
]
},
"devDependencies": {
"@ablota/store-cordova-plugin": "^1.0.0",
"@ablota/store-cordova-plugin": "^1.0.1",
"cordova-android": "^9.0.0",
"cordova-clipboard": "^1.3.0",
"cordova-electron": "^2.0.0",
......
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE "application/atom+xml" "application/javascript" "application/json" "application/ld+json" "application/manifest+json" "application/rdf+xml" "application/rss+xml" "application/schema+json" "application/vnd.geo+json" "application/vnd.ms-fontobject" "application/x-font-ttf" "application/x-javascript" "application/x-web-app-manifest+json" "application/xhtml+xml" "application/xml" "font/eot" "font/opentype" "image/bmp" "image/svg+xml" "image/vnd.microsoft.icon" "image/x-icon" "text/cache-manifest" "text/css" "text/html" "text/javascript" "text/plain" "text/vcard" "text/vnd.rim.location.xloc" "text/vtt" "text/x-component" "text/x-cross-domain-policy" "text/xml"
</IfModule>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|ico|gif|jpeg|jpg|png|svg|eot|ttf|woff|woff2)$">
Header set Cache-Control "max-age=31536000, public"
</FilesMatch>
</IfModule>
<IfModule mod_rewrite.c>
RewriteCond %{HTTP_HOST} =ablota.store [NC]
RewriteRule ^(.*)$ https://store.ablota.com/$1 [R=301,NE,L]
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,NE,L]
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,NE,L]
ErrorDocument 404 https://%{HTTP_HOST}
</IfModule>
[
{
"relation": [
"delegate_permission/common.handle_all_urls"
],
"target": {
"namespace": "android_app",
"package_name": "com.ablota.store",
"sha256_cert_fingerprints": [
"5E:A5:AE:2F:48:1E:00:E1:B0:A3:88:CA:4E:CC:0B:84:B3:89:1A:6C:0B:32:F5:9C:59:AA:B2:A0:71:A2:51:46"
]
}
}
]
......@@ -24,7 +24,7 @@
return {
url: 'https://store.ablota.com/',
server: {
url: 'http://localhost:8000/'
url: 'https://api.store.ablota.com/'
},
};
},
......
......@@ -14,7 +14,7 @@
<div class="timeline-item-inner">
<div class="timeline-item-title">{{ $t('words.download') }}</div>
<div class="timeline-item-text">{{ $t('popups.downloadApp.steps.download.description') }}</div>
<f7-button href="https://ablota.com/repo/Ablota_Store.apk" :text="$t('words.download')" fill raised></f7-button>
<f7-button href="https://ablota.com/repo/Ablota_Store.apk" :text="$t('words.download')" fill raised external></f7-button>
</div>
</div>
</div>
......@@ -45,7 +45,7 @@
<div class="timeline-item-inner">
<div class="timeline-item-title">{{ $t('words.done') }}</div>
<div class="timeline-item-text">{{ $t('popups.downloadApp.steps.done.description') }}</div>
<f7-button href="https://store.ablota.com" :text="$t('words.launch')" fill raised></f7-button>
<f7-button href="https://store.ablota.com" :text="$t('words.launch')" fill raised external></f7-button>
</div>
</div>
</div>
......
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<% if (process.env.NODE_ENV === 'production') { %>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: gap: content: https://store.ablota.com">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: gap: content: https://api.store.ablota.com">
<% } %>
<meta content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover" name="viewport">
<meta content="#007aff" name="theme-color">
<meta content="telephone=no" name="format-detection">
<meta content="no" name="msapplication-tap-highlight">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta property="og:image:width" content="128">
<meta property="og:image:height" content="128">
<meta property="og:description" content="The universal, decentralized and open source app store.">
<meta property="og:title" content="Ablota Store">
<meta property="og:url" content="https://store.ablota.com">
<meta property="og:image" content="https://store.ablota.com/static/icons/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
<meta name="robots" content="noarchive, notranslate, noimageindex">
<meta name="theme-color" content="#007aff">
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="date=no">
<meta name="format-detection" content="address=no">
<meta name="msapplication-TileColor" content="#007aff">
<meta name="msapplication-tap-highlight" content="no">
<meta name="author" content="Ablota (StarApps GmbH)">
<meta name="description" content="The universal, decentralized and open source app store.">
<meta name="robots" content="noarchive, noimageindex">
<meta name="keywords" content="ablota store, ablota, store, app, apps, game, game, app store, universal, open source, decentralized, foss, floss, android, ios, web, electron, desktop, starapps, starapps ltd, starapps gmbh">
<title>Ablota Store</title>
<% if (process.env.TARGET === 'web') { %>
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black-translucent" name="apple-mobile-web-app-status-bar-style">
<link href="static/icons/apple-touch-icon.png" rel="apple-touch-icon">
<link href="static/icons/favicon.png" rel="icon">
<link href="/manifest.json" rel="manifest">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<% for (var index in htmlWebpackPlugin.files.css) { %>
<link rel="preload" href="<%= htmlWebpackPlugin.files.css[index] %>" as="style">
<% } %>
<% for (var index in htmlWebpackPlugin.files.js) { %>
<link rel="preload" href="<%= htmlWebpackPlugin.files.js[index] %>" as="script">
<% } %>
<link rel="apple-touch-icon" sizes="256x256" href="/static/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="128x128" href="/static/icons/favicon.png">
<link rel="manifest" href="/manifest.json">
<% } %>
</head>
<body>
......
......@@ -109,7 +109,7 @@ export function fetchSource(url, server, age = 6) {
});
}
export function fetchAsset(url, age = 24) {
export function fetchAsset(url, server, age = 24) {
return new Promise((resolve, reject) => {
if(Device.cordova) {
ablota.store.file.hashName(url, data => {
......@@ -154,15 +154,15 @@ export function fetchAsset(url, age = 24) {
} else {
resolve({
originalUrl: url,
localUrl: `http://localhost:8000/v1/proxy/asset?url=${url}`
localUrl: `${server.url}v1/proxy/asset?url=${url}`
});
}
});
}
export function fetchIcons(items, icons, age = 24) {
export function fetchIcons(items, icons, server, age = 24) {
return new Promise((resolve) => {
const promises = items.filter(app => app.icon && !icons[app.icon]).map(app => fetchAsset(app.icon, age).then(asset => {
const promises = items.filter(app => app.icon && !icons[app.icon]).map(app => fetchAsset(app.icon, server, age).then(asset => {
return new Promise(resolve => {
const img = new Image();
......@@ -361,10 +361,10 @@ export function downloadPackage(app, appPackage, updateCallback) {
if(appPackage[obbFile] && appPackage[`${obbFile}Sha256`]) {
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, externalRootDirectory => {
externalRootDirectory.getDirectory('Android', { create: true, exclusive: false }, androidDirectory => {
androidDirectory.getDirectory('data', { create: true, exclusive: false }, dataDirectory => {
dataDirectory.getDirectory(appPackage.packageName, { create: true, exclusive: false }, appPackageDirectory => {
androidDirectory.getDirectory('obb', { create: true, exclusive: false }, obbDirectory => {
obbDirectory.getDirectory(appPackage.packageName, { create: true, exclusive: false }, appPackageDirectory => {
const obbFilename = appPackage[obbFile].substring(appPackage[obbFile].lastIndexOf('/') + 1);
const obbFilePath = `${cordova.file.externalRootDirectory}Android/data/${appPackage.packageName}/${obbFilename}`;
const obbFilePath = `${cordova.file.externalCacheDirectory}/${obbFilename}`;
const downloadObbFile = () => {
ablota.store.file.download(appPackage[obbFile], obbFilePath, {}, {
title: `${app.name} (${obbName})`,
......@@ -373,7 +373,13 @@ export function downloadPackage(app, appPackage, updateCallback) {
ablota.store.file.hash(obbFilePath, data => {
if(data.status === 'success') {
if(data.hash.toLowerCase() === appPackage[`${obbFile}Sha256`]) {
window.resolveLocalFileSystemURL(cordova.file.externalCacheDirectory, externalCacheDirectory => {
externalCacheDirectory.getFile(obbFilename, { create: false }, obbFileEntry => {
obbFileEntry.moveTo(appPackageDirectory, obbFilename, () => {
finish(obbName);
}, () => reject('utils.downloadPackage.directory'));
}, () => reject('utils.downloadPackage.directory'));
}, () => reject('utils.downloadPackage.directory'));
} else {
appPackageDirectory.getFile(obbFilename, { create: false }, obbFileEntry => {
obbFileEntry.remove();
......@@ -398,7 +404,7 @@ export function downloadPackage(app, appPackage, updateCallback) {
finished[obbName] = false;
appPackageDirectory.getFile(obbFilename, { create: false }, () => {
ablota.store.file.hash(obbFilePath, data => {
ablota.store.file.hash(`${cordova.file.externalRootDirectory}Android/obb/${appPackage.packageName}/${obbFilename}`, data => {
if(data.status === 'success') {
if(data.hash.toLowerCase() === appPackage[`${obbFile}Sha256`]) {
finish(obbName);
......
......@@ -5,8 +5,8 @@
"lang": "en-US",
"start_url": "/",
"display": "standalone",
"background_color": "#EE350F",
"theme_color": "#EE350F",
"background_color": "#ffffff",
"theme_color": "#007aff",
"icons": [
{
"src": "/static/icons/128x128.png",
......
......@@ -18,22 +18,22 @@
<f7-block-title>{{ $t('words.socialMedia') }}</f7-block-title>
<f7-list>
<f7-list-item footer="@Ablota_com" link="https://mastodon.online/@Ablota_com" :title="$t('words.mastodon')" external target="_system">
<img src="static/images/logos/mastodon.svg" slot="media" class="icon-30" />
<img src="static/images/logos/mastodon.svg" slot="media" alt="" class="icon-30" />
</f7-list-item>
<f7-list-item footer="@Ablota_com" link="https://twitter.com/Ablota_com" :title="$t('words.twitter')" external target="_system">
<img src="static/images/logos/twitter.png" slot="media" class="icon-30" />
<img src="static/images/logos/twitter.png" slot="media" alt="" class="icon-30" />
</f7-list-item>
<f7-list-item footer="r/Ablota" link="https://reddit.com/r/Ablota" :title="$t('words.reddit')" external target="_system">
<img src="static/images/logos/reddit.png" slot="media" class="icon-30" />
<img src="static/images/logos/reddit.png" slot="media" alt="" class="icon-30" />
</f7-list-item>
</f7-list>
<f7-block-title>{{ $t('words.donate') }}</f7-block-title>
<f7-list>
<f7-list-item link="https://liberapay.com/Ablota_com" :title="$t('words.liberapay')" external target="_system">
<img src="static/images/logos/liberapay.svg" slot="media" class="icon-30" />
<img src="static/images/logos/liberapay.svg" slot="media" alt="" class="icon-30" />
</f7-list-item>
<f7-list-item link="https://opencollective.com/Ablota_com" :title="$t('words.openCollective')" external target="_system">
<img src="static/images/logos/opencollective.svg" slot="media" class="icon-30" />
<img src="static/images/logos/opencollective.svg" slot="media" alt="" class="icon-30" />
</f7-list-item>
</f7-list>
<f7-block>
......
......@@ -268,11 +268,11 @@ export default {
this.checkPackages();
fetchIcons([app], {}, this.$store.state.settings.assetsAge).then(icons => {
fetchIcons([app], {}, this.$f7.data.server, this.$store.state.settings.assetsAge).then(icons => {
this.appIcon = icons[app.icon] ? icons[app.icon] : null;
});
fetchIcons(this.appGraphicsBundle.map(graphic => ({icon: graphic})), this.appGraphics, this.$store.state.settings.assetsAge).then(appGraphics => {
fetchIcons(this.appGraphicsBundle.map(graphic => ({icon: graphic})), this.appGraphics, this.$f7.data.server, this.$store.state.settings.assetsAge).then(appGraphics => {
this.appGraphics = {
...this.appGraphics,
...appGraphics,
......@@ -280,7 +280,7 @@ export default {
});
this.appScreenshotsBundle.forEach(screenshots => {
fetchIcons(screenshots.map(screenshot => ({icon: screenshot})), this.appScreenshots, this.$store.state.settings.assetsAge).then(appScreenshots => {
fetchIcons(screenshots.map(screenshot => ({icon: screenshot})), this.appScreenshots, this.$f7.data.server, this.$store.state.settings.assetsAge).then(appScreenshots => {
this.appScreenshots = {
...this.appScreenshots,
...appScreenshots,
......
......@@ -140,7 +140,7 @@ import { fetchIcons } from '../js/utils';
}
});
fetchIcons(items, this.appsIcons, this.$store.state.settings.assetsAge).then(icons => {
fetchIcons(items, this.appsIcons, this.$f7.data.server, this.$store.state.settings.assetsAge).then(icons => {
this.appsIcons = {
...this.appsIcons,
...icons,
......
......@@ -112,7 +112,7 @@
clearTimeout(this.appsIconsTimeout);
this.appsIconsTimeout = setTimeout(() => {
fetchIcons(this.newUpdatedApps, this.appsIcons, this.$store.state.settings.assetsAge).then(icons => {
fetchIcons(this.newUpdatedApps, this.appsIcons, this.$f7.data.server, this.$store.state.settings.assetsAge).then(icons => {
this.appsIcons = {
...this.appsIcons,
...icons,
......@@ -127,7 +127,7 @@
}
});
fetchIcons(items, this.appsIcons, this.$store.state.settings.assetsAge).then(icons => {
fetchIcons(items, this.appsIcons, this.$f7.data.server, this.$store.state.settings.assetsAge).then(icons => {
this.appsIcons = {
...this.appsIcons,
...icons,
......
......@@ -117,7 +117,7 @@ export default {
}
});
fetchIcons(items, this.icons, this.$store.state.settings.assetsAge).then(icons => {
fetchIcons(items, this.icons, this.$f7.data.server, this.$store.state.settings.assetsAge).then(icons => {
this.icons = {
...this.icons,
...icons,
......
......@@ -136,7 +136,7 @@
});
if(source.repo.icon) {
fetchAsset(source.repo.icon, this.$store.state.settings.assetsAge).then(asset => {
fetchAsset(source.repo.icon, this.$f7.data.server, this.$store.state.settings.assetsAge).then(asset => {
const img = new Image();
img.src = asset.localUrl;
......
......@@ -219,7 +219,7 @@
});
},
fetchIcons: function() {
fetchIcons(this.sources.map(source => source.repo), this.icons, this.$store.state.settings.assetsAge).then(icons => {
fetchIcons(this.sources.map(source => source.repo), this.icons, this.$f7.data.server, this.$store.state.settings.assetsAge).then(icons => {
this.icons = {
...this.icons,
...icons,
......
User-agent: *
Disallow: /static/images/
Sitemap: https://store.ablota.com/sitemap.xml
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment