revert 8de25f391ce515f549a678d49f7297aa30cea3ec

revert  -
This commit is contained in:
Marks 2024-07-19 12:17:10 +03:00
parent 8de25f391c
commit 4c9bfc5cfa
17 changed files with 1704 additions and 10 deletions

View File

@ -14,7 +14,7 @@ function defaultDataResponseProcessor(data) {
let BasicURLValue = "https://hostapay.trade/api/v1/";
let BasicURLValue = "https://test.0x000f.ru/api/v1/";

View File

@ -0,0 +1,2 @@
export const prerender = true;
export const ssr = false;

View File

@ -0,0 +1,67 @@
<script>
import { SvelteToast } from "@zerodevx/svelte-toast";
import { onMount } from "svelte";
import "../../app.css";
import Navbar from "$lib/ui-components/navbar.svelte";
import { checkAuth, checkAuthSync } from "$lib/auth/Auth";
import { redirect } from "$lib/tools/url/URLTools";
import { AuthStorage } from "$lib/tools/storages/auth-storage";
let loggedIn = false;
let authCheckInProgress = false;
AuthStorage.subscribe((s)=>{
loggedIn = s.logged;
});
onMount(() => {
if (!checkAuthSync() && !window.location.pathname.toLocaleLowerCase().includes("/login")) {
redirect("/admin/login");
}
else if (checkAuthSync())
{
loggedIn = true;
}
});
</script>
{#if loggedIn}
<div class="fixed left-0 right-0 top-0 flex z-10">
<Navbar />
</div>
{/if}
<div class="flex justify-center flex-shrink-0 w-full h-[calc(100vh-52px-68px)] mt-[68px] overflow-auto p-4">
<div class="flex w-full md:max-w-[90%] lg:max-w-[1080px]">
<slot />
</div>
</div>
<footer class="footer footer-center bg-base-300 text-base-content p-4">
<aside>
<p>
Copyright © {new Date().getFullYear()} - All right reserved
</p>
</aside>
</footer>
<SvelteToast
options={{
duration: 2000, // duration of progress bar tween to the `next` value
initial: 1, // initial progress bar value
next: 0, // next progress value
pausable: false, // pause progress bar tween on mouse hover
dismissable: true, // allow dismiss with close button
reversed: false, // insert new toast to bottom of stack
intro: { x: 256 }, // toast intro fly animation settings
theme: {
// "--toastColor": "var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)))",
// "--toastBackground": "var(--fallback-er,oklch(var(--er)/var(--tw-border-opacity)))",
// "--toastBarBackground": "#2F855A",
}, // css var overrides
classes: [], // user-defined classes
}}
/>

View File

@ -0,0 +1,12 @@
<script>
import { checkAuth, checkAuthSync } from "$lib/auth/Auth";
import { redirect } from "$lib/tools/url/URLTools";
import { onMount } from "svelte";
onMount(()=>{
if(checkAuthSync())
redirect("/admin/userslist");
else
redirect("/admin/login");
});
</script>

View File

@ -0,0 +1,342 @@
<script>
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
import { makePost } from "$lib/tools/requests/requests";
import { sayError, sayInfo } from "$lib/tools/toaster/Toaster";
import Pagination from "$lib/ui-components/pagination.svelte";
import { redirect } from "$lib/tools/url/URLTools";
import axios from "axios";
const disputesStatusMap = {
"4": "Требует проверки",
"5": "На проверке у трейдера",
"6": "Принят",
"7": "Отклонён",
"8": "Чек на проверке"
};
const disputesStatusMapColors = {
"4": "text-white",
"5": "text-warning",
"6": "text-primary",
"7": "text-error",
"8": "text-warning",
};
//admin/getDisputes
let disputes = [];
let numOfPagesDisputes = 1;
let currentPageDisputes = 1;
let disablePagesDisputes = false;
let currentDisputesFilter = -1;
async function getDisputes() {
disablePagesDisputes = true;
const result = await makePost(
"admin/getDisputes",
{
page: 1,
filter: currentDisputesFilter
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
console.log(result);
if (result.status === 401) {
sayError("Данные авторизации устарели");
redirect("/admin/");
disablePagesDisputes = false;
// disablePagesUserDeposits = false;
return;
}
if (result.error) {
sayError("Не удалось получить споры");
disablePagesDisputes = false;
// disablePagesUserDeposits = false;
return;
}
disputes = result.data.disputes;
console.log(disputes);
numOfPagesDisputes = result.data.pages;
disablePagesDisputes = false;
}
getDisputes();
let selectedDispute = {
amount: "",
is_sbp: "",
name: "",
order_creationtime: "",
order_id: "",
order_rate: "",
order_requisites_id: "",
order_status: "",
receipt: "",
requisite_cardnumber: "",
requisite_phone: "",
dispute_uuid: "",
new_amount: "",
creation_time: "",
out_code: "",
in_code: ""
};
let showFullInfo = false;
async function acceptDispute() {
//admin/changeDispute
const result = await makePost(
"admin/changeDispute",
{
action: selectedDispute.order_status === "8" ? 3:1,
order_id: selectedDispute.order_id,
dispute_uuid: selectedDispute.dispute_uuid
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
console.log(result);
if (result.status === 401) {
sayError("Данные авторизации устарели");
redirect("/admin/");
// disablePagesUserDeposits = false;
return;
}
if (result.error) {
sayError("Не удалось принять спор");
// disablePagesUserDeposits = false;
return;
}
sayInfo("Спор одобрен!");
showFullInfo = false;
getDisputes();
}
async function rejectDispute() {
const result = await makePost(
"admin/changeDispute",
{
action: selectedDispute.order_status === "8" ? 4:2,
order_id: selectedDispute.order_id,
dispute_uuid: selectedDispute.dispute_uuid
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
console.log(result);
if (result.status === 401) {
sayError("Данные авторизации устарели");
redirect("/admin/");
// disablePagesUserDeposits = false;
return;
}
if (result.error) {
sayError("Не удалось отклонить спор");
// disablePagesUserDeposits = false;
return;
}
sayInfo("Спор отклонён!");
showFullInfo = false;
getDisputes();
}
function mimeToExtension(mimeType) {
const mimeTypes = {
"application/pdf": "pdf",
"application/zip": "zip",
"image/jpeg": "jpg",
"image/jpg": "jpg",
"image/png": "png",
"text/plain": "txt",
"application/msword": "doc",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
"docx",
// Добавьте дополнительные MIME-типы по необходимости
};
return mimeTypes[mimeType] || "bin"; // Если тип неизвестен, возвращаем .bin
}
</script>
<div class="w-full flex flex-col gap-8">
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Споры</h1>
</div>
<div class="w-full flex flex-col bg-base-300 p-4 rounded-box">
<div class="flex p-4">
<select
on:change={(e)=>{
currentDisputesFilter = Number(e.target.value);
getDisputes();
}}
class="select select-bordered w-full text-base"
>
<option selected value="-1">Показать все споры</option>
<option value="7">Отклонённые</option>
<option value="6">Принятые</option>
<option value="5">На проверке у трейдера</option>
<option value="4">Требует проверки</option>
</select>
</div>
<div class="w-full flex flex-col justify-center items-center">
<Pagination
totalPages={numOfPagesDisputes}
pageChangedCallback={(n) => {
currentPageDisputes = n;
getDisputes();
}}
disableButtons={disablePagesDisputes}
css={"btn-neutral"}
/>
<p class="opacity-50 text-xs mt-1">
Всего страниц: {numOfPagesDisputes}
</p>
</div>
<div class="overflow-x-auto mt-4 flex flex-col">
{#if !disablePagesDisputes}
<table class="table">
<!-- head -->
<thead>
<tr>
<th>ID</th>
<th>Статус</th>
<th>СБП</th>
<th>Карта</th>
<th>Телефон</th>
<th>Курс</th>
<th>Сумма</th>
<th>Время создания</th>
<th>Опции</th>
<!-- <th></th> -->
</tr>
</thead>
<tbody>
{#each disputes as dispute}
<tr class="hover:bg-neutral group">
<td class="font-semibold">{dispute["order_id"]}</td>
<td class={disputesStatusMapColors[dispute["order_status"]]}
>{disputesStatusMap[dispute["order_status"]]}</td
>
<td>{dispute["is_sbp"] === "t" ? "да" : "нет"}</td>
<td>{dispute["requisite_cardnumber"]}</td>
<td>{dispute["requisite_phone"]}</td>
<td>{dispute["order_rate"]}</td>
<td>{dispute["amount"]}</td>
<td>{dispute["order_creationtime"]}</td>
<td
on:click={() => {
selectedDispute = dispute;
showFullInfo = true;
}}
class="flex gap-2"
>
<button class="btn btn-info">Подробнее</button>
</td>
</tr>
{/each}
</tbody>
</table>
{:else}
<span class="loading loading-spinner self-center"></span>
{/if}
</div>
</div>
</div>
{#if showFullInfo}
<div
class="fixed inset-0 flex justify-center items-center md:p-32 p-8 pt-[68px] bg-black bg-opacity-50"
>
<div class="flex flex-col w-full max-w-[600px] p-4 rounded-box bg-base-300">
<p class="self-center opacity-60">ID: {selectedDispute.dispute_uuid}</p>
<p
class={"self-center text-3xl " +
disputesStatusMapColors[selectedDispute["order_status"]]}
>
{disputesStatusMap[selectedDispute["order_status"]]}
</p>
<p class="self-center opacity-60">{selectedDispute.order_creationtime}</p>
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
<p class="text-lg font-bold">{selectedDispute.name}</p>
<p class="">Карта: {selectedDispute.requisite_cardnumber}</p>
<p class="">Телефон: {selectedDispute.requisite_phone}</p>
</div>
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
<p class="text-lg font-bold">Курс</p>
<p>{selectedDispute.order_rate} {selectedDispute.out_code}</p>
</div>
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
<p class="text-lg font-bold">Сумма в споре</p>
<p>
{(Number(selectedDispute.new_amount) / Number(selectedDispute.order_rate)).toFixed(2)} {selectedDispute.out_code} ({Number(selectedDispute.new_amount)} USDT)
</p>
</div>
<div class="rounded-box flex flex-col bg-base-100 p-4 gap-1 mt-4">
<p class="text-lg font-bold">Сумма в заявке</p>
<p>
{Number(selectedDispute.order_rate) * Number(selectedDispute.amount)} {selectedDispute.out_code} ({Number(selectedDispute.amount)} USDT)
</p>
</div>
{#if selectedDispute["order_status"] === "4" || selectedDispute["order_status"] === "8"}
<button
on:click={() => {
acceptDispute();
}}
class="btn btn-outline btn-primary mt-4"
>
Одобрить
</button>
<button
on:click={() => {
rejectDispute();
}}
class="btn btn-outline btn-error mt-1"
>
Отклонить
</button>
{/if}
{#if selectedDispute["order_status"] !== "4"}
<button
on:click={() => {
axios
.get(
"https://test.0x000f.ru/api/v1/loadFile?dispute=" +
selectedDispute["order_id"],
{
responseType: "blob",
headers: makeAuthHeaderForAxios(getAuthInfo()?.a).headers,
}
)
.then(function (response) {
// Создаем ссылку для скачивания
const url = window.URL.createObjectURL(
new Blob([response.data])
);
const contentType = response?.data?.type; // Извлекаем MIME-тип из Blob
let extension = mimeToExtension(contentType); // Получаем расширение из функции
const a = document.createElement("a");
a.style.display = "none";
a.href = url;
a.download = `Чек_${Date.now()}.${extension}`;
document.body.appendChild(a);
a.click();
// Очищаем
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
})
.catch(function (error) {
console.error("Error:", error);
});
}}
class="btn btn-outline btn-info mt-4"
>
Скачать чек
</button>
{/if}
<button
on:click={() => {
showFullInfo = false;
}}
class="btn btn-outline btn-ghost mt-4"
>
Закрыть
</button>
</div>
</div>
{/if}

View File

@ -0,0 +1,118 @@
<script>
import { makePost } from "$lib/tools/requests/Requests";
import { sayError, sayInfo, sayWarning } from "$lib/tools/toaster/Toaster";
import { onMount } from "svelte";
import { checkAuth, saveAuthInfo } from "$lib/auth/Auth";
import { redirect } from "$lib/tools/url/URLTools";
import { AuthStorage } from "$lib/tools/storages/auth-storage";
//6da8a96a-7253-45e2-a3c1-e00d5ecdc65d - valid
let tokenValid = true;
let totpValid = true;
let showLoadingSpinner = false;
let input_token = null;
let input_totp = null;
async function sendLogin() {
if (input_token.value === "" || input_totp.value === "") {
sayError("Заполните все поля!");
tokenValid = input_token.value !== "";
totpValid = input_totp.value !== "";
return;
} else {
tokenValid = true;
totpValid = true;
showLoadingSpinner = true;
const result = await makePost("client/login", {
user_token: input_token.value,
totp: input_totp.value,
});
if (result.error) {
sayError("Ошибка входа!");
tokenValid = false;
totpValid = false;
showLoadingSpinner = false;
return;
} else {
saveAuthInfo(result.data.token);
AuthStorage.update((s) => {
s.logged = true;
});
redirect("/admin/");
showLoadingSpinner = false;
return;
}
}
}
// let checkAuthInProgress = true;
// onMount(async () => {
// let result = await checkAuth();
// if (result && window.location.pathname === "/login") {
// window.location.href = "/";
// }
// else
// checkAuthInProgress = false;
// });
</script>
<div class="w-full h-full flex items-center justify-center">
<div
class="bg-base-300 w-full h-full max-w-[500px] max-h-[300px] flex flex-col justify-center gap-2 p-4 rounded-box"
>
<h1 class="text-4xl self-center mb-4">Вход</h1>
<label
class={"input input-bordered flex items-center gap-2 " +
(tokenValid ? "input-warning" : "input-error")}
>
Токен:
<input
bind:this={input_token}
type="text"
class="grow text-lg text-info"
/>
</label>
<label
class={"input input-bordered flex items-center gap-2 " +
(totpValid ? "input-warning" : "input-error")}
>
2FA Код:
<input
bind:this={input_totp}
type="text"
class="grow text-lg text-info"
/>
</label>
<!-- <button class="btn-link btn max-w-[140px] self-center">У меня нет 2FA</button> -->
<button
on:click={() => {
sendLogin();
}}
class="btn btn-primary text-lg"
>
Войти
{#if showLoadingSpinner}
<svg
class="animate-spin -ml-1 mr-3 h-5 w-5 text-black"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
{/if}
</button>
</div>
</div>

View File

@ -0,0 +1,214 @@
<script>
import { isStringEmptyOrSpaces } from "$lib/tools/strings/Strings";
import { makePost, makeGet } from "$lib/tools/requests/Requests";
import {
checkAuthSync,
getAuthInfo,
makeAuthHeaderForAxios,
} from "$lib/auth/Auth";
import { onMount } from "svelte";
import { sayError } from "$lib/tools/toaster/Toaster";
import { redirect } from "$lib/tools/url/URLTools";
let value_name = "";
let value_surname = "";
let value_balance = 0;
let value_role = "-1";
let value_bid = 0;
let value_trading = false;
let value_currency_code = "-1";
$: checkFields(
value_name,
value_surname,
value_balance,
value_role,
value_bid,
value_currency_code
);
function checkFields(name, surname, balance, role, bid, code) {
if(isStringEmptyOrSpaces(code) || code === "-1")
{
canCreateUser = false;
return;
}
if (isStringEmptyOrSpaces(name) || name.length < 3) {
canCreateUser = false;
return;
}
if ((bid !== 0 && !bid) || bid < 0) {
canCreateUser = false;
return;
}
if (isStringEmptyOrSpaces(surname) || surname.length < 3) {
canCreateUser = false;
return;
}
if (role === "-1" || isStringEmptyOrSpaces(role)) {
canCreateUser = false;
return;
}
if ((balance !== 0 && !balance) || balance < 0) {
canCreateUser = false;
return;
}
canCreateUser = true;
}
let canCreateUser = false;
let loadingUserToken = false;
let userToken = "";
let temp_userToken = "";
async function createUser() {
loadingUserToken = true;
const result = await makePost(
"createUser",
{
"code": value_currency_code
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (result.status === 401) {
sayError("Данные авторизации устарели");
redirect("/admin/");
return;
}
if (result.error) {
sayError("Не удалось создать пользователя");
return;
}
temp_userToken = result.data.token;
setupNewUser();
}
async function setupNewUser() {
//todo
const result = await makePost(
"admin/changeUser",
{
token: temp_userToken,
name: value_name,
surname: value_surname,
balance: value_balance.toString(),
rate: "0",
can_trade: value_trading ? "true":"false",
can_trade_global: value_trading ? "true":"false",
role: value_role,
bid: value_bid.toString(),
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if(result.status === 401)
{
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
return;
}
if(result.error)
{
sayError("Не удалось настроить пользователя");
return;
}
userToken = temp_userToken;
loadingUserToken = false;
}
</script>
<div class="w-full flex flex-col gap-8">
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Создание нового пользователя</h1>
</div>
<div class="w-full flex flex-col gap-4 p-4 rounded-box bg-base-300">
<label class={"input input-bordered flex items-center gap-2 "}>
Имя:
<input
bind:value={value_name}
type="text"
class="grow text-lg text-info"
/>
</label>
<label class={"input input-bordered flex items-center gap-2 "}>
Фамилия:
<input
bind:value={value_surname}
type="text"
class="grow text-lg text-info"
/>
</label>
<label class={"input input-bordered flex items-center gap-2 "}>
Баланс (USDT):
<input
bind:value={value_balance}
type="number"
min="0"
class="grow text-lg text-info"
/>
</label>
<label class={"input input-bordered flex items-center gap-2 "}>
Ставка (%):
<input
bind:value={value_bid}
type="number"
min="0"
class="grow text-lg text-info"
/>
</label>
<select
bind:value={value_role}
class="select select-bordered w-full text-base"
>
<option disabled selected value="-1">Права доступа</option>
<option value="1">Пользователь</option>
<option value="2">Модератор</option>
<option value="4">Администратор</option>
</select>
<select
bind:value={value_currency_code}
class="select select-bordered w-full text-base"
>
<option disabled selected value="-1">Валюта</option>
<option value="KGS">KGS</option>
<!-- <option value="2">Модератор</option>
<option value="4">Администратор</option> -->
</select>
<div class="flex p-4 items-center gap-2">
<p>Торговля:</p>
<input
bind:checked={value_trading}
type="checkbox"
class="toggle toggle-primary"
/>
</div>
<button
on:click={() => {
createUser();
}}
class={"btn text-base " +
(loadingUserToken ? "btn-warning " : "btn-primary ") +
(userToken !== "" ? "disabled:text-info" : "disabled:text-warning") +
(canCreateUser ? "" : "disabled:text-opacity-50")}
disabled={!canCreateUser || loadingUserToken || userToken !== ""}
>
{#if loadingUserToken}
{"Ожидайте токен"}
<span class="loading loading-spinner"></span>
{:else if userToken === ""}
{"Создать пользователя"}
{:else if canCreateUser}
{"Пользователь создан"}
{/if}
</button>
{#if userToken !== ""}
<div class="w-full flex p-4">
<p class="text-xl">
Токен пользователя: <span class="text-info text-xl">{userToken}</span>
</p>
</div>
{/if}
</div>
</div>

View File

@ -0,0 +1,35 @@
import { browser } from "$app/environment";
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
import { makePost } from "$lib/tools/requests/requests";
import { sayError } from "$lib/tools/toaster/Toaster";
import { redirect } from "$lib/tools/url/URLTools";
export async function load({ params }) {
if(!browser) return;
const { userToken } = params;
let userResult = await makePost(
"getUser",
{
token: userToken,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (userResult.status === 401) {
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
return {};
}
if (userResult.error) {
sayError("Не удалось настроить пользователя");
return {};
}
userResult["can_trade"] = userResult["can_trade"] === "t" ? true:false;
userResult["can_trade_global"] = userResult["can_trade_global"] === "t" ? true:false;
userResult["balance"] = Number(userResult["balance"]);
userResult["bid"] = Number(userResult["bid"]);
return {
token: userToken,
userData: userResult.data
};
}

View File

@ -0,0 +1,153 @@
<script>
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
import { makePost } from "$lib/tools/requests/requests.js";
import { isStringEmptyOrSpaces } from "$lib/tools/strings/Strings";
import { sayError } from "$lib/tools/toaster/Toaster.js";
import { redirect } from "$lib/tools/url/URLTools.js";
import { jwtDecode } from "jwt-decode";
export let data;
let { userData } = data;
let { token } = data;
let value_role = jwtDecode(getAuthInfo()?.a).role.toString();
let changingUser = false;
let canChangeUser = true;
$: checkFields(
userData.name,
userData.surname,
userData.balance,
value_role,
userData.bid
);
function checkFields(name, surname, balance, role, bid) {
// balance = Number(balance);
// bid = Number(balance);
if (isStringEmptyOrSpaces(name) || name.length < 3) {
canChangeUser = false;
return;
}
if ((bid !== 0 && !bid) || bid < 0) {
canChangeUser = false;
return;
}
if (isStringEmptyOrSpaces(surname) || surname.length < 3) {
canChangeUser = false;
return;
}
if (role === "-1") {
canChangeUser = false;
return;
}
if ((balance !== 0 && !balance) || balance < 0) {
canChangeUser = false;
return;
}
canChangeUser = true;
}
async function changeUserInfo() {
changingUser = true;
const userResult = await makePost(
"admin/changeUser",
{
token: token,
name: userData.name,
surname: userData.surname,
balance: userData.balance.toString(),
rate: "0",
can_trade: userData["can_trade"] ? "true":"false",
can_trade_global: userData["can_trade_global"] ? "true":"false",
role: value_role,
bid: userData.bid.toString(),
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (userResult.status === 401) {
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
changingUser = false;
return;
}
if (userResult.error) {
sayError("Не удалось изменить пользователя");
changingUser = false;
return;
}
document.getElementById("ref")?.click();
}
</script>
<div class="w-full flex flex-col gap-8">
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Изменение информации о пользователе</h1>
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
<div class="w-full flex flex-col gap-4 p-4 rounded-box bg-base-300">
<label class={"input input-bordered flex items-center gap-2 "}>
Имя:
<input
bind:value={userData.name}
type="text"
class="grow text-lg text-info"
/>
</label>
<label class={"input input-bordered flex items-center gap-2 "}>
Фамилия:
<input
bind:value={userData.surname}
type="text"
class="grow text-lg text-info"
/>
</label>
<label class={"input input-bordered flex items-center gap-2 "}>
Баланс (USDT):
<input
bind:value={userData.balance}
type="number"
min="0"
class="grow text-lg text-info"
/>
</label>
<label class={"input input-bordered flex items-center gap-2 "}>
Ставка (%):
<input
bind:value={userData.bid}
type="number"
min="0"
class="grow text-lg text-info"
/>
</label>
<select
bind:value={value_role}
class="select select-bordered w-full text-base"
>
<option disabled selected value="-1">Права доступа</option>
<option value="1">Пользователь</option>
<option value="2">Модератор</option>
<option value="4">Администратор</option>
</select>
<div class="flex p-4 items-center gap-2">
<p>Торговля:</p>
<input
bind:checked={userData["can_trade"]}
type="checkbox"
class="toggle toggle-primary"
/>
</div>
<button on:click={()=>{
changeUserInfo();
}} class="btn btn-primary text-base" disabled={changingUser || !canChangeUser}>
Сохранить
{#if changingUser}
<span class="loading loading-spinner"></span>
{/if}
</button>
<a href={window.location.pathname.replace("change", "profile")} class="hidden" id="ref"></a>
</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
import { browser } from "$app/environment";
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
import { makePost } from "$lib/tools/requests/requests";
import { sayError } from "$lib/tools/toaster/Toaster";
export async function load({ params }) {
if(!browser) return;
const { userToken } = params;
const { reqID } = params;
let spt = reqID.split("_");
return {
token: userToken,
reqID: spt[0],
currency: spt[1]
};
}

View File

@ -0,0 +1,179 @@
<script>
// @ts-nocheck
import { browser } from "$app/environment";
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
import { makeGet, makePost } from "$lib/tools/requests/requests.js";
import { sayError } from "$lib/tools/toaster/Toaster.js";
import { redirect } from "$lib/tools/url/URLTools.js";
export let data;
let { reqID } = data;
let {currency} = data;
let { token } = data;
let reqInfo = [];
let value_bankName;
let value_cardNumber;
let value_phone;
let value_status;
let value_limit;
let canChange = false;
$: reqInfo, bindValues();
$: value_cardNumber, value_phone, value_status, checkFields();
function bindValues() {
value_bankName = reqInfo[0]?.bank_name;
value_cardNumber = reqInfo[0]?.cardnumber;
value_phone = reqInfo[0]?.phone;
value_status = reqInfo[0]?.status === "t";
value_limit = Number(reqInfo[0]?.daily_volume_limit);
value_bankName = reqInfo[0]?.bank_id;
}
function checkFields() {
if (value_cardNumber?.length !== 16) {
canChange = false;
return;
}
if (value_phone?.length !== 11) {
canChange = false;
return;
}
if (value_limit < 0) {
canChange = false;
return;
}
canChange = true;
}
async function getReqInfo() {
const result = await makePost(
"getRequisite",
{
token: token,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (result.status === 401) {
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
return;
}
if (result.error) {
sayError("Не удалось получить реквизиты пользователя");
return;
}
console.log(result.data);
reqInfo = result.data.filter((s) => s.id === reqID);
}
async function changeRequisite() {
//changeRequisite
canChange = false;
const result = await makePost(
"changeRequisite",
{
...reqInfo[0],
bankname: value_bankName,
cardnumber: value_cardNumber,
phone: value_phone,
status: value_status ? "true" : "false",
limit: value_limit.toString(),
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (result.status === 401) {
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
// canChange = true;
return;
}
if (result.error) {
sayError("Не удалось изменить реквизит пользователя");
canChange = true;
return;
}
document.getElementById("ref")?.click();
// canChange = true;
}
let activeBanks = [];
async function getActiveBanks() {
let res = await makeGet(
"client/banksList",
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (res.error) {
sayError("Не удалось получить банки. Ошибка #4143");
} else {
console.log(res.data);
activeBanks = res.data;
}
}
if (browser) {
getReqInfo();
getActiveBanks();
}
</script>
<div class="w-full flex flex-col gap-8">
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Изменение реквизита (ID: {reqID})</h1>
</div>
<div class="w-full flex flex-col p-4 gap-2 rounded-box bg-base-300">
{#each reqInfo as req}
<select
bind:value={value_bankName}
class="select select-bordered w-full text-base"
>
<option disabled selected value="-1">Банк</option>
{#each activeBanks as bank}
<option value={bank.id}>{bank.name}</option>
{/each}
<!-- <option value="sber">Сбер</option> -->
<!-- <option value="2">Модератор</option>
<option value="4">Администратор</option> -->
</select>
<label class="input input-bordered flex items-center gap-2">
Номер карты
<input
type="text"
class="grow text-info"
bind:value={value_cardNumber}
/>
</label>
<label class="input input-bordered flex items-center gap-2">
Телефон
<input type="text" class="grow text-info" bind:value={value_phone} />
</label>
<label class="input input-bordered flex items-center gap-2">
Лимит ({currency})
<input type="number" class="grow text-info" bind:value={value_limit} />
</label>
<div class="flex p-4 items-center gap-2">
<p>Активный:</p>
<input
bind:checked={value_status}
type="checkbox"
class="toggle toggle-primary"
/>
</div>
<button
on:click={() => {
changeRequisite();
}}
class="btn btn-primary"
disabled={!canChange}>Сохранить</button
>
<a href={"/admin/user/profile/" + token} class="hidden" id="ref"></a>
{/each}
</div>
</div>

View File

@ -0,0 +1,12 @@
import { browser } from "$app/environment";
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
import { makePost } from "$lib/tools/requests/requests";
import { sayError } from "$lib/tools/toaster/Toaster";
export async function load({ params }) {
if(!browser) return;
const { userToken } = params;
return {
token: userToken,
};
}

View File

@ -0,0 +1,49 @@
<script>
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
import { makePost } from "$lib/tools/requests/requests.js";
import { isStringEmptyOrSpaces } from "$lib/tools/strings/Strings";
import { sayError } from "$lib/tools/toaster/Toaster.js";
import { jwtDecode } from "jwt-decode";
export let data;
let { token } = data;
let numOfOrders = 0;
</script>
<div class="w-full flex flex-col gap-8">
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Создание заявок</h1>
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
<label class="input input-bordered flex items-center gap-2 max-w-lg">
Количество заявок:
<input
bind:value={numOfOrders}
type="number"
min={0}
class="grow text-info"
/>
</label>
{#each { length: numOfOrders } as _, index}
<div class="w-full flex flex-wrap items-center gap-2 p-4">
<p>Заявка #{index+1}</p>
<select class="select select-bordered w-full text-base">
<option disabled selected value="-1">Реквизит</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<label class="w-full input input-bordered flex items-center gap-2">
Сумма (USDT):
<input
type="number"
min={0}
class="grow text-info"
/>
</label>
</div>
{/each}
</div>
</div>

View File

@ -0,0 +1,31 @@
import { browser } from "$app/environment";
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
import { makePost } from "$lib/tools/requests/requests";
import { sayError } from "$lib/tools/toaster/Toaster";
import { redirect } from "$lib/tools/url/URLTools";
export async function load({ params }) {
if(!browser) return;
const { userToken } = params;
const userResult = await makePost(
"getUser",
{
token: userToken,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (userResult.status === 401) {
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
return {};
}
if (userResult.error) {
sayError("Не удалось настроить пользователя");
return {};
}
return {
token: userToken,
userData: userResult.data
};
}

View File

@ -0,0 +1,391 @@
<script>
import { browser } from "$app/environment";
import { getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth.js";
import { makePost } from "$lib/tools/requests/requests.js";
import { sayError } from "$lib/tools/toaster/Toaster.js";
import { redirect } from "$lib/tools/url/URLTools.js";
import Pagination from "$lib/ui-components/pagination.svelte";
export let data;
let { userData } = data;
let { token } = data;
let userInfoReloading = false;
async function deleteUser() {
const result = await makePost(
"deleteUser",
{
token: token,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (result.status === 401) {
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
return;
}
if (result.error) {
sayError("Не удалось удалить пользователя");
return;
}
redirect("/admin/userslist");
}
async function reloadUserInfo() {
userInfoReloading = true;
const userResult = await makePost(
"getUser",
{
token: token,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (userResult.status === 401) {
userInfoReloading = false;
sayError("Данные авторизации устарели");
redirect("/admin/");
return;
}
if (userResult.error) {
userInfoReloading = false;
sayError("Не удалось настроить пользователя");
return;
}
userData = userResult.data;
userInfoReloading = false;
}
let userOrders = [];
let numPagesUserOrders = 1;
let currentPageUserOrders = 1;
let disablePagesUserOrders = false;
async function getUserOrders() {
disablePagesUserOrders = true;
const result = await makePost(
"getUserOrders",
{
token: token,
page: currentPageUserOrders - 1,
orderBy: "id",
statuses_to_send: 2,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (result.status === 401) {
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
disablePagesUserOrders = false;
return;
}
if (result.error) {
sayError("Не удалось получить ордеры пользователя");
disablePagesUserOrders = false;
return;
}
userOrders = result.data.data;
if(!Array.isArray(userOrders)) userOrders = [];
numPagesUserOrders = Number(result.data?.pages);
if (numPagesUserOrders === 0) numPagesUserOrders = 1;
console.log(result.data);
disablePagesUserOrders = false;
}
let userRequisites = [];
async function getUserRequisites() {
const result = await makePost(
"getRequisite",
{
token: token,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (result.status === 401) {
sayError("Данные авторизации устарели");
redirect("/admin/");
return;
}
if (result.error) {
sayError("Не удалось получить реквизиты пользователя");
return;
}
userRequisites = result.data;
if(!Array.isArray(userDeposits)) userRequisites = [];
console.log(result.data);
}
let userDeposits = [];
let numOfPagesUserDeposits = 1;
let currentPageUserDeposits = 1;
let disablePagesUserDeposits = false;
const depoStatusMap = {
"0": "Активна",
"1": "Завершена",
"2": "Отклонена"
};
///api/v1/admin/getUserDeposits
async function getUserDeposits() {
disablePagesUserDeposits = true;
const result = await makePost(
"admin/getUserDeposits",
{
token: token,
page: 1,
},
makeAuthHeaderForAxios(getAuthInfo()?.a)
);
if (result.status === 401) {
sayError("Данные авторизации устарели");
redirect("/admin/");
disablePagesUserDeposits = false;
return;
}
if (result.error) {
sayError("Не удалось получить депозиты пользователя");
disablePagesUserDeposits = false;
return;
}
userDeposits = result.data.deposits;
if(!Array.isArray(userDeposits)) userDeposits = [];
numPagesUserOrders = result.data.pages;
disablePagesUserDeposits = false;
// console.log(result.data, 'depos');
}
if (browser) {
getUserOrders();
getUserRequisites();
getUserDeposits();
}
</script>
<div class="w-full flex flex-col gap-8">
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Информация о пользователе</h1>
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
<div class="flex gap-2 p-2 items-center text-lg">
<p class="font-bold">Токен:</p>
<p class="text-info">{token}</p>
</div>
<div class="flex gap-2 p-2 items-center text-lg">
<p class="font-bold">Имя и фамилия:</p>
<p class="text-info">{userData.name} {userData.surname}</p>
</div>
<div class="flex gap-2 p-2 items-center text-lg">
<p class="font-bold">Валюта:</p>
<p class="text-info">{userData?.code}</p>
</div>
<div class="flex gap-2 p-2 items-center text-lg">
<p class="font-bold">Баланс:</p>
<p class="text-info">{userData.balance} USDT</p>
</div>
<div class="flex gap-2 p-2 items-center text-lg">
<p class="font-bold">Заморожено:</p>
<p class="text-info">{userData?.insurance} USDT</p>
</div>
<div class="flex gap-2 p-2 items-center text-lg">
<p class="font-bold">Ставка:</p>
<p class="text-info">{userData.bid} %</p>
</div>
<div class="flex gap-2 p-2 items-center text-lg">
<p class="font-bold">Торговля:</p>
<p class={userData["can_trade"] === "t" ? "text-primary" : "text-error"}>
{userData["can_trade"] === "t" ? "Активна" : "Отключена"}
</p>
</div>
<div class="w-full flex gap-4 mt-8">
<button
class="btn btn-neutral w-[100px]"
on:click={() => {
reloadUserInfo();
}}
disabled={userInfoReloading}
>
{#if !userInfoReloading}
{"Обновить"}
{:else}
<span class="loading loading-spinner"></span>
{/if}
</button>
<a
href={window.location.pathname.replace("profile", "change")}
class="btn btn-info">Изменить</a
>
<button
on:click={() => {
deleteUser();
}}
class="btn btn-error ml-auto">Удалить</button
>
</div>
</div>
<div class="flex items-center gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Реквизиты пользователя</h1>
<button disabled class="btn btn-accent">Добавить реквизит</button>
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
<div class="overflow-x-auto mt-4">
<table class="table">
<!-- head -->
<thead>
<tr>
<th>ID</th>
<th>Статус</th>
<th>Банк</th>
<th>СБП</th>
<th>Карта</th>
<th>Телефон</th>
<th>Имя</th>
<th>Девайс</th>
<th>Объем {userData.code}</th>
<th>Лимит {userData.code}</th>
<th>Опции</th>
<!-- <th></th> -->
</tr>
</thead>
<tbody>
{#each userRequisites as requisite}
<tr class="hover:bg-neutral group">
<td class="font-semibold">{requisite["id"]}</td>
<td class=""
>{requisite["status"] === "t" ? "активен" : "отключен"}</td
>
<td>{requisite["bank_name"]}</td>
<td>{requisite["is_sbp"] === "t" ? "да" : "нет"}</td>
<td>{requisite["cardnumber"]}</td>
<td>{requisite["phone"]}</td>
<td>{requisite["name"]}</td>
<td>{requisite["device_id"]}</td>
<td>{requisite["daily_volume"]}</td>
<td>{requisite["daily_volume_limit"]}</td>
<td class="flex gap-2">
<a
href={"/admin/user/edit/req/" + token + "/" + requisite["id"] + "_" + userData.code}
class="btn btn-info">Изменить</a
>
<button class="btn btn-error">Удалить</button>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<!-- <div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Девайсы пользователя</h1>
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300"></div> -->
<div class="flex items-center gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Текущие заявки</h1>
<!-- <button href={"/user/neworder/"+token} disabled class="btn btn-accent">Добавить заявку</button> -->
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
<div class="w-full flex flex-col justify-center items-center">
<Pagination
totalPages={numPagesUserOrders}
pageChangedCallback={(n) => {
currentPageUserOrders = n;
getUserOrders();
}}
disableButtons={disablePagesUserOrders}
css={"btn-neutral"}
/>
<p class="opacity-50 text-xs mt-1">Всего страниц: {numPagesUserOrders}</p>
</div>
<div class="overflow-x-auto mt-4">
<table class="table">
<!-- head -->
<thead>
<tr>
<th>ID</th>
<th>Статус</th>
<th>Сумма</th>
<th>Курс</th>
<th>СБП</th>
<th>Мерчант</th>
<th>Время создания</th>
<th>Время закрытия</th>
<!-- <th>Опции</th> -->
<!-- <th></th> -->
</tr>
</thead>
<tbody>
{#each userOrders as order}
<tr class="hover:bg-neutral group">
<td class="font-semibold">{order["id"]}</td>
<td class="">{order["status"]}</td>
<td>{order["summa"]} USDT</td>
<td>{order["rate"]} RUB</td>
<td>{order["is_sbp"] === "t" ? "да" : "нет"}</td>
<td>{order["merchant_id"]}</td>
<td>{order["creationtime"]}</td>
<td>{order["closetime"]}</td>
<!-- <td class="flex gap-2">
<a href="" class="btn btn-info">Изменить</a>
<button class="btn btn-error">Удалить</button>
</td> -->
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Заявки на пополнение</h1>
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
<div class="w-full flex flex-col justify-center items-center">
<Pagination
totalPages={numOfPagesUserDeposits}
pageChangedCallback={(n) => {
currentPageUserDeposits = n;
getUserDeposits();
}}
disableButtons={disablePagesUserDeposits}
css={"btn-neutral"}
/>
<p class="opacity-50 text-xs mt-1">
Всего страниц: {numOfPagesUserDeposits}
</p>
</div>
<div class="overflow-x-auto mt-4">
<table class="table">
<!-- head -->
<thead>
<tr>
<th>Статус</th>
<th>Сумма</th>
<th>Время создания</th>
<!-- <th>Опции</th> -->
<!-- <th></th> -->
</tr>
</thead>
<tbody>
{#each userDeposits as depo}
<tr class="hover:bg-neutral group">
<td class="">{depoStatusMap[depo["pending"]]}</td>
<td>{depo["amount"]} USDT</td>
<td>{depo["creationtime"]}</td>
<!-- <td class="flex gap-2">
<a href="" class="btn btn-info">Изменить</a>
<button class="btn btn-error">Удалить</button>
</td> -->
</tr>
{/each}
</tbody>
</table>
</div>
</div>
</div>

View File

@ -0,0 +1,73 @@
<script>
import { browser } from "$app/environment";
import { checkAuthSync, getAuthInfo, makeAuthHeaderForAxios } from "$lib/auth/Auth";
import { makeGet } from "$lib/tools/requests/requests";
import { sayError } from "$lib/tools/toaster/Toaster";
import { redirect } from "$lib/tools/url/URLTools";
let users = [];
let usersReady = false;
async function getUsers() {
if(checkAuthSync())
{
const result = await makeGet("getUsers", makeAuthHeaderForAxios(getAuthInfo()?.a));
users = result.data;
usersReady = true;
// console.log(result);
}
else
{
sayError("Данные авторизации устарели");
// window.location.href = "/";
redirect("/admin/");
return;
}
}
if(browser)
{
getUsers();
}
</script>
<div class="w-full flex flex-col gap-8">
<div class="flex gap-4">
<div class="bg-accent rounded-[4px] w-[10px] h-full"></div>
<h1 class="text-2xl font-semibold">Все пользователи</h1>
</div>
<div class="w-full flex flex-col p-4 rounded-box bg-base-300">
<div class="overflow-x-auto">
{#if usersReady}
<table class="table">
<!-- head -->
<thead>
<tr>
<th>Токен</th>
<th>Имя</th>
<th>Баланс</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- row 1 -->
{#each users as user}
<tr class="hover:bg-neutral group">
<th class="font-normal">{user["token"]}</th>
<td class="font-semibold">{user["name"]} {user["surname"]}</td>
<td>{user["balance"]} USDT</td>
<td>
<a href={"/admin/user/profile/"+user["token"]} class="btn btn-outline btn-info group-hover:btn-warning">Профиль</a>
</td>
</tr>
{/each}
</tbody>
</table>
{:else}
<div class="flex p-4 w-full justify-center items-center">
<span class="loading loading-spinner"></span>
</div>
{/if}
</div>
</div>
</div>

View File

@ -20,15 +20,15 @@ const config = {
prerender: {
// default: true,
entries: [
"/user/change/sampleToken",
"/user/edit/req/sampleToken/sampleReqID",
"/user/neworder/sampleToken",
"/user/profile/sampleToken",
"/",
"/login",
"/newuser",
"/userslist",
"/disputes"
"/admin/user/change/sampleToken",
"/admin/user/edit/req/sampleToken/sampleReqID",
"/admin/user/neworder/sampleToken",
"/admin/user/profile/sampleToken",
"/admin/",
"/admin/login",
"/admin/newuser",
"/admin/userslist",
"/admin/disputes"
],
},
},