Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2deb306419 | ||
|
|
fd283bf6c6 |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/project-settings",
|
||||
"baseLocale": "en",
|
||||
"locales": ["en", "ru", "id", "pt-BR", "fr"],
|
||||
"locales": ["en", "ru", "id", "pt", "fr"],
|
||||
"modules": [
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format/dist/index.js"
|
||||
],
|
||||
|
||||
@@ -141,7 +141,7 @@ export function parseAuthToken(token: string): { id: string; username: string; r
|
||||
// ─── Hook ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
function getTextDirection(locale: string): string {
|
||||
// All supported locales (en, ru, id, pt-BR, fr) are LTR
|
||||
// All supported locales (en, ru, id, pt, fr) are LTR
|
||||
return 'ltr';
|
||||
}
|
||||
|
||||
|
||||
@@ -197,8 +197,8 @@ async function countCollection(collection: string, filter = ''): Promise<number>
|
||||
return (data as { totalItems: number }).totalItems ?? 0;
|
||||
}
|
||||
|
||||
async function listOne<T>(collection: string, filter: string): Promise<T | null> {
|
||||
const params = new URLSearchParams({ perPage: '1', filter });
|
||||
async function listOne<T>(collection: string, filter: string, sort = '-updated'): Promise<T | null> {
|
||||
const params = new URLSearchParams({ perPage: '1', filter, sort });
|
||||
const data = await pbGet<PBList<T>>(
|
||||
`/api/collections/${collection}/records?${params.toString()}`
|
||||
);
|
||||
@@ -1012,6 +1012,8 @@ export async function createUserSession(
|
||||
throw new Error(`Failed to create session: ${res.status}`);
|
||||
}
|
||||
const rec = (await res.json()) as { id: string };
|
||||
// Best-effort: prune stale sessions in the background so the list doesn't grow forever
|
||||
pruneStaleUserSessions(userId).catch(() => {});
|
||||
return rec.id;
|
||||
}
|
||||
|
||||
@@ -1048,6 +1050,28 @@ export async function listUserSessions(userId: string): Promise<UserSession[]> {
|
||||
return listAll<UserSession>('user_sessions', `user_id="${userId}"`, '-last_seen');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete sessions for a user that haven't been seen in the last `days` days.
|
||||
* Called on login so the list self-cleans without a separate cron job.
|
||||
*/
|
||||
async function pruneStaleUserSessions(userId: string, days = 30): Promise<void> {
|
||||
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
||||
const stale = await listAll<UserSession>(
|
||||
'user_sessions',
|
||||
`user_id="${userId}" && last_seen<"${cutoff}"`
|
||||
);
|
||||
if (stale.length === 0) return;
|
||||
const token = await getToken();
|
||||
await Promise.all(
|
||||
stale.map((s) =>
|
||||
fetch(`${PB_URL}/api/collections/user_sessions/records/${s.id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
}).catch(() => {})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke (delete) a specific session by its PocketBase record ID.
|
||||
* Only allows deletion if the session belongs to the given userId.
|
||||
|
||||
@@ -35,20 +35,23 @@ export const load: LayoutServerLoad = async ({ locals, url, cookies }) => {
|
||||
log.warn('layout', 'failed to load settings', { err: String(e) });
|
||||
}
|
||||
|
||||
// If user is logged in and has a non-English locale saved, ensure the
|
||||
// PARAGLIDE_LOCALE cookie is set so the locale persists after refresh.
|
||||
// If user is logged in, keep the PARAGLIDE_LOCALE cookie in sync with
|
||||
// the saved locale so it persists across page loads and navigations.
|
||||
if (locals.user) {
|
||||
const savedLocale = settings.locale ?? 'en';
|
||||
if (savedLocale !== 'en') {
|
||||
const currentCookieLocale = cookies.get('PARAGLIDE_LOCALE');
|
||||
if (currentCookieLocale !== savedLocale) {
|
||||
cookies.set('PARAGLIDE_LOCALE', savedLocale, {
|
||||
path: '/',
|
||||
maxAge: 34560000,
|
||||
sameSite: 'lax',
|
||||
httpOnly: false
|
||||
});
|
||||
const currentCookieLocale = cookies.get('PARAGLIDE_LOCALE');
|
||||
if (savedLocale === 'en') {
|
||||
// Clear the cookie when the user's locale is English (the default)
|
||||
if (currentCookieLocale) {
|
||||
cookies.delete('PARAGLIDE_LOCALE', { path: '/' });
|
||||
}
|
||||
} else if (currentCookieLocale !== savedLocale) {
|
||||
cookies.set('PARAGLIDE_LOCALE', savedLocale, {
|
||||
path: '/',
|
||||
maxAge: 34560000,
|
||||
sameSite: 'lax',
|
||||
httpOnly: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export const PUT: RequestHandler = async ({ request, locals }) => {
|
||||
}
|
||||
|
||||
// locale is optional — if provided it must be a known value
|
||||
const validLocales = ['en', 'ru', 'id', 'pt-BR', 'fr'];
|
||||
const validLocales = ['en', 'ru', 'id', 'pt', 'fr'];
|
||||
if (body.locale !== undefined && !validLocales.includes(body.locale)) {
|
||||
error(400, `Invalid locale — must be one of: ${validLocales.join(', ')}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user