Design
@@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Auto-generated type declarations
|
||||
auto-imports.d.ts
|
||||
components.d.ts
|
||||
@@ -0,0 +1,4 @@
|
||||
semi: false
|
||||
tabWidth: 2
|
||||
trailingComma: all
|
||||
singleQuote: true
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright 2018 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// If the loader is already loaded, just stop.
|
||||
if (!self.define) {
|
||||
let registry = {};
|
||||
|
||||
// Used for `eval` and `importScripts` where we can't get script URL by other means.
|
||||
// In both cases, it's safe to use a global var because those functions are synchronous.
|
||||
let nextDefineUri;
|
||||
|
||||
const singleRequire = (uri, parentUri) => {
|
||||
uri = new URL(uri + ".js", parentUri).href;
|
||||
return registry[uri] || (
|
||||
|
||||
new Promise(resolve => {
|
||||
if ("document" in self) {
|
||||
const script = document.createElement("script");
|
||||
script.src = uri;
|
||||
script.onload = resolve;
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
nextDefineUri = uri;
|
||||
importScripts(uri);
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
|
||||
.then(() => {
|
||||
let promise = registry[uri];
|
||||
if (!promise) {
|
||||
throw new Error(`Module ${uri} didn’t register its module`);
|
||||
}
|
||||
return promise;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
self.define = (depsNames, factory) => {
|
||||
const uri = nextDefineUri || ("document" in self ? document.currentScript.src : "") || location.href;
|
||||
if (registry[uri]) {
|
||||
// Module is already loading or loaded.
|
||||
return;
|
||||
}
|
||||
let exports = {};
|
||||
const require = depUri => singleRequire(depUri, uri);
|
||||
const specialDeps = {
|
||||
module: { uri },
|
||||
exports,
|
||||
require
|
||||
};
|
||||
registry[uri] = Promise.all(depsNames.map(
|
||||
depName => specialDeps[depName] || require(depName)
|
||||
)).then(deps => {
|
||||
factory(...deps);
|
||||
return exports;
|
||||
});
|
||||
};
|
||||
}
|
||||
define(['./workbox-f0c192c2'], (function (workbox) { 'use strict';
|
||||
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The precacheAndRoute() method efficiently caches and responds to
|
||||
* requests for URLs in the manifest.
|
||||
* See https://goo.gl/S9QRab
|
||||
*/
|
||||
workbox.precacheAndRoute([{
|
||||
"url": "index.html",
|
||||
"revision": "0.kapsgh4ngf8"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
allowlist: [/^\/$/]
|
||||
}));
|
||||
|
||||
}));
|
||||
@@ -0,0 +1,31 @@
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
import prettierConfig from 'eslint-config-prettier'
|
||||
import pluginPrettier from 'eslint-plugin-prettier'
|
||||
|
||||
export default [
|
||||
// Рекомендованные правила для Vue 3 (плоский конфиг)
|
||||
...pluginVue.configs['flat/recommended'],
|
||||
|
||||
// Интеграция Prettier: отключает правила ESLint, конфликтующие с Prettier,
|
||||
// и добавляет правило проверки форматирования через eslint-plugin-prettier
|
||||
{
|
||||
plugins: {
|
||||
prettier: pluginPrettier,
|
||||
},
|
||||
rules: {
|
||||
...prettierConfig.rules,
|
||||
'prettier/prettier': 'warn', // Выводить предупреждения при нарушении форматирования Prettier
|
||||
|
||||
// Ваши кастомные правила (при необходимости)
|
||||
'vue/multi-word-component-names': 'off', // Пример: отключить обязательное многословное имя для Vue компонентов
|
||||
},
|
||||
},
|
||||
|
||||
// Глобальные настройки окружения и игнорирования файлов
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="keywords" content="pgtune, postgresql, postgres, tuning, config, configuration, free, open source" />
|
||||
<meta name="description" content="PgTune - Tuning PostgreSQL config by your hardware" />
|
||||
<link type="text/plain" rel="author" href="/humans.txt" />
|
||||
<title>PGTune - calculate configuration for PostgreSQL based on the maximum performance for a given hardware configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" class="isolate"></div>
|
||||
<script async="async" defer="defer" type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "pgtune",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.41"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/lucide": "^1.2.129",
|
||||
"@iconify-json/simple-icons": "^1.2.94",
|
||||
"@nuxt/ui": "^4.11.0",
|
||||
"@tailwindcss/typography": "^0.5.20",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@unhead/vue": "^3.0.0-beta.9",
|
||||
"@vitejs/plugin-vue": "^6.0.8",
|
||||
"@vueuse/core": "^14.4.0",
|
||||
"eslint": "^10.9.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-config-vue": "^2.0.2",
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-vue": "^10.10.0",
|
||||
"pinia": "^4.0.3",
|
||||
"prettier": "^3.9.6",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"vite": "^8.2.2",
|
||||
"vite-plugin-pwa": "^1.3.0",
|
||||
"vue-router": "^5.3.1",
|
||||
"yup": "^1.7.1"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="112" height="98" xmlns="http://www.w3.org/2000/svg"><g id="Page-1" fill="none" fill-rule="evenodd"><g id="pgtune" fill-rule="nonzero"><path d="M25.3 96.8h-7.1c-1.4 0-2.8-.7-4.4-2.2-2.4-2.2-3.9-5-4.9-7.1-1-2.2-3.3-7.8-4-10-2.2-6.7-3.2-11.4-3.2-14 0-.3-.1-3.6.4-6.4.4-2.6 1-6.6 2.8-11.5 1.9-5 3.6-7.9 8.7-14.2 2.8-3.5 6.1-7 9.5-10.2 3.9-3.7 8.2-6.8 12.2-9.6C48.8 2.2 60 1.5 63.1 1.5c1.2 0 2.7.1 4.4.2 5.1.4 7.4 1.2 8.9 1.7l.5.2c7.6 2.7 11.5 6.3 13.6 8.3l.3.3c2.8 2.6 7.1 6.9 11.1 13.6 2.5 4.1 5.3 11 7.7 19.2.7 2.5 1.2 11.2.9 14.7-.2 2.6-1 7.8-3.6 13.3-2.1 4.4-4.5 9.4-8.3 13-1.9 1.8-4.4 3.5-6.9 4.7-2.9 1.4-8.2 2.1-8.4 2.1-4.8 0-6.3-.8-7.4-2.4-.7-.9-.4-1.6-.2-1.9l.5-.8.9-2.1c.5-1.2.9-2.4 1.7-3.6l.1-.2c.7-1 1.4-1.9 2.3-1.9l.1-.2h.9l.3.3s.1.1.2.1c.3.3.7.6 1.1.6h.2c.8-.2 1.7-.6 3.8-3.3 2.8-3.6 3.6-5.2 4.5-7 .2-.4.3-.7.5-1.1 1.4-2.7 2.5-7.6 1.8-12.4-.7-4.9-1.6-7.5-4.8-10.3-1.3-1.1-2.7-1.4-3.5-1.4-.4 0-.6 0-.7.1h-.1v.8c0 .1.6 9.3.4 11.8-.3 5-.8 8.8-1.6 11.3-.9 2.9-2.6 6.1-4.7 9.9-.2.3-.4.7-.6 1.1-.3.5-.6 1.1-.7 1.3-.1.2-2.4 5.1-2.5 5.2v.1c-1.3 2.2-2.1 3.5-2.6 4.1-.7 1-2 2.5-2.8 3.2-1.3 1.4-3 2.2-5 2.2h-9.1c-2.1 0-3.6-1-4.2-2.8l-.3-.9c-.4-1.3-1-3.3-2.1-4.7-1.3-1.7-2.8-2.6-6-2.7h-.2c-2.3 0-5.1.1-5.8.4-1.5.5-1.5.5-1.7 1.6-.4 1.5-.3 1.9-.1 3.5v.2c.4 3.2-.8 4.5-1.9 5.1-.7.4-1.6.4-3 .4h-.7c-1.3.4-3.6.4-5 .4zm17.9-69.7c-.4 1.7-.5 3.5-.4 5.2.1 2.5.8 6.4 3.6 10.9 2.8 4.6 7.1 6.9 8.8 7.7 1 .5 2.3.8 3.7 1-.5-.2-1-.3-1.5-.5-4.7-2.1-6.1-3.7-7.5-5.4l-.1-.1c-2-2.3-3.4-4.7-4.8-8.2-1.7-4.3-2-7.2-1.8-10.3v-.3zm27.6-4.4c-.2.3-.6 1.8-.1 4.1.5 2.2 1.4 3.4 1.8 3.6.3-.4.6-1.8.1-4.1-.5-2.2-1.4-3.4-1.8-3.6z" id="Shape" fill="#cdd9ea"/><path d="M63.2 2.5c1.2 0 2.6.1 4.3.2 5 .3 7.2 1.2 8.7 1.7.2.1.4.1.5.2 7.8 2.8 11.6 6.5 13.5 8.4 2.8 2.6 7 6.8 11 13.4 2.5 4.1 5.2 10.8 7.6 19v.1c.7 2.3 1.1 10.9.8 14.2-.2 2.6-1 7.6-3.5 13-2.1 4.3-4.4 9.2-8.1 12.7-1.9 1.7-4.2 3.3-6.7 4.5-2.8 1.3-7.9 2-7.9 2-4.7 0-5.8-.9-6.6-2-.4-.6-.2-.7-.2-.7l.5-.9c.9-2 1.4-3.8 2.5-5.6.3-.4 1.1-1.6 1.6-1.6h.3c.4 0 .3-.2.3-.2l.2.2c.5.4 1.1.8 1.7.8h.4c1.2-.3 2.3-1 4.4-3.6 2.9-3.7 3.7-5.3 4.6-7.2.2-.3.3-.7.5-1.1 1.4-2.9 2.6-8 1.9-13-.7-5-1.7-8-5.1-10.9-1.6-1.3-3.1-1.6-4.2-1.6-.6 0-1 .1-1.1.1-.1 0-.1.1-.2.1-.2.1-.3.2-.3.3-.1.2-.2.5-.2 1.5.1 1.8.5 9.6.4 11.7-.3 4.9-.8 8.6-1.5 11.1-.8 2.7-2.3 5.6-4.6 9.7-.3.5-1 1.9-1.3 2.4l-2.5 5.3c-1.2 1.9-2.1 3.4-2.5 4.1-.8 1.2-2.6 3.1-2.7 3.2-.4.4-1.7 1.8-4.3 1.8h-9.2c-1.6 0-2.7-.8-3.2-2.1l-.3-.9c-.4-1.4-1-3.4-2.3-5-1.5-1.8-3.2-3-6.8-3h-.2c-1.2 0-5 0-6.1.4-1.8.6-2 .9-2.4 2.3-.4 1.6-.3 2.2-.1 3.8v.2c.3 2.6-.6 3.6-1.4 4.1-.5.3-1.3.3-2.6.3H18c-1.1 0-2.3-.6-3.7-1.9-2.3-2.1-3.7-4.7-4.7-6.8-1-2.1-3.3-7.7-4-9.9-2.1-6.6-3.2-11.2-3.1-13.7 0-.3-.1-3.5.4-6.3.4-2.6.9-6.5 2.8-11.3 1.8-4.9 3.6-7.7 8.6-13.9 2.8-3.4 6-6.9 9.4-10 3.9-3.6 8.1-6.7 12.1-9.5C49.2 3.2 60.3 2.5 63.2 2.5zm-9 10s-2.4 1-5.4 3.2c-1.8 1.4-3.4 3.3-4.7 5.9-.6 1.1-2.6 5.2-2.3 10.8.2 3.1 1.1 7.1 3.7 11.4 2.4 4 6.2 6.7 9.3 8.1 2 .9 4.9 1.4 7.7 1.4 1.5 0 2.9-.1 4.2-.4 3.5-.8 6.1-2.3 7.3-3.1 1.2-.8 3-2.5 3-2.5s-1.9 1.4-3.3 2c-1.4.7-3.6 1.4-5.2 1.7-.9.2-2.9.6-5.2.6-1.7 0-3.6-.2-5.4-1-4.6-2.1-5.9-3.6-7.3-5.2-1.7-2-3.1-4.2-4.6-7.9-1.6-4-1.9-6.8-1.8-9.9.1-2.9.6-4.3 1.3-5.8.6-1.5 1.9-3.6 3.5-5.2 1.7-2 5.1-4 5.2-4.1zm18.4 19h.2c1.1-.2 1.4-2.6.8-5.3-.6-2.5-1.8-4.5-2.8-4.5h-.2c-1.1.2-1.4 2.6-.8 5.3.5 2.6 1.8 4.5 2.8 4.5zM63.2.5C50.6.5 38.3 8.4 34.8 10.8c-4 2.8-8.3 5.9-12.3 9.6-3.4 3.2-6.7 6.7-9.6 10.3-5.2 6.4-7 9.4-8.9 14.5-1.9 5.1-2.5 9.1-2.9 11.7-.4 2.9-.4 6.1-.4 6.6 0 2.7 1 7.4 3.2 14.3.7 2.3 3 7.9 4.1 10.2 1.1 2.2 2.6 5.1 5.1 7.4 1.8 1.7 3.4 2.4 5 2.4h12.6c1.3 0 2.5 0 3.5-.6 1.1-.6 2.9-2.2 2.4-6.1v-.2c-.2-1.6-.2-1.8.1-3.1.1-.2.1-.4.1-.5.2-.1.5-.2 1-.4s3-.3 5.4-.3h.2c2.9 0 4 .8 5.3 2.3 1 1.3 1.6 3.1 1.9 4.3.1.4.2.7.3 1 .7 2.2 2.6 3.5 5.1 3.5h9.2c2.3 0 4.2-.9 5.7-2.5.9-1 2.2-2.4 2.9-3.4.2-.3.5-.7.8-1.3.1.1.2.3.3.4 1.3 1.8 3.1 2.8 8.3 2.8.4 0 5.8-.8 8.8-2.2 2.6-1.3 5.2-3 7.2-4.9 4-3.7 6.4-8.8 8.5-13.3 2.7-5.6 3.5-11 3.7-13.7.3-3.3-.1-12.2-.9-15v-.1c-2.5-8.3-5.3-15.2-7.8-19.4-4.2-6.8-8.5-11.2-11.3-13.8l-.3-.3C89 9 85 5.3 77.2 2.5l-.2-.1-.3-.1c-1.6-.6-4-1.4-9.2-1.8-1.6.1-3.1 0-4.3 0zm18 78.4c1.9-3.4 3.4-6.4 4.2-9.1.8-2.6 1.3-6.5 1.6-11.5.1-2.4-.3-10.6-.4-11.8.7 0 1.6.3 2.6 1.1 3 2.6 3.8 4.9 4.5 9.7.7 4.6-.4 9.3-1.7 11.8-.2.4-.4.7-.5 1.1-.9 1.8-1.6 3.3-4.4 6.8-1.8 2.3-2.6 2.7-3.2 2.9-.1 0-.2-.1-.4-.3l-.1-.1-.6-.5h-1.6v-.1z" id="Shape" fill="#656b74"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,15 @@
|
||||
# humanstxt.org/
|
||||
# The humans responsible & technology colophon
|
||||
|
||||
/* TEAM */
|
||||
Developer: Oleksii Vasyliev
|
||||
Twitter: @leopard_me
|
||||
|
||||
Web designer: Anna Rotar
|
||||
Twitter: @Ste4any
|
||||
|
||||
/* SITE */
|
||||
Language: English
|
||||
Standards: HTML5, CSS3
|
||||
Doctype: HTML5
|
||||
Source: https://github.com/le0pard/pgtune
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 74 KiB |
@@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
||||
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
||||
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
||||
</symbol>
|
||||
<symbol id="discord-icon" viewBox="0 0 20 19">
|
||||
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
||||
</symbol>
|
||||
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
||||
</symbol>
|
||||
<symbol id="github-icon" viewBox="0 0 19 19">
|
||||
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
||||
</symbol>
|
||||
<symbol id="social-icon" viewBox="0 0 20 20">
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
||||
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
||||
</symbol>
|
||||
<symbol id="x-icon" viewBox="0 0 19 19">
|
||||
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="112" height="98" xmlns="http://www.w3.org/2000/svg"><g id="Page-1" fill="none" fill-rule="evenodd"><g id="pgtune" fill-rule="nonzero"><path d="M25.3 96.8h-7.1c-1.4 0-2.8-.7-4.4-2.2-2.4-2.2-3.9-5-4.9-7.1-1-2.2-3.3-7.8-4-10-2.2-6.7-3.2-11.4-3.2-14 0-.3-.1-3.6.4-6.4.4-2.6 1-6.6 2.8-11.5 1.9-5 3.6-7.9 8.7-14.2 2.8-3.5 6.1-7 9.5-10.2 3.9-3.7 8.2-6.8 12.2-9.6C48.8 2.2 60 1.5 63.1 1.5c1.2 0 2.7.1 4.4.2 5.1.4 7.4 1.2 8.9 1.7l.5.2c7.6 2.7 11.5 6.3 13.6 8.3l.3.3c2.8 2.6 7.1 6.9 11.1 13.6 2.5 4.1 5.3 11 7.7 19.2.7 2.5 1.2 11.2.9 14.7-.2 2.6-1 7.8-3.6 13.3-2.1 4.4-4.5 9.4-8.3 13-1.9 1.8-4.4 3.5-6.9 4.7-2.9 1.4-8.2 2.1-8.4 2.1-4.8 0-6.3-.8-7.4-2.4-.7-.9-.4-1.6-.2-1.9l.5-.8.9-2.1c.5-1.2.9-2.4 1.7-3.6l.1-.2c.7-1 1.4-1.9 2.3-1.9l.1-.2h.9l.3.3s.1.1.2.1c.3.3.7.6 1.1.6h.2c.8-.2 1.7-.6 3.8-3.3 2.8-3.6 3.6-5.2 4.5-7 .2-.4.3-.7.5-1.1 1.4-2.7 2.5-7.6 1.8-12.4-.7-4.9-1.6-7.5-4.8-10.3-1.3-1.1-2.7-1.4-3.5-1.4-.4 0-.6 0-.7.1h-.1v.8c0 .1.6 9.3.4 11.8-.3 5-.8 8.8-1.6 11.3-.9 2.9-2.6 6.1-4.7 9.9-.2.3-.4.7-.6 1.1-.3.5-.6 1.1-.7 1.3-.1.2-2.4 5.1-2.5 5.2v.1c-1.3 2.2-2.1 3.5-2.6 4.1-.7 1-2 2.5-2.8 3.2-1.3 1.4-3 2.2-5 2.2h-9.1c-2.1 0-3.6-1-4.2-2.8l-.3-.9c-.4-1.3-1-3.3-2.1-4.7-1.3-1.7-2.8-2.6-6-2.7h-.2c-2.3 0-5.1.1-5.8.4-1.5.5-1.5.5-1.7 1.6-.4 1.5-.3 1.9-.1 3.5v.2c.4 3.2-.8 4.5-1.9 5.1-.7.4-1.6.4-3 .4h-.7c-1.3.4-3.6.4-5 .4zm17.9-69.7c-.4 1.7-.5 3.5-.4 5.2.1 2.5.8 6.4 3.6 10.9 2.8 4.6 7.1 6.9 8.8 7.7 1 .5 2.3.8 3.7 1-.5-.2-1-.3-1.5-.5-4.7-2.1-6.1-3.7-7.5-5.4l-.1-.1c-2-2.3-3.4-4.7-4.8-8.2-1.7-4.3-2-7.2-1.8-10.3v-.3zm27.6-4.4c-.2.3-.6 1.8-.1 4.1.5 2.2 1.4 3.4 1.8 3.6.3-.4.6-1.8.1-4.1-.5-2.2-1.4-3.4-1.8-3.6z" id="Shape" fill="#cdd9ea"/><path d="M63.2 2.5c1.2 0 2.6.1 4.3.2 5 .3 7.2 1.2 8.7 1.7.2.1.4.1.5.2 7.8 2.8 11.6 6.5 13.5 8.4 2.8 2.6 7 6.8 11 13.4 2.5 4.1 5.2 10.8 7.6 19v.1c.7 2.3 1.1 10.9.8 14.2-.2 2.6-1 7.6-3.5 13-2.1 4.3-4.4 9.2-8.1 12.7-1.9 1.7-4.2 3.3-6.7 4.5-2.8 1.3-7.9 2-7.9 2-4.7 0-5.8-.9-6.6-2-.4-.6-.2-.7-.2-.7l.5-.9c.9-2 1.4-3.8 2.5-5.6.3-.4 1.1-1.6 1.6-1.6h.3c.4 0 .3-.2.3-.2l.2.2c.5.4 1.1.8 1.7.8h.4c1.2-.3 2.3-1 4.4-3.6 2.9-3.7 3.7-5.3 4.6-7.2.2-.3.3-.7.5-1.1 1.4-2.9 2.6-8 1.9-13-.7-5-1.7-8-5.1-10.9-1.6-1.3-3.1-1.6-4.2-1.6-.6 0-1 .1-1.1.1-.1 0-.1.1-.2.1-.2.1-.3.2-.3.3-.1.2-.2.5-.2 1.5.1 1.8.5 9.6.4 11.7-.3 4.9-.8 8.6-1.5 11.1-.8 2.7-2.3 5.6-4.6 9.7-.3.5-1 1.9-1.3 2.4l-2.5 5.3c-1.2 1.9-2.1 3.4-2.5 4.1-.8 1.2-2.6 3.1-2.7 3.2-.4.4-1.7 1.8-4.3 1.8h-9.2c-1.6 0-2.7-.8-3.2-2.1l-.3-.9c-.4-1.4-1-3.4-2.3-5-1.5-1.8-3.2-3-6.8-3h-.2c-1.2 0-5 0-6.1.4-1.8.6-2 .9-2.4 2.3-.4 1.6-.3 2.2-.1 3.8v.2c.3 2.6-.6 3.6-1.4 4.1-.5.3-1.3.3-2.6.3H18c-1.1 0-2.3-.6-3.7-1.9-2.3-2.1-3.7-4.7-4.7-6.8-1-2.1-3.3-7.7-4-9.9-2.1-6.6-3.2-11.2-3.1-13.7 0-.3-.1-3.5.4-6.3.4-2.6.9-6.5 2.8-11.3 1.8-4.9 3.6-7.7 8.6-13.9 2.8-3.4 6-6.9 9.4-10 3.9-3.6 8.1-6.7 12.1-9.5C49.2 3.2 60.3 2.5 63.2 2.5zm-9 10s-2.4 1-5.4 3.2c-1.8 1.4-3.4 3.3-4.7 5.9-.6 1.1-2.6 5.2-2.3 10.8.2 3.1 1.1 7.1 3.7 11.4 2.4 4 6.2 6.7 9.3 8.1 2 .9 4.9 1.4 7.7 1.4 1.5 0 2.9-.1 4.2-.4 3.5-.8 6.1-2.3 7.3-3.1 1.2-.8 3-2.5 3-2.5s-1.9 1.4-3.3 2c-1.4.7-3.6 1.4-5.2 1.7-.9.2-2.9.6-5.2.6-1.7 0-3.6-.2-5.4-1-4.6-2.1-5.9-3.6-7.3-5.2-1.7-2-3.1-4.2-4.6-7.9-1.6-4-1.9-6.8-1.8-9.9.1-2.9.6-4.3 1.3-5.8.6-1.5 1.9-3.6 3.5-5.2 1.7-2 5.1-4 5.2-4.1zm18.4 19h.2c1.1-.2 1.4-2.6.8-5.3-.6-2.5-1.8-4.5-2.8-4.5h-.2c-1.1.2-1.4 2.6-.8 5.3.5 2.6 1.8 4.5 2.8 4.5zM63.2.5C50.6.5 38.3 8.4 34.8 10.8c-4 2.8-8.3 5.9-12.3 9.6-3.4 3.2-6.7 6.7-9.6 10.3-5.2 6.4-7 9.4-8.9 14.5-1.9 5.1-2.5 9.1-2.9 11.7-.4 2.9-.4 6.1-.4 6.6 0 2.7 1 7.4 3.2 14.3.7 2.3 3 7.9 4.1 10.2 1.1 2.2 2.6 5.1 5.1 7.4 1.8 1.7 3.4 2.4 5 2.4h12.6c1.3 0 2.5 0 3.5-.6 1.1-.6 2.9-2.2 2.4-6.1v-.2c-.2-1.6-.2-1.8.1-3.1.1-.2.1-.4.1-.5.2-.1.5-.2 1-.4s3-.3 5.4-.3h.2c2.9 0 4 .8 5.3 2.3 1 1.3 1.6 3.1 1.9 4.3.1.4.2.7.3 1 .7 2.2 2.6 3.5 5.1 3.5h9.2c2.3 0 4.2-.9 5.7-2.5.9-1 2.2-2.4 2.9-3.4.2-.3.5-.7.8-1.3.1.1.2.3.3.4 1.3 1.8 3.1 2.8 8.3 2.8.4 0 5.8-.8 8.8-2.2 2.6-1.3 5.2-3 7.2-4.9 4-3.7 6.4-8.8 8.5-13.3 2.7-5.6 3.5-11 3.7-13.7.3-3.3-.1-12.2-.9-15v-.1c-2.5-8.3-5.3-15.2-7.8-19.4-4.2-6.8-8.5-11.2-11.3-13.8l-.3-.3C89 9 85 5.3 77.2 2.5l-.2-.1-.3-.1c-1.6-.6-4-1.4-9.2-1.8-1.6.1-3.1 0-4.3 0zm18 78.4c1.9-3.4 3.4-6.4 4.2-9.1.8-2.6 1.3-6.5 1.6-11.5.1-2.4-.3-10.6-.4-11.8.7 0 1.6.3 2.6 1.1 3 2.6 3.8 4.9 4.5 9.7.7 4.6-.4 9.3-1.7 11.8-.2.4-.4.7-.5 1.1-.9 1.8-1.6 3.3-4.4 6.8-1.8 2.3-2.6 2.7-3.2 2.9-.1 0-.2-.1-.4-.3l-.1-.1-.6-.5h-1.6v-.1z" id="Shape" fill="#656b74"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="112" height="98" xmlns="http://www.w3.org/2000/svg"><g id="Page-1" fill="none" fill-rule="evenodd"><g id="pgtune" fill-rule="nonzero"><path d="M25.3 96.8h-7.1c-1.4 0-2.8-.7-4.4-2.2-2.4-2.2-3.9-5-4.9-7.1-1-2.2-3.3-7.8-4-10-2.2-6.7-3.2-11.4-3.2-14 0-.3-.1-3.6.4-6.4.4-2.6 1-6.6 2.8-11.5 1.9-5 3.6-7.9 8.7-14.2 2.8-3.5 6.1-7 9.5-10.2 3.9-3.7 8.2-6.8 12.2-9.6C48.8 2.2 60 1.5 63.1 1.5c1.2 0 2.7.1 4.4.2 5.1.4 7.4 1.2 8.9 1.7l.5.2c7.6 2.7 11.5 6.3 13.6 8.3l.3.3c2.8 2.6 7.1 6.9 11.1 13.6 2.5 4.1 5.3 11 7.7 19.2.7 2.5 1.2 11.2.9 14.7-.2 2.6-1 7.8-3.6 13.3-2.1 4.4-4.5 9.4-8.3 13-1.9 1.8-4.4 3.5-6.9 4.7-2.9 1.4-8.2 2.1-8.4 2.1-4.8 0-6.3-.8-7.4-2.4-.7-.9-.4-1.6-.2-1.9l.5-.8.9-2.1c.5-1.2.9-2.4 1.7-3.6l.1-.2c.7-1 1.4-1.9 2.3-1.9l.1-.2h.9l.3.3s.1.1.2.1c.3.3.7.6 1.1.6h.2c.8-.2 1.7-.6 3.8-3.3 2.8-3.6 3.6-5.2 4.5-7 .2-.4.3-.7.5-1.1 1.4-2.7 2.5-7.6 1.8-12.4-.7-4.9-1.6-7.5-4.8-10.3-1.3-1.1-2.7-1.4-3.5-1.4-.4 0-.6 0-.7.1h-.1v.8c0 .1.6 9.3.4 11.8-.3 5-.8 8.8-1.6 11.3-.9 2.9-2.6 6.1-4.7 9.9-.2.3-.4.7-.6 1.1-.3.5-.6 1.1-.7 1.3-.1.2-2.4 5.1-2.5 5.2v.1c-1.3 2.2-2.1 3.5-2.6 4.1-.7 1-2 2.5-2.8 3.2-1.3 1.4-3 2.2-5 2.2h-9.1c-2.1 0-3.6-1-4.2-2.8l-.3-.9c-.4-1.3-1-3.3-2.1-4.7-1.3-1.7-2.8-2.6-6-2.7h-.2c-2.3 0-5.1.1-5.8.4-1.5.5-1.5.5-1.7 1.6-.4 1.5-.3 1.9-.1 3.5v.2c.4 3.2-.8 4.5-1.9 5.1-.7.4-1.6.4-3 .4h-.7c-1.3.4-3.6.4-5 .4zm17.9-69.7c-.4 1.7-.5 3.5-.4 5.2.1 2.5.8 6.4 3.6 10.9 2.8 4.6 7.1 6.9 8.8 7.7 1 .5 2.3.8 3.7 1-.5-.2-1-.3-1.5-.5-4.7-2.1-6.1-3.7-7.5-5.4l-.1-.1c-2-2.3-3.4-4.7-4.8-8.2-1.7-4.3-2-7.2-1.8-10.3v-.3zm27.6-4.4c-.2.3-.6 1.8-.1 4.1.5 2.2 1.4 3.4 1.8 3.6.3-.4.6-1.8.1-4.1-.5-2.2-1.4-3.4-1.8-3.6z" id="Shape" fill="#cdd9ea"/><path d="M63.2 2.5c1.2 0 2.6.1 4.3.2 5 .3 7.2 1.2 8.7 1.7.2.1.4.1.5.2 7.8 2.8 11.6 6.5 13.5 8.4 2.8 2.6 7 6.8 11 13.4 2.5 4.1 5.2 10.8 7.6 19v.1c.7 2.3 1.1 10.9.8 14.2-.2 2.6-1 7.6-3.5 13-2.1 4.3-4.4 9.2-8.1 12.7-1.9 1.7-4.2 3.3-6.7 4.5-2.8 1.3-7.9 2-7.9 2-4.7 0-5.8-.9-6.6-2-.4-.6-.2-.7-.2-.7l.5-.9c.9-2 1.4-3.8 2.5-5.6.3-.4 1.1-1.6 1.6-1.6h.3c.4 0 .3-.2.3-.2l.2.2c.5.4 1.1.8 1.7.8h.4c1.2-.3 2.3-1 4.4-3.6 2.9-3.7 3.7-5.3 4.6-7.2.2-.3.3-.7.5-1.1 1.4-2.9 2.6-8 1.9-13-.7-5-1.7-8-5.1-10.9-1.6-1.3-3.1-1.6-4.2-1.6-.6 0-1 .1-1.1.1-.1 0-.1.1-.2.1-.2.1-.3.2-.3.3-.1.2-.2.5-.2 1.5.1 1.8.5 9.6.4 11.7-.3 4.9-.8 8.6-1.5 11.1-.8 2.7-2.3 5.6-4.6 9.7-.3.5-1 1.9-1.3 2.4l-2.5 5.3c-1.2 1.9-2.1 3.4-2.5 4.1-.8 1.2-2.6 3.1-2.7 3.2-.4.4-1.7 1.8-4.3 1.8h-9.2c-1.6 0-2.7-.8-3.2-2.1l-.3-.9c-.4-1.4-1-3.4-2.3-5-1.5-1.8-3.2-3-6.8-3h-.2c-1.2 0-5 0-6.1.4-1.8.6-2 .9-2.4 2.3-.4 1.6-.3 2.2-.1 3.8v.2c.3 2.6-.6 3.6-1.4 4.1-.5.3-1.3.3-2.6.3H18c-1.1 0-2.3-.6-3.7-1.9-2.3-2.1-3.7-4.7-4.7-6.8-1-2.1-3.3-7.7-4-9.9-2.1-6.6-3.2-11.2-3.1-13.7 0-.3-.1-3.5.4-6.3.4-2.6.9-6.5 2.8-11.3 1.8-4.9 3.6-7.7 8.6-13.9 2.8-3.4 6-6.9 9.4-10 3.9-3.6 8.1-6.7 12.1-9.5C49.2 3.2 60.3 2.5 63.2 2.5zm-9 10s-2.4 1-5.4 3.2c-1.8 1.4-3.4 3.3-4.7 5.9-.6 1.1-2.6 5.2-2.3 10.8.2 3.1 1.1 7.1 3.7 11.4 2.4 4 6.2 6.7 9.3 8.1 2 .9 4.9 1.4 7.7 1.4 1.5 0 2.9-.1 4.2-.4 3.5-.8 6.1-2.3 7.3-3.1 1.2-.8 3-2.5 3-2.5s-1.9 1.4-3.3 2c-1.4.7-3.6 1.4-5.2 1.7-.9.2-2.9.6-5.2.6-1.7 0-3.6-.2-5.4-1-4.6-2.1-5.9-3.6-7.3-5.2-1.7-2-3.1-4.2-4.6-7.9-1.6-4-1.9-6.8-1.8-9.9.1-2.9.6-4.3 1.3-5.8.6-1.5 1.9-3.6 3.5-5.2 1.7-2 5.1-4 5.2-4.1zm18.4 19h.2c1.1-.2 1.4-2.6.8-5.3-.6-2.5-1.8-4.5-2.8-4.5h-.2c-1.1.2-1.4 2.6-.8 5.3.5 2.6 1.8 4.5 2.8 4.5zM63.2.5C50.6.5 38.3 8.4 34.8 10.8c-4 2.8-8.3 5.9-12.3 9.6-3.4 3.2-6.7 6.7-9.6 10.3-5.2 6.4-7 9.4-8.9 14.5-1.9 5.1-2.5 9.1-2.9 11.7-.4 2.9-.4 6.1-.4 6.6 0 2.7 1 7.4 3.2 14.3.7 2.3 3 7.9 4.1 10.2 1.1 2.2 2.6 5.1 5.1 7.4 1.8 1.7 3.4 2.4 5 2.4h12.6c1.3 0 2.5 0 3.5-.6 1.1-.6 2.9-2.2 2.4-6.1v-.2c-.2-1.6-.2-1.8.1-3.1.1-.2.1-.4.1-.5.2-.1.5-.2 1-.4s3-.3 5.4-.3h.2c2.9 0 4 .8 5.3 2.3 1 1.3 1.6 3.1 1.9 4.3.1.4.2.7.3 1 .7 2.2 2.6 3.5 5.1 3.5h9.2c2.3 0 4.2-.9 5.7-2.5.9-1 2.2-2.4 2.9-3.4.2-.3.5-.7.8-1.3.1.1.2.3.3.4 1.3 1.8 3.1 2.8 8.3 2.8.4 0 5.8-.8 8.8-2.2 2.6-1.3 5.2-3 7.2-4.9 4-3.7 6.4-8.8 8.5-13.3 2.7-5.6 3.5-11 3.7-13.7.3-3.3-.1-12.2-.9-15v-.1c-2.5-8.3-5.3-15.2-7.8-19.4-4.2-6.8-8.5-11.2-11.3-13.8l-.3-.3C89 9 85 5.3 77.2 2.5l-.2-.1-.3-.1c-1.6-.6-4-1.4-9.2-1.8-1.6.1-3.1 0-4.3 0zm18 78.4c1.9-3.4 3.4-6.4 4.2-9.1.8-2.6 1.3-6.5 1.6-11.5.1-2.4-.3-10.6-.4-11.8.7 0 1.6.3 2.6 1.1 3 2.6 3.8 4.9 4.5 9.7.7 4.6-.4 9.3-1.7 11.8-.2.4-.4.7-.5 1.1-.9 1.8-1.6 3.3-4.4 6.8-1.8 2.3-2.6 2.7-3.2 2.9-.1 0-.2-.1-.4-.3l-.1-.1-.6-.5h-1.6v-.1z" id="Shape" fill="#333333"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
@@ -0,0 +1,19 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('/src/pages/index.vue'),
|
||||
name: 'Index',
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
component: () => import('/src/pages/about.vue'),
|
||||
name: 'About',
|
||||
},
|
||||
]
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup>
|
||||
import { RouterView, useRoute } from 'vue-router'
|
||||
import Header from './components/header.vue'
|
||||
import { useThemeColor } from './composables/useThemeColor.js'
|
||||
import { onMounted } from 'vue'
|
||||
import { useUpdateApp } from './composables/useUpdateApp.js'
|
||||
|
||||
/*
|
||||
const register = registerSW()
|
||||
*/
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
onMounted(() => {
|
||||
useThemeColor()
|
||||
useUpdateApp()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp>
|
||||
<UContainer>
|
||||
<Header />
|
||||
<UMain class="p-4">
|
||||
<RouterView :key="route.fullPath" />
|
||||
</UMain>
|
||||
</UContainer>
|
||||
</UApp>
|
||||
</template>
|
||||
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="112" height="98" xmlns="http://www.w3.org/2000/svg"><g id="Page-1" fill="none" fill-rule="evenodd"><g id="pgtune" fill-rule="nonzero"><path d="M25.3 96.8h-7.1c-1.4 0-2.8-.7-4.4-2.2-2.4-2.2-3.9-5-4.9-7.1-1-2.2-3.3-7.8-4-10-2.2-6.7-3.2-11.4-3.2-14 0-.3-.1-3.6.4-6.4.4-2.6 1-6.6 2.8-11.5 1.9-5 3.6-7.9 8.7-14.2 2.8-3.5 6.1-7 9.5-10.2 3.9-3.7 8.2-6.8 12.2-9.6C48.8 2.2 60 1.5 63.1 1.5c1.2 0 2.7.1 4.4.2 5.1.4 7.4 1.2 8.9 1.7l.5.2c7.6 2.7 11.5 6.3 13.6 8.3l.3.3c2.8 2.6 7.1 6.9 11.1 13.6 2.5 4.1 5.3 11 7.7 19.2.7 2.5 1.2 11.2.9 14.7-.2 2.6-1 7.8-3.6 13.3-2.1 4.4-4.5 9.4-8.3 13-1.9 1.8-4.4 3.5-6.9 4.7-2.9 1.4-8.2 2.1-8.4 2.1-4.8 0-6.3-.8-7.4-2.4-.7-.9-.4-1.6-.2-1.9l.5-.8.9-2.1c.5-1.2.9-2.4 1.7-3.6l.1-.2c.7-1 1.4-1.9 2.3-1.9l.1-.2h.9l.3.3s.1.1.2.1c.3.3.7.6 1.1.6h.2c.8-.2 1.7-.6 3.8-3.3 2.8-3.6 3.6-5.2 4.5-7 .2-.4.3-.7.5-1.1 1.4-2.7 2.5-7.6 1.8-12.4-.7-4.9-1.6-7.5-4.8-10.3-1.3-1.1-2.7-1.4-3.5-1.4-.4 0-.6 0-.7.1h-.1v.8c0 .1.6 9.3.4 11.8-.3 5-.8 8.8-1.6 11.3-.9 2.9-2.6 6.1-4.7 9.9-.2.3-.4.7-.6 1.1-.3.5-.6 1.1-.7 1.3-.1.2-2.4 5.1-2.5 5.2v.1c-1.3 2.2-2.1 3.5-2.6 4.1-.7 1-2 2.5-2.8 3.2-1.3 1.4-3 2.2-5 2.2h-9.1c-2.1 0-3.6-1-4.2-2.8l-.3-.9c-.4-1.3-1-3.3-2.1-4.7-1.3-1.7-2.8-2.6-6-2.7h-.2c-2.3 0-5.1.1-5.8.4-1.5.5-1.5.5-1.7 1.6-.4 1.5-.3 1.9-.1 3.5v.2c.4 3.2-.8 4.5-1.9 5.1-.7.4-1.6.4-3 .4h-.7c-1.3.4-3.6.4-5 .4zm17.9-69.7c-.4 1.7-.5 3.5-.4 5.2.1 2.5.8 6.4 3.6 10.9 2.8 4.6 7.1 6.9 8.8 7.7 1 .5 2.3.8 3.7 1-.5-.2-1-.3-1.5-.5-4.7-2.1-6.1-3.7-7.5-5.4l-.1-.1c-2-2.3-3.4-4.7-4.8-8.2-1.7-4.3-2-7.2-1.8-10.3v-.3zm27.6-4.4c-.2.3-.6 1.8-.1 4.1.5 2.2 1.4 3.4 1.8 3.6.3-.4.6-1.8.1-4.1-.5-2.2-1.4-3.4-1.8-3.6z" id="Shape" fill="#859900"/><path d="M63.2 2.5c1.2 0 2.6.1 4.3.2 5 .3 7.2 1.2 8.7 1.7.2.1.4.1.5.2 7.8 2.8 11.6 6.5 13.5 8.4 2.8 2.6 7 6.8 11 13.4 2.5 4.1 5.2 10.8 7.6 19v.1c.7 2.3 1.1 10.9.8 14.2-.2 2.6-1 7.6-3.5 13-2.1 4.3-4.4 9.2-8.1 12.7-1.9 1.7-4.2 3.3-6.7 4.5-2.8 1.3-7.9 2-7.9 2-4.7 0-5.8-.9-6.6-2-.4-.6-.2-.7-.2-.7l.5-.9c.9-2 1.4-3.8 2.5-5.6.3-.4 1.1-1.6 1.6-1.6h.3c.4 0 .3-.2.3-.2l.2.2c.5.4 1.1.8 1.7.8h.4c1.2-.3 2.3-1 4.4-3.6 2.9-3.7 3.7-5.3 4.6-7.2.2-.3.3-.7.5-1.1 1.4-2.9 2.6-8 1.9-13-.7-5-1.7-8-5.1-10.9-1.6-1.3-3.1-1.6-4.2-1.6-.6 0-1 .1-1.1.1-.1 0-.1.1-.2.1-.2.1-.3.2-.3.3-.1.2-.2.5-.2 1.5.1 1.8.5 9.6.4 11.7-.3 4.9-.8 8.6-1.5 11.1-.8 2.7-2.3 5.6-4.6 9.7-.3.5-1 1.9-1.3 2.4l-2.5 5.3c-1.2 1.9-2.1 3.4-2.5 4.1-.8 1.2-2.6 3.1-2.7 3.2-.4.4-1.7 1.8-4.3 1.8h-9.2c-1.6 0-2.7-.8-3.2-2.1l-.3-.9c-.4-1.4-1-3.4-2.3-5-1.5-1.8-3.2-3-6.8-3h-.2c-1.2 0-5 0-6.1.4-1.8.6-2 .9-2.4 2.3-.4 1.6-.3 2.2-.1 3.8v.2c.3 2.6-.6 3.6-1.4 4.1-.5.3-1.3.3-2.6.3H18c-1.1 0-2.3-.6-3.7-1.9-2.3-2.1-3.7-4.7-4.7-6.8-1-2.1-3.3-7.7-4-9.9-2.1-6.6-3.2-11.2-3.1-13.7 0-.3-.1-3.5.4-6.3.4-2.6.9-6.5 2.8-11.3 1.8-4.9 3.6-7.7 8.6-13.9 2.8-3.4 6-6.9 9.4-10 3.9-3.6 8.1-6.7 12.1-9.5C49.2 3.2 60.3 2.5 63.2 2.5zm-9 10s-2.4 1-5.4 3.2c-1.8 1.4-3.4 3.3-4.7 5.9-.6 1.1-2.6 5.2-2.3 10.8.2 3.1 1.1 7.1 3.7 11.4 2.4 4 6.2 6.7 9.3 8.1 2 .9 4.9 1.4 7.7 1.4 1.5 0 2.9-.1 4.2-.4 3.5-.8 6.1-2.3 7.3-3.1 1.2-.8 3-2.5 3-2.5s-1.9 1.4-3.3 2c-1.4.7-3.6 1.4-5.2 1.7-.9.2-2.9.6-5.2.6-1.7 0-3.6-.2-5.4-1-4.6-2.1-5.9-3.6-7.3-5.2-1.7-2-3.1-4.2-4.6-7.9-1.6-4-1.9-6.8-1.8-9.9.1-2.9.6-4.3 1.3-5.8.6-1.5 1.9-3.6 3.5-5.2 1.7-2 5.1-4 5.2-4.1zm18.4 19h.2c1.1-.2 1.4-2.6.8-5.3-.6-2.5-1.8-4.5-2.8-4.5h-.2c-1.1.2-1.4 2.6-.8 5.3.5 2.6 1.8 4.5 2.8 4.5zM63.2.5C50.6.5 38.3 8.4 34.8 10.8c-4 2.8-8.3 5.9-12.3 9.6-3.4 3.2-6.7 6.7-9.6 10.3-5.2 6.4-7 9.4-8.9 14.5-1.9 5.1-2.5 9.1-2.9 11.7-.4 2.9-.4 6.1-.4 6.6 0 2.7 1 7.4 3.2 14.3.7 2.3 3 7.9 4.1 10.2 1.1 2.2 2.6 5.1 5.1 7.4 1.8 1.7 3.4 2.4 5 2.4h12.6c1.3 0 2.5 0 3.5-.6 1.1-.6 2.9-2.2 2.4-6.1v-.2c-.2-1.6-.2-1.8.1-3.1.1-.2.1-.4.1-.5.2-.1.5-.2 1-.4s3-.3 5.4-.3h.2c2.9 0 4 .8 5.3 2.3 1 1.3 1.6 3.1 1.9 4.3.1.4.2.7.3 1 .7 2.2 2.6 3.5 5.1 3.5h9.2c2.3 0 4.2-.9 5.7-2.5.9-1 2.2-2.4 2.9-3.4.2-.3.5-.7.8-1.3.1.1.2.3.3.4 1.3 1.8 3.1 2.8 8.3 2.8.4 0 5.8-.8 8.8-2.2 2.6-1.3 5.2-3 7.2-4.9 4-3.7 6.4-8.8 8.5-13.3 2.7-5.6 3.5-11 3.7-13.7.3-3.3-.1-12.2-.9-15v-.1c-2.5-8.3-5.3-15.2-7.8-19.4-4.2-6.8-8.5-11.2-11.3-13.8l-.3-.3C89 9 85 5.3 77.2 2.5l-.2-.1-.3-.1c-1.6-.6-4-1.4-9.2-1.8-1.6.1-3.1 0-4.3 0zm18 78.4c1.9-3.4 3.4-6.4 4.2-9.1.8-2.6 1.3-6.5 1.6-11.5.1-2.4-.3-10.6-.4-11.8.7 0 1.6.3 2.6 1.1 3 2.6 3.8 4.9 4.5 9.7.7 4.6-.4 9.3-1.7 11.8-.2.4-.4.7-.5 1.1-.9 1.8-1.6 3.3-4.4 6.8-1.8 2.3-2.6 2.7-3.2 2.9-.1 0-.2-.1-.4-.3l-.1-.1-.6-.5h-1.6v-.1z" id="Shape" fill="#032C36"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,4 @@
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@plugin "@tailwindcss/typography";
|
||||
@@ -0,0 +1,104 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const dbVersions = ref(['18', '17', '16', '15', '14', '13', '12', '11', '10'])
|
||||
const dbVersion = ref('18')
|
||||
|
||||
const osTypes = ref(['Linux', 'Windows', 'OS X'])
|
||||
const osType = ref('Linux')
|
||||
|
||||
const dbTypes = ref([
|
||||
'Web application',
|
||||
'Online transaction processing system',
|
||||
'Data warehouse',
|
||||
'Desktop application',
|
||||
'Mixed type of application',
|
||||
])
|
||||
const dbType = ref('Web application')
|
||||
|
||||
const totalRam = ref()
|
||||
const ramUnits = ref(['TB', 'GB', 'MB'])
|
||||
const ramUnit = ref('GB')
|
||||
|
||||
const totalCPUs = ref(1)
|
||||
|
||||
const totalConnections = ref(100)
|
||||
|
||||
const dataStorages = ref([
|
||||
'NVME storage',
|
||||
'SSD storage',
|
||||
'Network (SAN) storage',
|
||||
'HDD storage',
|
||||
])
|
||||
const dataStorage = ref('SSD storage')
|
||||
|
||||
const dataSizes = ref(['Less than RAM', 'RAM - 3xRAM', 'More than 3xRAM'])
|
||||
const dataSize = ref('Less than RAM')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm class="space-y-4">
|
||||
<UFormField
|
||||
label="DB version"
|
||||
help="PostgreSQL version (find out via 'SELECT version();')"
|
||||
>
|
||||
<USelect v-model="dbVersion" :items="dbVersions" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="OS Type"
|
||||
help="Operation system of the PostgreSQL server host"
|
||||
>
|
||||
<USelect v-model="osType" :items="osTypes" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="DB type"
|
||||
help="What type of application is PostgreSQL used for"
|
||||
>
|
||||
<USelect v-model="dbType" :items="dbTypes" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="Total memory (RAM)"
|
||||
help="How much memory can PostgreSQL use"
|
||||
required
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<UInputNumber
|
||||
v-model="totalRam"
|
||||
class="grow"
|
||||
placeholder="Total memory (RAM) (* required)"
|
||||
/>
|
||||
<USelect v-model="ramUnit" :items="ramUnits" />
|
||||
</div>
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="Number of CPUs"
|
||||
help="Number of CPUs, which PostgreSQL can use. CPUs = N threads per core * N cores per socket * N sockets"
|
||||
>
|
||||
<UInputNumber
|
||||
v-model="totalCPUs"
|
||||
class="w-full"
|
||||
placeholder="Number of CPUs"
|
||||
/>
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="Number of connections"
|
||||
help="Maximum number of PostgreSQL client connections"
|
||||
>
|
||||
<UInputNumber
|
||||
v-model="totalConnections"
|
||||
class="w-full"
|
||||
placeholder="Number of connections"
|
||||
/>
|
||||
</UFormField>
|
||||
<UFormField label="Data storage" help="Type of data storage device">
|
||||
<USelect v-model="dataStorage" :items="dataStorages" class="w-full" />
|
||||
</UFormField>
|
||||
<UFormField
|
||||
label="Total data size"
|
||||
help="Estimated total size of your database compared to total server RAM"
|
||||
>
|
||||
<USelect v-model="dataSize" :items="dataSizes" class="w-full" />
|
||||
</UFormField>
|
||||
<UButton size="xl" label="GENERATE" block type="submit" class="h-16" />
|
||||
</UForm>
|
||||
</template>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<article class="prose dark:prose-invert max-w-full">
|
||||
<p>
|
||||
Please provide basic information about the configuration of your hardware
|
||||
on which the PostgreSQL database is running. Results will be calculated
|
||||
after clicking the "Generate" button
|
||||
</p>
|
||||
|
||||
<p>More information about "DB Type" setting:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Web Application (web)</p>
|
||||
<ul>
|
||||
<li>Typically CPU-bound</li>
|
||||
<li>DB much smaller than RAM</li>
|
||||
<li>90% or more simple queries</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Online Transaction Processing (oltp)</p>
|
||||
<ul>
|
||||
<li>Typically CPU- or I/O-bound</li>
|
||||
<li>DB slightly larger than RAM to 1TB</li>
|
||||
<li>20-40% small data write queries</li>
|
||||
<li>Some long transactions and complex read queries</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Data Warehouse (dw)</p>
|
||||
<ul>
|
||||
<li>Typically I/O- or RAM-bound</li>
|
||||
<li>Large bulk loads of data</li>
|
||||
<li>Large complex reporting queries</li>
|
||||
<li>Also called "Decision Support" or "Business Intelligence"</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Desktop application</p>
|
||||
<ul>
|
||||
<li>Not a dedicated database</li>
|
||||
<li>A general workstation, perhaps for a developer</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Mixed type of application</p>
|
||||
<ul>
|
||||
<li>Mixed DW and OLTP characteristics</li>
|
||||
<li>wide mixture of queries</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</template>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import Logo from '/favicon.svg?url'
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: 'Home',
|
||||
to: '/',
|
||||
},
|
||||
{
|
||||
label: 'How it works',
|
||||
to: '/about',
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UHeader>
|
||||
<template #title>
|
||||
<img
|
||||
:src="Logo"
|
||||
alt="PGTune"
|
||||
class="h-(--ui-header-height) w-auto py-2"
|
||||
/>
|
||||
<h1 class="text-xl font-bold self-center ml-2">PGTune</h1>
|
||||
</template>
|
||||
|
||||
<UNavigationMenu :items />
|
||||
|
||||
<template #right>
|
||||
<UTooltip text="Open on Gitea" :kbds="['meta', 'G']">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://gitea.minis.mudrov.tech"
|
||||
target="_blank"
|
||||
icon="simple-icons:gitea"
|
||||
aria-label="Gitea"
|
||||
/>
|
||||
</UTooltip>
|
||||
|
||||
<UColorModeButton class="cursor-pointer" />
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<UNavigationMenu :items orientation="vertical" />
|
||||
</template>
|
||||
</UHeader>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<div>To be refactored</div>
|
||||
</template>
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useHead } from '@unhead/vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { useDark } from '@vueuse/core'
|
||||
import { convertOklchToHex } from '../utils/oklch2hex'
|
||||
|
||||
export function useThemeColor() {
|
||||
const isDark = useDark()
|
||||
const currentBgColor = ref()
|
||||
|
||||
const getBGColor = () => {
|
||||
const rootStyles = window.getComputedStyle(document.documentElement)
|
||||
// Получаем текущее значение переменной --ui-bg
|
||||
const c = rootStyles.getPropertyValue('--ui-bg').trim()
|
||||
currentBgColor.value = c.startsWith('oklch') ? convertOklchToHex(c) : c
|
||||
}
|
||||
|
||||
const head = useHead()
|
||||
|
||||
watch(
|
||||
isDark,
|
||||
(n, o) => {
|
||||
if (n !== o) {
|
||||
setTimeout(() => {
|
||||
getBGColor()
|
||||
head.patch({
|
||||
meta: [
|
||||
{
|
||||
name: 'theme-color',
|
||||
content: currentBgColor.value,
|
||||
},
|
||||
],
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { useRegisterSW } from 'virtual:pwa-register/vue'
|
||||
import { watch } from 'vue'
|
||||
|
||||
const intervalMS = import.meta.env.DEV ? 5 * 60 * 1000 : 60 * 60 * 1000
|
||||
|
||||
export function useUpdateApp() {
|
||||
const { needRefresh, updateServiceWorker } = useRegisterSW({
|
||||
onRegistered(r) {
|
||||
r &&
|
||||
setInterval(() => {
|
||||
r.update()
|
||||
}, intervalMS)
|
||||
},
|
||||
})
|
||||
const toast = useToast()
|
||||
|
||||
if (import.meta.env.DEV) console.log({ SWNeedRefresh: needRefresh.value })
|
||||
|
||||
async function close(e) {
|
||||
needRefresh.value = false
|
||||
e?.stopPropagation()
|
||||
}
|
||||
|
||||
// New version
|
||||
|
||||
watch(
|
||||
() => needRefresh,
|
||||
(n) => {
|
||||
if (n) {
|
||||
toast.add({
|
||||
title: 'Update',
|
||||
description:
|
||||
'A newer version of the app is available. Click the “Update” button to install.',
|
||||
duration: 0,
|
||||
icon: 'lucide:cloud-download',
|
||||
close: false,
|
||||
actions: [
|
||||
{
|
||||
icon: 'lucide:arrow-big-up-dash',
|
||||
label: 'Update',
|
||||
onClick: updateServiceWorker,
|
||||
color: 'error',
|
||||
},
|
||||
|
||||
{
|
||||
icon: 'lucide:x',
|
||||
label: 'Dismiss',
|
||||
onClick: close,
|
||||
variant: 'outline',
|
||||
},
|
||||
],
|
||||
onEscapeKeyDown: close,
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import '/src/assets/styles/app.css'
|
||||
import App from './App.vue'
|
||||
import { router } from '/router.js'
|
||||
import ui from '@nuxt/ui/vue-plugin'
|
||||
import { createHead } from '@unhead/vue/client'
|
||||
|
||||
createApp(App)
|
||||
.use(router)
|
||||
.use(createPinia())
|
||||
.use(ui)
|
||||
.use(createHead())
|
||||
.mount('#app')
|
||||
@@ -0,0 +1,24 @@
|
||||
<script setup>
|
||||
import HeroImage from '/src/assets/images/logo.svg'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article class="prose dark:prose-invert max-w-full">
|
||||
<img :src="HeroImage" class="w-full h-auto rounded-2xl" alt="PGTune" />
|
||||
<p>
|
||||
<strong>PGTune</strong> calculate configuration for PostgreSQL based on
|
||||
the maximum performance for a given hardware configuration. It isn't a
|
||||
<a
|
||||
href="https://en.wikipedia.org/wiki/No_Silver_Bullet"
|
||||
target="_blank"
|
||||
class="font-medium underline"
|
||||
>silver bullet</a
|
||||
>
|
||||
for the optimization settings of PostgreSQL. Many settings depend not only
|
||||
on the hardware configuration, but also on the size of the database, the
|
||||
number of clients and the complexity of queries. An optimal configuration
|
||||
of the database can only be made given all these parameters are taken into
|
||||
account.
|
||||
</p>
|
||||
</article>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup>
|
||||
import ConfigurationForm from '../components/configurationForm.vue'
|
||||
import DefaultDescription from '../components/defaultDescription.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid lg:grid-cols-2 gap-16">
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-xl font-bold">Parameters of your system</h3>
|
||||
<ConfigurationForm />
|
||||
</div>
|
||||
<DefaultDescription />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,479 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { reactive, computed } from 'vue'
|
||||
import { useSettingsStore } from './useSettingsStore'
|
||||
import {
|
||||
DEFAULT_DB_VERSION,
|
||||
OS_LINUX,
|
||||
OS_WINDOWS,
|
||||
OS_MAC,
|
||||
DB_TYPE_WEB,
|
||||
DB_TYPE_OLTP,
|
||||
DB_TYPE_DW,
|
||||
DB_TYPE_DESKTOP,
|
||||
DB_TYPE_MIXED,
|
||||
SIZE_UNIT_GB,
|
||||
HARD_DRIVE_SSD,
|
||||
HARD_DRIVE_HDD,
|
||||
HARD_DRIVE_SAN,
|
||||
HARD_DRIVE_NVME,
|
||||
DB_SIZE_LESS_RAM,
|
||||
DB_SIZE_MID_RAM,
|
||||
DB_SIZE_GREATER_RAM,
|
||||
} from '/src/utils/constants/configuration.js'
|
||||
|
||||
const SIZE_UNIT_MAP = {
|
||||
KB: 1024,
|
||||
MB: 1048576,
|
||||
GB: 1073741824,
|
||||
TB: 1099511627776,
|
||||
PB: 1125899906842624,
|
||||
}
|
||||
|
||||
const DEFAULT_DB_SETTINGS = {
|
||||
default: {
|
||||
['max_worker_processes']: 8,
|
||||
['max_parallel_workers_per_gather']: 2,
|
||||
['max_parallel_workers']: 8,
|
||||
},
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
dbVersion: DEFAULT_DB_VERSION,
|
||||
osType: OS_LINUX,
|
||||
dbType: DB_TYPE_WEB,
|
||||
totalMemory: null,
|
||||
totalMemoryUnit: SIZE_UNIT_GB,
|
||||
cpuNum: null,
|
||||
connectionNum: null,
|
||||
hdType: HARD_DRIVE_SSD,
|
||||
dbSize: DB_SIZE_MID_RAM,
|
||||
}
|
||||
|
||||
const selectCheckpointCompletionTarget = 0.9
|
||||
|
||||
export const useConfigurationStore = defineStore('configuration', () => {
|
||||
const state = reactive({ ...initialState })
|
||||
|
||||
const getConfiguration = computed(() => ({ ...state }))
|
||||
const getDBVersion = computed(() => getConfiguration.value.dbVersion)
|
||||
const getOSType = computed(() => getConfiguration.value.osType)
|
||||
const getDBType = computed(() => getConfiguration.value.dbType)
|
||||
const getTotalMemory = computed(() => getConfiguration.value.totalMemory)
|
||||
const getTotalMemoryUnit = computed(
|
||||
() => getConfiguration.value.totalMemoryUnit,
|
||||
)
|
||||
const getCPUNum = computed(() => getConfiguration.value.cpuNum || null)
|
||||
const getConnectionNum = computed(
|
||||
() => getConfiguration.value.connectionNum || null,
|
||||
)
|
||||
const getHDType = computed(() => getConfiguration.value.hdType)
|
||||
const getDBSize = computed(() => getConfiguration.value.dbSize)
|
||||
const getTotalMemoryInBytes = computed(
|
||||
() => getTotalMemory.value * SIZE_UNIT_MAP[getTotalMemoryUnit.value],
|
||||
)
|
||||
const getTotalMemoryInKb = computed(
|
||||
() => getTotalMemoryInBytes.value / SIZE_UNIT_MAP['KB'],
|
||||
)
|
||||
const getDBDefaultValues = computed(
|
||||
() => DEFAULT_DB_SETTINGS[getDBVersion] || DEFAULT_DB_VERSION.default,
|
||||
)
|
||||
const getIsConfigured = computed(() => !!getTotalMemory.value)
|
||||
const getMaxConnections = computed(
|
||||
() =>
|
||||
getConnectionNum.value ??
|
||||
{
|
||||
[DB_TYPE_WEB]: 200,
|
||||
[DB_TYPE_OLTP]: 300,
|
||||
[DB_TYPE_DW]: 40,
|
||||
[DB_TYPE_DESKTOP]: 20,
|
||||
[DB_TYPE_MIXED]: 100,
|
||||
}[getDBType.value],
|
||||
)
|
||||
const getSharedBuffers = computed(() => {
|
||||
const totalMemoryKb = getTotalMemoryInKb.value
|
||||
|
||||
const val = {
|
||||
[DB_TYPE_WEB]: Math.floor(totalMemoryKb / 4),
|
||||
[DB_TYPE_OLTP]: Math.floor(totalMemoryKb / 4),
|
||||
[DB_TYPE_DW]: Math.floor(totalMemoryKb / 4),
|
||||
[DB_TYPE_DESKTOP]: Math.floor(totalMemoryKb / 16),
|
||||
[DB_TYPE_MIXED]: Math.floor(totalMemoryKb / 4),
|
||||
}[getDBType.value]
|
||||
|
||||
return getDBVersion.value < 10 && OS_WINDOWS === getOSType.value
|
||||
? Math.min(val, (512 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'])
|
||||
: val
|
||||
})
|
||||
const getHugePages = computed(() =>
|
||||
getOSType.value !== OS_MAC &&
|
||||
getSharedBuffers.value >= (2 * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB']
|
||||
? 'try'
|
||||
: 'off',
|
||||
)
|
||||
const getEffectiveCacheSize = computed(() => {
|
||||
const totalMemoryKb = getTotalMemoryInKb.value
|
||||
|
||||
return {
|
||||
[DB_TYPE_WEB]: Math.floor((totalMemoryKb * 3) / 4),
|
||||
[DB_TYPE_OLTP]: Math.floor((totalMemoryKb * 3) / 4),
|
||||
[DB_TYPE_DW]: Math.floor((totalMemoryKb * 3) / 4),
|
||||
[DB_TYPE_DESKTOP]: Math.floor(totalMemoryKb / 4),
|
||||
[DB_TYPE_MIXED]: Math.floor((totalMemoryKb * 3) / 4),
|
||||
}[getDBType.value]
|
||||
})
|
||||
const getMaintenanceWorkMem = computed(() => {
|
||||
const totalMemoryKb = getTotalMemoryInKb.value
|
||||
// 1. Вычисляем базовое значение по типу БД
|
||||
const baseMemValue = {
|
||||
[DB_TYPE_WEB]: Math.floor(totalMemoryKb / 16),
|
||||
[DB_TYPE_OLTP]: Math.floor(totalMemoryKb / 16),
|
||||
[DB_TYPE_DW]: Math.floor(totalMemoryKb / 8),
|
||||
[DB_TYPE_DESKTOP]: Math.floor(totalMemoryKb / 16),
|
||||
[DB_TYPE_MIXED]: Math.floor(totalMemoryKb / 16),
|
||||
}[getDBType.value]
|
||||
|
||||
// 2. Определяем, действует ли строгое ограничение для Windows
|
||||
const isLegacyWindows =
|
||||
OS_WINDOWS === getOSType.value && getDBVersion.value <= 17
|
||||
|
||||
// 3. Задаем лимит памяти (2GB для старых Windows, иначе 8GB)
|
||||
const maxLimitGb = isLegacyWindows ? 2 : 8
|
||||
const memoryLimit = (maxLimitGb * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB']
|
||||
|
||||
// 4. Если превышен лимит на Windows, вычитаем 1MB, иначе просто возвращаем минимум
|
||||
return baseMemValue >= memoryLimit && isLegacyWindows
|
||||
? memoryLimit - (1 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB']
|
||||
: Math.min(baseMemValue, memoryLimit)
|
||||
})
|
||||
const getCheckpointSegment = computed(() => [
|
||||
{
|
||||
key: 'min_wal_size',
|
||||
value: {
|
||||
[DB_TYPE_WEB]: (1024 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_OLTP]: (2048 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_DW]: (4096 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_DESKTOP]: (100 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_MIXED]: (1024 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
}[getDBType.value],
|
||||
},
|
||||
{
|
||||
key: 'max_wal_size',
|
||||
value: {
|
||||
[DB_TYPE_WEB]: (4096 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_OLTP]: (8192 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_DW]: (16384 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_DESKTOP]: (2048 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
[DB_TYPE_MIXED]: (4096 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'],
|
||||
}[getDBType.value],
|
||||
},
|
||||
])
|
||||
const getWalBuffers = computed(() => {
|
||||
const KB_IN_MB = SIZE_UNIT_MAP['MB'] / SIZE_UNIT_MAP['KB']
|
||||
const baseWal = Math.floor((3 * sharedBuffersValue) / 100)
|
||||
|
||||
return Math.max(
|
||||
32,
|
||||
baseWal > 14 * KB_IN_MB
|
||||
? 16 * KB_IN_MB
|
||||
: Math.min(baseWal, 16 * KB_IN_MB),
|
||||
)
|
||||
})
|
||||
const getDefaultStatisticsTarget = computed(
|
||||
() =>
|
||||
({
|
||||
[DB_TYPE_WEB]: 100,
|
||||
[DB_TYPE_OLTP]: 100,
|
||||
[DB_TYPE_DW]: 500,
|
||||
[DB_TYPE_DESKTOP]: 100,
|
||||
[DB_TYPE_MIXED]: 100,
|
||||
})[getDBType.value],
|
||||
)
|
||||
const getRandomPageCost = computed(() => {
|
||||
// Если база помещается в RAM, поиск на диске не важен
|
||||
if (getDBSize.value === DB_SIZE_LESS_RAM) return 1.1
|
||||
|
||||
// HDD или аналитические БД (DW), не помещающиеся в RAM, требуют дефолтной стоимости (4.0)
|
||||
if (getHDType.value === HARD_DRIVE_HDD || getDBType.value === DB_TYPE_DW)
|
||||
return 4
|
||||
|
||||
return 1.1
|
||||
})
|
||||
const getEffectiveIOConcurrency = computed(() =>
|
||||
getOSType.value === OS_LINUX
|
||||
? {
|
||||
[HARD_DRIVE_HDD]: 2,
|
||||
[HARD_DRIVE_SSD]: 200,
|
||||
[HARD_DRIVE_SAN]: 300,
|
||||
[HARD_DRIVE_NVME]: 1000,
|
||||
}[getHDType.value]
|
||||
: null,
|
||||
)
|
||||
const getParallelSettings = computed(() => {
|
||||
const cpuNum = getCPUNum.value
|
||||
const dbVersion = getDBVersion.value
|
||||
const dbType = getDBType.value
|
||||
|
||||
if (!cpuNum || cpuNum < 4) return []
|
||||
|
||||
const halfCpu = Math.ceil(cpuNum / 2)
|
||||
const cappedWorkers = Math.min(4, halfCpu) // Ограничение в 4 воркера
|
||||
|
||||
const config = [
|
||||
{ key: 'max_worker_processes', value: cpuNum },
|
||||
{
|
||||
key: 'max_parallel_workers_per_gather',
|
||||
value: dbType !== DB_TYPE_DW ? cappedWorkers : halfCpu,
|
||||
},
|
||||
...(dbVersion >= 10
|
||||
? [{ key: 'max_parallel_workers', value: cpuNum }]
|
||||
: []),
|
||||
...(dbVersion >= 11
|
||||
? [{ key: 'max_parallel_maintenance_workers', value: cappedWorkers }]
|
||||
: []),
|
||||
]
|
||||
|
||||
return config
|
||||
})
|
||||
const getWorkMem = computed(() => {
|
||||
const dbDefaultValues = getDBDefaultValues.value
|
||||
|
||||
const maxWorkerProcesses = getParallelSettings.value.find(
|
||||
(param) => param['key'] === 'max_worker_processes',
|
||||
)
|
||||
|
||||
const parallelForWorkMem =
|
||||
maxWorkerProcesses?.value > 0
|
||||
? maxWorkerProcesses.value
|
||||
: dbDefaultValues['max_worker_processes'] > 0
|
||||
? dbDefaultValues['max_worker_processes']
|
||||
: 1
|
||||
|
||||
// Базовая формула расчёта work_mem
|
||||
const baseWorkMem =
|
||||
(getTotalMemoryInKb.value - sharedBuffersValue) /
|
||||
((maxConnectionsValue + parallelForWorkMem) * 3)
|
||||
|
||||
// Коэффициенты для разных типов БД
|
||||
const dbTypeCoefficients = {
|
||||
[DB_TYPE_WEB]: 1,
|
||||
[DB_TYPE_OLTP]: 1,
|
||||
[DB_TYPE_DW]: 0.5,
|
||||
[DB_TYPE_DESKTOP]: 1 / 6,
|
||||
[DB_TYPE_MIXED]: 0.5,
|
||||
}
|
||||
|
||||
// Базовая формула с коэффициентом типа БД
|
||||
const baseWorkMemResult = Math.floor(
|
||||
baseWorkMem * dbTypeCoefficients[dbType],
|
||||
)
|
||||
|
||||
// Корректировка в зависимости от размера БД относительно RAM
|
||||
const sizeAdjustedWorkMem =
|
||||
dbSize === DB_SIZE_LESS_RAM
|
||||
? Math.floor(baseWorkMemResult * 1.3)
|
||||
: dbSize === DB_SIZE_GREATER_RAM
|
||||
? Math.floor(baseWorkMemResult * 0.9)
|
||||
: baseWorkMemResult
|
||||
|
||||
// Применяем минимальное значение 4 MB
|
||||
const minWorkMem = 4 * 1024 // 4 MB в KB
|
||||
const workMemWithMin = Math.max(sizeAdjustedWorkMem, minWorkMem)
|
||||
|
||||
// Применяем ограничение для Windows 64-bit до PostgreSQL 17
|
||||
const winMemoryLimit = 2 * 1024 * 1024 - 1024 // ~2GB - 1MB в KB
|
||||
const workMemResult =
|
||||
osType === OS_WINDOWS && dbVersion <= 17
|
||||
? Math.min(workMemWithMin, winMemoryLimit)
|
||||
: workMemWithMin
|
||||
|
||||
return workMemResult
|
||||
})
|
||||
const getWalLevel = computed(() =>
|
||||
getDBType.value === DB_TYPE_DESKTOP
|
||||
? [
|
||||
{
|
||||
key: 'wal_level',
|
||||
value: 'minimal',
|
||||
},
|
||||
// max_wal_senders must be 0 when wal_level=minimal
|
||||
{
|
||||
key: 'max_wal_senders',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
: [],
|
||||
)
|
||||
const getJit = computed(() =>
|
||||
getDBVersion.value >= 12 &&
|
||||
[DB_TYPE_WEB, DB_TYPE_OLTP, DB_TYPE_MIXED].includes(getDBType.value)
|
||||
? 'off'
|
||||
: null,
|
||||
)
|
||||
const getWalCompression = computed(() => {
|
||||
const ver = getDBVersion.value
|
||||
|
||||
return ver >= 15 ? 'lz4' : ver >= 10 ? 'on' : null
|
||||
})
|
||||
const getAutovacuumMaxWorkers = computed(() => {
|
||||
const cpuNum = getCPUNum.value
|
||||
|
||||
if (!cpuNum) return null
|
||||
if (cpuNum >= 32) return 5
|
||||
if (cpuNum >= 16) return 4
|
||||
return null
|
||||
})
|
||||
const getAutovacuumWorkMem = computed(() => {
|
||||
const threshold = (2 * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB']
|
||||
|
||||
// Windows 64-bit has a strict 2GB limit up to PostgreSQL 17
|
||||
const winMemoryLimit =
|
||||
(2 * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB'] -
|
||||
(1 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB']
|
||||
|
||||
const isWindowsWithLowLimit =
|
||||
getOSType.value === OS_WINDOWS && getDBVersion.value <= 17
|
||||
|
||||
if (getMaintenanceWorkMem.value < threshold) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isWindowsWithLowLimit) {
|
||||
return Math.min(threshold, winMemoryLimit)
|
||||
}
|
||||
|
||||
return threshold
|
||||
})
|
||||
const getIOMethod = computed(() =>
|
||||
getDBVersion.value < 18
|
||||
? null
|
||||
: getOSType.value === OS_LINUX
|
||||
? 'io_uring'
|
||||
: 'worker',
|
||||
)
|
||||
const getIOWorkers = computed(() => {
|
||||
if (dbVersion < 18 || !cpuNum || ioMethod === 'io_uring') return null
|
||||
|
||||
return Math.max(4, Math.min(32, Math.floor(cpuNum / 4))) || null
|
||||
})
|
||||
const getWarningInfoMessages = computed(() => {
|
||||
const warnings = []
|
||||
const totalMemory = getTotalMemoryInBytes.value
|
||||
|
||||
// Memory warnings
|
||||
if (totalMemory < 256 * SIZE_UNIT_MAP['MB']) {
|
||||
warnings.push('this tool not being optimal', 'for low memory systems')
|
||||
} else if (totalMemory > 100 * SIZE_UNIT_MAP['GB']) {
|
||||
warnings.push(
|
||||
'this tool not being optimal',
|
||||
'for very high memory systems',
|
||||
)
|
||||
}
|
||||
|
||||
// Advanced features compilation warnings
|
||||
if (getWalCompression.value === 'lz4') {
|
||||
if (warnings.length > 0) warnings.push('')
|
||||
|
||||
warnings.push(
|
||||
'wal_compression = lz4 requires PostgreSQL',
|
||||
'to be compiled with --with-lz4',
|
||||
)
|
||||
}
|
||||
|
||||
if (getIOMethod.value === 'io_uring') {
|
||||
if (warnings.length > 0) warnings.push('')
|
||||
|
||||
warnings.push(
|
||||
'io_method = io_uring requires PostgreSQL',
|
||||
'to be compiled with --with-liburing',
|
||||
)
|
||||
}
|
||||
|
||||
// I/O Cost Warning for Analytical DBs
|
||||
if (
|
||||
getDBType.value === DB_TYPE_DW &&
|
||||
getHDType.value !== HARD_DRIVE_HDD &&
|
||||
getDBSize.value !== DB_SIZE_LESS_RAM
|
||||
) {
|
||||
const driveName = {
|
||||
[HARD_DRIVE_SSD]: 'SSDs',
|
||||
[HARD_DRIVE_NVME]: 'NVMe drives',
|
||||
[HARD_DRIVE_SAN]: 'SAN storage',
|
||||
}[hdType]
|
||||
|
||||
if (warnings.length > 0) warnings.push('')
|
||||
|
||||
warnings.push(
|
||||
`Cost parameters for Data Warehouses on ${driveName} are left at defaults`,
|
||||
'to avoid catastrophic index scan selections',
|
||||
'Monitor query planner behavior and adjust random_page_cost if necessary',
|
||||
)
|
||||
}
|
||||
|
||||
return warnings.length > 0 ? ['WARNING', ...warnings] : []
|
||||
})
|
||||
|
||||
function setState(payload) {
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
state.value = {
|
||||
dbVersion: parseFloat(payload.dbVersion),
|
||||
osType: payload.osType,
|
||||
dbType: payload.dbType,
|
||||
totalMemory: parseInt(payload.totalMemory, 10),
|
||||
totalMemoryUnit: payload.totalMemoryUnit,
|
||||
cpuNum: payload.cpuNum ? parseInt(payload.cpuNum, 10) : null,
|
||||
connectionNum: payload.connectionNum
|
||||
? parseInt(payload.connectionNum, 10)
|
||||
: null,
|
||||
hdType: payload.hdType,
|
||||
dbSize: payload.dbSize,
|
||||
}
|
||||
|
||||
settingsStore.onSubmitConfiguration()
|
||||
}
|
||||
|
||||
const $reset = () => ({ ...initialState })
|
||||
|
||||
return {
|
||||
state,
|
||||
|
||||
// Getters
|
||||
getDBVersion,
|
||||
getOSType,
|
||||
getDBType,
|
||||
getTotalMemory,
|
||||
getTotalMemoryUnit,
|
||||
getCPUNum,
|
||||
getConnectionNum,
|
||||
getHDType,
|
||||
getDBSize,
|
||||
getTotalMemoryInBytes,
|
||||
getTotalMemoryInKb,
|
||||
getDBDefaultValues,
|
||||
getIsConfigured,
|
||||
getMaxConnections,
|
||||
getSharedBuffers,
|
||||
getHugePages,
|
||||
getEffectiveCacheSize,
|
||||
getMaintenanceWorkMem,
|
||||
getCheckpointSegment,
|
||||
getWalBuffers,
|
||||
getDefaultStatisticsTarget,
|
||||
getRandomPageCost,
|
||||
getEffectiveIOConcurrency,
|
||||
getParallelSettings,
|
||||
getWorkMem,
|
||||
getWalLevel,
|
||||
getJit,
|
||||
getWalCompression,
|
||||
getAutovacuumMaxWorkers,
|
||||
getAutovacuumWorkMem,
|
||||
getIOMethod,
|
||||
getIOWorkers,
|
||||
getWarningInfoMessages,
|
||||
|
||||
// Actions
|
||||
setState,
|
||||
$reset,
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { reactive, computed } from 'vue'
|
||||
|
||||
import { TAB_CONFIG, APP_THEMES_LIGHT } from '/src/utils/constants/settings.js'
|
||||
|
||||
const initialState = {
|
||||
tabState: TAB_CONFIG,
|
||||
theme: APP_THEMES_LIGHT,
|
||||
}
|
||||
|
||||
export const useSettingsStore = defineStore('settings', () => {
|
||||
const state = reactive({ ...initialState })
|
||||
|
||||
const getSettings = computed(() => state)
|
||||
const getThemeSettings = computed(() => state.theme)
|
||||
const getTabSettings = computed(() => state.tabState)
|
||||
|
||||
function toggleTheme() {
|
||||
state.theme =
|
||||
APP_THEMES_LIGHT === state.theme ? APP_THEMES_DARK : APP_THEMES_LIGHT
|
||||
}
|
||||
function onSubmitConfiguration() {
|
||||
state.tabState = TAB_CONFIG
|
||||
}
|
||||
function $reset() {
|
||||
state = { ...initialState }
|
||||
}
|
||||
return {
|
||||
// STATE
|
||||
state,
|
||||
|
||||
// GETTERS
|
||||
getSettings,
|
||||
getThemeSettings,
|
||||
getTabSettings,
|
||||
toggleTheme,
|
||||
|
||||
// ACTIONS
|
||||
onSubmitConfiguration,
|
||||
$reset,
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useConfigurationStore = defineStore('sw', () => {
|
||||
const state = ref()
|
||||
|
||||
return { state }
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
// postgresql versions
|
||||
export const DEFAULT_DB_VERSION = 18
|
||||
export const DB_VERSIONS = [DEFAULT_DB_VERSION, 17, 16, 15, 14, 13, 12, 11, 10]
|
||||
// os types
|
||||
export const OS_LINUX = 'linux'
|
||||
export const OS_WINDOWS = 'windows'
|
||||
export const OS_MAC = 'mac'
|
||||
// db types
|
||||
export const DB_TYPE_WEB = 'web'
|
||||
export const DB_TYPE_OLTP = 'oltp'
|
||||
export const DB_TYPE_DW = 'dw'
|
||||
export const DB_TYPE_DESKTOP = 'desktop'
|
||||
export const DB_TYPE_MIXED = 'mixed'
|
||||
// size units
|
||||
export const SIZE_UNIT_MB = 'MB'
|
||||
export const SIZE_UNIT_GB = 'GB'
|
||||
export const SIZE_UNIT_TB = 'TB'
|
||||
// harddrive types
|
||||
export const HARD_DRIVE_SSD = 'ssd'
|
||||
export const HARD_DRIVE_SAN = 'san'
|
||||
export const HARD_DRIVE_HDD = 'hdd'
|
||||
export const HARD_DRIVE_NVME = 'nvme'
|
||||
// database size vs ram
|
||||
export const DB_SIZE_LESS_RAM = 'less_ram'
|
||||
export const DB_SIZE_MID_RAM = 'mid_ram'
|
||||
export const DB_SIZE_GREATER_RAM = 'greater_ram'
|
||||
// maximum value for integer fields
|
||||
export const MAX_NUMERIC_VALUE = 999999
|
||||
@@ -0,0 +1,5 @@
|
||||
export const APP_THEMES_LIGHT = 'light'
|
||||
export const APP_THEMES_DARK = 'dark'
|
||||
// tabs
|
||||
export const TAB_CONFIG = 'config_tab'
|
||||
export const TAB_ALTER_SYSTEM = 'alter_tab'
|
||||
@@ -0,0 +1,24 @@
|
||||
export function convertOklchToHex(oklchString) {
|
||||
// 1. Создаем виртуальный пиксель
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 1
|
||||
canvas.height = 1
|
||||
const ctx = canvas.getContext('2d', { willReadFrequently: true })
|
||||
if (!ctx) return ''
|
||||
|
||||
// 2. Закрашиваем пиксель цветом oklch
|
||||
ctx.fillStyle = oklchString
|
||||
ctx.fillRect(0, 0, 1, 1)
|
||||
|
||||
// 3. Получаем массив [r, g, b, a] со значениями от 0 до 255
|
||||
const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data
|
||||
|
||||
// Вспомогательная функция для перевода числа в 16-ричный формат (например, 255 -> "ff")
|
||||
const toHex = (num) => num.toString(16).padStart(2, '0')
|
||||
|
||||
// 4. Собираем HEX-строку
|
||||
const hexRGB = `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
||||
|
||||
// Если есть прозрачность (альфа-канал меньше 255), добавляем её в конец HEX
|
||||
return a === 255 ? hexRGB : `${hexRGB}${toHex(a)}`
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import * as Yup from 'yup'
|
||||
import {
|
||||
DB_VERSIONS,
|
||||
OS_LINUX,
|
||||
OS_WINDOWS,
|
||||
OS_MAC,
|
||||
DB_TYPE_WEB,
|
||||
DB_TYPE_OLTP,
|
||||
DB_TYPE_DW,
|
||||
DB_TYPE_DESKTOP,
|
||||
DB_TYPE_MIXED,
|
||||
SIZE_UNIT_MB,
|
||||
SIZE_UNIT_GB,
|
||||
SIZE_UNIT_TB,
|
||||
HARD_DRIVE_HDD,
|
||||
HARD_DRIVE_SSD,
|
||||
HARD_DRIVE_SAN,
|
||||
HARD_DRIVE_NVME,
|
||||
MAX_NUMERIC_VALUE,
|
||||
DB_SIZE_LESS_RAM,
|
||||
DB_SIZE_MID_RAM,
|
||||
DB_SIZE_GREATER_RAM,
|
||||
} from './constants/configuration'
|
||||
|
||||
const MIN_MB_MEMORY = 512
|
||||
|
||||
const DB_TYPES = [
|
||||
DB_TYPE_WEB,
|
||||
DB_TYPE_OLTP,
|
||||
DB_TYPE_DW,
|
||||
DB_TYPE_DESKTOP,
|
||||
DB_TYPE_MIXED,
|
||||
]
|
||||
|
||||
const HARD_DRIVE_TYPES = [
|
||||
HARD_DRIVE_HDD,
|
||||
HARD_DRIVE_SSD,
|
||||
HARD_DRIVE_SAN,
|
||||
HARD_DRIVE_NVME,
|
||||
]
|
||||
|
||||
const DB_SIZES = [DB_SIZE_LESS_RAM, DB_SIZE_MID_RAM, DB_SIZE_GREATER_RAM]
|
||||
|
||||
export const validationSchema = Yup.object().shape({
|
||||
dbVersion: Yup.number()
|
||||
.required('Required')
|
||||
.oneOf(DB_VERSIONS, 'Unsupported database version'),
|
||||
osType: Yup.string()
|
||||
.required('Required')
|
||||
.oneOf([OS_LINUX, OS_WINDOWS, OS_MAC], 'Unsupported OS'),
|
||||
dbType: Yup.string()
|
||||
.required('Required')
|
||||
.oneOf(DB_TYPES, 'Unsupported database type'),
|
||||
totalMemoryUnit: Yup.string()
|
||||
.required('Required')
|
||||
.oneOf([SIZE_UNIT_MB, SIZE_UNIT_GB, SIZE_UNIT_TB], 'Unsupported unit'),
|
||||
totalMemory: Yup.number()
|
||||
.required('Required')
|
||||
.integer('Must be an integer')
|
||||
.when('totalMemoryUnit', (totalMemoryUnit, schema) => {
|
||||
if (totalMemoryUnit === SIZE_UNIT_MB) {
|
||||
return schema.min(
|
||||
MIN_MB_MEMORY,
|
||||
`Must be greater than or equal to ${MIN_MB_MEMORY} MB`,
|
||||
)
|
||||
}
|
||||
return schema.min(1, 'Must be greater than zero')
|
||||
})
|
||||
.max(
|
||||
MAX_NUMERIC_VALUE,
|
||||
`Must be less than or equal to ${MAX_NUMERIC_VALUE}`,
|
||||
),
|
||||
hdType: Yup.string()
|
||||
.required('Required')
|
||||
.oneOf(HARD_DRIVE_TYPES, 'Unsupported hard drive'),
|
||||
dbSize: Yup.string()
|
||||
.required('Required')
|
||||
.oneOf(DB_SIZES, 'Unsupported db size'),
|
||||
cpuNum: Yup.number()
|
||||
.transform((value) => (isNaN(value) ? null : value))
|
||||
.nullable()
|
||||
.notRequired()
|
||||
.integer('Must be an integer')
|
||||
.min(1, 'Must be greater than zero')
|
||||
.max(
|
||||
MAX_NUMERIC_VALUE,
|
||||
`Must be less than or equal to ${MAX_NUMERIC_VALUE}`,
|
||||
),
|
||||
connectionNum: Yup.number()
|
||||
.transform((value) => (isNaN(value) ? null : value))
|
||||
.nullable()
|
||||
.notRequired()
|
||||
.integer('Must be an integer')
|
||||
.min(20, 'Must be greater than or equal to 20')
|
||||
.max(
|
||||
MAX_NUMERIC_VALUE,
|
||||
`Must be less than or equal to ${MAX_NUMERIC_VALUE}`,
|
||||
),
|
||||
})
|
||||
@@ -0,0 +1,113 @@
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
import { VitePWA } from 'vite-plugin-pwa'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui(),
|
||||
VitePWA({
|
||||
injectRegister: null,
|
||||
registerType: 'prompt',
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
type: 'module',
|
||||
},
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
|
||||
},
|
||||
includeAssets: [
|
||||
'favicon.svg',
|
||||
'favicon.ico',
|
||||
'icon-192x192.png',
|
||||
'icon-512x512.png',
|
||||
'maskable_icon.png',
|
||||
],
|
||||
manifest: {
|
||||
name: 'PGTune',
|
||||
short_name: 'PGTune',
|
||||
description: 'PgTune - Tuning PostgreSQL config by your hardware',
|
||||
display: 'standalone',
|
||||
start_url: '/',
|
||||
theme_color: '#0f172b',
|
||||
background_color: '#fff',
|
||||
icons: [
|
||||
{
|
||||
src: '/icon-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: '/icon-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: '/maskable_icon.png',
|
||||
sizes: '1024x1024',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
/*
|
||||
VitePWA({
|
||||
injectRegister: null,
|
||||
strategies: 'injectManifest',
|
||||
registerType: 'prompt',
|
||||
srcDir: 'src',
|
||||
// filename: 'sw.js',
|
||||
base: '/',
|
||||
scope: '/',
|
||||
includeAssets: [
|
||||
'favicon.svg',
|
||||
'favicon.ico',
|
||||
'icon-192x192.png',
|
||||
'icon-512x512.png',
|
||||
'maskable_icon.png',
|
||||
'about.html',
|
||||
],
|
||||
injectManifest: {
|
||||
globPatterns: ['\*\*\/*.{css,js,html,png,svg,ico}'],
|
||||
},
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
type: 'module',
|
||||
},
|
||||
manifest: {
|
||||
name: 'PGTune',
|
||||
short_name: 'PGTune',
|
||||
description: 'PgTune - Tuning PostgreSQL config by your hardware',
|
||||
display: 'standalone',
|
||||
start_url: '/',
|
||||
theme_color: '#fdf6e3',
|
||||
background_color: '#fdf6e3',
|
||||
icons: [
|
||||
{
|
||||
src: '/icon-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: '/icon-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: '/maskable_icon.png',
|
||||
sizes: '1024x1024',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
},
|
||||
}), */
|
||||
],
|
||||
})
|
||||