itube-specs 0.0.815 → 0.0.816
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/nuxt.config.ts
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SiteDataService } from '../../../services/site-data.service';
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler((event) => {
|
|
4
|
+
try {
|
|
5
|
+
const languages = SiteDataService.getSiteLanguages();
|
|
6
|
+
if (languages.length) {
|
|
7
|
+
return languages.map(lang => lang.iso);
|
|
8
|
+
}
|
|
9
|
+
} catch (e) {
|
|
10
|
+
console.error('Failed to get site languages:', e);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return useRuntimeConfig(event).public.locales.codes;
|
|
14
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Отдаёт 404 на пути с префиксом локали, которая поддерживается в коде, но
|
|
2
|
+
// выключена на конкретном сайте (список включённых локалей — из SiteDataService).
|
|
3
|
+
// Коды поддерживаемых локалей приходят из runtimeConfig.public.locales (задаёт проект).
|
|
4
|
+
// Чистое решение вынесено в server/utils/is-locale-blocked.ts (покрыто тестами).
|
|
5
|
+
|
|
6
|
+
import { SiteDataService } from '../../services/site-data.service';
|
|
7
|
+
import { isLocaleBlocked } from '../utils/is-locale-blocked';
|
|
8
|
+
|
|
9
|
+
export default defineEventHandler((event) => {
|
|
10
|
+
const path = event.node.req.url || '';
|
|
11
|
+
const { codes, default: defaultCode } = useRuntimeConfig(event).public.locales;
|
|
12
|
+
const nonDefaultCodes = codes.filter((code: string) => code !== defaultCode);
|
|
13
|
+
|
|
14
|
+
const blocked = isLocaleBlocked(
|
|
15
|
+
path,
|
|
16
|
+
nonDefaultCodes,
|
|
17
|
+
() => SiteDataService.getSiteLanguages().map(lang => lang.iso),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (blocked) {
|
|
21
|
+
throw createError({ statusCode: 404, statusMessage: 'Not Found' });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { isLocaleBlocked } from '../is-locale-blocked';
|
|
3
|
+
|
|
4
|
+
const KNOWN = ['de', 'fr', 'ru'];
|
|
5
|
+
|
|
6
|
+
describe('isLocaleBlocked', () => {
|
|
7
|
+
it('пропускает не-страничные запросы без обращения к геттеру', () => {
|
|
8
|
+
const getEnabled = vi.fn(() => ['fr']);
|
|
9
|
+
expect(isLocaleBlocked('/api/foo', KNOWN, getEnabled)).toBe(false);
|
|
10
|
+
expect(isLocaleBlocked('/_nuxt/x.js', KNOWN, getEnabled)).toBe(false);
|
|
11
|
+
expect(isLocaleBlocked('/__nuxt', KNOWN, getEnabled)).toBe(false);
|
|
12
|
+
expect(getEnabled).not.toHaveBeenCalled();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('путь без префикса локали — разрешён', () => {
|
|
16
|
+
expect(isLocaleBlocked('/videos/1', KNOWN, () => ['fr'])).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('неизвестный префикс — разрешён (разбирается Nuxt)', () => {
|
|
20
|
+
expect(isLocaleBlocked('/it/x', KNOWN, () => ['fr'])).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('список локалей пуст (не загружен) — разрешено всё', () => {
|
|
24
|
+
expect(isLocaleBlocked('/de/x', KNOWN, () => [])).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('известная, но выключенная локаль — блокируется', () => {
|
|
28
|
+
expect(isLocaleBlocked('/de/x', KNOWN, () => ['fr'])).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('известная и включённая локаль — разрешена', () => {
|
|
32
|
+
expect(isLocaleBlocked('/fr/x', KNOWN, () => ['fr', 'de'])).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('префикс в конце пути без слеша тоже матчится', () => {
|
|
36
|
+
expect(isLocaleBlocked('/ru', KNOWN, () => ['fr'])).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const LOCALE_PREFIX_RE = /^\/([a-z]{2})(\/|$)/;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Решает, нужно ли отдать 404 на запрос с префиксом локали в пути.
|
|
5
|
+
* Блокирует только известную, но выключенную на сайте локаль; пропускает
|
|
6
|
+
* не-страничные запросы (`/api/`, `/_nuxt/`, `/__nuxt`), пути без префикса,
|
|
7
|
+
* неизвестные префиксы и случай, когда список локалей ещё не загружен.
|
|
8
|
+
* `getEnabledCodes` вызывается лениво — только дойдя до известного префикса,
|
|
9
|
+
* чтобы не дёргать сервис на каждый ассет.
|
|
10
|
+
* @param path - путь запроса
|
|
11
|
+
* @param knownNonDefaultCodes - коды поддерживаемых не-дефолтных локалей
|
|
12
|
+
* @param getEnabledCodes - геттер кодов реально включённых на сайте локалей
|
|
13
|
+
*/
|
|
14
|
+
export function isLocaleBlocked(
|
|
15
|
+
path: string,
|
|
16
|
+
knownNonDefaultCodes: string[],
|
|
17
|
+
getEnabledCodes: () => string[],
|
|
18
|
+
): boolean {
|
|
19
|
+
if (path.startsWith('/api/') || path.startsWith('/_nuxt/') || path.startsWith('/__nuxt')) return false;
|
|
20
|
+
|
|
21
|
+
const match = path.match(LOCALE_PREFIX_RE);
|
|
22
|
+
if (!match) return false; // нет префикса локали (дефолт 'en') — всегда разрешено
|
|
23
|
+
|
|
24
|
+
const localeCode = match[1];
|
|
25
|
+
if (!knownNonDefaultCodes.includes(localeCode)) return false; // неизвестный префикс — пусть разбирается Nuxt
|
|
26
|
+
|
|
27
|
+
const enabledCodes = getEnabledCodes();
|
|
28
|
+
if (!enabledCodes.length) return false; // данные сайта ещё не загружены — разрешаем всё
|
|
29
|
+
|
|
30
|
+
return !enabledCodes.includes(localeCode);
|
|
31
|
+
}
|