diff --git a/.gitignore b/.gitignore
index 403adbc..8e166d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,7 @@
-.DS_Store
+# Node Modules
node_modules
-/dist
-
-# local env files
+# Local ENV files
.env.local
.env.*.local
@@ -13,7 +11,7 @@ yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
-# Editor directories and files
+# Editor Directories/Files
.idea
.vscode
*.suo
@@ -21,3 +19,10 @@ pnpm-debug.log*
*.njsproj
*.sln
*.sw?
+.DS_Store
+
+yarn.lock
+
+# Custom
+.env
+config/config.json
\ No newline at end of file
diff --git a/config/config.json b/config/config.json
deleted file mode 100644
index e1cf688..0000000
--- a/config/config.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "development": {
- "username": "troplo_watchdogs",
- "password": "Hum%r4T3g#*@W7F65wz%",
- "database": "troplo_watchdogs",
- "host": "192.168.0.13",
- "dialect": "mysql",
- "logging": false
- },
- "test": {
- "username": "troplo_watchdogs",
- "password": "Hum%r4T3g#*@W7F65wz%",
- "database": "troplo_watchdogs",
- "host": "192.168.0.13",
- "dialect": "mysql",
- "logging": false
- },
- "production": {
- "username": "troplo_watchdogs",
- "password": "Hum%r4T3g#*@W7F65wz%",
- "database": "troplo_watchdogs",
- "host": "192.168.0.13",
- "dialect": "mysql",
- "logging": false
- }
-}
diff --git a/index.js b/index.js
deleted file mode 100644
index 970656f..0000000
--- a/index.js
+++ /dev/null
@@ -1,142 +0,0 @@
-const crc32 = require('js-crc').crc32;
-
-const fs = require("fs");
-const glob = require("glob");
-const { File } = require("./models")
-const {Op} = require("sequelize");
-const express = require('express')
-const os = require("os");
-const moment = require("moment");
-const app = express()
-const port = 34895
-const fsPromises = require("fs").promises
-const readdir = fsPromises.readdir
-const nodePath = require("path")
-const path = require("path");
-const stat = fsPromises.stat
-async function list(depot, path) {
- try {
- let root
- if(depot === "wd1") {
- root = "/root/depots/" + depot
- } else if(depot === "wdl-2450") {
- root = "/root/depots/wdl-2450-main"
- } else {
- return {success: false, message: "Depot not found."}
- }
- let code = depot
- let dirs = [],
- files = [];
-
- if (path[path.length - 1] !== "/") {
- path += "/";
- }
-
- let filePath = nodePath.join(root + path);
- console.log(filePath)
- if (filePath.startsWith(root)) {
- let items = await readdir(root + path, { withFileTypes: true });
-
- for (let item of items) {
- let isFile = item.isFile(),
- isDir = item.isDirectory();
-
- if (!isFile && !isDir) {
- continue;
- }
-
- let result = {
- type: isFile ? "file" : "dir",
- path: path + item.name,
- };
-
- result.basename = result.name = nodePath.basename(result.path);
-
- if (isFile) {
- let fileStat = await stat(root + result.path);
- result.size = fileStat.size;
- result.extension = nodePath.extname(result.path).slice(1);
- result.name = nodePath.basename(result.path, "." + result.extension);
- files.push(result);
- } else {
- result.path += "/";
- dirs.push(result);
- }
- }
-
- return dirs.concat(files);
- } else {
- return {success: false, message: "Folder was not found in the depot"}
- }
- } catch (err) {
- console.error(err);
- }
-}
-
-app.get('/api/v1/browser/:depot/list', async(req, res) => {
- try {
- let result = await list(req.params.depot, req.query.path);
- return res.json(result);
- } catch (e){
- console.log(e)
- res.json({success: false, message: "Something went wrong."})
- }
-})
-
-app.get('/api/v1/browser/:depot/download', async(req, res) => {
- try {
- let root
- if(req.params.depot === "wd1") {
- root = "/root/depots/" + req.params.depot + "/"
- } else if(req.params.depot === "wdl-2450") {
- root = "M:/"
- } else {
- return {success: false, message: "Depot not found."}
- }
- let file = root + req.query.path
- let fileTest = fs.readFileSync(file, 'utf8')
- let filePath = nodePath.join(file);
- console.log(filePath)
- if (filePath.startsWith(root)) {
- res.download(file, file.name)
- } else {
- res.status(400)
- res.json({success: false, message: "Something went wrong."})
- }
- } catch (e){
- console.log(e)
- res.status(400)
- res.json({success: false, message: "Something went wrong."})
- }
-})
-
-app.get('/api/v1/dl/:id', async(req, res) => {
- try {
- const find = await File.findOne({
- where: {
- id: req.params.id
- }
- })
- if (find) {
- if (find.fileObject) {
- let file = find.path.replace(/^.*[\\\/]/, '')
- res.download("/root/depots/" + find.project + "/" + find.path, file)
- }
- } else {
- res.json({success: false, message: "This hash, or definition does not yet exist in our database."})
- }
- } catch (e){
- console.log(e)
- res.json({success: false, message: "Something went wrong while retrieving this file."})
- }
-})
-
-app.get('/', async(req, res) => {
- res.redirect('https://discord.gg/WKXjxj6kRN')
-})
-
-app.listen(port, () => {
- console.log(`Nexus API running at http://localhost:${port}`)
-})
-
-module.exports = app;
diff --git a/migrations/20210924121330-files.js b/migrations/20210924121330-files.js
deleted file mode 100644
index e8447ad..0000000
--- a/migrations/20210924121330-files.js
+++ /dev/null
@@ -1,30 +0,0 @@
-'use strict';
-module.exports = {
- up: async(queryInterface, Sequelize) => {
- await queryInterface.createTable('files', {
- id: {
- allowNull: false,
- autoIncrement: true,
- primaryKey: true,
- type: Sequelize.BIGINT
- },
- crc32: Sequelize.STRING,
- fnv32: Sequelize.STRING,
- fnv64: Sequelize.STRING,
- path: Sequelize.STRING,
- project: {
- type: Sequelize.STRING,
- defaultValue: "wd1Retail"
- },
- createdAt: {
- type: Sequelize.DATE
- },
- updatedAt: {
- type: Sequelize.DATE
- }
- });
- },
- down: async(queryInterface, Sequelize) => {
- await queryInterface.dropTable('files');
- }
-};
\ No newline at end of file
diff --git a/migrations/20210924132702-definition.js b/migrations/20210924132702-definition.js
deleted file mode 100644
index 831d9c0..0000000
--- a/migrations/20210924132702-definition.js
+++ /dev/null
@@ -1,48 +0,0 @@
-module.exports = {
- up(queryInterface, Sequelize) {
- return Promise.all([
- queryInterface.addColumn(
- 'files',
- 'definition',
- {
- type: Sequelize.TEXT,
- },
- ),
- queryInterface.addColumn(
- 'files',
- 'fnv32Rev',
- {
- type: Sequelize.STRING,
- },
- ),
- queryInterface.addColumn(
- 'files',
- 'fnv64Rev',
- {
- type: Sequelize.STRING,
- },
- ),
- queryInterface.addColumn(
- 'files',
- 'crc32Rev',
- {
- type: Sequelize.STRING,
- },
- ),
- queryInterface.addColumn(
- 'files',
- 'fileObject',
- {
- type: Sequelize.JSON,
- },
- ),
- queryInterface.addColumn(
- 'files',
- 'otherProperties',
- {
- type: Sequelize.JSON,
- },
- ),
- ]);
- },
-}
\ No newline at end of file
diff --git a/models/files.js b/models/files.js
deleted file mode 100644
index 5a23404..0000000
--- a/models/files.js
+++ /dev/null
@@ -1,44 +0,0 @@
-'use strict';
-const {
- Model, DATE
-} = require('sequelize');
-
-module.exports = (sequelize, DataTypes) => {
- class File extends Model {
- /**
- * Helper method for defining associations.
- * This method is not a part of Sequelize lifecycle.
- * The `models/index` file will call this method automatically.
- */
- static associate(models) {
-
- }
- }
- File.init({
- crc32: DataTypes.STRING,
- fnv32: DataTypes.STRING,
- fnv64: DataTypes.STRING,
- path: DataTypes.STRING,
- project: {
- type: DataTypes.STRING,
- defaultValue: "wd1Retail"
- },
- createdAt: {
- type: DataTypes.DATE
- },
- updatedAt: {
- type: DataTypes.DATE
- },
- definition: DataTypes.TEXT,
- fnv32Rev: DataTypes.STRING,
- fnv64Rev: DataTypes.STRING,
- crc32Rev: DataTypes.STRING,
- fileObject: DataTypes.JSON,
- otherProperties: DataTypes.JSON
- }, {
- sequelize,
- modelName: 'File',
- });
-
- return File;
-};
\ No newline at end of file
diff --git a/models/index.js b/models/index.js
deleted file mode 100644
index 4f8171f..0000000
--- a/models/index.js
+++ /dev/null
@@ -1,37 +0,0 @@
-'use strict';
-
-const fs = require('fs');
-const path = require('path');
-const Sequelize = require('sequelize');
-const basename = path.basename(__filename);
-const env = process.env.NODE_ENV || 'development';
-const config = require(__dirname + '/../config/config.json')[env];
-const db = {};
-
-let sequelize;
-if (config.use_env_variable) {
- sequelize = new Sequelize(process.env[config.use_env_variable], config);
-} else {
- sequelize = new Sequelize(config.database, config.username, config.password, config);
-}
-
-fs
- .readdirSync(__dirname)
- .filter(file => {
- return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
- })
- .forEach(file => {
- const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
- db[model.name] = model;
- });
-
-Object.keys(db).forEach(modelName => {
- if (db[modelName].associate) {
- db[modelName].associate(db);
- }
-});
-
-db.sequelize = sequelize;
-db.Sequelize = Sequelize;
-
-module.exports = db;
diff --git a/package.json b/package.json
index 2f9b872..5a86df1 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,41 @@
{
+ "name": "jays-host",
+ "version": "1.0.0",
+ "description": "A ShareX photo gallery.",
+ "main": "index.js",
+ "scripts": {
+ "serve": "nodemon --ignore ./ui/"
+ },
+ "author": "Troplo",
+ "license": "Internal use only.",
+ "private": true,
"dependencies": {
+ "@discordjs/rest": "^0.1.0-canary.0",
+ "axios": "^0.21.1",
+ "bcryptjs": "^2.4.3",
+ "body-parser": "^1.19.0",
+ "btcpay": "^0.2.5",
+ "btoa": "^1.2.1",
+ "cloudflare": "^2.8.0",
+ "cors": "^2.8.5",
+ "crypto-random-string": "3.3.1",
+ "discord.js": "^13.1.0",
+ "dotenv": "^10.0.0",
"express": "^4.17.1",
- "glob": "^7.2.0",
- "js-crc": "^0.2.0",
+ "express-autosanitizer": "^1.0.2",
+ "form-data": "^4.0.0",
+ "helmet": "^4.6.0",
+ "is-valid-domain": "^0.1.2",
+ "jsonwebtoken": "^8.5.1",
+ "jw-paginate": "^1.0.4",
+ "microstats": "^0.1.2",
"moment": "^2.29.1",
- "mysql2": "^2.3.0",
- "sequelize": "^6.6.5"
+ "multer": "^1.4.2",
+ "mysql2": "^2.2.5",
+ "nodemon": "^2.0.12",
+ "sequelize": "^6.6.5",
+ "sequelize-cli": "^6.2.0",
+ "speakeasy": "^2.0.0",
+ "whois": "^2.13.5"
}
}
-
diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js
index 3391da1..8ad90db 100644
--- a/ui/.eslintrc.js
+++ b/ui/.eslintrc.js
@@ -4,7 +4,7 @@ module.exports = {
node: true
},
'extends': [
- 'plugin:vue/vue3-essential',
+ 'plugin:vue/essential',
'eslint:recommended'
],
parserOptions: {
diff --git a/ui/package.json b/ui/package.json
index 20fd121..d819e5c 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -1,35 +1,34 @@
{
"name": "ui",
- "version": "0.1.0",
+ "version": "1.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
- "lint": "vue-cli-service lint"
+ "lint": "vue-cli-service lint",
+ "postinstall": "patch-package"
},
"dependencies": {
- "@mdi/font": "5.9.55",
"core-js": "^3.6.5",
- "roboto-fontface": "*",
- "vue": "^3.0.0",
- "vue-router": "^4.0.0-0",
- "vuetify": "^3.0.0-alpha.0",
- "vuex": "^4.0.0-0",
- "webfontloader": "^1.0.0"
+ "patch-package": "^6.4.7",
+ "register-service-worker": "^1.7.1",
+ "vue": "^2.6.11",
+ "vue-router": "^3.2.0",
+ "vuetify": "^2.4.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
+ "@vue/cli-plugin-pwa": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
- "@vue/cli-plugin-vuex": "~4.5.0",
- "@vue/cli-service": "~5.0.0-beta.3",
- "@vue/compiler-sfc": "^3.0.0",
+ "@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
- "eslint-plugin-vue": "^7.0.0",
- "sass": "^1.38.0",
+ "eslint-plugin-vue": "^6.2.2",
+ "sass": "~1.32.0",
"sass-loader": "^10.0.0",
- "vue-cli-plugin-vuetify": "~2.4.3",
- "vuetify-loader": "^2.0.0-alpha.0"
+ "vue-cli-plugin-vuetify": "~2.4.2",
+ "vue-template-compiler": "^2.6.11",
+ "vuetify-loader": "^1.7.0"
}
}
diff --git a/ui/patches/vuetify+2.5.8.patch b/ui/patches/vuetify+2.5.8.patch
new file mode 100644
index 0000000..6bf4e99
--- /dev/null
+++ b/ui/patches/vuetify+2.5.8.patch
@@ -0,0 +1,425 @@
+diff --git a/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass b/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass
+index ee4419a..6d1ba3d 100644
+--- a/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass
++++ b/node_modules/vuetify/src/components/VNavigationDrawer/VNavigationDrawer.sass
+@@ -9,7 +9,7 @@
+ background-color: map-get($material, 'dividers')
+
+ .v-divider
+- border-color: map-get($material, 'dividers')
++ width: 0
+
+ // Block
+ .v-navigation-drawer
+diff --git a/node_modules/vuetify/src/styles/settings/_variables.scss b/node_modules/vuetify/src/styles/settings/_variables.scss
+index f5cc26b..6b998f1 100644
+--- a/node_modules/vuetify/src/styles/settings/_variables.scss
++++ b/node_modules/vuetify/src/styles/settings/_variables.scss
+@@ -2,23 +2,24 @@
+
+ $color-pack: true !default;
+
+-$body-font-family: 'Roboto', sans-serif !default;
++$body-font-family: 'Montserrat', sans-serif !default;
+ $font-size-root: 16px !default;
+ $line-height-root: 1.5 !default;
+ $border-radius-root: 4px !default;
+
+ $rounded: () !default;
+ $rounded: map-deep-merge(
+- (
+- 0: 0,
+- 'sm': $border-radius-root / 2,
+- null: $border-radius-root,
+- 'lg': $border-radius-root * 2,
+- 'xl': $border-radius-root * 6,
+- 'pill': 9999px,
+- 'circle': 50%
+- ),
+- $rounded
++ (
++ 0: 0,
++ 'sm': $border-radius-root / 2,
++ null: $border-radius-root,
++ 'lg': $border-radius-root * 2,
++ 'xl': $border-radius-root * 3,
++ 'xxl': $border-radius-root * 6,
++ 'pill': 9999px,
++ 'circle': 50%
++ ),
++ $rounded
+ );
+
+ $spacer: 4px !default;
+@@ -39,14 +40,14 @@ $negative-spacers: () !default;
+
+ $grid-breakpoints: () !default;
+ $grid-breakpoints: map-deep-merge(
+- (
+- 'xs': 0,
+- 'sm': 600px,
+- 'md': 960px,
+- 'lg': 1280px - 16px,
+- 'xl': 1920px - 16px
+- ),
+- $grid-breakpoints
++ (
++ 'xs': 0,
++ 'sm': 600px,
++ 'md': 960px,
++ 'lg': 1280px - 16px,
++ 'xl': 1920px - 16px
++ ),
++ $grid-breakpoints
+ );
+
+ $grid-gutter: $spacer * 6 !default;
+@@ -57,191 +58,191 @@ $container-padding-x: $grid-gutter / 2 !default;
+
+ $grid-gutters: () !default;
+ $grid-gutters: map-deep-merge(
+- (
+- 'xs': $grid-gutter / 12,
+- 'sm': $grid-gutter / 6,
+- 'md': $grid-gutter / 3,
+- 'lg': $grid-gutter * 2/3,
+- 'xl': $grid-gutter
+- ),
+- $grid-gutters
++ (
++ 'xs': $grid-gutter / 12,
++ 'sm': $grid-gutter / 6,
++ 'md': $grid-gutter / 3,
++ 'lg': $grid-gutter * 2/3,
++ 'xl': $grid-gutter
++ ),
++ $grid-gutters
+ );
+
+ $container-max-widths: () !default;
+ $container-max-widths: map-deep-merge(
+- (
+- 'md': map-get($grid-breakpoints, 'md') * 0.9375,
+- 'lg': map-get($grid-breakpoints, 'lg') * 0.9375,
+- 'xl': map-get($grid-breakpoints, 'xl') * 0.9375
+- ),
+- $container-max-widths
++ (
++ 'md': map-get($grid-breakpoints, 'md') * 0.9375,
++ 'lg': map-get($grid-breakpoints, 'lg') * 0.9375,
++ 'xl': map-get($grid-breakpoints, 'xl') * 0.9375
++ ),
++ $container-max-widths
+ );
+
+ $display-breakpoints: () !default;
+ $display-breakpoints: map-deep-merge(
+- (
+- 'print-only': 'only print',
+- 'screen-only': 'only screen',
+- 'xs-only': 'only screen and (max-width: #{map-get($grid-breakpoints, 'sm') - 1})',
+- 'sm-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')}) and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
+- 'sm-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
+- 'sm-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')})',
+- 'md-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')}) and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
+- 'md-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
+- 'md-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')})',
+- 'lg-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')}) and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
+- 'lg-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
+- 'lg-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')})',
+- 'xl-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'xl')})'
+- ),
+- $display-breakpoints
++ (
++ 'print-only': 'only print',
++ 'screen-only': 'only screen',
++ 'xs-only': 'only screen and (max-width: #{map-get($grid-breakpoints, 'sm') - 1})',
++ 'sm-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')}) and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
++ 'sm-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'md') - 1})',
++ 'sm-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'sm')})',
++ 'md-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')}) and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
++ 'md-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'lg') - 1})',
++ 'md-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'md')})',
++ 'lg-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')}) and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
++ 'lg-and-down': 'only screen and (max-width: #{map-get($grid-breakpoints, 'xl') - 1})',
++ 'lg-and-up': 'only screen and (min-width: #{map-get($grid-breakpoints, 'lg')})',
++ 'xl-only': 'only screen and (min-width: #{map-get($grid-breakpoints, 'xl')})'
++ ),
++ $display-breakpoints
+ );
+
+ $font-weights: () !default;
+ $font-weights: map-deep-merge(
+- (
+- 'thin': 100,
+- 'light': 300,
+- 'regular': 400,
+- 'medium': 500,
+- 'bold': 700,
+- 'black': 900
+- ),
+- $font-weights
++ (
++ 'thin': 100,
++ 'light': 300,
++ 'regular': 400,
++ 'medium': 500,
++ 'bold': 700,
++ 'black': 900
++ ),
++ $font-weights
+ );
+
+ $heading-font-family: $body-font-family !default;
+
+ $headings: () !default;
+ $headings: map-deep-merge(
+- (
+- 'h1': (
+- 'size': 6rem,
+- 'weight': 300,
+- 'line-height': 6rem,
+- 'letter-spacing': -.015625em,
+- 'font-family': $heading-font-family,
+- 'text-transform': false
+- ),
+- 'h2': (
+- 'size': 3.75rem,
+- 'weight': 300,
+- 'line-height': 3.75rem,
+- 'letter-spacing': -.0083333333em,
+- 'font-family': $heading-font-family,
+- 'text-transform': false
+- ),
+- 'h3': (
+- 'size': 3rem,
+- 'weight': 400,
+- 'line-height': 3.125rem,
+- 'letter-spacing': normal,
+- 'font-family': $heading-font-family,
+- 'text-transform': false
+- ),
+- 'h4': (
+- 'size': 2.125rem,
+- 'weight': 400,
+- 'line-height': 2.5rem,
+- 'letter-spacing': .0073529412em,
+- 'font-family': $heading-font-family,
+- 'text-transform': false
+- ),
+- 'h5': (
+- 'size': 1.5rem,
+- 'weight': 400,
+- 'line-height': 2rem,
+- 'letter-spacing': normal,
+- 'font-family': $heading-font-family,
+- 'text-transform': false
+- ),
+- 'h6': (
+- 'size': 1.25rem,
+- 'weight': 500,
+- 'line-height': 2rem,
+- 'letter-spacing': .0125em,
+- 'font-family': $heading-font-family,
+- 'text-transform': false
+- ),
+- 'subtitle-1': (
+- 'size': 1rem,
+- 'weight': normal,
+- 'line-height': 1.75rem,
+- 'letter-spacing': .009375em,
+- 'font-family': $body-font-family,
+- 'text-transform': false
+- ),
+- 'subtitle-2': (
+- 'size': .875rem,
+- 'weight': 500,
+- 'line-height': 1.375rem,
+- 'letter-spacing': .0071428571em,
+- 'font-family': $body-font-family,
+- 'text-transform': false
+- ),
+- 'body-1': (
+- 'size': 1rem,
+- 'weight': 400,
+- 'line-height': 1.5rem,
+- 'letter-spacing': .03125em,
+- 'font-family': $body-font-family,
+- 'text-transform': false
+- ),
+- 'body-2': (
+- 'size': .875rem,
+- 'weight': 400,
+- 'line-height': 1.25rem,
+- 'letter-spacing': .0178571429em,
+- 'font-family': $body-font-family,
+- 'text-transform': false
+- ),
+- 'button': (
+- 'size': .875rem,
+- 'weight': 500,
+- 'line-height': 2.25rem,
+- 'letter-spacing': .0892857143em,
+- 'font-family': $body-font-family,
+- 'text-transform': uppercase
+- ),
+- 'caption': (
+- 'size': .75rem,
+- 'weight': 400,
+- 'line-height': 1.25rem,
+- 'letter-spacing': .0333333333em,
+- 'font-family': $body-font-family,
+- 'text-transform': false
+- ),
+- 'overline': (
+- 'size': .75rem,
+- 'weight': 500,
+- 'line-height': 2rem,
+- 'letter-spacing': .1666666667em,
+- 'font-family': $body-font-family,
+- 'text-transform': uppercase
+- )
+- ),
+- $headings
++ (
++ 'h1': (
++ 'size': 6rem,
++ 'weight': 300,
++ 'line-height': 6rem,
++ 'letter-spacing': -.015625em,
++ 'font-family': $heading-font-family,
++ 'text-transform': false
++ ),
++ 'h2': (
++ 'size': 3.75rem,
++ 'weight': 300,
++ 'line-height': 3.75rem,
++ 'letter-spacing': -.0083333333em,
++ 'font-family': $heading-font-family,
++ 'text-transform': false
++ ),
++ 'h3': (
++ 'size': 3rem,
++ 'weight': 400,
++ 'line-height': 3.125rem,
++ 'letter-spacing': normal,
++ 'font-family': $heading-font-family,
++ 'text-transform': false
++ ),
++ 'h4': (
++ 'size': 2.125rem,
++ 'weight': 400,
++ 'line-height': 2.5rem,
++ 'letter-spacing': .0073529412em,
++ 'font-family': $heading-font-family,
++ 'text-transform': false
++ ),
++ 'h5': (
++ 'size': 1.5rem,
++ 'weight': 400,
++ 'line-height': 2rem,
++ 'letter-spacing': normal,
++ 'font-family': $heading-font-family,
++ 'text-transform': false
++ ),
++ 'h6': (
++ 'size': 1.25rem,
++ 'weight': 500,
++ 'line-height': 2rem,
++ 'letter-spacing': .0125em,
++ 'font-family': $heading-font-family,
++ 'text-transform': false
++ ),
++ 'subtitle-1': (
++ 'size': 1rem,
++ 'weight': normal,
++ 'line-height': 1.75rem,
++ 'letter-spacing': .009375em,
++ 'font-family': $body-font-family,
++ 'text-transform': false
++ ),
++ 'subtitle-2': (
++ 'size': .875rem,
++ 'weight': 500,
++ 'line-height': 1.375rem,
++ 'letter-spacing': .0071428571em,
++ 'font-family': $body-font-family,
++ 'text-transform': false
++ ),
++ 'body-1': (
++ 'size': 1rem,
++ 'weight': 400,
++ 'line-height': 1.5rem,
++ 'letter-spacing': .03125em,
++ 'font-family': $body-font-family,
++ 'text-transform': false
++ ),
++ 'body-2': (
++ 'size': .875rem,
++ 'weight': 400,
++ 'line-height': 1.25rem,
++ 'letter-spacing': .0178571429em,
++ 'font-family': $body-font-family,
++ 'text-transform': false
++ ),
++ 'button': (
++ 'size': .875rem,
++ 'weight': 500,
++ 'line-height': 2.25rem,
++ 'letter-spacing': .0892857143em,
++ 'font-family': $body-font-family,
++ 'text-transform': uppercase
++ ),
++ 'caption': (
++ 'size': .75rem,
++ 'weight': 400,
++ 'line-height': 1.25rem,
++ 'letter-spacing': .0333333333em,
++ 'font-family': $body-font-family,
++ 'text-transform': false
++ ),
++ 'overline': (
++ 'size': .75rem,
++ 'weight': 500,
++ 'line-height': 2rem,
++ 'letter-spacing': .1666666667em,
++ 'font-family': $body-font-family,
++ 'text-transform': uppercase
++ )
++ ),
++ $headings
+ );
+
+ $typography: () !default;
+ @each $type, $values in $headings {
+ $typography: map-deep-merge(
+- $typography,
+- (#{$type}: map-values($values))
++ $typography,
++ (#{$type}: map-values($values))
+ );
+ }
+
+ $transition: () !default;
+ $transition: map-deep-merge(
+- (
+- 'fast-out-slow-in': cubic-bezier(0.4, 0, 0.2, 1),
+- 'linear-out-slow-in': cubic-bezier(0, 0, 0.2, 1),
+- 'fast-out-linear-in': cubic-bezier(0.4, 0, 1, 1),
+- 'ease-in-out': cubic-bezier(0.4, 0, 0.6, 1),
+- 'fast-in-fast-out': cubic-bezier(0.25, 0.8, 0.25, 1),
+- 'swing': cubic-bezier(0.25, 0.8, 0.5, 1)
+- ),
+- $transition
++ (
++ 'fast-out-slow-in': cubic-bezier(0.4, 0, 0.2, 1),
++ 'linear-out-slow-in': cubic-bezier(0, 0, 0.2, 1),
++ 'fast-out-linear-in': cubic-bezier(0.4, 0, 1, 1),
++ 'ease-in-out': cubic-bezier(0.4, 0, 0.6, 1),
++ 'fast-in-fast-out': cubic-bezier(0.25, 0.8, 0.25, 1),
++ 'swing': cubic-bezier(0.25, 0.8, 0.5, 1)
++ ),
++ $transition
+ );
+ $primary-transition: 0.3s map-get($transition, 'swing') !default;
+ $secondary-transition: 0.2s map-get($transition, 'ease-in-out') !default;
diff --git a/ui/public/img/icons/android-chrome-192x192.png b/ui/public/img/icons/android-chrome-192x192.png
new file mode 100644
index 0000000..b02aa64
Binary files /dev/null and b/ui/public/img/icons/android-chrome-192x192.png differ
diff --git a/ui/public/img/icons/android-chrome-512x512.png b/ui/public/img/icons/android-chrome-512x512.png
new file mode 100644
index 0000000..06088b0
Binary files /dev/null and b/ui/public/img/icons/android-chrome-512x512.png differ
diff --git a/ui/public/img/icons/android-chrome-maskable-192x192.png b/ui/public/img/icons/android-chrome-maskable-192x192.png
new file mode 100644
index 0000000..791e9c8
Binary files /dev/null and b/ui/public/img/icons/android-chrome-maskable-192x192.png differ
diff --git a/ui/public/img/icons/android-chrome-maskable-512x512.png b/ui/public/img/icons/android-chrome-maskable-512x512.png
new file mode 100644
index 0000000..5f2098e
Binary files /dev/null and b/ui/public/img/icons/android-chrome-maskable-512x512.png differ
diff --git a/ui/public/img/icons/apple-touch-icon-120x120.png b/ui/public/img/icons/apple-touch-icon-120x120.png
new file mode 100644
index 0000000..1427cf6
Binary files /dev/null and b/ui/public/img/icons/apple-touch-icon-120x120.png differ
diff --git a/ui/public/img/icons/apple-touch-icon-152x152.png b/ui/public/img/icons/apple-touch-icon-152x152.png
new file mode 100644
index 0000000..f24d454
Binary files /dev/null and b/ui/public/img/icons/apple-touch-icon-152x152.png differ
diff --git a/ui/public/img/icons/apple-touch-icon-180x180.png b/ui/public/img/icons/apple-touch-icon-180x180.png
new file mode 100644
index 0000000..404e192
Binary files /dev/null and b/ui/public/img/icons/apple-touch-icon-180x180.png differ
diff --git a/ui/public/img/icons/apple-touch-icon-60x60.png b/ui/public/img/icons/apple-touch-icon-60x60.png
new file mode 100644
index 0000000..cf10a56
Binary files /dev/null and b/ui/public/img/icons/apple-touch-icon-60x60.png differ
diff --git a/ui/public/img/icons/apple-touch-icon-76x76.png b/ui/public/img/icons/apple-touch-icon-76x76.png
new file mode 100644
index 0000000..c500769
Binary files /dev/null and b/ui/public/img/icons/apple-touch-icon-76x76.png differ
diff --git a/ui/public/img/icons/apple-touch-icon.png b/ui/public/img/icons/apple-touch-icon.png
new file mode 100644
index 0000000..03c0c5d
Binary files /dev/null and b/ui/public/img/icons/apple-touch-icon.png differ
diff --git a/ui/public/img/icons/favicon-16x16.png b/ui/public/img/icons/favicon-16x16.png
new file mode 100644
index 0000000..42af009
Binary files /dev/null and b/ui/public/img/icons/favicon-16x16.png differ
diff --git a/ui/public/img/icons/favicon-32x32.png b/ui/public/img/icons/favicon-32x32.png
new file mode 100644
index 0000000..46ca04d
Binary files /dev/null and b/ui/public/img/icons/favicon-32x32.png differ
diff --git a/ui/public/img/icons/msapplication-icon-144x144.png b/ui/public/img/icons/msapplication-icon-144x144.png
new file mode 100644
index 0000000..7808237
Binary files /dev/null and b/ui/public/img/icons/msapplication-icon-144x144.png differ
diff --git a/ui/public/img/icons/mstile-150x150.png b/ui/public/img/icons/mstile-150x150.png
new file mode 100644
index 0000000..3b37a43
Binary files /dev/null and b/ui/public/img/icons/mstile-150x150.png differ
diff --git a/ui/public/img/icons/safari-pinned-tab.svg b/ui/public/img/icons/safari-pinned-tab.svg
new file mode 100644
index 0000000..e44c0d5
--- /dev/null
+++ b/ui/public/img/icons/safari-pinned-tab.svg
@@ -0,0 +1,3 @@
+
diff --git a/ui/public/index.html b/ui/public/index.html
index 4123528..bc51465 100644
--- a/ui/public/index.html
+++ b/ui/public/index.html
@@ -6,6 +6,8 @@
<%= htmlWebpackPlugin.options.title %>
+
+