Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c70c24205 | |||
| c70ad1d08b | |||
| d0566a1df9 | |||
| bc95a2851f | |||
| e03d743307 | |||
| 99357848e5 | |||
| c41ae0d4c5 | |||
| 66f8bde206 | |||
| b3afb9ac1b |
@@ -5,13 +5,3 @@ updates:
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
rebase-strategy: "auto"
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
rebase-strategy: "auto"
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/src/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
rebase-strategy: "auto"
|
||||
|
||||
@@ -4,12 +4,13 @@ title: Optional Configuration
|
||||
|
||||
You can set these environment variables to configure the container. They are not required, but can be useful in some cases.
|
||||
|
||||
| Env | Default | Example | Description |
|
||||
| -------------- | --------- | ----------- | ---------------------------------- |
|
||||
| `PORT` | `51821` | `6789` | TCP port for Web UI. |
|
||||
| `HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. |
|
||||
| `INSECURE` | `false` | `true` | If access over http is allowed |
|
||||
| `DISABLE_IPV6` | `false` | `true` | If IPv6 support should be disabled |
|
||||
| Env | Default | Example | Description |
|
||||
| ----------------------- | --------- | ----------- | --------------------------------------- |
|
||||
| `PORT` | `51821` | `6789` | TCP port for Web UI. |
|
||||
| `HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. |
|
||||
| `INSECURE` | `false` | `true` | If access over http is allowed |
|
||||
| `DISABLE_IPV6` | `false` | `true` | If IPv6 support should be disabled |
|
||||
| `DISABLE_VERSION_CHECK` | `false` | `true` | If wg-easy should check for new updates |
|
||||
|
||||
/// note | IPv6 Caveats
|
||||
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@
|
||||
"devDependencies": {
|
||||
"prettier": "^3.8.3"
|
||||
},
|
||||
"packageManager": "pnpm@11.1.2"
|
||||
"packageManager": "pnpm@11.5.0"
|
||||
}
|
||||
|
||||
@@ -14,10 +14,11 @@ const props = defineProps<{ client: LocalClient }>();
|
||||
const clientsStore = useClientsStore();
|
||||
|
||||
const _showOneTimeLink = useSubmit(
|
||||
`/api/client/${props.client.id}/generateOneTimeLink`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/client/${props.client.id}/generateOneTimeLink`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async () => {
|
||||
await clientsStore.refresh();
|
||||
|
||||
@@ -18,10 +18,11 @@ const enabled = ref(props.client.enabled);
|
||||
const clientsStore = useClientsStore();
|
||||
|
||||
const _disableClient = useSubmit(
|
||||
`/api/client/${props.client.id}/disable`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/client/${props.client.id}/disable`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async () => {
|
||||
await clientsStore.refresh();
|
||||
@@ -31,10 +32,11 @@ const _disableClient = useSubmit(
|
||||
);
|
||||
|
||||
const _enableClient = useSubmit(
|
||||
`/api/client/${props.client.id}/enable`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/client/${props.client.id}/enable`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async () => {
|
||||
await clientsStore.refresh();
|
||||
|
||||
@@ -43,10 +43,11 @@ function createClient() {
|
||||
}
|
||||
|
||||
const _createClient = useSubmit(
|
||||
'/api/client',
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch('/api/client', {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: () => clientsStore.refresh(),
|
||||
successMsg: t('client.created'),
|
||||
|
||||
@@ -70,10 +70,11 @@ const authStore = useAuthStore();
|
||||
const toggleState = ref(false);
|
||||
|
||||
const _submit = useSubmit(
|
||||
'/api/session',
|
||||
{
|
||||
method: 'delete',
|
||||
},
|
||||
(data) =>
|
||||
$fetch('/api/session', {
|
||||
method: 'delete',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async () => {
|
||||
await navigateTo('/login');
|
||||
|
||||
@@ -1,49 +1,24 @@
|
||||
import type {
|
||||
NitroFetchRequest,
|
||||
NitroFetchOptions,
|
||||
TypedInternalResponse,
|
||||
ExtractedRouteMethod,
|
||||
} from 'nitropack/types';
|
||||
import { FetchError } from 'ofetch';
|
||||
|
||||
type RevertFn<
|
||||
R extends NitroFetchRequest,
|
||||
T = unknown,
|
||||
O extends NitroFetchOptions<R> = NitroFetchOptions<R>,
|
||||
> = (
|
||||
success: boolean,
|
||||
data:
|
||||
| TypedInternalResponse<
|
||||
R,
|
||||
T,
|
||||
NitroFetchOptions<R> extends O ? 'get' : ExtractedRouteMethod<R, O>
|
||||
>
|
||||
| undefined
|
||||
) => Promise<void>;
|
||||
type RevertFn<T> = (success: boolean, data: T | undefined) => Promise<void>;
|
||||
|
||||
type SubmitOpts<
|
||||
R extends NitroFetchRequest,
|
||||
T = unknown,
|
||||
O extends NitroFetchOptions<R> = NitroFetchOptions<R>,
|
||||
> = {
|
||||
revert: RevertFn<R, T, O>;
|
||||
type SubmitOpts<T> = {
|
||||
revert: RevertFn<T>;
|
||||
successMsg?: string;
|
||||
noSuccessToast?: boolean;
|
||||
};
|
||||
|
||||
export function useSubmit<
|
||||
R extends NitroFetchRequest,
|
||||
O extends NitroFetchOptions<R> & { body?: never },
|
||||
T = unknown,
|
||||
>(url: R, options: O, opts: SubmitOpts<R, T, O>) {
|
||||
type Body = Record<string, unknown> | null | undefined;
|
||||
|
||||
export function useSubmit<T>(
|
||||
fetcher: (data: Body) => Promise<T>,
|
||||
opts: SubmitOpts<T>
|
||||
) {
|
||||
const toast = useToast();
|
||||
|
||||
return async (data: unknown) => {
|
||||
return async (data: Body) => {
|
||||
try {
|
||||
const res = await $fetch(url, {
|
||||
...options,
|
||||
body: data,
|
||||
});
|
||||
const res = await fetcher(data);
|
||||
|
||||
if (!opts.noSuccessToast) {
|
||||
toast.showToast({
|
||||
|
||||
@@ -121,10 +121,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/userconfig`, {
|
||||
const data = toRef(_data.value);
|
||||
|
||||
const _submit = useSubmit(
|
||||
`/api/admin/userconfig`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/admin/userconfig`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{ revert }
|
||||
);
|
||||
|
||||
|
||||
@@ -46,10 +46,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/general`, {
|
||||
const data = toRef(_data.value);
|
||||
|
||||
const _submit = useSubmit(
|
||||
`/api/admin/general`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/admin/general`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{ revert }
|
||||
);
|
||||
|
||||
|
||||
@@ -40,10 +40,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/hooks`, {
|
||||
const data = toRef(_data.value);
|
||||
|
||||
const _submit = useSubmit(
|
||||
`/api/admin/hooks`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/admin/hooks`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{ revert }
|
||||
);
|
||||
|
||||
|
||||
@@ -176,10 +176,11 @@ const { data: _data, refresh } = await useFetch(`/api/admin/interface`, {
|
||||
const data = toRef(_data.value);
|
||||
|
||||
const _submit = useSubmit(
|
||||
`/api/admin/interface`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/admin/interface`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success) => {
|
||||
await revert();
|
||||
@@ -201,10 +202,11 @@ async function revert() {
|
||||
}
|
||||
|
||||
const _changeCidr = useSubmit(
|
||||
`/api/admin/interface/cidr`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/admin/interface/cidr`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert,
|
||||
successMsg: t('admin.interface.cidrSuccess'),
|
||||
@@ -216,10 +218,11 @@ async function changeCidr(ipv4Cidr: string, ipv6Cidr: string) {
|
||||
}
|
||||
|
||||
const _restartInterface = useSubmit(
|
||||
`/api/admin/interface/restart`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/admin/interface/restart`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert,
|
||||
successMsg: t('admin.interface.restartSuccess'),
|
||||
|
||||
@@ -225,10 +225,11 @@ const { data: _data, refresh } = await useFetch(`/api/client/${id}`, {
|
||||
const data = toRef(_data.value);
|
||||
|
||||
const _submit = useSubmit(
|
||||
`/api/client/${id}`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/client/${id}`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success) => {
|
||||
if (success) {
|
||||
@@ -250,10 +251,11 @@ async function revert() {
|
||||
}
|
||||
|
||||
const _deleteClient = useSubmit(
|
||||
`/api/client/${id}`,
|
||||
{
|
||||
method: 'delete',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/client/${id}`, {
|
||||
method: 'delete',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async () => {
|
||||
await navigateTo('/');
|
||||
|
||||
@@ -78,10 +78,11 @@ const totpRequired = ref(false);
|
||||
const totp = ref<string>('');
|
||||
|
||||
const _submit = useSubmit(
|
||||
'/api/session',
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch('/api/session', {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success, data) => {
|
||||
if (success) {
|
||||
|
||||
+25
-20
@@ -127,10 +127,11 @@ const name = ref(authStore.userData?.name);
|
||||
const email = ref(authStore.userData?.email);
|
||||
|
||||
const _submit = useSubmit(
|
||||
`/api/me`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/me`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: () => {
|
||||
return authStore.update();
|
||||
@@ -147,10 +148,11 @@ const newPassword = ref('');
|
||||
const confirmPassword = ref('');
|
||||
|
||||
const _updatePassword = useSubmit(
|
||||
`/api/me/password`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/me/password`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async () => {
|
||||
currentPassword.value = '';
|
||||
@@ -171,10 +173,11 @@ function updatePassword() {
|
||||
const twofa = ref<{ key: string; qrcode: string } | null>(null);
|
||||
|
||||
const _setup2fa = useSubmit(
|
||||
`/api/me/totp`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/me/totp`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success, data) => {
|
||||
if (success && data?.type === 'setup') {
|
||||
@@ -199,10 +202,11 @@ async function setup2fa() {
|
||||
const code = ref<string>('');
|
||||
|
||||
const _enable2fa = useSubmit(
|
||||
`/api/me/totp`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/me/totp`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success, data) => {
|
||||
if (success && data?.type === 'created') {
|
||||
@@ -224,10 +228,11 @@ async function enable2fa() {
|
||||
const disable2faPassword = ref('');
|
||||
|
||||
const _disable2fa = useSubmit(
|
||||
`/api/me/totp`,
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch(`/api/me/totp`, {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success, data) => {
|
||||
if (success && data?.type === 'deleted') {
|
||||
|
||||
@@ -50,10 +50,11 @@ const password = ref<string>('');
|
||||
const confirmPassword = ref<string>('');
|
||||
|
||||
const _submit = useSubmit(
|
||||
'/api/setup/2',
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch('/api/setup/2', {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success) => {
|
||||
if (success) {
|
||||
|
||||
@@ -43,10 +43,11 @@ const host = ref<null | string>(null);
|
||||
const port = ref<number>(51820);
|
||||
|
||||
const _submit = useSubmit(
|
||||
'/api/setup/4',
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch('/api/setup/4', {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success) => {
|
||||
if (success) {
|
||||
|
||||
@@ -36,10 +36,11 @@ function onChangeFile(evt: Event) {
|
||||
}
|
||||
|
||||
const _submit = useSubmit(
|
||||
'/api/setup/migrate',
|
||||
{
|
||||
method: 'post',
|
||||
},
|
||||
(data) =>
|
||||
$fetch('/api/setup/migrate', {
|
||||
method: 'post',
|
||||
body: data,
|
||||
}),
|
||||
{
|
||||
revert: async (success) => {
|
||||
if (success) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import uk from './locales/uk.json';
|
||||
import fr from './locales/fr.json';
|
||||
import de from './locales/de.json';
|
||||
import it from './locales/it.json';
|
||||
import ja from './locales/ja.json';
|
||||
import ru from './locales/ru.json';
|
||||
import zhhk from './locales/zh-HK.json';
|
||||
import zhcn from './locales/zh-CN.json';
|
||||
@@ -17,6 +18,7 @@ import id from './locales/id.json';
|
||||
import nl from './locales/nl.json';
|
||||
import nb from './locales/nb.json';
|
||||
import bg from './locales/bg.json';
|
||||
import hi from './locales/hi.json';
|
||||
import gl from './locales/gl.json';
|
||||
import cs from './locales/cs.json';
|
||||
import vi from './locales/vi.json';
|
||||
@@ -31,6 +33,7 @@ export default defineI18nConfig(() => ({
|
||||
fr,
|
||||
de,
|
||||
it,
|
||||
ja,
|
||||
ru,
|
||||
'zh-HK': zhhk,
|
||||
'zh-CN': zhcn,
|
||||
@@ -44,6 +47,7 @@ export default defineI18nConfig(() => ({
|
||||
nl,
|
||||
nb,
|
||||
bg,
|
||||
hi,
|
||||
gl,
|
||||
cs,
|
||||
vi,
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "खाता",
|
||||
"clients": "क्लाइंट",
|
||||
"admin": {
|
||||
"panel": "एडमिन पैनल",
|
||||
"general": "सामान्य",
|
||||
"config": "कॉन्फ़िगरेशन",
|
||||
"interface": "इंटरफ़ेस",
|
||||
"hooks": "हुक्स"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "ई-मेल"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "वर्तमान पासवर्ड",
|
||||
"enable2fa": "दो-कारक प्रमाणीकरण सक्षम करें",
|
||||
"enable2faDesc": "अपने प्रमाणक ऐप से QR कोड स्कैन करें या कुंजी मैन्युअल रूप से दर्ज करें।",
|
||||
"2faKey": "TOTP कुंजी",
|
||||
"2faCodeDesc": "अपने प्रमाणक ऐप से कोड दर्ज करें।",
|
||||
"disable2fa": "दो-कारक प्रमाणीकरण अक्षम करें",
|
||||
"disable2faDesc": "दो-कारक प्रमाणीकरण अक्षम करने के लिए अपना पासवर्ड दर्ज करें।"
|
||||
},
|
||||
"general": {
|
||||
"name": "नाम",
|
||||
"username": "उपयोगकर्ता नाम",
|
||||
"password": "पासवर्ड",
|
||||
"newPassword": "नया पासवर्ड",
|
||||
"updatePassword": "पासवर्ड अपडेट करें",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "अनुमत IPs",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "स्थायी कीपअलाइव",
|
||||
"logout": "लॉगआउट",
|
||||
"continue": "जारी रखें",
|
||||
"host": "होस्ट",
|
||||
"port": "पोर्ट",
|
||||
"yes": "हाँ",
|
||||
"no": "नहीं",
|
||||
"confirmPassword": "पासवर्ड की पुष्टि करें",
|
||||
"loading": "लोड हो रहा है...",
|
||||
"2fa": "दो-कारक प्रमाणीकरण",
|
||||
"2faCode": "TOTP कोड"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "wg-easy की प्रारंभिक सेटअप में आपका स्वागत है",
|
||||
"welcomeDesc": "आपने किसी भी Linux होस्ट पर WireGuard इंस्टॉल और प्रबंधित करने का सबसे आसान तरीका खोज लिया है",
|
||||
"existingSetup": "क्या आपके पास पहले से कोई सेटअप है?",
|
||||
"createAdminDesc": "कृपया पहले एडमिन उपयोगकर्ता नाम और एक मज़बूत सुरक्षित पासवर्ड दर्ज करें। इस जानकारी का उपयोग प्रशासन पैनल में लॉग इन करने के लिए किया जाएगा।",
|
||||
"setupConfigDesc": "कृपया होस्ट और पोर्ट जानकारी दर्ज करें। इसका उपयोग उनके डिवाइस पर WireGuard सेटअप करते समय क्लाइंट कॉन्फ़िगरेशन के लिए किया जाएगा।",
|
||||
"setupMigrationDesc": "यदि आप अपने पिछले wg-easy संस्करण से डेटा माइग्रेट करना चाहते हैं तो कृपया बैकअप फ़ाइल प्रदान करें।",
|
||||
"upload": "अपलोड",
|
||||
"migration": "बैकअप से पुनर्स्थापित करें:",
|
||||
"createAccount": "खाता बनाएं",
|
||||
"successful": "सेटअप सफल रहा",
|
||||
"hostDesc": "सार्वजनिक होस्टनाम जिससे क्लाइंट कनेक्ट होंगे",
|
||||
"portDesc": "सार्वजनिक UDP पोर्ट जिससे क्लाइंट कनेक्ट होंगे और WireGuard सुनेगा"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "एक अपडेट उपलब्ध है!",
|
||||
"update": "अपडेट करें"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "डार्क थीम",
|
||||
"light": "लाइट थीम",
|
||||
"system": "सिस्टम थीम"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "चार्ट दिखाएं/छिपाएं",
|
||||
"donate": "दान करें"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "साइन इन",
|
||||
"rememberMe": "मुझे याद रखें",
|
||||
"rememberMeDesc": "ब्राउज़र बंद करने के बाद भी लॉग इन रहें",
|
||||
"insecure": "आप असुरक्षित कनेक्शन से लॉग इन नहीं कर सकते। HTTPS का उपयोग करें।",
|
||||
"2faRequired": "दो-कारक प्रमाणीकरण आवश्यक है",
|
||||
"2faWrong": "दो-कारक प्रमाणीकरण गलत है"
|
||||
},
|
||||
"client": {
|
||||
"empty": "अभी तक कोई क्लाइंट नहीं है।",
|
||||
"newShort": "नया",
|
||||
"sort": "क्रमबद्ध करें",
|
||||
"create": "क्लाइंट बनाएं",
|
||||
"created": "क्लाइंट बनाया गया",
|
||||
"new": "नया क्लाइंट",
|
||||
"name": "नाम",
|
||||
"expireDate": "समाप्ति तिथि",
|
||||
"expireDateDesc": "वह तिथि जिसके बाद क्लाइंट अक्षम हो जाएगा। स्थायी के लिए खाली छोड़ें",
|
||||
"delete": "हटाएं",
|
||||
"deleteClient": "क्लाइंट हटाएं",
|
||||
"deleteDialog1": "क्या आप वाकई हटाना चाहते हैं",
|
||||
"deleteDialog2": "यह क्रिया पूर्ववत नहीं की जा सकती।",
|
||||
"enabled": "सक्षम",
|
||||
"address": "पता",
|
||||
"serverAllowedIps": "सर्वर द्वारा अनुमत IPs",
|
||||
"otlDesc": "छोटा एकबारगी लिंक उत्पन्न करें",
|
||||
"permanent": "स्थायी",
|
||||
"createdOn": "बनाया गया ",
|
||||
"lastSeen": "अंतिम बार देखा गया ",
|
||||
"totalDownload": "कुल डाउनलोड: ",
|
||||
"totalUpload": "कुल अपलोड: ",
|
||||
"newClient": "नया क्लाइंट",
|
||||
"disableClient": "क्लाइंट अक्षम करें",
|
||||
"enableClient": "क्लाइंट सक्षम करें",
|
||||
"noPrivKey": "इस क्लाइंट की कोई ज्ञात निजी कुंजी नहीं है। कॉन्फ़िगरेशन नहीं बना सकते।",
|
||||
"showQR": "QR कोड दिखाएं",
|
||||
"downloadConfig": "कॉन्फ़िगरेशन डाउनलोड करें",
|
||||
"allowedIpsDesc": "कौन से IPs VPN के माध्यम से रूट होंगे (वैश्विक कॉन्फ़िगरेशन को ओवरराइड करता है)",
|
||||
"serverAllowedIpsDesc": "कौन से IPs सर्वर क्लाइंट को रूट करेगा",
|
||||
"mtuDesc": "VPN टनल के लिए अधिकतम ट्रांसमिशन इकाई (पैकेट आकार) सेट करता है",
|
||||
"persistentKeepaliveDesc": "कीप-अलाइव पैकेट के लिए अंतराल (सेकंड में) सेट करता है। 0 इसे अक्षम करता है",
|
||||
"hooks": "हुक्स",
|
||||
"hooksDescription": "हुक्स केवल wg-quick के साथ काम करते हैं",
|
||||
"hooksLeaveEmpty": "केवल wg-quick के लिए। अन्यथा, खाली छोड़ें",
|
||||
"dnsDesc": "DNS सर्वर जिसे क्लाइंट उपयोग करेंगे (वैश्विक कॉन्फ़िगरेशन को ओवरराइड करता है)",
|
||||
"notConnected": "क्लाइंट कनेक्ट नहीं है",
|
||||
"endpoint": "एंडपॉइंट",
|
||||
"endpointDesc": "क्लाइंट का IP पता जहाँ से WireGuard कनेक्शन स्थापित होता है",
|
||||
"search": "क्लाइंट खोजें...",
|
||||
"config": "कॉन्फ़िगरेशन",
|
||||
"viewConfig": "कॉन्फ़िगरेशन देखें",
|
||||
"firewallIps": "फ़ायरवॉल द्वारा अनुमत IPs",
|
||||
"firewallIpsDesc": "गंतव्य IPs/CIDRs जिन तक यह क्लाइंट पहुँच सकता है (सर्वर-साइड नियंत्रण)। अनुमत IPs उपयोग करने के लिए खाली छोड़ें। वैकल्पिक पोर्ट और प्रोटोकॉल फ़िल्टरिंग का समर्थन करता है। सिंटैक्स के लिए दस्तावेज़ देखें।",
|
||||
"downloadPng": "PNG डाउनलोड करें",
|
||||
"copyPng": "PNG कॉपी करें"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "बदलें",
|
||||
"cancel": "रद्द करें",
|
||||
"create": "बनाएं"
|
||||
},
|
||||
"toast": {
|
||||
"success": "सफल",
|
||||
"saved": "सहेजा गया",
|
||||
"error": "त्रुटि",
|
||||
"unknown": "अज्ञात त्रुटि। अधिक जानकारी के लिए कंसोल देखें"
|
||||
},
|
||||
"form": {
|
||||
"actions": "क्रियाएं",
|
||||
"save": "सहेजें",
|
||||
"revert": "पूर्ववत करें",
|
||||
"sectionGeneral": "सामान्य",
|
||||
"sectionAdvanced": "उन्नत",
|
||||
"noItems": "कोई आइटम नहीं",
|
||||
"nullNoItems": "कोई आइटम नहीं। वैश्विक कॉन्फ़िगरेशन उपयोग में है",
|
||||
"add": "जोड़ें"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "सत्र समय समाप्ति",
|
||||
"sessionTimeoutDesc": "रिमेम्बर मी के लिए सत्र अवधि (सेकंड में)",
|
||||
"metrics": "मेट्रिक्स",
|
||||
"metricsPassword": "पासवर्ड",
|
||||
"metricsPasswordDesc": "मेट्रिक्स एंडपॉइंट के लिए बियरर पासवर्ड (पासवर्ड या argon2 हैश)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "JSON फ़ॉर्मेट में मेट्रिक्स का मार्ग",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Prometheus मेट्रिक्स का मार्ग"
|
||||
},
|
||||
"config": {
|
||||
"connection": "कनेक्शन",
|
||||
"hostDesc": "सार्वजनिक होस्टनाम जिससे क्लाइंट कनेक्ट होंगे (कॉन्फ़िगरेशन को अमान्य करता है)",
|
||||
"portDesc": "सार्वजनिक UDP पोर्ट जिससे क्लाइंट कनेक्ट होंगे (कॉन्फ़िगरेशन को अमान्य करता है, शायद आप इंटरफ़ेस पोर्ट भी बदलना चाहें)",
|
||||
"allowedIpsDesc": "क्लाइंट द्वारा उपयोग किए जाने वाले अनुमत IPs (वैश्विक कॉन्फ़िगरेशन)",
|
||||
"dnsDesc": "क्लाइंट द्वारा उपयोग किया जाने वाला DNS सर्वर (वैश्विक कॉन्फ़िगरेशन)",
|
||||
"mtuDesc": "क्लाइंट द्वारा उपयोग किया जाने वाला MTU (केवल नए क्लाइंट के लिए)",
|
||||
"persistentKeepaliveDesc": "सर्वर को कीपअलाइव भेजने का अंतराल सेकंड में। 0 = अक्षम (केवल नए क्लाइंट के लिए)",
|
||||
"suggest": "सुझाएं",
|
||||
"suggestDesc": "होस्ट फ़ील्ड के लिए IP पता या होस्टनाम चुनें"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR बदला गया",
|
||||
"device": "डिवाइस",
|
||||
"deviceDesc": "ईथरनेट डिवाइस जिसके माध्यम से WireGuard ट्रैफ़िक फ़ॉरवर्ड किया जाना चाहिए",
|
||||
"mtuDesc": "MTU जिसे WireGuard उपयोग करेगा",
|
||||
"portDesc": "UDP पोर्ट जिस पर WireGuard सुनेगा (शायद आप कॉन्फ़िग पोर्ट भी बदलना चाहें)",
|
||||
"changeCidr": "CIDR बदलें",
|
||||
"restart": "इंटरफ़ेस रीस्टार्ट करें",
|
||||
"restartDesc": "WireGuard इंटरफ़ेस को रीस्टार्ट करें",
|
||||
"restartWarn": "क्या आप वाकई इंटरफ़ेस रीस्टार्ट करना चाहते हैं? इससे सभी क्लाइंट डिस्कनेक्ट हो जाएंगे।",
|
||||
"restartSuccess": "इंटरफ़ेस रीस्टार्ट हो गया",
|
||||
"firewall": "ट्रैफ़िक फ़िल्टरिंग",
|
||||
"firewallEnabled": "प्रति-क्लाइंट फ़ायरवॉल सक्षम करें",
|
||||
"firewallEnabledDesc": "iptables का उपयोग करके क्लाइंट ट्रैफ़िक को विशिष्ट गंतव्य IPs तक सीमित करें। सक्षम होने पर, प्रत्येक क्लाइंट को अनुमत गंतव्यों के साथ कॉन्फ़िगर किया जा सकता है।"
|
||||
},
|
||||
"introText": "एडमिन पैनल में आपका स्वागत है।\n\nयहाँ आप सामान्य सेटिंग्स, कॉन्फ़िगरेशन, इंटरफ़ेस सेटिंग्स और हुक्स प्रबंधित कर सकते हैं।\n\nसाइडबार में किसी एक अनुभाग को चुनकर शुरू करें।"
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} आवश्यक है",
|
||||
"validNumber": "{0} एक वैध संख्या होनी चाहिए",
|
||||
"validNumberRange": "{0} एक वैध संख्या या संख्या श्रेणी होनी चाहिए",
|
||||
"validString": "{0} एक वैध स्ट्रिंग होनी चाहिए",
|
||||
"validBoolean": "{0} एक वैध बूलियन होना चाहिए",
|
||||
"validArray": "{0} एक वैध ऐरे होना चाहिए",
|
||||
"stringMin": "{0} कम से कम {1} अक्षर का होना चाहिए",
|
||||
"numberMin": "{0} कम से कम {1} होना चाहिए"
|
||||
},
|
||||
"client": {
|
||||
"id": "क्लाइंट ID",
|
||||
"name": "नाम",
|
||||
"expiresAt": "समाप्ति तिथि",
|
||||
"address4": "IPv4 पता",
|
||||
"address6": "IPv6 पता",
|
||||
"serverAllowedIps": "सर्वर द्वारा अनुमत IPs",
|
||||
"firewallIps": "फ़ायरवॉल द्वारा अनुमत IPs",
|
||||
"firewallIpsInvalid": "अमान्य फ़ायरवॉल IP प्रविष्टि। समर्थित सिंटैक्स के लिए दस्तावेज़ देखें।"
|
||||
},
|
||||
"user": {
|
||||
"username": "उपयोगकर्ता नाम",
|
||||
"password": "पासवर्ड",
|
||||
"remember": "याद रखें",
|
||||
"name": "नाम",
|
||||
"email": "ई-मेल",
|
||||
"emailInvalid": "ई-मेल एक वैध ईमेल होनी चाहिए",
|
||||
"passwordMatch": "पासवर्ड मेल खाने चाहिए",
|
||||
"totpEnable": "TOTP सक्षम करें",
|
||||
"totpEnableTrue": "TOTP सक्षम सत्य होना चाहिए",
|
||||
"totpCode": "TOTP कोड"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "होस्ट"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "सत्र समय समाप्ति",
|
||||
"metricsEnabled": "मेट्रिक्स",
|
||||
"metricsPassword": "मेट्रिक्स पासवर्ड"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "डिवाइस",
|
||||
"cidrValid": "CIDR वैध होना चाहिए"
|
||||
},
|
||||
"otl": "एकबारगी लिंक",
|
||||
"stringMalformed": "स्ट्रिंग विकृत है",
|
||||
"body": "बॉडी एक वैध ऑब्जेक्ट होनी चाहिए",
|
||||
"hook": "हुक",
|
||||
"enabled": "सक्षम",
|
||||
"mtu": "MTU",
|
||||
"port": "पोर्ट",
|
||||
"persistentKeepalive": "स्थायी कीपअलाइव",
|
||||
"address": "IP पता",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "अनुमत IPs",
|
||||
"file": "फ़ाइल"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
},
|
||||
"copy": {
|
||||
"notSupported": "कॉपी समर्थित नहीं है",
|
||||
"copied": "कॉपी हो गया!",
|
||||
"failed": "कॉपी विफल रहा",
|
||||
"copy": "कॉपी करें"
|
||||
},
|
||||
"awg": {
|
||||
"jCLabel": "जंक पैकेट गणना (Jc)",
|
||||
"jCDescription": "भेजे जाने वाले जंक पैकेट की संख्या (1-128, अनुशंसित: 4-12)",
|
||||
"jMinLabel": "जंक पैकेट न्यूनतम आकार (Jmin)",
|
||||
"jMinDescription": "जंक पैकेट का न्यूनतम आकार (0-1279*, अनुशंसित: 8, Jmax से कम होना चाहिए)",
|
||||
"jMaxLabel": "जंक पैकेट अधिकतम आकार (Jmax)",
|
||||
"jMaxDescription": "जंक पैकेट का अधिकतम आकार (1-1280*, अनुशंसित: 80, Jmin से अधिक होना चाहिए)",
|
||||
"s1Label": "इनिट पैकेट जंक आकार (S1)",
|
||||
"s1Description": "इनिट पैकेट जंक आकार (0-1132[1280* - 148 = 1132], अनुशंसित: 15-150, S1+56 ≠ S2)",
|
||||
"s2Label": "रिस्पॉन्स पैकेट जंक आकार (S2)",
|
||||
"s2Description": "रिस्पॉन्स पैकेट जंक आकार (0-1188[1280* - 92 = 1188], अनुशंसित: 15-150)",
|
||||
"s3Label": "कुकी रिप्लाई पैकेट जंक आकार (S3)",
|
||||
"s3Description": "कुकी रिप्लाई पैकेट जंक आकार",
|
||||
"s4Label": "ट्रांसपोर्ट पैकेट जंक आकार (S4)",
|
||||
"s4Description": "ट्रांसपोर्ट पैकेट जंक आकार",
|
||||
"h1Label": "इनिट मैजिक हेडर (H1)",
|
||||
"h1Description": "इनिट पैकेट हेडर मान या श्रेणी (X या X-Y, जहाँ X<Y। न्यूनतम 5, अधिकतम 2147483647। मान या श्रेणी अन्य हेडर से ओवरलैप नहीं होनी चाहिए)",
|
||||
"h2Label": "रिस्पॉन्स मैजिक हेडर (H2)",
|
||||
"h2Description": "रिस्पॉन्स पैकेट हेडर मान या श्रेणी (X या X-Y, जहाँ X<Y। न्यूनतम 5, अधिकतम 2147483647। मान या श्रेणी अन्य हेडर से ओवरलैप नहीं होनी चाहिए)",
|
||||
"h3Label": "कुकी रिप्लाई मैजिक हेडर (H3)",
|
||||
"h3Description": "कुकी रिप्लाई पैकेट हेडर मान या श्रेणी (X या X-Y, जहाँ X<Y। न्यूनतम 5, अधिकतम 2147483647। मान या श्रेणी अन्य हेडर से ओवरलैप नहीं होनी चाहिए)",
|
||||
"h4Label": "ट्रांसपोर्ट मैजिक हेडर (H4)",
|
||||
"h4Description": "ट्रांसपोर्ट पैकेट हेडर मान या श्रेणी (X या X-Y, जहाँ X<Y। न्यूनतम 5, अधिकतम 2147483647। मान या श्रेणी अन्य हेडर से ओवरलैप नहीं होनी चाहिए)",
|
||||
"i1Label": "विशेष जंक पैकेट 1 (I1)",
|
||||
"i1Description": "hex फ़ॉर्मेट में प्रोटोकॉल मिमिक पैकेट: <b 0x...>",
|
||||
"i2Label": "विशेष जंक पैकेट 2 (I2)",
|
||||
"i2Description": "hex फ़ॉर्मेट में प्रोटोकॉल मिमिक पैकेट: <b 0x...>",
|
||||
"i3Label": "विशेष जंक पैकेट 3 (I3)",
|
||||
"i3Description": "hex फ़ॉर्मेट में प्रोटोकॉल मिमिक पैकेट: <b 0x...>",
|
||||
"i4Label": "विशेष जंक पैकेट 4 (I4)",
|
||||
"i4Description": "hex फ़ॉर्मेट में प्रोटोकॉल मिमिक पैकेट: <b 0x...>",
|
||||
"i5Label": "विशेष जंक पैकेट 5 (I5)",
|
||||
"i5Description": "hex फ़ॉर्मेट में प्रोटोकॉल मिमिक पैकेट: <b 0x...>",
|
||||
"mtuNote": "मान MTU पर निर्भर करते हैं",
|
||||
"obfuscationParameters": "AmneziaWG ऑबफस्केशन पैरामीटर"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
{
|
||||
"pages": {
|
||||
"me": "アカウント",
|
||||
"clients": "クライアント",
|
||||
"admin": {
|
||||
"panel": "管理パネル",
|
||||
"general": "一般",
|
||||
"config": "構成",
|
||||
"interface": "インターフェイス",
|
||||
"hooks": "フック"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"email": "メール"
|
||||
},
|
||||
"me": {
|
||||
"currentPassword": "現在のパスワード",
|
||||
"enable2fa": "二要素認証を有効化",
|
||||
"enable2faDesc": "QRコードを認証アプリでスキャンするか、キーを手動で入力してください。",
|
||||
"2faKey": "TOTPキー",
|
||||
"2faCodeDesc": "認証アプリのコードを入力してください。",
|
||||
"disable2fa": "二要素認証を無効化",
|
||||
"disable2faDesc": "二要素認証を無効にするにはパスワードを入力してください。"
|
||||
},
|
||||
"general": {
|
||||
"name": "名前",
|
||||
"username": "ユーザー名",
|
||||
"password": "パスワード",
|
||||
"newPassword": "新しいパスワード",
|
||||
"updatePassword": "パスワードを更新",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "許可 IP",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "永続的 Keepalive",
|
||||
"logout": "ログアウト",
|
||||
"continue": "続行",
|
||||
"host": "ホスト",
|
||||
"port": "ポート",
|
||||
"yes": "はい",
|
||||
"no": "いいえ",
|
||||
"confirmPassword": "パスワードの確認",
|
||||
"loading": "読み込み中...",
|
||||
"2fa": "二要素認証",
|
||||
"2faCode": "TOTPコード"
|
||||
},
|
||||
"setup": {
|
||||
"welcome": "wg-easy の初期セットアップへようこそ",
|
||||
"welcomeDesc": "Linux ホストで WireGuard をインストールして管理する最も簡単な方法です",
|
||||
"existingSetup": "既存のセットアップがありますか?",
|
||||
"createAdminDesc": "まず管理者ユーザー名と強力で安全なパスワードを入力してください。この情報は管理パネルへのログインに使用されます。",
|
||||
"setupConfigDesc": "ホストとポート情報を入力してください。これは各デバイスで WireGuard を設定するときのクライアント構成に使用されます。",
|
||||
"setupMigrationDesc": "新しいセットアップへ以前の wg-easy バージョンからデータを移行する場合は、バックアップファイルを指定してください。",
|
||||
"upload": "アップロード",
|
||||
"migration": "バックアップを復元:",
|
||||
"createAccount": "アカウントを作成",
|
||||
"successful": "セットアップが完了しました",
|
||||
"hostDesc": "クライアントが接続する公開ホスト名",
|
||||
"portDesc": "クライアントが接続し、WireGuard が待ち受ける公開 UDP ポート"
|
||||
},
|
||||
"update": {
|
||||
"updateAvailable": "利用可能な更新があります!",
|
||||
"update": "更新"
|
||||
},
|
||||
"theme": {
|
||||
"dark": "ダークテーマ",
|
||||
"light": "ライトテーマ",
|
||||
"system": "システムテーマ"
|
||||
},
|
||||
"layout": {
|
||||
"toggleCharts": "グラフの表示/非表示",
|
||||
"donate": "寄付"
|
||||
},
|
||||
"login": {
|
||||
"signIn": "サインイン",
|
||||
"rememberMe": "ログイン状態を保持",
|
||||
"rememberMeDesc": "ブラウザーを閉じた後もログイン状態を保持します",
|
||||
"insecure": "安全でない接続ではログインできません。HTTPS を使用してください。",
|
||||
"2faRequired": "二要素認証が必要です",
|
||||
"2faWrong": "二要素認証が正しくありません"
|
||||
},
|
||||
"client": {
|
||||
"empty": "まだクライアントはありません。",
|
||||
"newShort": "新規",
|
||||
"sort": "並べ替え",
|
||||
"create": "クライアントを作成",
|
||||
"created": "クライアントを作成しました",
|
||||
"new": "新しいクライアント",
|
||||
"name": "名前",
|
||||
"expireDate": "有効期限",
|
||||
"expireDateDesc": "この日にクライアントが無効化されます。空欄の場合は無期限です",
|
||||
"delete": "削除",
|
||||
"deleteClient": "クライアントを削除",
|
||||
"deleteDialog1": "削除してもよろしいですか",
|
||||
"deleteDialog2": "この操作は元に戻せません。",
|
||||
"enabled": "有効",
|
||||
"address": "アドレス",
|
||||
"serverAllowedIps": "サーバー許可 IP",
|
||||
"otlDesc": "短いワンタイムリンクを生成",
|
||||
"permanent": "無期限",
|
||||
"createdOn": "作成日: ",
|
||||
"lastSeen": "最終接続: ",
|
||||
"totalDownload": "総ダウンロード: ",
|
||||
"totalUpload": "総アップロード: ",
|
||||
"newClient": "新しいクライアント",
|
||||
"disableClient": "クライアントを無効化",
|
||||
"enableClient": "クライアントを有効化",
|
||||
"noPrivKey": "このクライアントの秘密鍵は不明です。構成を作成できません。",
|
||||
"showQR": "QRコードを表示",
|
||||
"downloadConfig": "構成をダウンロード",
|
||||
"allowedIpsDesc": "VPN 経由でルーティングされる IP (グローバル設定を上書き)",
|
||||
"serverAllowedIpsDesc": "サーバーがこのクライアントへルーティングする IP",
|
||||
"mtuDesc": "VPN トンネルの最大転送単位 (パケットサイズ) を設定します",
|
||||
"persistentKeepaliveDesc": "Keepalive パケットを送信する間隔 (秒) を設定します。0 で無効化します",
|
||||
"hooks": "フック",
|
||||
"hooksDescription": "フックは wg-quick でのみ動作します",
|
||||
"hooksLeaveEmpty": "wg-quick 専用です。それ以外の場合は空のままにしてください",
|
||||
"dnsDesc": "クライアントが使用する DNS サーバー (グローバル設定を上書き)",
|
||||
"notConnected": "クライアントは接続されていません",
|
||||
"endpoint": "エンドポイント",
|
||||
"endpointDesc": "WireGuard 接続が確立されているクライアントの IP",
|
||||
"search": "クライアントを検索...",
|
||||
"config": "構成",
|
||||
"viewConfig": "構成を表示",
|
||||
"firewallIps": "ファイアウォール許可 IP",
|
||||
"firewallIpsDesc": "このクライアントがアクセスできる宛先 IP/CIDR (サーバー側で強制)。空欄の場合は Allowed IPs を使用します。任意のポートとプロトコルによるフィルタリングに対応しています。構文はドキュメントを参照してください。",
|
||||
"downloadPng": "PNG をダウンロード",
|
||||
"copyPng": "PNG をコピー"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "変更",
|
||||
"cancel": "キャンセル",
|
||||
"create": "作成"
|
||||
},
|
||||
"toast": {
|
||||
"success": "成功",
|
||||
"saved": "保存しました",
|
||||
"error": "エラー",
|
||||
"unknown": "不明なエラーです。詳細はコンソールを確認してください"
|
||||
},
|
||||
"form": {
|
||||
"actions": "アクション",
|
||||
"save": "保存",
|
||||
"revert": "元に戻す",
|
||||
"sectionGeneral": "一般",
|
||||
"sectionAdvanced": "詳細",
|
||||
"noItems": "項目なし",
|
||||
"nullNoItems": "項目なし。グローバル設定を使用しています",
|
||||
"add": "追加"
|
||||
},
|
||||
"admin": {
|
||||
"general": {
|
||||
"sessionTimeout": "セッションタイムアウト",
|
||||
"sessionTimeoutDesc": "ログイン状態を保持する場合のセッション期間 (秒)",
|
||||
"metrics": "メトリクス",
|
||||
"metricsPassword": "パスワード",
|
||||
"metricsPasswordDesc": "メトリクスエンドポイント用の Bearer パスワード (パスワードまたは argon2 ハッシュ)",
|
||||
"json": "JSON",
|
||||
"jsonDesc": "JSON 形式のメトリクスルート",
|
||||
"prometheus": "Prometheus",
|
||||
"prometheusDesc": "Prometheus メトリクスのルート"
|
||||
},
|
||||
"config": {
|
||||
"connection": "接続",
|
||||
"hostDesc": "クライアントが接続する公開ホスト名 (構成が無効化されます)",
|
||||
"portDesc": "クライアントが接続する公開 UDP ポート (構成が無効化されます。通常はインターフェイスのポートも変更します)",
|
||||
"allowedIpsDesc": "クライアントが使用する許可 IP (グローバル設定)",
|
||||
"dnsDesc": "クライアントが使用する DNS サーバー (グローバル設定)",
|
||||
"mtuDesc": "クライアントが使用する MTU (新規クライアントのみ)",
|
||||
"persistentKeepaliveDesc": "サーバーへ Keepalive を送信する間隔 (秒)。0 = 無効 (新規クライアントのみ)",
|
||||
"suggest": "候補",
|
||||
"suggestDesc": "ホスト欄に使用する IP アドレスまたはホスト名を選択してください"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "CIDR を変更しました",
|
||||
"device": "デバイス",
|
||||
"deviceDesc": "WireGuard トラフィックを転送する Ethernet デバイス",
|
||||
"mtuDesc": "WireGuard が使用する MTU",
|
||||
"portDesc": "WireGuard が待ち受ける UDP ポート (通常は構成ポートも変更します)",
|
||||
"changeCidr": "CIDR を変更",
|
||||
"restart": "インターフェイスを再起動",
|
||||
"restartDesc": "WireGuard インターフェイスを再起動します",
|
||||
"restartWarn": "インターフェイスを再起動してもよろしいですか?すべてのクライアントが切断されます。",
|
||||
"restartSuccess": "インターフェイスを再起動しました",
|
||||
"firewall": "トラフィックフィルタリング",
|
||||
"firewallEnabled": "クライアントごとのファイアウォールを有効化",
|
||||
"firewallEnabledDesc": "iptables を使用して、クライアントのトラフィックを特定の宛先 IP に制限します。有効にすると、クライアントごとに許可する宛先を設定できます。"
|
||||
},
|
||||
"introText": "管理パネルへようこそ。\n\nここでは一般設定、構成、インターフェイス設定、フックを管理できます。\n\nまずサイドバーからセクションを選択してください。"
|
||||
},
|
||||
"zod": {
|
||||
"generic": {
|
||||
"required": "{0} は必須です",
|
||||
"validNumber": "{0} は有効な数値である必要があります",
|
||||
"validNumberRange": "{0} は有効な数値または数値範囲である必要があります",
|
||||
"validString": "{0} は有効な文字列である必要があります",
|
||||
"validBoolean": "{0} は有効な真偽値である必要があります",
|
||||
"validArray": "{0} は有効な配列である必要があります",
|
||||
"stringMin": "{0} は {1} 文字以上である必要があります",
|
||||
"numberMin": "{0} は {1} 以上である必要があります"
|
||||
},
|
||||
"client": {
|
||||
"id": "クライアント ID",
|
||||
"name": "名前",
|
||||
"expiresAt": "有効期限",
|
||||
"address4": "IPv4 アドレス",
|
||||
"address6": "IPv6 アドレス",
|
||||
"serverAllowedIps": "サーバー許可 IP",
|
||||
"firewallIps": "ファイアウォール許可 IP",
|
||||
"firewallIpsInvalid": "ファイアウォール IP の指定が無効です。対応している構文はドキュメントを参照してください。"
|
||||
},
|
||||
"user": {
|
||||
"username": "ユーザー名",
|
||||
"password": "パスワード",
|
||||
"remember": "ログイン状態を保持",
|
||||
"name": "名前",
|
||||
"email": "メール",
|
||||
"emailInvalid": "メールは有効なメールアドレスである必要があります",
|
||||
"passwordMatch": "パスワードが一致しません",
|
||||
"totpEnable": "TOTP 有効化",
|
||||
"totpEnableTrue": "TOTP 有効化は true である必要があります",
|
||||
"totpCode": "TOTPコード"
|
||||
},
|
||||
"userConfig": {
|
||||
"host": "ホスト"
|
||||
},
|
||||
"general": {
|
||||
"sessionTimeout": "セッションタイムアウト",
|
||||
"metricsEnabled": "メトリクス",
|
||||
"metricsPassword": "メトリクスパスワード"
|
||||
},
|
||||
"interface": {
|
||||
"cidr": "CIDR",
|
||||
"device": "デバイス",
|
||||
"cidrValid": "CIDR は有効である必要があります"
|
||||
},
|
||||
"otl": "ワンタイムリンク",
|
||||
"stringMalformed": "文字列の形式が正しくありません",
|
||||
"body": "本文は有効なオブジェクトである必要があります",
|
||||
"hook": "フック",
|
||||
"enabled": "有効",
|
||||
"mtu": "MTU",
|
||||
"port": "ポート",
|
||||
"persistentKeepalive": "永続的 Keepalive",
|
||||
"address": "IP アドレス",
|
||||
"dns": "DNS",
|
||||
"allowedIps": "許可 IP",
|
||||
"file": "ファイル"
|
||||
},
|
||||
"hooks": {
|
||||
"preUp": "PreUp",
|
||||
"postUp": "PostUp",
|
||||
"preDown": "PreDown",
|
||||
"postDown": "PostDown"
|
||||
},
|
||||
"copy": {
|
||||
"notSupported": "コピーはサポートされていません",
|
||||
"copied": "コピーしました!",
|
||||
"failed": "コピーに失敗しました",
|
||||
"copy": "コピー"
|
||||
},
|
||||
"awg": {
|
||||
"jCLabel": "ジャンクパケット数 (Jc)",
|
||||
"jCDescription": "送信するジャンクパケット数 (1-128、推奨: 4-12)",
|
||||
"jMinLabel": "ジャンクパケット最小サイズ (Jmin)",
|
||||
"jMinDescription": "ジャンクパケットの最小サイズ (0-1279*、推奨: 8、Jmax 未満)",
|
||||
"jMaxLabel": "ジャンクパケット最大サイズ (Jmax)",
|
||||
"jMaxDescription": "ジャンクパケットの最大サイズ (1-1280*、推奨: 80、Jmin より大きい)",
|
||||
"s1Label": "初期化パケットのジャンクサイズ (S1)",
|
||||
"s1Description": "初期化パケットのジャンクサイズ (0-1132[1280* - 148 = 1132]、推奨: 15-150、S1+56 ≠ S2)",
|
||||
"s2Label": "応答パケットのジャンクサイズ (S2)",
|
||||
"s2Description": "応答パケットのジャンクサイズ (0-1188[1280* - 92 = 1188]、推奨: 15-150)",
|
||||
"s3Label": "Cookie 応答パケットのジャンクサイズ (S3)",
|
||||
"s3Description": "Cookie 応答パケットのジャンクサイズ",
|
||||
"s4Label": "トランスポートパケットのジャンクサイズ (S4)",
|
||||
"s4Description": "トランスポートパケットのジャンクサイズ",
|
||||
"h1Label": "初期化マジックヘッダー (H1)",
|
||||
"h1Description": "初期化パケットのヘッダー値または範囲 (X または X-Y、X<Y。最小 5、最大 2147483647。値または範囲は他のヘッダーと重複してはいけません)",
|
||||
"h2Label": "応答マジックヘッダー (H2)",
|
||||
"h2Description": "応答パケットのヘッダー値または範囲 (X または X-Y、X<Y。最小 5、最大 2147483647。値または範囲は他のヘッダーと重複してはいけません)",
|
||||
"h3Label": "Cookie 応答マジックヘッダー (H3)",
|
||||
"h3Description": "Cookie 応答パケットのヘッダー値または範囲 (X または X-Y、X<Y。最小 5、最大 2147483647。値または範囲は他のヘッダーと重複してはいけません)",
|
||||
"h4Label": "トランスポートマジックヘッダー (H4)",
|
||||
"h4Description": "トランスポートパケットのヘッダー値または範囲 (X または X-Y、X<Y。最小 5、最大 2147483647。値または範囲は他のヘッダーと重複してはいけません)",
|
||||
"i1Label": "特殊ジャンクパケット 1 (I1)",
|
||||
"i1Description": "16進数形式のプロトコル模倣パケット: <b 0x...>",
|
||||
"i2Label": "特殊ジャンクパケット 2 (I2)",
|
||||
"i2Description": "16進数形式のプロトコル模倣パケット: <b 0x...>",
|
||||
"i3Label": "特殊ジャンクパケット 3 (I3)",
|
||||
"i3Description": "16進数形式のプロトコル模倣パケット: <b 0x...>",
|
||||
"i4Label": "特殊ジャンクパケット 4 (I4)",
|
||||
"i4Description": "16進数形式のプロトコル模倣パケット: <b 0x...>",
|
||||
"i5Label": "特殊ジャンクパケット 5 (I5)",
|
||||
"i5Description": "16進数形式のプロトコル模倣パケット: <b 0x...>",
|
||||
"mtuNote": "値は MTU に依存します",
|
||||
"obfuscationParameters": "AmneziaWG 難読化パラメーター"
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,11 @@ export default defineNuxtConfig({
|
||||
language: 'it-IT',
|
||||
name: 'Italiano',
|
||||
},
|
||||
{
|
||||
code: 'ja',
|
||||
language: 'ja-JP',
|
||||
name: '日本語',
|
||||
},
|
||||
{
|
||||
code: 'fr',
|
||||
language: 'fr-FR',
|
||||
@@ -131,6 +136,11 @@ export default defineNuxtConfig({
|
||||
language: 'bg-BG',
|
||||
name: 'Български',
|
||||
},
|
||||
{
|
||||
code: 'hi',
|
||||
language: 'hi-IN',
|
||||
name: 'हिन्दी',
|
||||
},
|
||||
{
|
||||
code: 'gl',
|
||||
language: 'gl-ES',
|
||||
|
||||
+12
-12
@@ -24,31 +24,31 @@
|
||||
"@eschricht/nuxt-color-mode": "^1.2.0",
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"@libsql/client": "^0.17.3",
|
||||
"@nuxtjs/i18n": "^10.3.0",
|
||||
"@nuxtjs/i18n": "^10.4.0",
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
"@phc/format": "^1.0.0",
|
||||
"@pinia/nuxt": "^0.11.3",
|
||||
"@tailwindcss/forms": "^0.5.11",
|
||||
"@vueuse/core": "^14.3.0",
|
||||
"@vueuse/nuxt": "^14.3.0",
|
||||
"apexcharts": "^5.12.0",
|
||||
"apexcharts": "^5.13.0",
|
||||
"argon2": "^0.44.0",
|
||||
"cidr-tools": "^12.0.1",
|
||||
"cidr-tools": "^12.0.2",
|
||||
"citty": "^0.2.2",
|
||||
"consola": "^3.4.2",
|
||||
"crc-32": "^1.2.2",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"ip-bigint": "^9.0.4",
|
||||
"ip-bigint": "^9.0.5",
|
||||
"is-cidr": "^7.0.0",
|
||||
"is-ip": "^5.0.1",
|
||||
"js-sha256": "^0.11.1",
|
||||
"nuxt": "^3.21.5",
|
||||
"nuxt": "^3.21.6",
|
||||
"obug": "^2.1.1",
|
||||
"otpauth": "^9.5.1",
|
||||
"pinia": "^3.0.4",
|
||||
"qr": "^0.6.0",
|
||||
"radix-vue": "^1.9.17",
|
||||
"semver": "^7.8.0",
|
||||
"semver": "^7.8.1",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"timeago.js": "^4.0.2",
|
||||
"vue": "latest",
|
||||
@@ -60,18 +60,18 @@
|
||||
"@nuxt/test-utils": "^4.0.3",
|
||||
"@types/phc__format": "^1.0.1",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@vitest/coverage-v8": "^4.1.6",
|
||||
"@vitest/ui": "^4.1.6",
|
||||
"@vitest/coverage-v8": "^4.1.7",
|
||||
"@vitest/ui": "^4.1.7",
|
||||
"drizzle-kit": "^0.31.10",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-plugin-tailwindcss": "^0.8.0",
|
||||
"tsx": "^4.22.1",
|
||||
"tsx": "^4.22.3",
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^4.1.6",
|
||||
"vue-tsc": "^3.2.9"
|
||||
"vitest": "^4.1.7",
|
||||
"vue-tsc": "^3.3.3"
|
||||
},
|
||||
"packageManager": "pnpm@11.1.2"
|
||||
"packageManager": "pnpm@11.5.0"
|
||||
}
|
||||
|
||||
Generated
+1759
-1744
File diff suppressed because it is too large
Load Diff
@@ -4,3 +4,5 @@ allowBuilds:
|
||||
esbuild: false
|
||||
unrs-resolver: false
|
||||
vue-demi: false
|
||||
|
||||
minimumReleaseAgeStrict: true
|
||||
|
||||
@@ -18,9 +18,9 @@ export default defineEventHandler(async (event) => {
|
||||
statusMessage: 'Invalid username or password',
|
||||
});
|
||||
case 'TOTP_REQUIRED':
|
||||
return { status: 'TOTP_REQUIRED' };
|
||||
return { status: 'TOTP_REQUIRED' as const };
|
||||
case 'INVALID_TOTP_CODE':
|
||||
return { status: 'INVALID_TOTP_CODE' };
|
||||
return { status: 'INVALID_TOTP_CODE' as const };
|
||||
case 'USER_DISABLED':
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
@@ -47,5 +47,5 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
SERVER_DEBUG(`New Session: ${data.id} for ${user.id} (${user.username})`);
|
||||
|
||||
return { status: 'success' };
|
||||
return { status: 'success' as const };
|
||||
});
|
||||
|
||||
@@ -58,6 +58,17 @@ export class UserService {
|
||||
this.#statements = createPreparedStatement(db);
|
||||
}
|
||||
|
||||
#createTotp(user: { username: string; totpKey: string }) {
|
||||
return new TOTP({
|
||||
issuer: 'wg-easy',
|
||||
label: user.username,
|
||||
algorithm: 'SHA1',
|
||||
digits: 6,
|
||||
period: 30,
|
||||
secret: user.totpKey,
|
||||
});
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
return this.#statements.findAll.execute();
|
||||
}
|
||||
@@ -156,22 +167,13 @@ export class UserService {
|
||||
if (!code) {
|
||||
return { success: false, error: 'TOTP_REQUIRED' };
|
||||
} else {
|
||||
if (!txUser.totpKey) {
|
||||
const totpKey = txUser.totpKey;
|
||||
if (!totpKey) {
|
||||
return { success: false, error: 'UNEXPECTED_ERROR' };
|
||||
}
|
||||
|
||||
const totp = new TOTP({
|
||||
issuer: 'wg-easy',
|
||||
label: txUser.username,
|
||||
algorithm: 'SHA1',
|
||||
digits: 6,
|
||||
period: 30,
|
||||
secret: txUser.totpKey,
|
||||
});
|
||||
|
||||
const valid = totp.validate({ token: code, window: 1 });
|
||||
|
||||
if (valid === null) {
|
||||
const totp = this.#createTotp({ username: txUser.username, totpKey });
|
||||
if (totp.validate({ token: code, window: 1 }) === null) {
|
||||
return { success: false, error: 'INVALID_TOTP_CODE' };
|
||||
}
|
||||
}
|
||||
@@ -195,22 +197,13 @@ export class UserService {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
if (!txUser.totpKey) {
|
||||
const totpKey = txUser.totpKey;
|
||||
if (!totpKey) {
|
||||
throw new Error('TOTP key is not set');
|
||||
}
|
||||
|
||||
const totp = new TOTP({
|
||||
issuer: 'wg-easy',
|
||||
label: txUser.username,
|
||||
algorithm: 'SHA1',
|
||||
digits: 6,
|
||||
period: 30,
|
||||
secret: txUser.totpKey,
|
||||
});
|
||||
|
||||
const valid = totp.validate({ token: code, window: 1 });
|
||||
|
||||
if (valid === null) {
|
||||
const totp = this.#createTotp({ username: txUser.username, totpKey });
|
||||
if (totp.validate({ token: code, window: 1 }) === null) {
|
||||
throw new Error('Invalid TOTP code');
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,12 @@ export default defineMetricsHandler('prometheus', async ({ event }) => {
|
||||
async function getPrometheusResponse() {
|
||||
const wgInterface = await Database.interfaces.get();
|
||||
const clients = await WireGuard.getAllClients();
|
||||
let wireguardPeerCount = 0;
|
||||
let wireguardEnabledPeersCount = 0;
|
||||
let wireguardConnectedPeersCount = 0;
|
||||
const wireguardSentBytes = [];
|
||||
const wireguardReceivedBytes = [];
|
||||
const wireguardLatestHandshakeSeconds = [];
|
||||
for (const client of clients) {
|
||||
wireguardPeerCount++;
|
||||
if (client.enabled === true) {
|
||||
wireguardEnabledPeersCount++;
|
||||
}
|
||||
@@ -41,7 +39,7 @@ async function getPrometheusResponse() {
|
||||
const returnText = [
|
||||
'# HELP wireguard_configured_peers',
|
||||
'# TYPE wireguard_configured_peers gauge',
|
||||
`wireguard_configured_peers{${id}} ${wireguardPeerCount}`,
|
||||
`wireguard_configured_peers{${id}} ${clients.length}`,
|
||||
'',
|
||||
'# HELP wireguard_enabled_peers',
|
||||
'# TYPE wireguard_enabled_peers gauge',
|
||||
|
||||
@@ -6,7 +6,7 @@ type Opts = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache function for 1 hour
|
||||
* Cache the result of a function for the given expiry time in milliseconds
|
||||
*/
|
||||
export function cacheFunction<T>(fn: () => T, { expiry }: Opts): () => T {
|
||||
let cache: { value: T; expiry: number } | null = null;
|
||||
|
||||
@@ -38,6 +38,7 @@ export const WG_ENV = {
|
||||
/** If IPv6 should be disabled */
|
||||
DISABLE_IPV6: process.env.DISABLE_IPV6 === 'true',
|
||||
WG_EXECUTABLE: await detectAwg(),
|
||||
DISABLE_VERSION_CHECK: process.env.DISABLE_VERSION_CHECK === 'true',
|
||||
};
|
||||
|
||||
export const WG_INITIAL_ENV = {
|
||||
|
||||
@@ -4,6 +4,13 @@ type GithubRelease = {
|
||||
};
|
||||
|
||||
async function fetchLatestRelease() {
|
||||
if (WG_ENV.DISABLE_VERSION_CHECK) {
|
||||
return {
|
||||
version: RELEASE,
|
||||
changelog: '',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await $fetch<GithubRelease>(
|
||||
'https://api.github.com/repos/wg-easy/wg-easy/releases/latest',
|
||||
|
||||
Reference in New Issue
Block a user