Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65f0425b61 | ||
|
|
4e70a2981d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,6 +6,8 @@
|
||||
|
||||
# ── Compiled binaries ──────────────────────────────────────────────────────────
|
||||
backend/bin/
|
||||
backend/backend
|
||||
backend/runner
|
||||
|
||||
# ── Environment & secrets ──────────────────────────────────────────────────────
|
||||
# Secrets are managed by Doppler — never commit .env files.
|
||||
|
||||
BIN
backend/backend
BIN
backend/backend
Binary file not shown.
@@ -134,7 +134,7 @@ func run() error {
|
||||
if parseErr != nil {
|
||||
return fmt.Errorf("parse REDIS_ADDR: %w", parseErr)
|
||||
}
|
||||
asynqProducer := asynqqueue.NewProducer(store, redisOpt)
|
||||
asynqProducer := asynqqueue.NewProducer(store, redisOpt, log)
|
||||
defer asynqProducer.Close() //nolint:errcheck
|
||||
producer = asynqProducer
|
||||
log.Info("backend: asynq task dispatch enabled", "addr", cfg.Redis.Addr)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/libnovel/backend/internal/taskqueue"
|
||||
@@ -14,13 +15,15 @@ import (
|
||||
type Producer struct {
|
||||
pb taskqueue.Producer // underlying PocketBase producer
|
||||
client *asynq.Client
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
// NewProducer wraps an existing PocketBase Producer with Asynq dispatch.
|
||||
func NewProducer(pb taskqueue.Producer, redisOpt asynq.RedisConnOpt) *Producer {
|
||||
func NewProducer(pb taskqueue.Producer, redisOpt asynq.RedisConnOpt, log *slog.Logger) *Producer {
|
||||
return &Producer{
|
||||
pb: pb,
|
||||
client: asynq.NewClient(redisOpt),
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +52,9 @@ func (p *Producer) CreateScrapeTask(ctx context.Context, kind, targetURL string,
|
||||
}
|
||||
if err := p.enqueue(ctx, taskType, payload); err != nil {
|
||||
// Non-fatal: PB record exists; runner will pick it up on next poll.
|
||||
return id, fmt.Errorf("asynq enqueue scrape (task still in PB): %w", err)
|
||||
p.log.Warn("asynq enqueue scrape failed (task still in PB, runner will poll)",
|
||||
"task_id", id, "err", err)
|
||||
return id, nil
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
@@ -68,7 +73,10 @@ func (p *Producer) CreateAudioTask(ctx context.Context, slug string, chapter int
|
||||
Voice: voice,
|
||||
}
|
||||
if err := p.enqueue(ctx, TypeAudioGenerate, payload); err != nil {
|
||||
return id, fmt.Errorf("asynq enqueue audio (task still in PB): %w", err)
|
||||
// Non-fatal: PB record exists; runner will pick it up on next poll.
|
||||
p.log.Warn("asynq enqueue audio failed (task still in PB, runner will poll)",
|
||||
"task_id", id, "err", err)
|
||||
return id, nil
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
BIN
backend/runner
BIN
backend/runner
Binary file not shown.
@@ -289,6 +289,48 @@ services:
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# ── Redis (Asynq task queue) ────────────────────────────────────────────────
|
||||
# Dedicated Redis instance for Asynq job dispatch.
|
||||
# The prod backend enqueues jobs via redis.libnovel.cc:6380 (Caddy TLS proxy →
|
||||
# host:6379). The runner reads from this instance directly on the Docker network.
|
||||
# Port is bound to 0.0.0.0:6379 so the Caddy layer4 proxy on prod can reach it.
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD}"]
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# ── LibreTranslate ──────────────────────────────────────────────────────────
|
||||
# Self-hosted machine translation. Runner connects via http://libretranslate:5000.
|
||||
# Only English → configured target languages are loaded to save RAM.
|
||||
libretranslate:
|
||||
image: libretranslate/libretranslate:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
LT_API_KEYS: "true"
|
||||
LT_API_KEYS_DB_PATH: "/app/db/api_keys.db"
|
||||
LT_LOAD_ONLY: "en,ru,id,pt,fr"
|
||||
LT_DISABLE_WEB_UI: "true"
|
||||
LT_UPDATE_MODELS: "false"
|
||||
expose:
|
||||
- "5000"
|
||||
volumes:
|
||||
- libretranslate_data:/app/db
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "http://localhost:5000/languages"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
||||
# ── Valkey ──────────────────────────────────────────────────────────────────
|
||||
# Used by GlitchTip for task queuing.
|
||||
valkey:
|
||||
@@ -469,6 +511,8 @@ services:
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
libretranslate_data:
|
||||
valkey_data:
|
||||
uptime_kuma_data:
|
||||
gotify_data:
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_AnalyticsInputs */
|
||||
|
||||
const en_admin_nav_analytics = () => /** @type {LocalizedString} */ (`Analytics`);
|
||||
const ru_admin_nav_analytics = () => /** @type {LocalizedString} */ (`Аналитика`);
|
||||
const id_admin_nav_analytics = () => /** @type {LocalizedString} */ (`Analitik`);
|
||||
const pt_admin_nav_analytics = () => /** @type {LocalizedString} */ (`Análise`);
|
||||
const fr_admin_nav_analytics = () => /** @type {LocalizedString} */ (`Analytique`);
|
||||
const en_admin_nav_analytics = (_inputs = {}) => /** @type {LocalizedString} */ (`Analytics`);
|
||||
const ru_admin_nav_analytics = (_inputs = {}) => /** @type {LocalizedString} */ (`Аналитика`);
|
||||
const id_admin_nav_analytics = (_inputs = {}) => /** @type {LocalizedString} */ (`Analitik`);
|
||||
const pt_admin_nav_analytics = (_inputs = {}) => /** @type {LocalizedString} */ (`Análise`);
|
||||
const fr_admin_nav_analytics = (_inputs = {}) => /** @type {LocalizedString} */ (`Analytique`);
|
||||
|
||||
export const admin_nav_analytics = /** @type {((inputs?: Admin_Nav_AnalyticsInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_AnalyticsInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_AudioInputs */
|
||||
|
||||
const en_admin_nav_audio = () => /** @type {LocalizedString} */ (`Audio`);
|
||||
const ru_admin_nav_audio = () => /** @type {LocalizedString} */ (`Аудио`);
|
||||
const id_admin_nav_audio = () => /** @type {LocalizedString} */ (`Audio`);
|
||||
const pt_admin_nav_audio = () => /** @type {LocalizedString} */ (`Áudio`);
|
||||
const fr_admin_nav_audio = () => /** @type {LocalizedString} */ (`Audio`);
|
||||
const en_admin_nav_audio = (_inputs = {}) => /** @type {LocalizedString} */ (`Audio`);
|
||||
const ru_admin_nav_audio = (_inputs = {}) => /** @type {LocalizedString} */ (`Аудио`);
|
||||
const id_admin_nav_audio = (_inputs = {}) => /** @type {LocalizedString} */ (`Audio`);
|
||||
const pt_admin_nav_audio = (_inputs = {}) => /** @type {LocalizedString} */ (`Áudio`);
|
||||
const fr_admin_nav_audio = (_inputs = {}) => /** @type {LocalizedString} */ (`Audio`);
|
||||
|
||||
export const admin_nav_audio = /** @type {((inputs?: Admin_Nav_AudioInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_AudioInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_ChangelogInputs */
|
||||
|
||||
const en_admin_nav_changelog = () => /** @type {LocalizedString} */ (`Changelog`);
|
||||
const ru_admin_nav_changelog = () => /** @type {LocalizedString} */ (`Изменения`);
|
||||
const id_admin_nav_changelog = () => /** @type {LocalizedString} */ (`Perubahan`);
|
||||
const pt_admin_nav_changelog = () => /** @type {LocalizedString} */ (`Alterações`);
|
||||
const fr_admin_nav_changelog = () => /** @type {LocalizedString} */ (`Modifications`);
|
||||
const en_admin_nav_changelog = (_inputs = {}) => /** @type {LocalizedString} */ (`Changelog`);
|
||||
const ru_admin_nav_changelog = (_inputs = {}) => /** @type {LocalizedString} */ (`Изменения`);
|
||||
const id_admin_nav_changelog = (_inputs = {}) => /** @type {LocalizedString} */ (`Perubahan`);
|
||||
const pt_admin_nav_changelog = (_inputs = {}) => /** @type {LocalizedString} */ (`Alterações`);
|
||||
const fr_admin_nav_changelog = (_inputs = {}) => /** @type {LocalizedString} */ (`Modifications`);
|
||||
|
||||
export const admin_nav_changelog = /** @type {((inputs?: Admin_Nav_ChangelogInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_ChangelogInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_ErrorsInputs */
|
||||
|
||||
const en_admin_nav_errors = () => /** @type {LocalizedString} */ (`Errors`);
|
||||
const ru_admin_nav_errors = () => /** @type {LocalizedString} */ (`Ошибки`);
|
||||
const id_admin_nav_errors = () => /** @type {LocalizedString} */ (`Kesalahan`);
|
||||
const pt_admin_nav_errors = () => /** @type {LocalizedString} */ (`Erros`);
|
||||
const fr_admin_nav_errors = () => /** @type {LocalizedString} */ (`Erreurs`);
|
||||
const en_admin_nav_errors = (_inputs = {}) => /** @type {LocalizedString} */ (`Errors`);
|
||||
const ru_admin_nav_errors = (_inputs = {}) => /** @type {LocalizedString} */ (`Ошибки`);
|
||||
const id_admin_nav_errors = (_inputs = {}) => /** @type {LocalizedString} */ (`Kesalahan`);
|
||||
const pt_admin_nav_errors = (_inputs = {}) => /** @type {LocalizedString} */ (`Erros`);
|
||||
const fr_admin_nav_errors = (_inputs = {}) => /** @type {LocalizedString} */ (`Erreurs`);
|
||||
|
||||
export const admin_nav_errors = /** @type {((inputs?: Admin_Nav_ErrorsInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_ErrorsInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_FeedbackInputs */
|
||||
|
||||
const en_admin_nav_feedback = () => /** @type {LocalizedString} */ (`Feedback`);
|
||||
const ru_admin_nav_feedback = () => /** @type {LocalizedString} */ (`Отзывы`);
|
||||
const id_admin_nav_feedback = () => /** @type {LocalizedString} */ (`Masukan`);
|
||||
const pt_admin_nav_feedback = () => /** @type {LocalizedString} */ (`Feedback`);
|
||||
const fr_admin_nav_feedback = () => /** @type {LocalizedString} */ (`Retours`);
|
||||
const en_admin_nav_feedback = (_inputs = {}) => /** @type {LocalizedString} */ (`Feedback`);
|
||||
const ru_admin_nav_feedback = (_inputs = {}) => /** @type {LocalizedString} */ (`Отзывы`);
|
||||
const id_admin_nav_feedback = (_inputs = {}) => /** @type {LocalizedString} */ (`Masukan`);
|
||||
const pt_admin_nav_feedback = (_inputs = {}) => /** @type {LocalizedString} */ (`Feedback`);
|
||||
const fr_admin_nav_feedback = (_inputs = {}) => /** @type {LocalizedString} */ (`Retours`);
|
||||
|
||||
export const admin_nav_feedback = /** @type {((inputs?: Admin_Nav_FeedbackInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_FeedbackInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_LogsInputs */
|
||||
|
||||
const en_admin_nav_logs = () => /** @type {LocalizedString} */ (`Logs`);
|
||||
const ru_admin_nav_logs = () => /** @type {LocalizedString} */ (`Логи`);
|
||||
const id_admin_nav_logs = () => /** @type {LocalizedString} */ (`Log`);
|
||||
const pt_admin_nav_logs = () => /** @type {LocalizedString} */ (`Logs`);
|
||||
const fr_admin_nav_logs = () => /** @type {LocalizedString} */ (`Journaux`);
|
||||
const en_admin_nav_logs = (_inputs = {}) => /** @type {LocalizedString} */ (`Logs`);
|
||||
const ru_admin_nav_logs = (_inputs = {}) => /** @type {LocalizedString} */ (`Логи`);
|
||||
const id_admin_nav_logs = (_inputs = {}) => /** @type {LocalizedString} */ (`Log`);
|
||||
const pt_admin_nav_logs = (_inputs = {}) => /** @type {LocalizedString} */ (`Logs`);
|
||||
const fr_admin_nav_logs = (_inputs = {}) => /** @type {LocalizedString} */ (`Journaux`);
|
||||
|
||||
export const admin_nav_logs = /** @type {((inputs?: Admin_Nav_LogsInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_LogsInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_PushInputs */
|
||||
|
||||
const en_admin_nav_push = () => /** @type {LocalizedString} */ (`Push`);
|
||||
const ru_admin_nav_push = () => /** @type {LocalizedString} */ (`Уведомления`);
|
||||
const id_admin_nav_push = () => /** @type {LocalizedString} */ (`Notifikasi`);
|
||||
const pt_admin_nav_push = () => /** @type {LocalizedString} */ (`Notificações`);
|
||||
const fr_admin_nav_push = () => /** @type {LocalizedString} */ (`Notifications`);
|
||||
const en_admin_nav_push = (_inputs = {}) => /** @type {LocalizedString} */ (`Push`);
|
||||
const ru_admin_nav_push = (_inputs = {}) => /** @type {LocalizedString} */ (`Уведомления`);
|
||||
const id_admin_nav_push = (_inputs = {}) => /** @type {LocalizedString} */ (`Notifikasi`);
|
||||
const pt_admin_nav_push = (_inputs = {}) => /** @type {LocalizedString} */ (`Notificações`);
|
||||
const fr_admin_nav_push = (_inputs = {}) => /** @type {LocalizedString} */ (`Notifications`);
|
||||
|
||||
export const admin_nav_push = /** @type {((inputs?: Admin_Nav_PushInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_PushInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_ScrapeInputs */
|
||||
|
||||
const en_admin_nav_scrape = () => /** @type {LocalizedString} */ (`Scrape`);
|
||||
const ru_admin_nav_scrape = () => /** @type {LocalizedString} */ (`Скрейпинг`);
|
||||
const id_admin_nav_scrape = () => /** @type {LocalizedString} */ (`Scrape`);
|
||||
const pt_admin_nav_scrape = () => /** @type {LocalizedString} */ (`Scrape`);
|
||||
const fr_admin_nav_scrape = () => /** @type {LocalizedString} */ (`Scrape`);
|
||||
const en_admin_nav_scrape = (_inputs = {}) => /** @type {LocalizedString} */ (`Scrape`);
|
||||
const ru_admin_nav_scrape = (_inputs = {}) => /** @type {LocalizedString} */ (`Скрейпинг`);
|
||||
const id_admin_nav_scrape = (_inputs = {}) => /** @type {LocalizedString} */ (`Scrape`);
|
||||
const pt_admin_nav_scrape = (_inputs = {}) => /** @type {LocalizedString} */ (`Scrape`);
|
||||
const fr_admin_nav_scrape = (_inputs = {}) => /** @type {LocalizedString} */ (`Scrape`);
|
||||
|
||||
export const admin_nav_scrape = /** @type {((inputs?: Admin_Nav_ScrapeInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_ScrapeInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_TranslationInputs */
|
||||
|
||||
const en_admin_nav_translation = () => /** @type {LocalizedString} */ (`Translation`);
|
||||
const ru_admin_nav_translation = () => /** @type {LocalizedString} */ (`Перевод`);
|
||||
const id_admin_nav_translation = () => /** @type {LocalizedString} */ (`Terjemahan`);
|
||||
const pt_admin_nav_translation = () => /** @type {LocalizedString} */ (`Tradução`);
|
||||
const fr_admin_nav_translation = () => /** @type {LocalizedString} */ (`Traduction`);
|
||||
const en_admin_nav_translation = (_inputs = {}) => /** @type {LocalizedString} */ (`Translation`);
|
||||
const ru_admin_nav_translation = (_inputs = {}) => /** @type {LocalizedString} */ (`Перевод`);
|
||||
const id_admin_nav_translation = (_inputs = {}) => /** @type {LocalizedString} */ (`Terjemahan`);
|
||||
const pt_admin_nav_translation = (_inputs = {}) => /** @type {LocalizedString} */ (`Tradução`);
|
||||
const fr_admin_nav_translation = (_inputs = {}) => /** @type {LocalizedString} */ (`Traduction`);
|
||||
|
||||
export const admin_nav_translation = /** @type {((inputs?: Admin_Nav_TranslationInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_TranslationInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getLocale, experimentalStaticLocale } from '../runtime.js';
|
||||
|
||||
/** @typedef {{}} Admin_Nav_UptimeInputs */
|
||||
|
||||
const en_admin_nav_uptime = () => /** @type {LocalizedString} */ (`Uptime`);
|
||||
const ru_admin_nav_uptime = () => /** @type {LocalizedString} */ (`Мониторинг`);
|
||||
const id_admin_nav_uptime = () => /** @type {LocalizedString} */ (`Uptime`);
|
||||
const pt_admin_nav_uptime = () => /** @type {LocalizedString} */ (`Uptime`);
|
||||
const fr_admin_nav_uptime = () => /** @type {LocalizedString} */ (`Disponibilité`);
|
||||
const en_admin_nav_uptime = (_inputs = {}) => /** @type {LocalizedString} */ (`Uptime`);
|
||||
const ru_admin_nav_uptime = (_inputs = {}) => /** @type {LocalizedString} */ (`Мониторинг`);
|
||||
const id_admin_nav_uptime = (_inputs = {}) => /** @type {LocalizedString} */ (`Uptime`);
|
||||
const pt_admin_nav_uptime = (_inputs = {}) => /** @type {LocalizedString} */ (`Uptime`);
|
||||
const fr_admin_nav_uptime = (_inputs = {}) => /** @type {LocalizedString} */ (`Disponibilité`);
|
||||
|
||||
export const admin_nav_uptime = /** @type {((inputs?: Admin_Nav_UptimeInputs, options?: { locale?: "en" | "ru" | "id" | "pt" | "fr" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Admin_Nav_UptimeInputs, { locale?: "en" | "ru" | "id" | "pt" | "fr" }, {}>} */ ((inputs = {}, options = {}) => {
|
||||
const locale = experimentalStaticLocale ?? options.locale ?? getLocale()
|
||||
|
||||
Reference in New Issue
Block a user