Создание шифрованного API
This commit is contained in:
+193
@@ -0,0 +1,193 @@
|
||||
<script setup>
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import {appWindow} from "@tauri-apps/api/window";
|
||||
|
||||
import {useDark, useToggle} from "@vueuse/core";
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
import ConfirmModal from "./api_components/ConfirmModal.vue";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
import SettingsModal from "./api_components/SettingsModal.vue";
|
||||
|
||||
const isDark = useDark();
|
||||
const toggleDark = useToggle(isDark);
|
||||
|
||||
let pending = ref(true);
|
||||
let api_status = ref(false);
|
||||
|
||||
let confirm_modal = ref(false);
|
||||
let settings_modal = ref(false);
|
||||
|
||||
let settings_port = ref(0);
|
||||
let settings_autostart = ref(true);
|
||||
|
||||
let input_val = ref((() => {
|
||||
let string = "";
|
||||
for(let i = 0; i < 5; i++) string += (Math.random() + 1).toString(36).substring(2);
|
||||
return string;
|
||||
})())
|
||||
|
||||
async function get_status() {
|
||||
pending.value = true;
|
||||
await invoke('get_status')
|
||||
.then((res) => {
|
||||
api_status.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await invoke('check_or_create_config_file')
|
||||
.then(async () => {
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
});
|
||||
|
||||
await listen('update_api', async () => {
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
})
|
||||
});
|
||||
|
||||
async function run_api() {
|
||||
await invoke('start_tcp_server');
|
||||
}
|
||||
|
||||
async function stop_api() {
|
||||
await invoke('send_command_to_server', {
|
||||
command: "stop"
|
||||
});
|
||||
}
|
||||
|
||||
async function open_settings() {
|
||||
await invoke('get_port')
|
||||
.then(async (res) => {
|
||||
settings_port.value = res;
|
||||
await invoke('get_autostart')
|
||||
.then((res) => {
|
||||
settings_autostart.value = res;
|
||||
settings_modal.value = true;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function regen_token(){
|
||||
confirm_modal.value = false;
|
||||
await invoke('reset_token')
|
||||
.then((res) => {
|
||||
let input = document.getElementById('token_con');
|
||||
let timer = document.getElementById('timer');
|
||||
input.type = "text";
|
||||
input_val.value = res;
|
||||
|
||||
let i = 15;
|
||||
timer.textContent = i.toString() + "s";
|
||||
|
||||
let timerI = setInterval(() => {
|
||||
i--;
|
||||
if(i===0){
|
||||
input_val.value = (() => {
|
||||
let string = "";
|
||||
for(let i = 0; i < 5; i++) string += (Math.random() + 1).toString(36).substring(2);
|
||||
return string;
|
||||
})();
|
||||
input.type = "password";
|
||||
timer.textContent = "";
|
||||
clearInterval(timerI);
|
||||
} else{
|
||||
timer.textContent = i.toString() + "s";
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function select_input(event){
|
||||
event.target.select();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-tauri-drag-region class="z-50 opacity-50 titlebar h-[30px] select-none fixed flex justify-end top-0 right-0 left-0 bg-gray-100 dark:bg-zinc-800">
|
||||
<div @click="appWindow.minimize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-minimize">
|
||||
<Icon class="" icon="mdi:window-minimize" width="20" height="20"/>
|
||||
</div>
|
||||
<div @click="appWindow.toggleMaximize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-maximize">
|
||||
<Icon class="" icon="mdi:window-maximize" width="20" height="20"/>
|
||||
</div>
|
||||
<div @click="appWindow.close()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-close">
|
||||
<Icon class="" icon="mdi:close" width="26" height="26"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pending">
|
||||
<Icon icon="line-md:loading-twotone-loop" width="96" height="96" class="text-green-500 absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button class="absolute top-10 left-2" @click="open_settings">
|
||||
<Icon icon="material-symbols:settings-outline" width="38" height="38" class="text-zinc-600 dark:text-zinc-200"/>
|
||||
</button>
|
||||
<button class="absolute top-10 right-2" @click="toggleDark()">
|
||||
<Icon v-if="!isDark" class="group-hover:invert text-yellow-400" icon="flowbite:sun-solid" width="38" height="38"/>
|
||||
<Icon v-else class="group-hover:invert text-blue-400" icon="ri:moon-fill" width="38" height="38"/>
|
||||
</button>
|
||||
<div class="mt-8">
|
||||
<p class="text-center dark:text-white">API</p>
|
||||
<div class="grid grid-cols-1 grid-rows-1 sm:grid-cols-2 sm:grid-rows-2 mt-8 sm:mt-2 gap-2.5 dark:text-white md:mt-4 md:mx-16 lg:mt-8 lg:mx-24">
|
||||
<div class="text-center shadow dark:shadow-white mx-4 sm:mx-0 sm:ml-4 rounded-lg p-1">
|
||||
<p class="mb-4">Статус</p>
|
||||
<div v-if="!api_status" class="flex text-center align-middle items-center">
|
||||
<Icon class="text-red-500" icon="oui:dot" width="36" height="36"/>
|
||||
<span>Не подключено</span>
|
||||
<button @click="run_api" class="bg-green-500 text-white rounded-lg py-1 px-2 ml-2 hover:bg-green-600 transition-colors">Запустить</button>
|
||||
</div>
|
||||
<div v-else class="flex text-center align-middle items-center">
|
||||
<Icon class="text-green-500" icon="oui:dot" width="36" height="36"/>
|
||||
<span>Работает</span>
|
||||
<button @click="stop_api" class="bg-green-500 text-white rounded-lg py-1 px-2 ml-2 hover:bg-green-600 transition-colors">Остановить</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid text-center shadow dark:shadow-white mx-4 sm:mx-0 sm:mr-4 rounded-lg p-1 justify-center items-center">
|
||||
<div v-if="api_status">
|
||||
<p class="mb-2">Токен подключения</p>
|
||||
<div>
|
||||
<input @click="select_input" size="30" id="token_con" readonly type="password" class="rounded-lg my-0.5 px-2 dark:bg-zinc-700"
|
||||
:value="input_val">
|
||||
</div>
|
||||
<div class="flex justify-center items-center">
|
||||
<button class="text-[15px] m-1 rounded-lg bg-red-500 text-white py-1 px-2" @click="confirm_modal = true">Сгенерировать новый</button>
|
||||
<p id="timer" class="text-xl ml-2"></p>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="underline decoration-red-500 decoration-2 underline-offset-2">Нет подключения</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="confirm_modal || settings_modal" class="bg-black w-[100vw] h-full fixed top-0 opacity-70 z-40"/>
|
||||
<ConfirmModal v-if="confirm_modal" @close="confirm_modal = false" @yes="regen_token"/>
|
||||
<SettingsModal v-if="settings_modal" @close="settings_modal = false" :autostart="settings_autostart" :port="settings_port"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
overflow-x: hidden;
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
background-color: rgb(246 247 248);
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
background-color: rgb(30, 30, 30);
|
||||
}
|
||||
</style>
|
||||
+29
-13
@@ -9,12 +9,14 @@ import { appWindow } from '@tauri-apps/api/window';
|
||||
|
||||
import {useDark, useToggle} from "@vueuse/core";
|
||||
import PriorityModal from "./components/modals/PriorityModal.vue";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
|
||||
const isDark = useDark();
|
||||
const toggleDark = useToggle(isDark);
|
||||
|
||||
let tasks = ref({});
|
||||
let pending = ref(true);
|
||||
let api_status = ref(false);
|
||||
|
||||
let create_modal = ref(false);
|
||||
let edit_modal = ref(false);
|
||||
@@ -31,22 +33,24 @@ let to_change_id = ref("");
|
||||
let to_change_name = ref("");
|
||||
let to_change_priority = ref("");
|
||||
|
||||
async function get_status() {
|
||||
await invoke('get_status')
|
||||
.then((res) => {
|
||||
api_status.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await invoke('check_or_create_tasks_file')
|
||||
.then(async () => {
|
||||
await get_tasks();
|
||||
await get_status();
|
||||
pending.value = false;
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById('titlebar-minimize')
|
||||
.addEventListener('click', () => appWindow.minimize())
|
||||
document
|
||||
.getElementById('titlebar-maximize')
|
||||
.addEventListener('click', () => appWindow.toggleMaximize())
|
||||
document
|
||||
.getElementById('titlebar-close')
|
||||
.addEventListener('click', () => appWindow.close())
|
||||
await listen('update_api', async () => {
|
||||
await get_status();
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -134,23 +138,35 @@ function change_priority(id_task, name, priority){
|
||||
to_change_priority.value = priority;
|
||||
priority_modal.value = true;
|
||||
}
|
||||
|
||||
|
||||
async function open_api(){
|
||||
await invoke('open_api_window');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div data-tauri-drag-region class="z-50 opacity-50 titlebar h-[30px] select-none fixed flex justify-end top-0 right-0 left-0 bg-gray-100 dark:bg-zinc-800">
|
||||
<div class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-minimize">
|
||||
<div @click="appWindow.minimize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-minimize">
|
||||
<Icon class="" icon="mdi:window-minimize" width="20" height="20"/>
|
||||
</div>
|
||||
<div class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-maximize">
|
||||
<div @click="appWindow.toggleMaximize()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-maximize">
|
||||
<Icon class="" icon="mdi:window-maximize" width="20" height="20"/>
|
||||
</div>
|
||||
<div class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-close">
|
||||
<div @click="appWindow.close()" class="titlebar-button hover:bg-gray-200 dark:hover:bg-gray-700 justify-center inline-flex w-[30px] h-[30px] items-center dark:text-white" id="titlebar-close">
|
||||
<Icon class="" icon="mdi:close" width="26" height="26"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pending"></div>
|
||||
<div v-if="pending">
|
||||
<Icon icon="line-md:loading-twotone-loop" width="96" height="96" class="text-green-500 absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button class="absolute bottom-2 left-2" @click="open_api()">
|
||||
<Icon icon="icon-park-outline:earth" width="40" height="40" class="relative text-zinc-400 dark:text-zinc-300"/>
|
||||
<Icon v-if="api_status" class="absolute bottom-4 left-4 text-green-500" icon="oui:dot" width="36" height="36"/>
|
||||
<Icon v-else class="absolute bottom-4 left-4 text-red-500" icon="oui:dot" width="36" height="36"/>
|
||||
</button>
|
||||
<button class="absolute top-10 right-2" @click="toggleDark()">
|
||||
<Icon v-if="!isDark" class="group-hover:invert text-yellow-400" icon="flowbite:sun-solid" width="38" height="38"/>
|
||||
<Icon v-else class="group-hover:invert text-blue-400" icon="ri:moon-fill" width="38" height="38"/>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="absolute left-[50%] translate-x-[-50%] overflow-y-auto overflow-x-hidden w-50 top-20 lg:top-32 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100 mr-2">
|
||||
Вы уверены?
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body
|
||||
<div class="p-4 md:p-5 space-y-4"></div>
|
||||
Modal footer -->
|
||||
<div class="flex items-center py-3 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<button @click="$emit('yes')" class="text-white bg-red-500 hover:bg-red-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center">Да</button>
|
||||
<button @click="$emit('close')" class="ml-2 bg-zinc-200 hover:bg-zinc-300 text-black transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center">Нет</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {invoke} from "@tauri-apps/api";
|
||||
|
||||
const props = defineProps({
|
||||
port: Number,
|
||||
autostart: Boolean
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
let port = ref(props.port);
|
||||
let autostart = ref(props.autostart);
|
||||
|
||||
let error = ref(" ");
|
||||
|
||||
async function save_settings(){
|
||||
if (port.value < 1 || port.value > 65536){
|
||||
error.value = "Номер порта не может быть меньше 1 или больше 65536"
|
||||
} else {
|
||||
await invoke('edit_settings', {
|
||||
port: port.value,
|
||||
autostart: autostart.value
|
||||
}).then(() => {
|
||||
emit('close');
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="create-task-modal" tabindex="-1" aria-hidden="true" class="px-[5vw] md:px-[10vw] lg:px-[15vw] absolute overflow-y-auto overflow-x-hidden w-full top-20 lg:top-24 z-50">
|
||||
<div class="relative p-4 max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-[rgb(50,50,50)]" @submit.prevent="save_settings">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-center justify-between px-5 py-2 md:py-3 border-b rounded-t">
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-100">
|
||||
Настройки
|
||||
</h3>
|
||||
<button type="button" @click="$emit('close')" class="transition-colors text-gray-400 dark:text-gray-300 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="default-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="px-4 md:px-5 pt-4">
|
||||
<div>
|
||||
<div>
|
||||
<label id="autostart" class="items-center me-5 cursor-pointer">
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-gray-200 mb-0.5">Запуск при старте</p>
|
||||
<input v-model="autostart" type="checkbox" value="" class="sr-only peer">
|
||||
<div class="relative w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700
|
||||
peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full
|
||||
peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[2px]
|
||||
after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5
|
||||
after:transition-all dark:border-gray-600 peer-checked:bg-green-600"/>
|
||||
</label>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="port" class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-200">Порт</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<input @input="error = ' '" v-model="port" type="number" id="port" name="port" placeholder="1-65536" class="px-2 py-1.5 block w-48 rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-inset sm:text-sm sm:leading-6" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center py-1 px-5 md:p-3 border-t border-gray-200 rounded-b">
|
||||
<input type="submit" class="text-white bg-green-500 hover:bg-green-600 transition-colors font-medium rounded-lg text-sm px-5 py-2.5 text-center" value="Сохранить">
|
||||
<p class="ml-3 text-red-500">{{error}}</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,4 @@
|
||||
import { createApp } from "vue";
|
||||
import ApiApp from "./ApiApp.vue";
|
||||
|
||||
createApp(ApiApp).mount("#app_api");
|
||||
Reference in New Issue
Block a user