Compare commits
4 Commits
69e5f4e320
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 02e9aa1ac4 | |||
| b3be74e652 | |||
| af437963bb | |||
| 6ff28dae13 |
@ -105,7 +105,7 @@ class Users extends \TPsoft\DBmodel\DBmodel {
|
||||
return password_verify($password, $password_hash);
|
||||
}
|
||||
|
||||
public function generateToken(int $user_id, int $ttl_seconds = 3600): string {
|
||||
public function generateToken(int $user_id, int $ttl_seconds = 604800): string {
|
||||
if ($user_id <= 0) {
|
||||
throw new \Exception('Invalid user_id');
|
||||
}
|
||||
@ -152,7 +152,7 @@ class Users extends \TPsoft\DBmodel\DBmodel {
|
||||
if (!hash_equals($stored_token, $token)) {
|
||||
return false;
|
||||
}
|
||||
$refresh_expires = date('Y-m-d H:i:s', time() + 3600);
|
||||
$refresh_expires = date('Y-m-d H:i:s', time() + 604800); // 7 days
|
||||
$updated = $this->user($user_id, array(
|
||||
'token_expires' => $refresh_expires
|
||||
));
|
||||
|
||||
9
deploy.bat
Normal file
@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
|
||||
call php d:\www\sftpsync\src\sftpsync.php --host nutrio.tpsoft.org --user igor ^
|
||||
--delete-dir /storage/tpsoft.org/nutrio/public ^
|
||||
--sync d:/www/Nutrio/dist /storage/tpsoft.org/nutrio ^
|
||||
--skip .git ^
|
||||
--print-relative
|
||||
|
||||
echo ✔️ Done.
|
||||
@ -163,3 +163,76 @@ Požiadavky na build.bat:
|
||||
- na konci vypíš, že DocumentRoot má byť nastavený na dist/public a frontend je v dist/public
|
||||
|
||||
Dodaj kompletný obsah súboru build.bat.
|
||||
|
||||
----- 2026-02-15 17:05:23 -----------------------------------------------------
|
||||
Úloha:
|
||||
V projekte vytvor plne funkčnú konfiguráciu PWA.
|
||||
|
||||
1️⃣ Manifest
|
||||
Vytvor súbor: frontend/public/manifest.json
|
||||
Obsah musí:
|
||||
- byť validný JSON
|
||||
obsahovať:
|
||||
- name
|
||||
- short_name
|
||||
- start_url
|
||||
- display = "standalone"
|
||||
- background_color
|
||||
- theme_color
|
||||
- icons pole
|
||||
Použi tieto hodnoty:
|
||||
{
|
||||
"name": "Nutrio",
|
||||
"short_name": "Nutrio",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#22c55e"
|
||||
}
|
||||
2️⃣ Ikony
|
||||
Zdrojový obrázok:
|
||||
frontend/public/Nutrio.600.png
|
||||
Z neho vytvor PNG ikony do adresára:
|
||||
frontend/public/icons/
|
||||
Vygeneruj tieto veľkosti:
|
||||
16x16
|
||||
32x32
|
||||
48x48
|
||||
180x180 (apple touch icon)
|
||||
192x192 (PWA required)
|
||||
512x512 (PWA required)
|
||||
Požiadavky:
|
||||
zachovať pomer strán
|
||||
zachovať priehľadnosť
|
||||
nepoužívať žiadne orezávanie
|
||||
výstupné názvy:
|
||||
icon-16.png
|
||||
icon-32.png
|
||||
icon-48.png
|
||||
icon-180.png
|
||||
icon-192.png
|
||||
icon-512.png
|
||||
3️⃣ Manifest icons sekcia
|
||||
Pole icons musí obsahovať iba:
|
||||
[
|
||||
{
|
||||
"src": "/icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
"src": "/icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
}
|
||||
]
|
||||
4️⃣ Úprava index.html
|
||||
Do frontend/index.html pridaj do <head>:
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16.png">
|
||||
<link rel="apple-touch-icon" href="/icons/icon-180.png">
|
||||
<meta name="theme-color" content="#22c55e">
|
||||
5️⃣ Výstup
|
||||
nevysvetľuj
|
||||
nevypisuj komentáre
|
||||
vytvor alebo uprav iba potrebné súbory
|
||||
ak adresár icons neexistuje, vytvor ho
|
||||
@ -2,7 +2,11 @@
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16.png">
|
||||
<link rel="apple-touch-icon" href="/icons/icon-180.png">
|
||||
<meta name="theme-color" content="#22c55e">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nutrio</title>
|
||||
</head>
|
||||
|
||||
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 66 KiB |
BIN
frontend/public/icons/icon-16.png
Normal file
|
After Width: | Height: | Size: 874 B |
BIN
frontend/public/icons/icon-180.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
frontend/public/icons/icon-192.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
frontend/public/icons/icon-32.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
frontend/public/icons/icon-48.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
frontend/public/icons/icon-512.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
20
frontend/public/manifest.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "Nutrio",
|
||||
"short_name": "Nutrio",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#22c55e",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -167,6 +167,7 @@
|
||||
"accountTitle": "Nastavení účtu",
|
||||
"loggedInAs": "Přihlášený uživatel",
|
||||
"themeTitle": "Vzhled",
|
||||
"languageTitle": "Jazyk",
|
||||
"logout": "Odhlásit se"
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,7 @@
|
||||
"accountTitle": "Kontoeinstellungen",
|
||||
"loggedInAs": "Angemeldet als",
|
||||
"themeTitle": "Darstellung",
|
||||
"languageTitle": "Sprache",
|
||||
"logout": "Abmelden"
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,7 @@
|
||||
"accountTitle": "Account settings",
|
||||
"loggedInAs": "Logged in as",
|
||||
"themeTitle": "Appearance",
|
||||
"languageTitle": "Language",
|
||||
"logout": "Log out"
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,7 @@
|
||||
"accountTitle": "Ajustes de cuenta",
|
||||
"loggedInAs": "Sesión iniciada como",
|
||||
"themeTitle": "Apariencia",
|
||||
"languageTitle": "Idioma",
|
||||
"logout": "Cerrar sesión"
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,7 @@
|
||||
"accountTitle": "Nastavenia účtu",
|
||||
"loggedInAs": "Prihlásený používateľ",
|
||||
"themeTitle": "Vzhľad",
|
||||
"languageTitle": "Jazyk",
|
||||
"logout": "Odhlásiť sa"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts">
|
||||
import { computed, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ThemeToggle from '@/components/common/ThemeToggle.vue'
|
||||
import i18n from '@/i18n'
|
||||
import { SUPPORTED_LOCALES, type AppLocale } from '@/i18n'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const { t } = useI18n()
|
||||
const { t } = useI18n({ useScope: 'global' })
|
||||
|
||||
const isSupportedLocale = (value: string): value is AppLocale => {
|
||||
return SUPPORTED_LOCALES.includes(value as AppLocale)
|
||||
}
|
||||
|
||||
const setLocale = (value: string) => {
|
||||
if (!isSupportedLocale(value)) {
|
||||
return
|
||||
}
|
||||
i18n.global.locale.value = value
|
||||
document.documentElement.setAttribute('lang', value)
|
||||
localStorage.setItem('locale', value)
|
||||
}
|
||||
|
||||
const localeValue = computed({
|
||||
get: () => i18n.global.locale.value as AppLocale,
|
||||
set: (value: string) => setLocale(value),
|
||||
})
|
||||
|
||||
watch(
|
||||
() => i18n.global.locale.value,
|
||||
(nextLocale) => {
|
||||
document.documentElement.setAttribute('lang', nextLocale)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const onLogout = async () => {
|
||||
await auth.logout()
|
||||
@ -25,6 +54,16 @@ const onLogout = async () => {
|
||||
<span>{{ t('settings.themeTitle') }}</span>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
<div class="settings-card__theme">
|
||||
<span>{{ t('settings.languageTitle') }}</span>
|
||||
<label class="locale-control" :aria-label="t('settings.languageTitle')">
|
||||
<select v-model="localeValue">
|
||||
<option v-for="lang in SUPPORTED_LOCALES" :key="lang" :value="lang">
|
||||
{{ t(`locale.${lang}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-danger" type="button" @click="onLogout">{{ t('settings.logout') }}</button>
|
||||
</section>
|
||||
</section>
|
||||
|
||||