fix and refactor code
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"exclude": ["node_modules", "dist"]
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { watch } from 'vue'
|
import { watch } from 'vue'
|
||||||
import { useSettingsStore } from './stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
|
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
to="/dashboard"
|
to="/dashboard"
|
||||||
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
||||||
:class="[
|
:class="[
|
||||||
$route.path === '/dashboard' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
route.path === '/dashboard' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
||||||
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
||||||
]"
|
]"
|
||||||
:title="sidebarCollapsed ? t('app.dashboard') : ''"
|
:title="sidebarCollapsed ? t('app.dashboard') : ''"
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
to="/users"
|
to="/users"
|
||||||
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
||||||
:class="[
|
:class="[
|
||||||
$route.path === '/users' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
route.path === '/users' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
||||||
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
||||||
]"
|
]"
|
||||||
:title="sidebarCollapsed ? t('app.users') : ''"
|
:title="sidebarCollapsed ? t('app.users') : ''"
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
to="/restaurants"
|
to="/restaurants"
|
||||||
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
||||||
:class="[
|
:class="[
|
||||||
$route.path === '/restaurants' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
route.path === '/restaurants' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
||||||
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
||||||
]"
|
]"
|
||||||
:title="sidebarCollapsed ? t('app.restaurants') : ''"
|
:title="sidebarCollapsed ? t('app.restaurants') : ''"
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
to="/settings"
|
to="/settings"
|
||||||
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
||||||
:class="[
|
:class="[
|
||||||
$route.path === '/settings' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
route.path === '/settings' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
||||||
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
||||||
]"
|
]"
|
||||||
:title="sidebarCollapsed ? t('app.settings') : ''"
|
:title="sidebarCollapsed ? t('app.settings') : ''"
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors no-router-link"
|
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors no-router-link"
|
||||||
:class="[
|
:class="[
|
||||||
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3',
|
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3',
|
||||||
$route.path === '/phpmyadmin' ? 'bg-primary-50 text-primary-700' : 'text-gray-700'
|
route.path === '/phpmyadmin' ? 'bg-primary-50 text-primary-700' : 'text-gray-700'
|
||||||
]"
|
]"
|
||||||
:title="sidebarCollapsed ? t('app.database') : ''"
|
:title="sidebarCollapsed ? t('app.database') : ''"
|
||||||
>
|
>
|
||||||
@@ -209,12 +209,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, onMounted } from 'vue'
|
import { computed, ref, onMounted } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useSettingsStore } from '../../stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
import { useUserStore } from '../../stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useNotification } from '../../composables/useNotification'
|
import { useNotification } from '@/composables/useNotification'
|
||||||
|
|
||||||
const { notification } = useNotification()
|
const { notification, showNotification } = useNotification()
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"totalUsers": "Total Users",
|
"totalUsers": "Total Users",
|
||||||
"activeSessions": "Active Sessions",
|
"totalRestaurants": "Total Restaurants",
|
||||||
"systemHealth": "System Health",
|
"systemHealth": "System Health",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"vsLastMonth": "vs last month",
|
"vsLastMonth": "vs last month",
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"totalUsers": "Всего пользователей",
|
"totalUsers": "Всего пользователей",
|
||||||
"activeSessions": "Активных сессий",
|
"totalRestaurants": "Всего ресторанов",
|
||||||
"systemHealth": "Здоровье системы",
|
"systemHealth": "Здоровье системы",
|
||||||
"uptime": "Время работы",
|
"uptime": "Время работы",
|
||||||
"vsLastMonth": "по сравнению с прошлым месяцем",
|
"vsLastMonth": "по сравнению с прошлым месяцем",
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import { createApp } from 'vue'
|
|||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import './style.css'
|
import '@/style.css'
|
||||||
import { useSettingsStore } from './stores/settings'
|
import { useSettingsStore } from './stores/settings'
|
||||||
import { useUserStore } from './stores/user'
|
import { useUserStore } from './stores/user'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import en from './locales/en.json'
|
import en from '@/locales/en.json'
|
||||||
import ru from './locales/ru.json'
|
import ru from '@/locales/ru.json'
|
||||||
|
|
||||||
// Функция определения языка браузера
|
// Функция определения языка браузера
|
||||||
function getBrowserLocale(): string {
|
function getBrowserLocale(): string {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import { useUserStore } from '../stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { useSettingsStore } from '../stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
import Login from '../views/auth/Login.vue'
|
import Login from '@/views/auth/Login.vue'
|
||||||
import Setup from '../views/auth/Setup.vue'
|
import Setup from '@/views/auth/Setup.vue'
|
||||||
import Register from '../views/auth/Register.vue'
|
import Register from '@/views/auth/Register.vue'
|
||||||
import Dashboard from '../views/Dashboard.vue'
|
import Dashboard from '@/views/Dashboard.vue'
|
||||||
import Users from '../views/Users.vue'
|
import Users from '@/views/Users.vue'
|
||||||
import Restaurants from '../views/Restaurants.vue'
|
import Restaurants from '@/views/Restaurants.vue'
|
||||||
import AdminSettings from '../views/AdminSettings.vue'
|
import AdminSettings from '@/views/AdminSettings.vue'
|
||||||
import Profile from '../views/Profile.vue'
|
import Profile from '@/views/Profile.vue'
|
||||||
import NotFound from '../views/NotFound.vue'
|
import NotFound from '@/views/NotFound.vue'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: '/login', component: Login, meta: { title: 'Login', requiresAuth: false } },
|
{ path: '/login', component: Login, meta: { title: 'Login', requiresAuth: false } },
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
|
|
||||||
async function fetchProfile() {
|
async function fetchProfile() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/admin/profile')
|
const res = await fetch('/api/profile')
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
id.value = data.id
|
id.value = data.id
|
||||||
@@ -27,7 +27,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function updateProfile(updates: { email?: string; password?: string; language?: string }) {
|
async function updateProfile(updates: { email?: string; password?: string; language?: string }) {
|
||||||
const res = await fetch('/api/admin/profile', {
|
const res = await fetch('/api/profile', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(updates)
|
body: JSON.stringify(updates)
|
||||||
|
|||||||
@@ -58,22 +58,18 @@
|
|||||||
<button type="submit" class="btn-primary">{{ t('settings.save') }}</button>
|
<button type="submit" class="btn-primary">{{ t('settings.save') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div v-if="message" class="mt-4 p-3 rounded-lg" :class="messageClass">
|
|
||||||
{{ message }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import AppLayout from '../components/Layout/AppLayout.vue';
|
import AppLayout from '@/components/Layout/AppLayout.vue';
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useNotification } from '../composables/useNotification'
|
import { useNotification } from '@/composables/useNotification'
|
||||||
|
|
||||||
const { showNotification } = useNotification()
|
const { showNotification } = useNotification()
|
||||||
const { t, locale } = useI18n()
|
const { t } = useI18n()
|
||||||
interface FieldMeta {
|
interface FieldMeta {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -86,11 +82,9 @@ interface FieldMeta {
|
|||||||
|
|
||||||
const meta = ref<FieldMeta[]>([]);
|
const meta = ref<FieldMeta[]>([]);
|
||||||
const values = ref<Record<string, string>>({});
|
const values = ref<Record<string, string>>({});
|
||||||
const message = ref('');
|
|
||||||
const messageClass = ref('');
|
|
||||||
|
|
||||||
async function loadMeta() {
|
async function loadMeta() {
|
||||||
const res = await fetch('/api/settings/meta');
|
const res = await fetch('/api/admin/settings/meta');
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
meta.value = await res.json();
|
meta.value = await res.json();
|
||||||
} else {
|
} else {
|
||||||
@@ -99,7 +93,7 @@ async function loadMeta() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadValues() {
|
async function loadValues() {
|
||||||
const res = await fetch('/api/settings/all');
|
const res = await fetch('/api/admin/settings');
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
values.value = await res.json();
|
values.value = await res.json();
|
||||||
} else {
|
} else {
|
||||||
@@ -128,13 +122,5 @@ async function saveSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMessage(text: string, cssClass: string) {
|
|
||||||
message.value = text;
|
|
||||||
messageClass.value = cssClass;
|
|
||||||
setTimeout(() => {
|
|
||||||
message.value = '';
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(loadData);
|
onMounted(loadData);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
<div class="card hover:shadow-md transition-shadow">
|
<div class="card hover:shadow-md transition-shadow">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm font-medium text-gray-600">{{ t('dashboard.activeSessions') }}</p>
|
<p class="text-sm font-medium text-gray-600">{{ t('dashboard.totalRestaurants') }}</p>
|
||||||
<p class="text-3xl font-bold text-gray-900 mt-2">{{ stats.activeSessions }}</p>
|
<p class="text-3xl font-bold text-gray-900 mt-2">{{ stats.totalRestaurants }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center">
|
<div class="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center">
|
||||||
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -175,13 +175,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted, onUnmounted } from 'vue';
|
||||||
import AppLayout from '../components/Layout/AppLayout.vue';
|
import AppLayout from '@/components/Layout/AppLayout.vue';
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
import { useNotification } from '../composables/useNotification'
|
import { useNotification } from '@/composables/useNotification'
|
||||||
|
|
||||||
const { showNotification } = useNotification()
|
const { showNotification } = useNotification()
|
||||||
const stats = ref({ totalUsers: 0, activeSessions: 0, systemHealth: 100, uptime: '99.9%' });
|
const stats = ref({ totalUsers: 0, totalRestaurants: 0, systemHealth: 100, uptime: '99.9%' });
|
||||||
const userGrowth = ref(12);
|
const userGrowth = ref(12);
|
||||||
const sessionGrowth = ref(5);
|
const sessionGrowth = ref(5);
|
||||||
const recentUsers = ref([]);
|
const recentUsers = ref([]);
|
||||||
@@ -195,20 +195,18 @@ let interval: number;
|
|||||||
|
|
||||||
async function loadDashboardData() {
|
async function loadDashboardData() {
|
||||||
try {
|
try {
|
||||||
const [usersRes, sessionsRes, healthRes, restaurantsRes] = await Promise.all([
|
const [usersRes, healthRes, restaurantsRes] = await Promise.all([
|
||||||
fetch('/api/admin/users'),
|
fetch('/api/admin/users'),
|
||||||
fetch('/api/admin/active-sessions'),
|
|
||||||
fetch('/api/health'),
|
fetch('/api/health'),
|
||||||
fetch('/api/admin/restaurants')
|
fetch('/api/admin/restaurants')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const users = await usersRes.json();
|
const users = await usersRes.json();
|
||||||
const sessions = await sessionsRes.json();
|
|
||||||
const health = await healthRes.json();
|
const health = await healthRes.json();
|
||||||
const restaurants = await restaurantsRes.json();
|
const restaurants = await restaurantsRes.json();
|
||||||
|
|
||||||
stats.value.totalUsers = users.length;
|
stats.value.totalUsers = users.length;
|
||||||
stats.value.activeSessions = sessions.count || 0;
|
stats.value.totalRestaurants = restaurants.length;
|
||||||
recentUsers.value = users.slice(-5).reverse();
|
recentUsers.value = users.slice(-5).reverse();
|
||||||
recentRestaurants.value = restaurants.slice(-5).reverse();
|
recentRestaurants.value = restaurants.slice(-5).reverse();
|
||||||
|
|
||||||
|
|||||||
@@ -84,10 +84,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted } from 'vue';
|
import { ref, reactive, computed, onMounted } from 'vue';
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '@/stores/user';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import AppLayout from '../components/Layout/AppLayout.vue';
|
import AppLayout from '@/components/Layout/AppLayout.vue';
|
||||||
|
import {useNotification} from "@/composables/useNotification";
|
||||||
|
|
||||||
|
const { showNotification } = useNotification();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
|
|
||||||
@@ -129,7 +131,7 @@ async function saveProfile() {
|
|||||||
if (ok) {
|
if (ok) {
|
||||||
locale.value = form.language;
|
locale.value = form.language;
|
||||||
showNotification('profile.updateSuccess', 'success');
|
showNotification('profile.updateSuccess', 'success');
|
||||||
resetForm(); // очищаем поля пароля
|
resetForm();
|
||||||
} else {
|
} else {
|
||||||
showNotification('profile.updateError', 'error');
|
showNotification('profile.updateError', 'error');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,9 +153,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import AppLayout from '../components/Layout/AppLayout.vue';
|
import AppLayout from '@/components/Layout/AppLayout.vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotification } from '../composables/useNotification';
|
import { useNotification } from '@/composables/useNotification';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { showNotification } = useNotification();
|
const { showNotification } = useNotification();
|
||||||
|
|||||||
@@ -155,10 +155,10 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import { ref, onMounted, computed } from 'vue';
|
||||||
import AppLayout from '../components/Layout/AppLayout.vue';
|
import AppLayout from '@/components/Layout/AppLayout.vue';
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '@/stores/user';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotification } from '../composables/useNotification';
|
import { useNotification } from '@/composables/useNotification';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { showNotification } = useNotification();
|
const { showNotification } = useNotification();
|
||||||
|
|||||||
@@ -103,8 +103,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useSettingsStore } from '../../stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
import { useUserStore } from '../../stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
|
|||||||
20
frontend/tsconfig.json
Normal file
20
frontend/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "vue",
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -3,6 +3,11 @@ import vue from '@vitejs/plugin-vue'
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': '/src',
|
||||||
|
},
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': 'http://localhost:8080' // для разработки
|
'/api': 'http://localhost:8080' // для разработки
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import io.vertx.ext.web.sstore.redis.RedisSessionStore;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import su.xserver.iikocon.config.AppConfig;
|
import su.xserver.iikocon.config.AppConfig;
|
||||||
|
import su.xserver.iikocon.handler.AdminHandler;
|
||||||
import su.xserver.iikocon.handler.AuthHandler;
|
import su.xserver.iikocon.handler.AuthHandler;
|
||||||
import su.xserver.iikocon.handler.SecurityHandler;
|
import su.xserver.iikocon.handler.SecurityHandler;
|
||||||
import su.xserver.iikocon.handler.SetupHandler;
|
import su.xserver.iikocon.handler.SetupHandler;
|
||||||
@@ -35,6 +36,7 @@ public class MainVerticle extends AbstractVerticle {
|
|||||||
private RedisService redis;
|
private RedisService redis;
|
||||||
private HttpServer httpServer;
|
private HttpServer httpServer;
|
||||||
private AppConfig config;
|
private AppConfig config;
|
||||||
|
private SessionStore sessionStore;
|
||||||
|
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
private RestaurantService restaurantService;
|
private RestaurantService restaurantService;
|
||||||
@@ -98,7 +100,7 @@ public class MainVerticle extends AbstractVerticle {
|
|||||||
}
|
}
|
||||||
long timeoutMs = timeoutMinutes * 60 * 1000;
|
long timeoutMs = timeoutMinutes * 60 * 1000;
|
||||||
|
|
||||||
SessionStore sessionStore = RedisSessionStore.create(vertx, redis.getRedis());
|
sessionStore = RedisSessionStore.create(vertx, redis.getRedis());
|
||||||
SessionHandler sessionHandler = SessionHandler.create(sessionStore)
|
SessionHandler sessionHandler = SessionHandler.create(sessionStore)
|
||||||
.setSessionCookieName("admin.session")
|
.setSessionCookieName("admin.session")
|
||||||
.setCookieHttpOnlyFlag(true)
|
.setCookieHttpOnlyFlag(true)
|
||||||
@@ -196,14 +198,14 @@ public class MainVerticle extends AbstractVerticle {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// В initRouter после настройки authHandler, до объявления /api/admin/*:
|
// В initRouter после настройки authHandler, до объявления /api/admin/*:
|
||||||
router.route("/api/admin/profile").handler(authHandler::requireAuth);
|
router.route("/api/profile").handler(authHandler::requireAuth);
|
||||||
router.get("/api/admin/profile").handler(rc -> {
|
router.get("/api/profile").handler(rc -> {
|
||||||
Integer userId = rc.session().get("userId");
|
Integer userId = rc.session().get("userId");
|
||||||
userService.getProfile(userId)
|
userService.getProfile(userId)
|
||||||
.onSuccess(profile -> rc.response().putHeader("Content-Type", "application/json").end(profile.encode()))
|
.onSuccess(profile -> rc.response().putHeader("Content-Type", "application/json").end(profile.encode()))
|
||||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||||
});
|
});
|
||||||
router.put("/api/admin/profile").handler(rc -> {
|
router.put("/api/profile").handler(rc -> {
|
||||||
Integer userId = rc.session().get("userId");
|
Integer userId = rc.session().get("userId");
|
||||||
JsonObject body = rc.body().asJsonObject();
|
JsonObject body = rc.body().asJsonObject();
|
||||||
String email = body.getString("email");
|
String email = body.getString("email");
|
||||||
@@ -235,8 +237,7 @@ public class MainVerticle extends AbstractVerticle {
|
|||||||
// Затем существующий блок router.route("/api/admin/*").handler(authHandler::requireAuth);
|
// Затем существующий блок router.route("/api/admin/*").handler(authHandler::requireAuth);
|
||||||
router.route("/api/admin/*").handler(authHandler::requireAuth);
|
router.route("/api/admin/*").handler(authHandler::requireAuth);
|
||||||
// Добавить проверку роли для чувствительных эндпоинтов:
|
// Добавить проверку роли для чувствительных эндпоинтов:
|
||||||
// router.route("/api/admin/users*").handler(AdminHandler::requireAdmin);
|
// router.route("/api/settings/meta*").handler(AdminHandler::requireAdmin);
|
||||||
// router.route("/api/admin/restaurants*").handler(AdminHandler::requireAdmin);
|
|
||||||
// router.route("/api/admin/settings*").handler(AdminHandler::requireAdmin);
|
// router.route("/api/admin/settings*").handler(AdminHandler::requireAdmin);
|
||||||
// router.route("/api/admin/active-sessions").handler(AdminHandler::requireAdmin);
|
// router.route("/api/admin/active-sessions").handler(AdminHandler::requireAdmin);
|
||||||
|
|
||||||
@@ -416,14 +417,15 @@ public class MainVerticle extends AbstractVerticle {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Получить метаданные всех настроек (для построения формы)
|
// Получить метаданные всех настроек (для построения формы)
|
||||||
router.get("/api/settings/meta").handler(rc -> {
|
router.route("/api/admin/settings*").handler(AdminHandler::requireAdmin);
|
||||||
|
router.get("/api/admin/settings/meta").handler(rc -> {
|
||||||
settingsService.getMetadata()
|
settingsService.getMetadata()
|
||||||
.onSuccess(meta -> rc.response().putHeader("Content-Type", "application/json").end(meta.encode()))
|
.onSuccess(meta -> rc.response().putHeader("Content-Type", "application/json").end(meta.encode()))
|
||||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Получить все настройки со значениями по умолчанию
|
// Получить все настройки со значениями по умолчанию
|
||||||
router.get("/api/settings/all").handler(rc -> {
|
router.get("/api/admin/settings").handler(rc -> {
|
||||||
settingsService.getAllWithDefaults()
|
settingsService.getAllWithDefaults()
|
||||||
.onSuccess(settings -> rc.response().putHeader("Content-Type", "application/json").end(settings.encode()))
|
.onSuccess(settings -> rc.response().putHeader("Content-Type", "application/json").end(settings.encode()))
|
||||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||||
@@ -439,17 +441,10 @@ public class MainVerticle extends AbstractVerticle {
|
|||||||
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
.onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage()));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Количество активных сессий (на основе Redis)
|
|
||||||
router.get("/api/admin/active-sessions").handler(rc -> {
|
|
||||||
// TODO: реализовать подсчёт активных сессий через Redis или другой механизм
|
|
||||||
rc.response().end(new JsonObject().put("count", 0).encode());
|
|
||||||
});
|
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startHttp(Router router, Promise<Void> startPromise) {
|
private void startHttp(Router router, Promise<Void> startPromise) {
|
||||||
// Запуск HTTP-сервера
|
|
||||||
httpServer = vertx.createHttpServer();
|
httpServer = vertx.createHttpServer();
|
||||||
httpServer.requestHandler(router).listen(config.server.port, config.server.host)
|
httpServer.requestHandler(router).listen(config.server.port, config.server.host)
|
||||||
.onSuccess(server -> {
|
.onSuccess(server -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user