feat: added cookies to use subdomains
All checks were successful
Update changelog / changelog (push) Successful in 27s
All checks were successful
Update changelog / changelog (push) Successful in 27s
This commit is contained in:
18
kittyurl-frontend/package-lock.json
generated
18
kittyurl-frontend/package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
|
"js-cookie": "^3.0.5",
|
||||||
"js-sha512": "^0.9.0",
|
"js-sha512": "^0.9.0",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.39.1",
|
||||||
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
@@ -1588,6 +1590,13 @@
|
|||||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/js-cookie": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.15",
|
"version": "7.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||||
@@ -3007,6 +3016,15 @@
|
|||||||
"jiti": "lib/jiti-cli.mjs"
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-cookie": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/js-sha512": {
|
"node_modules/js-sha512": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
|
"js-cookie": "^3.0.5",
|
||||||
"js-sha512": "^0.9.0",
|
"js-sha512": "^0.9.0",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.39.1",
|
||||||
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { useState, useCallback, type ReactNode } from 'react';
|
import { useState, useCallback, type ReactNode } from 'react';
|
||||||
|
import Cookies from 'js-cookie'; // Importujemy bibliotekę do ciasteczek
|
||||||
import { AuthContext } from './AuthContext';
|
import { AuthContext } from './AuthContext';
|
||||||
import { sha512 } from '../utils/crypto';
|
import { sha512 } from '../utils/crypto';
|
||||||
import type { AuthResponse } from '../types/auth';
|
import type { AuthResponse } from '../types/auth';
|
||||||
|
|
||||||
|
const TOKEN_KEY = 'ktty_shared_token';
|
||||||
|
const PARENT_DOMAIN = '.ktty.is'; // Kropka na początku pozwala na dostęp ze wszystkich subdomen
|
||||||
|
|
||||||
const getSubdomain = (): string | null => {
|
const getSubdomain = (): string | null => {
|
||||||
const hostname = window.location.hostname;
|
const hostname = window.location.hostname;
|
||||||
const parts = hostname.split('.');
|
const parts = hostname.split('.');
|
||||||
@@ -12,9 +16,9 @@ const getSubdomain = (): string | null => {
|
|||||||
|
|
||||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||||
const subdomain = getSubdomain();
|
const subdomain = getSubdomain();
|
||||||
const storageKey = subdomain ? `ktty_token_${subdomain}` : 'ktty_token';
|
|
||||||
|
|
||||||
const [token, setToken] = useState<string | null>(sessionStorage.getItem(storageKey));
|
// Pobieramy token z ciasteczka zamiast z sessionStorage
|
||||||
|
const [token, setToken] = useState<string | null>(Cookies.get(TOKEN_KEY) || null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -24,9 +28,6 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const hashedPassword = await sha512(pass);
|
const hashedPassword = await sha512(pass);
|
||||||
|
|
||||||
// POPRAWKA: Ścieżka URL jest stała, zgodna z Twoim API.
|
|
||||||
// Vite Proxy przechwyci '/api' i skieruje to na https://www.ktty.is
|
|
||||||
const fullUrl = `/api/v1/user/${endpoint}`;
|
const fullUrl = `/api/v1/user/${endpoint}`;
|
||||||
|
|
||||||
const response = await fetch(fullUrl, {
|
const response = await fetch(fullUrl, {
|
||||||
@@ -34,7 +35,6 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
headers: {
|
headers: {
|
||||||
'accept': 'application/json',
|
'accept': 'application/json',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
// Subdomenę przekazujemy w nagłówku, aby backend wiedział, który to klient
|
|
||||||
'X-Subdomain': subdomain || '',
|
'X-Subdomain': subdomain || '',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ name, password: hashedPassword, ttl: 86400 }),
|
body: JSON.stringify({ name, password: hashedPassword, ttl: 86400 }),
|
||||||
@@ -43,12 +43,17 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
const data: AuthResponse = await response.json();
|
const data: AuthResponse = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorMsg = data?.error || data?.message || `Błąd serwera: ${response.status}`;
|
throw new Error(data?.error || data?.message || 'Błąd logowania');
|
||||||
throw new Error(errorMsg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data?.token) {
|
if (data?.token) {
|
||||||
sessionStorage.setItem(storageKey, data.token);
|
// ZAPISYWANIE CIASTECZKA DLA WSZYSTKICH SUBDOMEN
|
||||||
|
Cookies.set(TOKEN_KEY, data.token, {
|
||||||
|
domain: PARENT_DOMAIN,
|
||||||
|
expires: 1, // 1 dzień
|
||||||
|
secure: true, // Wymagane dla HTTPS
|
||||||
|
sameSite: 'lax'
|
||||||
|
});
|
||||||
setToken(data.token);
|
setToken(data.token);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
@@ -60,12 +65,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [subdomain, storageKey]);
|
}, [subdomain]);
|
||||||
|
|
||||||
const logout = useCallback(() => {
|
const logout = useCallback(() => {
|
||||||
sessionStorage.removeItem(storageKey);
|
// Usuwamy ciasteczko z domeny nadrzędnej
|
||||||
|
Cookies.remove(TOKEN_KEY, { domain: PARENT_DOMAIN });
|
||||||
setToken(null);
|
setToken(null);
|
||||||
}, [storageKey]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{
|
<AuthContext.Provider value={{
|
||||||
|
|||||||
Reference in New Issue
Block a user