fix: add admin
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
3
.env
3
.env
@@ -1 +1,4 @@
|
|||||||
NUXT_VATSTACK_API_KEY=pk_live_3038ce089ee7197879fcf95e624da019
|
NUXT_VATSTACK_API_KEY=pk_live_3038ce089ee7197879fcf95e624da019
|
||||||
|
NUXT_DB=mongodb+srv://development:35XRgDMsPkb1opbp@cluster0.ajj3s.mongodb.net/vat-api
|
||||||
|
NUXT_REDIS_URL=redis://:eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81@localhost:6379
|
||||||
|
NUXT_ADMIN_PASSWORD=changeme
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
# Vatstack API key for live VAT rate data (optional — falls back to hardcoded rates)
|
# Vatstack API key for live VAT rate data (optional — falls back to hardcoded rates)
|
||||||
NUXT_VATSTACK_API_KEY=
|
NUXT_VATSTACK_API_KEY=
|
||||||
|
NUXT_DB=mongodb+srv://development:35XRgDMsPkb1opbp@cluster0.ajj3s.mongodb.net/vat-api
|
||||||
|
NUXT_REDIS_URL=redis://:eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81@localhost:6379
|
||||||
|
NUXT_ADMIN_PASSWORD=changeme
|
||||||
|
|||||||
1
.nuxt/imports.d.ts
vendored
1
.nuxt/imports.d.ts
vendored
@@ -32,3 +32,4 @@ export { definePageMeta } from '../node_modules/nuxt/dist/pages/runtime/composab
|
|||||||
export { defineLazyHydrationComponent } from '#app/composables/lazy-hydration';
|
export { defineLazyHydrationComponent } from '#app/composables/lazy-hydration';
|
||||||
export { useScrollAnimation } from '../app/composables/useScrollAnimation';
|
export { useScrollAnimation } from '../app/composables/useScrollAnimation';
|
||||||
export { useVatRates, VatRate } from '../app/composables/useVatRates';
|
export { useVatRates, VatRate } from '../app/composables/useVatRates';
|
||||||
|
export { useNuxtDevTools } from '../node_modules/@nuxt/devtools/dist/runtime/use-nuxt-devtools';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// generated by the @nuxtjs/tailwindcss <https://github.com/nuxt-modules/tailwindcss> module at 2/13/2026, 10:13:46 PM
|
// generated by the @nuxtjs/tailwindcss <https://github.com/nuxt-modules/tailwindcss> module at 2/14/2026, 10:13:39 AM
|
||||||
import "@nuxtjs/tailwindcss/config-ctx"
|
import "@nuxtjs/tailwindcss/config-ctx"
|
||||||
import configMerger from "@nuxtjs/tailwindcss/merger";
|
import configMerger from "@nuxtjs/tailwindcss/merger";
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
"./imports"
|
"./imports"
|
||||||
],
|
],
|
||||||
"#app-manifest": [
|
"#app-manifest": [
|
||||||
"./manifest/meta/28eed3f6-8bb7-4ce5-bd5e-c2f1ff73389b"
|
"./manifest/meta/dev"
|
||||||
],
|
],
|
||||||
"#components": [
|
"#components": [
|
||||||
"./components"
|
"./components"
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
"../node_modules/dist/runtime/server",
|
"../node_modules/dist/runtime/server",
|
||||||
"../node_modules/*.*",
|
"../node_modules/*.*",
|
||||||
"../node_modules/dist/*.*",
|
"../node_modules/dist/*.*",
|
||||||
"../.output",
|
"dev",
|
||||||
"../server"
|
"../server"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
"./imports"
|
"./imports"
|
||||||
],
|
],
|
||||||
"#app-manifest": [
|
"#app-manifest": [
|
||||||
"./manifest/meta/28eed3f6-8bb7-4ce5-bd5e-c2f1ff73389b"
|
"./manifest/meta/dev"
|
||||||
],
|
],
|
||||||
"#components": [
|
"#components": [
|
||||||
"./components"
|
"./components"
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
"./imports"
|
"./imports"
|
||||||
],
|
],
|
||||||
"#app-manifest": [
|
"#app-manifest": [
|
||||||
"./manifest/meta/28eed3f6-8bb7-4ce5-bd5e-c2f1ff73389b"
|
"./manifest/meta/dev"
|
||||||
],
|
],
|
||||||
"#build": [
|
"#build": [
|
||||||
"."
|
"."
|
||||||
|
|||||||
2
.nuxt/types/build.d.ts
vendored
2
.nuxt/types/build.d.ts
vendored
@@ -12,6 +12,8 @@ declare module "#build/paths.mjs";
|
|||||||
declare module "#build/root-component.mjs";
|
declare module "#build/root-component.mjs";
|
||||||
declare module "#build/plugins.server.mjs";
|
declare module "#build/plugins.server.mjs";
|
||||||
declare module "#build/test-component-wrapper.mjs";
|
declare module "#build/test-component-wrapper.mjs";
|
||||||
|
declare module "#build/devtools/settings.mjs";
|
||||||
|
declare module "#build/runtime.vue-devtools-client.nCg_nTMck4Vjq9J_TXBbUMCbducTRhoEa7hkWr4xMsk.js";
|
||||||
declare module "#build/routes.mjs";
|
declare module "#build/routes.mjs";
|
||||||
declare module "#build/pages.mjs";
|
declare module "#build/pages.mjs";
|
||||||
declare module "#build/router.options.mjs";
|
declare module "#build/router.options.mjs";
|
||||||
|
|||||||
2
.nuxt/types/imports.d.ts
vendored
2
.nuxt/types/imports.d.ts
vendored
@@ -113,6 +113,7 @@ declare global {
|
|||||||
const useModel: typeof import('vue').useModel
|
const useModel: typeof import('vue').useModel
|
||||||
const useNuxtApp: typeof import('../../node_modules/nuxt/dist/app/nuxt').useNuxtApp
|
const useNuxtApp: typeof import('../../node_modules/nuxt/dist/app/nuxt').useNuxtApp
|
||||||
const useNuxtData: typeof import('../../node_modules/nuxt/dist/app/composables/asyncData').useNuxtData
|
const useNuxtData: typeof import('../../node_modules/nuxt/dist/app/composables/asyncData').useNuxtData
|
||||||
|
const useNuxtDevTools: typeof import('../../node_modules/@nuxt/devtools/dist/runtime/use-nuxt-devtools').useNuxtDevTools
|
||||||
const usePreviewMode: typeof import('../../node_modules/nuxt/dist/app/composables/preview').usePreviewMode
|
const usePreviewMode: typeof import('../../node_modules/nuxt/dist/app/composables/preview').usePreviewMode
|
||||||
const useRequestEvent: typeof import('../../node_modules/nuxt/dist/app/composables/ssr').useRequestEvent
|
const useRequestEvent: typeof import('../../node_modules/nuxt/dist/app/composables/ssr').useRequestEvent
|
||||||
const useRequestFetch: typeof import('../../node_modules/nuxt/dist/app/composables/ssr').useRequestFetch
|
const useRequestFetch: typeof import('../../node_modules/nuxt/dist/app/composables/ssr').useRequestFetch
|
||||||
@@ -302,6 +303,7 @@ declare module 'vue' {
|
|||||||
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
||||||
readonly useNuxtApp: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['useNuxtApp']>
|
readonly useNuxtApp: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/nuxt')['useNuxtApp']>
|
||||||
readonly useNuxtData: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useNuxtData']>
|
readonly useNuxtData: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/asyncData')['useNuxtData']>
|
||||||
|
readonly useNuxtDevTools: UnwrapRef<typeof import('../../node_modules/@nuxt/devtools/dist/runtime/use-nuxt-devtools')['useNuxtDevTools']>
|
||||||
readonly usePreviewMode: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/preview')['usePreviewMode']>
|
readonly usePreviewMode: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/preview')['usePreviewMode']>
|
||||||
readonly useRequestEvent: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestEvent']>
|
readonly useRequestEvent: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestEvent']>
|
||||||
readonly useRequestFetch: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestFetch']>
|
readonly useRequestFetch: UnwrapRef<typeof import('../../node_modules/nuxt/dist/app/composables/ssr')['useRequestFetch']>
|
||||||
|
|||||||
12
.nuxt/types/nitro-imports.d.ts
vendored
12
.nuxt/types/nitro-imports.d.ts
vendored
@@ -15,11 +15,14 @@ declare global {
|
|||||||
const callNodeListener: typeof import('../../node_modules/h3').callNodeListener
|
const callNodeListener: typeof import('../../node_modules/h3').callNodeListener
|
||||||
const clearResponseHeaders: typeof import('../../node_modules/h3').clearResponseHeaders
|
const clearResponseHeaders: typeof import('../../node_modules/h3').clearResponseHeaders
|
||||||
const clearSession: typeof import('../../node_modules/h3').clearSession
|
const clearSession: typeof import('../../node_modules/h3').clearSession
|
||||||
|
const closeDb: typeof import('../../server/utils/mongodb').closeDb
|
||||||
|
const closeRedis: typeof import('../../server/utils/redis').closeRedis
|
||||||
const createApp: typeof import('../../node_modules/h3').createApp
|
const createApp: typeof import('../../node_modules/h3').createApp
|
||||||
const createAppEventHandler: typeof import('../../node_modules/h3').createAppEventHandler
|
const createAppEventHandler: typeof import('../../node_modules/h3').createAppEventHandler
|
||||||
const createError: typeof import('../../node_modules/h3').createError
|
const createError: typeof import('../../node_modules/h3').createError
|
||||||
const createEvent: typeof import('../../node_modules/h3').createEvent
|
const createEvent: typeof import('../../node_modules/h3').createEvent
|
||||||
const createEventStream: typeof import('../../node_modules/h3').createEventStream
|
const createEventStream: typeof import('../../node_modules/h3').createEventStream
|
||||||
|
const createRedisClient: typeof import('../../server/utils/redis').createRedisClient
|
||||||
const createRouter: typeof import('../../node_modules/h3').createRouter
|
const createRouter: typeof import('../../node_modules/h3').createRouter
|
||||||
const defaultContentType: typeof import('../../node_modules/h3').defaultContentType
|
const defaultContentType: typeof import('../../node_modules/h3').defaultContentType
|
||||||
const defineAppConfig: typeof import('../../node_modules/@nuxt/nitro-server/dist/runtime/utils/config').defineAppConfig
|
const defineAppConfig: typeof import('../../node_modules/@nuxt/nitro-server/dist/runtime/utils/config').defineAppConfig
|
||||||
@@ -47,12 +50,14 @@ declare global {
|
|||||||
const fromWebHandler: typeof import('../../node_modules/h3').fromWebHandler
|
const fromWebHandler: typeof import('../../node_modules/h3').fromWebHandler
|
||||||
const getAllRates: typeof import('../../server/utils/vatRates').getAllRates
|
const getAllRates: typeof import('../../server/utils/vatRates').getAllRates
|
||||||
const getCookie: typeof import('../../node_modules/h3').getCookie
|
const getCookie: typeof import('../../node_modules/h3').getCookie
|
||||||
|
const getDb: typeof import('../../server/utils/mongodb').getDb
|
||||||
const getHeader: typeof import('../../node_modules/h3').getHeader
|
const getHeader: typeof import('../../node_modules/h3').getHeader
|
||||||
const getHeaders: typeof import('../../node_modules/h3').getHeaders
|
const getHeaders: typeof import('../../node_modules/h3').getHeaders
|
||||||
const getMethod: typeof import('../../node_modules/h3').getMethod
|
const getMethod: typeof import('../../node_modules/h3').getMethod
|
||||||
const getProxyRequestHeaders: typeof import('../../node_modules/h3').getProxyRequestHeaders
|
const getProxyRequestHeaders: typeof import('../../node_modules/h3').getProxyRequestHeaders
|
||||||
const getQuery: typeof import('../../node_modules/h3').getQuery
|
const getQuery: typeof import('../../node_modules/h3').getQuery
|
||||||
const getRateByCode: typeof import('../../server/utils/vatRates').getRateByCode
|
const getRateByCode: typeof import('../../server/utils/vatRates').getRateByCode
|
||||||
|
const getRedis: typeof import('../../server/utils/redis').getRedis
|
||||||
const getRequestFingerprint: typeof import('../../node_modules/h3').getRequestFingerprint
|
const getRequestFingerprint: typeof import('../../node_modules/h3').getRequestFingerprint
|
||||||
const getRequestHeader: typeof import('../../node_modules/h3').getRequestHeader
|
const getRequestHeader: typeof import('../../node_modules/h3').getRequestHeader
|
||||||
const getRequestHeaders: typeof import('../../node_modules/h3').getRequestHeaders
|
const getRequestHeaders: typeof import('../../node_modules/h3').getRequestHeaders
|
||||||
@@ -83,6 +88,7 @@ declare global {
|
|||||||
const isStream: typeof import('../../node_modules/h3').isStream
|
const isStream: typeof import('../../node_modules/h3').isStream
|
||||||
const isWebResponse: typeof import('../../node_modules/h3').isWebResponse
|
const isWebResponse: typeof import('../../node_modules/h3').isWebResponse
|
||||||
const lazyEventHandler: typeof import('../../node_modules/h3').lazyEventHandler
|
const lazyEventHandler: typeof import('../../node_modules/h3').lazyEventHandler
|
||||||
|
const logRequest: typeof import('../../server/utils/requestLogger').logRequest
|
||||||
const nitroPlugin: typeof import('../../node_modules/nitropack/dist/runtime/internal/plugin').nitroPlugin
|
const nitroPlugin: typeof import('../../node_modules/nitropack/dist/runtime/internal/plugin').nitroPlugin
|
||||||
const parseCookies: typeof import('../../node_modules/h3').parseCookies
|
const parseCookies: typeof import('../../node_modules/h3').parseCookies
|
||||||
const promisifyNodeListener: typeof import('../../node_modules/h3').promisifyNodeListener
|
const promisifyNodeListener: typeof import('../../node_modules/h3').promisifyNodeListener
|
||||||
@@ -135,6 +141,9 @@ declare global {
|
|||||||
export type { EventHandler, EventHandlerRequest, EventHandlerResponse, EventHandlerObject, H3EventContext } from '../../node_modules/h3'
|
export type { EventHandler, EventHandlerRequest, EventHandlerResponse, EventHandlerObject, H3EventContext } from '../../node_modules/h3'
|
||||||
import('../../node_modules/h3')
|
import('../../node_modules/h3')
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
export type { RequestLogEntry } from '../../server/utils/requestLogger'
|
||||||
|
import('../../server/utils/requestLogger')
|
||||||
|
// @ts-ignore
|
||||||
export type { VatRateResponse } from '../../server/utils/vatRates'
|
export type { VatRateResponse } from '../../server/utils/vatRates'
|
||||||
import('../../server/utils/vatRates')
|
import('../../server/utils/vatRates')
|
||||||
}
|
}
|
||||||
@@ -152,4 +161,7 @@ export { defineTask, runTask } from 'nitropack/runtime/internal/task';
|
|||||||
export { defineNitroErrorHandler } from 'nitropack/runtime/internal/error/utils';
|
export { defineNitroErrorHandler } from 'nitropack/runtime/internal/error/utils';
|
||||||
export { buildAssetsURL as __buildAssetsURL, publicAssetsURL as __publicAssetsURL } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths';
|
export { buildAssetsURL as __buildAssetsURL, publicAssetsURL as __publicAssetsURL } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths';
|
||||||
export { defineAppConfig } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/config';
|
export { defineAppConfig } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/config';
|
||||||
|
export { getDb, closeDb } from '/home/bennet/source/vat-api/server/utils/mongodb';
|
||||||
|
export { getRedis, createRedisClient, closeRedis } from '/home/bennet/source/vat-api/server/utils/redis';
|
||||||
|
export { logRequest } from '/home/bennet/source/vat-api/server/utils/requestLogger';
|
||||||
export { getAllRates, getRateByCode } from '/home/bennet/source/vat-api/server/utils/vatRates';
|
export { getAllRates, getRateByCode } from '/home/bennet/source/vat-api/server/utils/vatRates';
|
||||||
9
.nuxt/types/nitro-routes.d.ts
vendored
9
.nuxt/types/nitro-routes.d.ts
vendored
@@ -3,6 +3,15 @@ import type { Serialize, Simplify } from "nitropack/types";
|
|||||||
declare module "nitropack/types" {
|
declare module "nitropack/types" {
|
||||||
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
|
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
|
||||||
interface InternalApi {
|
interface InternalApi {
|
||||||
|
'/api/admin/login': {
|
||||||
|
'post': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/api/admin/login.post').default>>>>
|
||||||
|
}
|
||||||
|
'/api/admin/requests': {
|
||||||
|
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/api/admin/requests.get').default>>>>
|
||||||
|
}
|
||||||
|
'/api/admin/stats': {
|
||||||
|
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/api/admin/stats.get').default>>>>
|
||||||
|
}
|
||||||
'/api/v1/rates/:code': {
|
'/api/v1/rates/:code': {
|
||||||
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/api/v1/rates/[code].get').default>>>>
|
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/api/v1/rates/[code].get').default>>>>
|
||||||
}
|
}
|
||||||
|
|||||||
11
.nuxt/types/plugins.d.ts
vendored
11
.nuxt/types/plugins.d.ts
vendored
@@ -9,18 +9,25 @@ type NuxtAppInjections =
|
|||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/revive-payload.client.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/revive-payload.client.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/head/runtime/plugins/unhead.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/head/runtime/plugins/unhead.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/router.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/router.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/browser-devtools-timing.client.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/payload.client.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/payload.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/dev-server-logs.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/navigation-repaint.client.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/navigation-repaint.client.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/check-outdated-build.client.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/check-outdated-build.client.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/revive-payload.server.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/revive-payload.server.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/chunk-reload.client.js")> &
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/chunk-reload.client.js")> &
|
||||||
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/prefetch.client.js")>
|
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/prefetch.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/pages/runtime/plugins/check-if-page-unused.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/@nuxt/devtools/dist/runtime/plugins/devtools.server.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/@nuxt/devtools/dist/runtime/plugins/devtools.client.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/warn.dev.server.js")> &
|
||||||
|
InjectionType<typeof import("../../node_modules/nuxt/dist/app/plugins/check-if-layout-used.js")>
|
||||||
|
|
||||||
declare module '#app' {
|
declare module '#app' {
|
||||||
interface NuxtApp extends NuxtAppInjections { }
|
interface NuxtApp extends NuxtAppInjections { }
|
||||||
|
|
||||||
interface NuxtAppLiterals {
|
interface NuxtAppLiterals {
|
||||||
pluginName: 'nuxt:revive-payload:client' | 'nuxt:head' | 'nuxt:router' | 'nuxt:payload' | 'nuxt:revive-payload:server' | 'nuxt:chunk-reload' | 'nuxt:global-components' | 'nuxt:prefetch'
|
pluginName: 'vue-devtools-client' | 'nuxt:revive-payload:client' | 'nuxt:head' | 'nuxt:router' | 'nuxt:browser-devtools-timing' | 'nuxt:payload' | 'nuxt:revive-payload:server' | 'nuxt:chunk-reload' | 'nuxt:global-components' | 'nuxt:prefetch' | 'nuxt:checkIfPageUnused' | 'nuxt:checkIfLayoutUsed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
.nuxt/types/runtime-config.d.ts
vendored
6
.nuxt/types/runtime-config.d.ts
vendored
@@ -12,6 +12,12 @@ import { RuntimeConfig as UserRuntimeConfig, PublicRuntimeConfig as UserPublicRu
|
|||||||
|
|
||||||
vatstackApiKey: string,
|
vatstackApiKey: string,
|
||||||
|
|
||||||
|
db: string,
|
||||||
|
|
||||||
|
redisUrl: string,
|
||||||
|
|
||||||
|
adminPassword: string,
|
||||||
|
|
||||||
nitro: {
|
nitro: {
|
||||||
envPrefix: string,
|
envPrefix: string,
|
||||||
},
|
},
|
||||||
|
|||||||
302
app/pages/admin/dashboard.vue
Normal file
302
app/pages/admin/dashboard.vue
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({ layout: false })
|
||||||
|
|
||||||
|
useHead({ title: 'Dashboard — vat-api.eu Admin' })
|
||||||
|
|
||||||
|
interface Stats {
|
||||||
|
totalRequests: { last24h: number; last7d: number; last30d: number }
|
||||||
|
uniqueIPs: number
|
||||||
|
topIPs: { ip: string; count: number; lastSeen: string }[]
|
||||||
|
requestsByHour: { hour: string; count: number }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestsResponse {
|
||||||
|
requests: { ip: string; path: string; method: string; statusCode: number; userAgent: string; timestamp: string }[]
|
||||||
|
total: number
|
||||||
|
page: number
|
||||||
|
limit: number
|
||||||
|
totalPages: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = ref<Stats | null>(null)
|
||||||
|
const reqs = ref<RequestsResponse | null>(null)
|
||||||
|
const error = ref('')
|
||||||
|
const page = ref(1)
|
||||||
|
const ipFilter = ref('')
|
||||||
|
let refreshTimer: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
|
async function fetchStats() {
|
||||||
|
try {
|
||||||
|
stats.value = await $fetch<Stats>('/api/admin/stats')
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e?.response?.status === 401) return navigateTo('/admin')
|
||||||
|
error.value = 'Failed to load stats'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRequests() {
|
||||||
|
try {
|
||||||
|
const params: Record<string, string | number> = { page: page.value, limit: 50 }
|
||||||
|
if (ipFilter.value) params.ip = ipFilter.value
|
||||||
|
reqs.value = await $fetch<RequestsResponse>('/api/admin/requests', { params })
|
||||||
|
} catch (e: any) {
|
||||||
|
if (e?.response?.status === 401) return navigateTo('/admin')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterByIp(ip: string) {
|
||||||
|
ipFilter.value = ip
|
||||||
|
page.value = 1
|
||||||
|
fetchRequests()
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFilter() {
|
||||||
|
ipFilter.value = ''
|
||||||
|
page.value = 1
|
||||||
|
fetchRequests()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePage(p: number) {
|
||||||
|
page.value = p
|
||||||
|
fetchRequests()
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(ts: string) {
|
||||||
|
return new Date(ts).toLocaleString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function reqsPerMinute() {
|
||||||
|
if (!stats.value) return '0'
|
||||||
|
return (stats.value.totalRequests.last24h / 1440).toFixed(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
await Promise.all([fetchStats(), fetchRequests()])
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
refresh()
|
||||||
|
refreshTimer = setInterval(refresh, 30_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (refreshTimer) clearInterval(refreshTimer)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-screen bg-surface-soft">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="bg-white border-b border-surface-border">
|
||||||
|
<div class="section-container flex items-center justify-between h-16">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<NuxtLink to="/" class="flex items-center gap-2 group">
|
||||||
|
<span class="inline-flex items-center justify-center w-8 h-8 rounded-lg bg-eu-blue text-white font-heading font-bold text-sm transition-transform group-hover:scale-105">
|
||||||
|
V
|
||||||
|
</span>
|
||||||
|
<span class="font-heading font-bold text-lg text-ink">
|
||||||
|
vat-api<span class="text-eu-blue">.eu</span>
|
||||||
|
</span>
|
||||||
|
</NuxtLink>
|
||||||
|
<span class="text-ink-faint mx-1">/</span>
|
||||||
|
<span class="text-sm font-medium text-ink-secondary">Admin</span>
|
||||||
|
</div>
|
||||||
|
<NuxtLink
|
||||||
|
to="/admin"
|
||||||
|
class="text-sm text-ink-muted hover:text-ink transition-colors"
|
||||||
|
>
|
||||||
|
Sign out
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="section-container py-8">
|
||||||
|
<div v-if="error" class="mb-6 px-4 py-3 rounded-xl bg-red-50 border border-red-200 text-red-700 text-sm">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Summary cards -->
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||||||
|
<div class="bg-white rounded-2xl border border-surface-border shadow-soft p-5">
|
||||||
|
<p class="text-xs font-medium text-ink-muted uppercase tracking-wide">Requests (24h)</p>
|
||||||
|
<p class="mt-1 text-2xl font-bold text-ink font-heading tabular-nums">
|
||||||
|
{{ stats?.totalRequests.last24h?.toLocaleString() ?? '...' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-2xl border border-surface-border shadow-soft p-5">
|
||||||
|
<p class="text-xs font-medium text-ink-muted uppercase tracking-wide">Requests (7d)</p>
|
||||||
|
<p class="mt-1 text-2xl font-bold text-ink font-heading tabular-nums">
|
||||||
|
{{ stats?.totalRequests.last7d?.toLocaleString() ?? '...' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-2xl border border-surface-border shadow-soft p-5">
|
||||||
|
<p class="text-xs font-medium text-ink-muted uppercase tracking-wide">Unique IPs (24h)</p>
|
||||||
|
<p class="mt-1 text-2xl font-bold text-ink font-heading tabular-nums">
|
||||||
|
{{ stats?.uniqueIPs?.toLocaleString() ?? '...' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-2xl border border-surface-border shadow-soft p-5">
|
||||||
|
<p class="text-xs font-medium text-ink-muted uppercase tracking-wide">Reqs / min (avg)</p>
|
||||||
|
<p class="mt-1 text-2xl font-bold text-ink font-heading tabular-nums">
|
||||||
|
{{ stats ? reqsPerMinute() : '...' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid lg:grid-cols-2 gap-6 mb-8">
|
||||||
|
<!-- Top IPs -->
|
||||||
|
<div class="bg-white rounded-2xl border border-surface-border shadow-soft overflow-hidden">
|
||||||
|
<div class="px-5 py-4 border-b border-surface-border bg-surface-soft">
|
||||||
|
<h2 class="text-sm font-semibold text-ink font-heading">Top IPs (24h)</h2>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-surface-border">
|
||||||
|
<th class="text-left font-semibold text-ink-secondary px-5 py-2.5 font-heading">IP</th>
|
||||||
|
<th class="text-right font-semibold text-ink-secondary px-5 py-2.5 font-heading w-24">Count</th>
|
||||||
|
<th class="text-right font-semibold text-ink-secondary px-5 py-2.5 font-heading w-44">Last seen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="row in stats?.topIPs"
|
||||||
|
:key="row.ip"
|
||||||
|
class="border-b border-surface-border/60 last:border-0 hover:bg-eu-blue-100/40 transition-colors cursor-pointer"
|
||||||
|
@click="filterByIp(row.ip)"
|
||||||
|
>
|
||||||
|
<td class="px-5 py-2.5">
|
||||||
|
<span class="font-mono text-xs text-ink">{{ row.ip }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-5 py-2.5 text-right">
|
||||||
|
<span class="font-semibold text-ink tabular-nums">{{ row.count.toLocaleString() }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-5 py-2.5 text-right text-ink-muted text-xs">
|
||||||
|
{{ formatTime(row.lastSeen) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="!stats?.topIPs?.length">
|
||||||
|
<td colspan="3" class="px-5 py-8 text-center text-ink-muted">No data yet</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Requests by hour -->
|
||||||
|
<div class="bg-white rounded-2xl border border-surface-border shadow-soft overflow-hidden">
|
||||||
|
<div class="px-5 py-4 border-b border-surface-border bg-surface-soft">
|
||||||
|
<h2 class="text-sm font-semibold text-ink font-heading">Requests by Hour (24h)</h2>
|
||||||
|
</div>
|
||||||
|
<div class="p-5">
|
||||||
|
<div v-if="stats?.requestsByHour?.length" class="flex items-end gap-1 h-40">
|
||||||
|
<div
|
||||||
|
v-for="bar in stats.requestsByHour"
|
||||||
|
:key="bar.hour"
|
||||||
|
class="flex-1 flex flex-col items-center gap-1"
|
||||||
|
>
|
||||||
|
<span class="text-[10px] text-ink-muted tabular-nums">{{ bar.count }}</span>
|
||||||
|
<div
|
||||||
|
class="w-full bg-eu-blue/80 rounded-t transition-all"
|
||||||
|
:style="{ height: `${Math.max(4, (bar.count / Math.max(...stats.requestsByHour.map(b => b.count))) * 120)}px` }"
|
||||||
|
/>
|
||||||
|
<span class="text-[10px] text-ink-faint tabular-nums">{{ bar.hour }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p v-else class="text-center text-ink-muted py-8">No data yet</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Recent requests -->
|
||||||
|
<div class="bg-white rounded-2xl border border-surface-border shadow-soft overflow-hidden">
|
||||||
|
<div class="px-5 py-4 border-b border-surface-border bg-surface-soft flex items-center justify-between">
|
||||||
|
<h2 class="text-sm font-semibold text-ink font-heading">
|
||||||
|
Recent Requests
|
||||||
|
<span v-if="ipFilter" class="ml-2 text-xs font-normal text-ink-muted">
|
||||||
|
filtered: {{ ipFilter }}
|
||||||
|
<button class="ml-1 text-eu-blue hover:underline" @click="clearFilter">clear</button>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<span class="text-xs text-ink-muted tabular-nums">
|
||||||
|
{{ reqs?.total?.toLocaleString() ?? '...' }} total
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-surface-border">
|
||||||
|
<th class="text-left font-semibold text-ink-secondary px-5 py-2.5 font-heading">IP</th>
|
||||||
|
<th class="text-left font-semibold text-ink-secondary px-5 py-2.5 font-heading">Method</th>
|
||||||
|
<th class="text-left font-semibold text-ink-secondary px-5 py-2.5 font-heading">Path</th>
|
||||||
|
<th class="text-center font-semibold text-ink-secondary px-5 py-2.5 font-heading w-20">Status</th>
|
||||||
|
<th class="text-right font-semibold text-ink-secondary px-5 py-2.5 font-heading w-44">Time</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="(req, i) in reqs?.requests"
|
||||||
|
:key="i"
|
||||||
|
class="border-b border-surface-border/60 last:border-0 hover:bg-eu-blue-100/40 transition-colors"
|
||||||
|
>
|
||||||
|
<td class="px-5 py-2.5">
|
||||||
|
<button
|
||||||
|
class="font-mono text-xs text-eu-blue hover:underline"
|
||||||
|
@click="filterByIp(req.ip)"
|
||||||
|
>
|
||||||
|
{{ req.ip }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
<td class="px-5 py-2.5">
|
||||||
|
<span class="inline-flex px-2 py-0.5 rounded bg-surface-muted font-mono text-xs font-medium text-ink-secondary">
|
||||||
|
{{ req.method }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-5 py-2.5 font-mono text-xs text-ink">{{ req.path }}</td>
|
||||||
|
<td class="px-5 py-2.5 text-center">
|
||||||
|
<span
|
||||||
|
class="inline-flex px-2 py-0.5 rounded text-xs font-semibold tabular-nums"
|
||||||
|
:class="req.statusCode < 400 ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'"
|
||||||
|
>
|
||||||
|
{{ req.statusCode }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-5 py-2.5 text-right text-ink-muted text-xs">
|
||||||
|
{{ formatTime(req.timestamp) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="!reqs?.requests?.length">
|
||||||
|
<td colspan="5" class="px-5 py-8 text-center text-ink-muted">No requests yet</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div v-if="reqs && reqs.totalPages > 1" class="px-5 py-3 border-t border-surface-border flex items-center justify-between">
|
||||||
|
<button
|
||||||
|
:disabled="page <= 1"
|
||||||
|
class="text-sm text-eu-blue hover:underline disabled:text-ink-faint disabled:no-underline"
|
||||||
|
@click="changePage(page - 1)"
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<span class="text-xs text-ink-muted tabular-nums">
|
||||||
|
Page {{ reqs.page }} of {{ reqs.totalPages }}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
:disabled="page >= reqs.totalPages"
|
||||||
|
class="text-sm text-eu-blue hover:underline disabled:text-ink-faint disabled:no-underline"
|
||||||
|
@click="changePage(page + 1)"
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-center text-xs text-ink-faint mt-6">
|
||||||
|
Auto-refreshes every 30 seconds.
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
74
app/pages/admin/index.vue
Normal file
74
app/pages/admin/index.vue
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({ layout: false })
|
||||||
|
|
||||||
|
useHead({ title: 'Admin Login — vat-api.eu' })
|
||||||
|
|
||||||
|
const password = ref('')
|
||||||
|
const error = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
error.value = ''
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await $fetch('/api/admin/login', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { password: password.value },
|
||||||
|
})
|
||||||
|
navigateTo('/admin/dashboard')
|
||||||
|
} catch {
|
||||||
|
error.value = 'Invalid password'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-screen bg-surface-soft flex items-center justify-center px-4">
|
||||||
|
<div class="w-full max-w-sm">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="text-center mb-8">
|
||||||
|
<NuxtLink to="/" class="inline-flex items-center gap-2 group">
|
||||||
|
<span class="inline-flex items-center justify-center w-10 h-10 rounded-xl bg-eu-blue text-white font-heading font-bold text-lg transition-transform group-hover:scale-105">
|
||||||
|
V
|
||||||
|
</span>
|
||||||
|
<span class="font-heading font-bold text-xl text-ink">
|
||||||
|
vat-api<span class="text-eu-blue">.eu</span>
|
||||||
|
</span>
|
||||||
|
</NuxtLink>
|
||||||
|
<p class="mt-3 text-sm text-ink-muted">Admin Dashboard</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Login card -->
|
||||||
|
<form
|
||||||
|
class="bg-white rounded-2xl border border-surface-border shadow-card p-6"
|
||||||
|
@submit.prevent="login"
|
||||||
|
>
|
||||||
|
<h1 class="text-title text-ink font-heading mb-6">Sign in</h1>
|
||||||
|
|
||||||
|
<div v-if="error" class="mb-4 px-4 py-2.5 rounded-xl bg-red-50 border border-red-200 text-red-700 text-sm">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="block text-sm font-medium text-ink-secondary mb-1.5">Password</label>
|
||||||
|
<input
|
||||||
|
v-model="password"
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
placeholder="Enter admin password"
|
||||||
|
class="w-full px-4 py-2.5 rounded-xl border border-surface-border bg-white text-sm text-ink placeholder:text-ink-faint focus:outline-none focus:ring-2 focus:ring-eu-blue/20 focus:border-eu-blue/40 transition-all"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
:disabled="loading"
|
||||||
|
class="mt-5 w-full px-4 py-2.5 rounded-xl bg-eu-blue text-white font-semibold text-sm hover:bg-eu-blue-dark transition-all disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{{ loading ? 'Signing in...' : 'Sign in' }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
7
node_modules/.cache/jiti/vat-api-nuxt.config.38f7f2db.mjs
generated
vendored
7
node_modules/.cache/jiti/vat-api-nuxt.config.38f7f2db.mjs
generated
vendored
@@ -20,7 +20,10 @@ var _default = exports.default = defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
|
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
vatstackApiKey: ''
|
vatstackApiKey: '',
|
||||||
|
db: '',
|
||||||
|
redisUrl: '',
|
||||||
|
adminPassword: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
app: {
|
app: {
|
||||||
@@ -31,4 +34,4 @@ var _default = exports.default = defineNuxtConfig({
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}); /* v9-40b9d46909467c59 */
|
}); /* v9-d85c2260d73c206e */
|
||||||
|
|||||||
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/BGLmsXN0.js
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/BGLmsXN0.js
generated
vendored
@@ -1 +0,0 @@
|
|||||||
import{S as a,a as s,U as r,V as u,W as o}from"./Dgz6sxCl.js";function d(t){const e=t||s();return e.ssrContext?.head||e.runWithContext(()=>{if(r()){const n=u(o);if(!n)throw new Error("[nuxt] [unhead] Missing Unhead instance.");return n}})}function i(t,e={}){const n=e.head||d(e.nuxt);return a(t,{head:n,...e})}export{i as u};
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/CBwmZ8LB.js
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/CBwmZ8LB.js
generated
vendored
@@ -1 +0,0 @@
|
|||||||
import{_ as o,y as s,z as a,A as t,B as r}from"./Dgz6sxCl.js";import{u as i}from"./BGLmsXN0.js";const u={class:"antialiased bg-white dark:bg-[#020420] dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-[#020420] tracking-wide"},l={class:"max-w-520px text-center"},c=["textContent"],d=["textContent"],p=["textContent"],f={__name:"error-500",props:{appName:{type:String,default:"Nuxt"},status:{type:Number,default:500},statusText:{type:String,default:"Internal server error"},description:{type:String,default:"This page is temporarily unavailable."},refresh:{type:String,default:"Refresh this page"}},setup(e){const n=e;return i({title:`${n.status} - ${n.statusText} | ${n.appName}`,script:[{innerHTML:`!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver(e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)}).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`}],style:[{innerHTML:'*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1,h2{font-size:inherit;font-weight:inherit}h1,h2,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }'}]}),(h,m)=>(s(),a("div",u,[t("div",l,[t("h1",{class:"font-semibold leading-none mb-4 sm:text-[110px] tabular-nums text-[80px]",textContent:r(e.status)},null,8,c),t("h2",{class:"font-semibold mb-2 sm:text-3xl text-2xl",textContent:r(e.statusText)},null,8,d),t("p",{class:"mb-4 px-2 text-[#64748B] text-md",textContent:r(e.description)},null,8,p)])]))}},x=o(f,[["__scopeId","data-v-d349100d"]]);export{x as default};
|
|
||||||
18
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/CeoJbNH2.js
generated
vendored
18
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/CeoJbNH2.js
generated
vendored
File diff suppressed because one or more lines are too long
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-400-latin-ext.C2zvOubV.woff2
generated
vendored
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-400-latin-ext.C2zvOubV.woff2
generated
vendored
Binary file not shown.
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-400-latin.4GdczIuU.woff2
generated
vendored
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-400-latin.4GdczIuU.woff2
generated
vendored
Binary file not shown.
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-500-latin-ext.BtRyHRi6.woff2
generated
vendored
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-500-latin-ext.BtRyHRi6.woff2
generated
vendored
Binary file not shown.
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-500-latin.DRMDZjhP.woff2
generated
vendored
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Mono-normal-500-latin.DRMDZjhP.woff2
generated
vendored
Binary file not shown.
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Sans-normal-400-latin-ext.BOFOeGcA.woff2
generated
vendored
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Sans-normal-400-latin-ext.BOFOeGcA.woff2
generated
vendored
Binary file not shown.
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Sans-normal-400-latin.Xz1IZZA0.woff2
generated
vendored
BIN
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DM_Sans-normal-400-latin.Xz1IZZA0.woff2
generated
vendored
Binary file not shown.
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DNdyEWwv.js
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/DNdyEWwv.js
generated
vendored
File diff suppressed because one or more lines are too long
4
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/Dgz6sxCl.js
generated
vendored
4
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/Dgz6sxCl.js
generated
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/entry.DJL3iLlR.css
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/entry.DJL3iLlR.css
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/error-404.C-Ezrlz-.css
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/error-404.C-Ezrlz-.css
generated
vendored
@@ -1 +0,0 @@
|
|||||||
.grid[data-v-204d37bf]{display:grid}.mb-2[data-v-204d37bf]{margin-bottom:.5rem}.mb-4[data-v-204d37bf]{margin-bottom:1rem}.max-w-520px[data-v-204d37bf]{max-width:520px}.min-h-screen[data-v-204d37bf]{min-height:100vh}.w-full[data-v-204d37bf]{width:100%}.flex[data-v-204d37bf]{display:flex}.place-content-center[data-v-204d37bf]{place-content:center}.items-center[data-v-204d37bf]{align-items:center}.justify-center[data-v-204d37bf]{justify-content:center}.overflow-hidden[data-v-204d37bf]{overflow:hidden}.bg-white[data-v-204d37bf]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-2[data-v-204d37bf]{padding-left:.5rem;padding-right:.5rem}.text-center[data-v-204d37bf]{text-align:center}.text-\[80px\][data-v-204d37bf]{font-size:80px}.text-2xl[data-v-204d37bf]{font-size:1.5rem;line-height:2rem}.text-sm[data-v-204d37bf]{font-size:.875rem;line-height:1.25rem}.text-\[\#020420\][data-v-204d37bf]{--un-text-opacity:1;color:rgb(2 4 32/var(--un-text-opacity))}.text-\[\#64748B\][data-v-204d37bf]{--un-text-opacity:1;color:rgb(100 116 139/var(--un-text-opacity))}.hover\:text-\[\#00DC82\][data-v-204d37bf]:hover{--un-text-opacity:1;color:rgb(0 220 130/var(--un-text-opacity))}.font-medium[data-v-204d37bf]{font-weight:500}.font-semibold[data-v-204d37bf]{font-weight:600}.leading-none[data-v-204d37bf]{line-height:1}.tracking-wide[data-v-204d37bf]{letter-spacing:.025em}.font-sans[data-v-204d37bf]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.tabular-nums[data-v-204d37bf]{--un-numeric-spacing:tabular-nums;font-variant-numeric:var(--un-ordinal) var(--un-slashed-zero) var(--un-numeric-figure) var(--un-numeric-spacing) var(--un-numeric-fraction)}.underline[data-v-204d37bf]{text-decoration-line:underline}.underline-offset-3[data-v-204d37bf]{text-underline-offset:3px}.antialiased[data-v-204d37bf]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(prefers-color-scheme:dark){.dark\:bg-\[\#020420\][data-v-204d37bf]{--un-bg-opacity:1;background-color:rgb(2 4 32/var(--un-bg-opacity))}.dark\:text-white[data-v-204d37bf]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media(min-width:640px){.sm\:text-\[110px\][data-v-204d37bf]{font-size:110px}.sm\:text-3xl[data-v-204d37bf]{font-size:1.875rem;line-height:2.25rem}}
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/error-500.DBWf9FGj.css
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/client/_nuxt/error-500.DBWf9FGj.css
generated
vendored
@@ -1 +0,0 @@
|
|||||||
.grid[data-v-d349100d]{display:grid}.mb-2[data-v-d349100d]{margin-bottom:.5rem}.mb-4[data-v-d349100d]{margin-bottom:1rem}.max-w-520px[data-v-d349100d]{max-width:520px}.min-h-screen[data-v-d349100d]{min-height:100vh}.place-content-center[data-v-d349100d]{place-content:center}.overflow-hidden[data-v-d349100d]{overflow:hidden}.bg-white[data-v-d349100d]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-2[data-v-d349100d]{padding-left:.5rem;padding-right:.5rem}.text-center[data-v-d349100d]{text-align:center}.text-\[80px\][data-v-d349100d]{font-size:80px}.text-2xl[data-v-d349100d]{font-size:1.5rem;line-height:2rem}.text-\[\#020420\][data-v-d349100d]{--un-text-opacity:1;color:rgb(2 4 32/var(--un-text-opacity))}.text-\[\#64748B\][data-v-d349100d]{--un-text-opacity:1;color:rgb(100 116 139/var(--un-text-opacity))}.font-semibold[data-v-d349100d]{font-weight:600}.leading-none[data-v-d349100d]{line-height:1}.tracking-wide[data-v-d349100d]{letter-spacing:.025em}.font-sans[data-v-d349100d]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.tabular-nums[data-v-d349100d]{--un-numeric-spacing:tabular-nums;font-variant-numeric:var(--un-ordinal) var(--un-slashed-zero) var(--un-numeric-figure) var(--un-numeric-spacing) var(--un-numeric-fraction)}.antialiased[data-v-d349100d]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(prefers-color-scheme:dark){.dark\:bg-\[\#020420\][data-v-d349100d]{--un-bg-opacity:1;background-color:rgb(2 4 32/var(--un-bg-opacity))}.dark\:text-white[data-v-d349100d]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media(min-width:640px){.sm\:text-\[110px\][data-v-d349100d]{font-size:110px}.sm\:text-3xl[data-v-d349100d]{font-size:1.875rem;line-height:2.25rem}}
|
|
||||||
23
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/composables-Piy9capG.js
generated
vendored
23
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/composables-Piy9capG.js
generated
vendored
@@ -1,23 +0,0 @@
|
|||||||
import { hasInjectionContext, inject } from "vue";
|
|
||||||
import { useHead as useHead$1, headSymbol } from "/home/bennet/source/vat-api/node_modules/@unhead/vue/dist/index.mjs";
|
|
||||||
import { a as useNuxtApp } from "../server.mjs";
|
|
||||||
function injectHead(nuxtApp) {
|
|
||||||
const nuxt = nuxtApp || useNuxtApp();
|
|
||||||
return nuxt.ssrContext?.head || nuxt.runWithContext(() => {
|
|
||||||
if (hasInjectionContext()) {
|
|
||||||
const head = inject(headSymbol);
|
|
||||||
if (!head) {
|
|
||||||
throw new Error("[nuxt] [unhead] Missing Unhead instance.");
|
|
||||||
}
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function useHead(input, options = {}) {
|
|
||||||
const head = options.head || injectHead(options.nuxt);
|
|
||||||
return useHead$1(input, { head, ...options });
|
|
||||||
}
|
|
||||||
export {
|
|
||||||
useHead as u
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=composables-Piy9capG.js.map
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/composables-Piy9capG.js.map
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/composables-Piy9capG.js.map
generated
vendored
@@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"composables-Piy9capG.js","sources":["../../../../../../nuxt/dist/head/runtime/composables.js"],"sourcesContent":["import { hasInjectionContext, inject } from \"vue\";\nimport {\n useHead as headCore,\n useHeadSafe as headSafe,\n headSymbol,\n useSeoMeta as seoMeta,\n useServerHead as serverHead,\n useServerHeadSafe as serverHeadSafe,\n useServerSeoMeta as serverSeoMeta\n} from \"@unhead/vue\";\nimport { useNuxtApp } from \"#app/nuxt\";\nexport function injectHead(nuxtApp) {\n const nuxt = nuxtApp || useNuxtApp();\n return nuxt.ssrContext?.head || nuxt.runWithContext(() => {\n if (hasInjectionContext()) {\n const head = inject(headSymbol);\n if (!head) {\n throw new Error(\"[nuxt] [unhead] Missing Unhead instance.\");\n }\n return head;\n }\n });\n}\nexport function useHead(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return headCore(input, { head, ...options });\n}\nexport function useHeadSafe(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return headSafe(input, { head, ...options });\n}\nexport function useSeoMeta(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return seoMeta(input, { head, ...options });\n}\nexport function useServerHead(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return serverHead(input, { head, ...options });\n}\nexport function useServerHeadSafe(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return serverHeadSafe(input, { head, ...options });\n}\nexport function useServerSeoMeta(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return serverSeoMeta(input, { head, ...options });\n}\n"],"names":["headCore"],"mappings":";;;AAWO,SAAS,WAAW,SAAS;AAClC,QAAM,OAAO,WAAW,WAAU;AAClC,SAAO,KAAK,YAAY,QAAQ,KAAK,eAAe,MAAM;AACxD,QAAI,oBAAmB,GAAI;AACzB,YAAM,OAAO,OAAO,UAAU;AAC9B,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAC5D;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AACO,SAAS,QAAQ,OAAO,UAAU,IAAI;AAC3C,QAAM,OAAO,QAAQ,QAAQ,WAAW,QAAQ,IAAI;AACpD,SAAOA,UAAS,OAAO,EAAE,MAAM,GAAG,QAAO,CAAE;AAC7C;"}
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/composables-Piy9capG.js.map.json
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/composables-Piy9capG.js.map.json
generated
vendored
@@ -1 +0,0 @@
|
|||||||
{"file":"composables-Piy9capG.js","mappings":";;;AAWO,SAAS,WAAW,SAAS;AAClC,QAAM,OAAO,WAAW,WAAU;AAClC,SAAO,KAAK,YAAY,QAAQ,KAAK,eAAe,MAAM;AACxD,QAAI,oBAAmB,GAAI;AACzB,YAAM,OAAO,OAAO,UAAU;AAC9B,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAC5D;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AACO,SAAS,QAAQ,OAAO,UAAU,IAAI;AAC3C,QAAM,OAAO,QAAQ,QAAQ,WAAW,QAAQ,IAAI;AACpD,SAAOA,UAAS,OAAO,EAAE,MAAM,GAAG,QAAO,CAAE;AAC7C;","names":["headCore"],"sources":["../../../../../../nuxt/dist/head/runtime/composables.js"],"sourcesContent":["import { hasInjectionContext, inject } from \"vue\";\nimport {\n useHead as headCore,\n useHeadSafe as headSafe,\n headSymbol,\n useSeoMeta as seoMeta,\n useServerHead as serverHead,\n useServerHeadSafe as serverHeadSafe,\n useServerSeoMeta as serverSeoMeta\n} from \"@unhead/vue\";\nimport { useNuxtApp } from \"#app/nuxt\";\nexport function injectHead(nuxtApp) {\n const nuxt = nuxtApp || useNuxtApp();\n return nuxt.ssrContext?.head || nuxt.runWithContext(() => {\n if (hasInjectionContext()) {\n const head = inject(headSymbol);\n if (!head) {\n throw new Error(\"[nuxt] [unhead] Missing Unhead instance.\");\n }\n return head;\n }\n });\n}\nexport function useHead(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return headCore(input, { head, ...options });\n}\nexport function useHeadSafe(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return headSafe(input, { head, ...options });\n}\nexport function useSeoMeta(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return seoMeta(input, { head, ...options });\n}\nexport function useServerHead(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return serverHead(input, { head, ...options });\n}\nexport function useServerHeadSafe(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return serverHeadSafe(input, { head, ...options });\n}\nexport function useServerSeoMeta(input, options = {}) {\n const head = options.head || injectHead(options.nuxt);\n return serverSeoMeta(input, { head, ...options });\n}\n"],"version":3}
|
|
||||||
369
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-BY_x-_oz.js
generated
vendored
369
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-BY_x-_oz.js
generated
vendored
@@ -1,369 +0,0 @@
|
|||||||
import { defineComponent, shallowRef, h, resolveComponent, computed, mergeProps, withCtx, createTextVNode, toDisplayString, useSSRContext } from "vue";
|
|
||||||
import { parseQuery, hasProtocol, joinURL, withTrailingSlash, withoutTrailingSlash } from "/home/bennet/source/vat-api/node_modules/ufo/dist/index.mjs";
|
|
||||||
import { u as useRouter, e as encodeRoutePath, r as resolveRouteObject, n as navigateTo, a as useNuxtApp, b as useRuntimeConfig, c as nuxtLinkDefaults, _ as _export_sfc } from "../server.mjs";
|
|
||||||
import { ssrRenderAttrs, ssrInterpolate, ssrRenderComponent } from "vue/server-renderer";
|
|
||||||
import { u as useHead } from "./composables-Piy9capG.js";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/ofetch/dist/node.mjs";
|
|
||||||
import "#internal/nuxt/paths";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/hookable/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/unctx/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/h3/dist/index.mjs";
|
|
||||||
import "vue-router";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/defu/dist/defu.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/@unhead/vue/dist/index.mjs";
|
|
||||||
const firstNonUndefined = (...args) => args.find((arg) => arg !== void 0);
|
|
||||||
// @__NO_SIDE_EFFECTS__
|
|
||||||
function defineNuxtLink(options) {
|
|
||||||
const componentName = options.componentName || "NuxtLink";
|
|
||||||
function isHashLinkWithoutHashMode(link) {
|
|
||||||
return typeof link === "string" && link.startsWith("#");
|
|
||||||
}
|
|
||||||
function resolveTrailingSlashBehavior(to, resolve, trailingSlash) {
|
|
||||||
const effectiveTrailingSlash = trailingSlash ?? options.trailingSlash;
|
|
||||||
if (!to || effectiveTrailingSlash !== "append" && effectiveTrailingSlash !== "remove") {
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
if (typeof to === "string") {
|
|
||||||
return applyTrailingSlashBehavior(to, effectiveTrailingSlash);
|
|
||||||
}
|
|
||||||
const path = "path" in to && to.path !== void 0 ? to.path : resolve(to).path;
|
|
||||||
const resolvedPath = {
|
|
||||||
...to,
|
|
||||||
name: void 0,
|
|
||||||
// named routes would otherwise always override trailing slash behavior
|
|
||||||
path: applyTrailingSlashBehavior(path, effectiveTrailingSlash)
|
|
||||||
};
|
|
||||||
return resolvedPath;
|
|
||||||
}
|
|
||||||
function useNuxtLink(props) {
|
|
||||||
const router = useRouter();
|
|
||||||
const config = useRuntimeConfig();
|
|
||||||
const hasTarget = computed(() => !!props.target && props.target !== "_self");
|
|
||||||
const isAbsoluteUrl = computed(() => {
|
|
||||||
const path = props.to || props.href || "";
|
|
||||||
return typeof path === "string" && hasProtocol(path, { acceptRelative: true });
|
|
||||||
});
|
|
||||||
const builtinRouterLink = resolveComponent("RouterLink");
|
|
||||||
const useBuiltinLink = builtinRouterLink && typeof builtinRouterLink !== "string" ? builtinRouterLink.useLink : void 0;
|
|
||||||
const isExternal = computed(() => {
|
|
||||||
if (props.external) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const path = props.to || props.href || "";
|
|
||||||
if (typeof path === "object") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return path === "" || isAbsoluteUrl.value;
|
|
||||||
});
|
|
||||||
const to = computed(() => {
|
|
||||||
const path = props.to || props.href || "";
|
|
||||||
if (isExternal.value) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
return resolveTrailingSlashBehavior(path, router.resolve, props.trailingSlash);
|
|
||||||
});
|
|
||||||
const link = isExternal.value ? void 0 : useBuiltinLink?.({ ...props, to });
|
|
||||||
const href = computed(() => {
|
|
||||||
const effectiveTrailingSlash = props.trailingSlash ?? options.trailingSlash;
|
|
||||||
if (!to.value || isAbsoluteUrl.value || isHashLinkWithoutHashMode(to.value)) {
|
|
||||||
return to.value;
|
|
||||||
}
|
|
||||||
if (isExternal.value) {
|
|
||||||
const path = typeof to.value === "object" && "path" in to.value ? resolveRouteObject(to.value) : to.value;
|
|
||||||
const href2 = typeof path === "object" ? router.resolve(path).href : path;
|
|
||||||
return applyTrailingSlashBehavior(href2, effectiveTrailingSlash);
|
|
||||||
}
|
|
||||||
if (typeof to.value === "object") {
|
|
||||||
return router.resolve(to.value)?.href ?? null;
|
|
||||||
}
|
|
||||||
return applyTrailingSlashBehavior(joinURL(config.app.baseURL, to.value), effectiveTrailingSlash);
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
to,
|
|
||||||
hasTarget,
|
|
||||||
isAbsoluteUrl,
|
|
||||||
isExternal,
|
|
||||||
//
|
|
||||||
href,
|
|
||||||
isActive: link?.isActive ?? computed(() => to.value === router.currentRoute.value.path),
|
|
||||||
isExactActive: link?.isExactActive ?? computed(() => to.value === router.currentRoute.value.path),
|
|
||||||
route: link?.route ?? computed(() => router.resolve(to.value)),
|
|
||||||
async navigate(_e) {
|
|
||||||
await navigateTo(href.value, { replace: props.replace, external: isExternal.value || hasTarget.value });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return defineComponent({
|
|
||||||
name: componentName,
|
|
||||||
props: {
|
|
||||||
// Routing
|
|
||||||
to: {
|
|
||||||
type: [String, Object],
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
href: {
|
|
||||||
type: [String, Object],
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
// Attributes
|
|
||||||
target: {
|
|
||||||
type: String,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
rel: {
|
|
||||||
type: String,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
noRel: {
|
|
||||||
type: Boolean,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
// Prefetching
|
|
||||||
prefetch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
prefetchOn: {
|
|
||||||
type: [String, Object],
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
noPrefetch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
// Styling
|
|
||||||
activeClass: {
|
|
||||||
type: String,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
exactActiveClass: {
|
|
||||||
type: String,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
prefetchedClass: {
|
|
||||||
type: String,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
// Vue Router's `<RouterLink>` additional props
|
|
||||||
replace: {
|
|
||||||
type: Boolean,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
ariaCurrentValue: {
|
|
||||||
type: String,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
// Edge cases handling
|
|
||||||
external: {
|
|
||||||
type: Boolean,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
// Slot API
|
|
||||||
custom: {
|
|
||||||
type: Boolean,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
// Behavior
|
|
||||||
trailingSlash: {
|
|
||||||
type: String,
|
|
||||||
default: void 0,
|
|
||||||
required: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
useLink: useNuxtLink,
|
|
||||||
setup(props, { slots }) {
|
|
||||||
const router = useRouter();
|
|
||||||
const { to, href, navigate, isExternal, hasTarget, isAbsoluteUrl } = useNuxtLink(props);
|
|
||||||
shallowRef(false);
|
|
||||||
const el = void 0;
|
|
||||||
const elRef = void 0;
|
|
||||||
async function prefetch(nuxtApp = useNuxtApp()) {
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
if (!isExternal.value && !hasTarget.value && !isHashLinkWithoutHashMode(to.value)) {
|
|
||||||
const routerLinkProps = {
|
|
||||||
ref: elRef,
|
|
||||||
to: to.value,
|
|
||||||
activeClass: props.activeClass || options.activeClass,
|
|
||||||
exactActiveClass: props.exactActiveClass || options.exactActiveClass,
|
|
||||||
replace: props.replace,
|
|
||||||
ariaCurrentValue: props.ariaCurrentValue,
|
|
||||||
custom: props.custom
|
|
||||||
};
|
|
||||||
if (!props.custom) {
|
|
||||||
routerLinkProps.rel = props.rel || void 0;
|
|
||||||
}
|
|
||||||
return h(
|
|
||||||
resolveComponent("RouterLink"),
|
|
||||||
routerLinkProps,
|
|
||||||
slots.default
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const target = props.target || null;
|
|
||||||
const rel = firstNonUndefined(
|
|
||||||
// converts `""` to `null` to prevent the attribute from being added as empty (`rel=""`)
|
|
||||||
props.noRel ? "" : props.rel,
|
|
||||||
options.externalRelAttribute,
|
|
||||||
/*
|
|
||||||
* A fallback rel of `noopener noreferrer` is applied for external links or links that open in a new tab.
|
|
||||||
* This solves a reverse tabnapping security flaw in browsers pre-2021 as well as improving privacy.
|
|
||||||
*/
|
|
||||||
isAbsoluteUrl.value || hasTarget.value ? "noopener noreferrer" : ""
|
|
||||||
) || null;
|
|
||||||
if (props.custom) {
|
|
||||||
if (!slots.default) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return slots.default({
|
|
||||||
href: href.value,
|
|
||||||
navigate,
|
|
||||||
prefetch,
|
|
||||||
get route() {
|
|
||||||
if (!href.value) {
|
|
||||||
return void 0;
|
|
||||||
}
|
|
||||||
const url = new URL(href.value, "http://localhost");
|
|
||||||
return {
|
|
||||||
path: url.pathname,
|
|
||||||
fullPath: url.pathname,
|
|
||||||
get query() {
|
|
||||||
return parseQuery(url.search);
|
|
||||||
},
|
|
||||||
hash: url.hash,
|
|
||||||
params: {},
|
|
||||||
name: void 0,
|
|
||||||
matched: [],
|
|
||||||
redirectedFrom: void 0,
|
|
||||||
meta: {},
|
|
||||||
href: href.value
|
|
||||||
};
|
|
||||||
},
|
|
||||||
rel,
|
|
||||||
target,
|
|
||||||
isExternal: isExternal.value || hasTarget.value,
|
|
||||||
isActive: false,
|
|
||||||
isExactActive: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return h("a", {
|
|
||||||
ref: el,
|
|
||||||
href: href.value || null,
|
|
||||||
// converts `""` to `null` to prevent the attribute from being added as empty (`href=""`)
|
|
||||||
rel,
|
|
||||||
target,
|
|
||||||
onClick: async (event) => {
|
|
||||||
if (isExternal.value || hasTarget.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
try {
|
|
||||||
const encodedHref = encodeRoutePath(href.value);
|
|
||||||
return await (props.replace ? router.replace(encodedHref) : router.push(encodedHref));
|
|
||||||
} finally {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, slots.default?.());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const __nuxt_component_0 = /* @__PURE__ */ defineNuxtLink(nuxtLinkDefaults);
|
|
||||||
function applyTrailingSlashBehavior(to, trailingSlash) {
|
|
||||||
const normalizeFn = trailingSlash === "append" ? withTrailingSlash : withoutTrailingSlash;
|
|
||||||
const hasProtocolDifferentFromHttp = hasProtocol(to) && !to.startsWith("http");
|
|
||||||
if (hasProtocolDifferentFromHttp) {
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
return normalizeFn(to, true);
|
|
||||||
}
|
|
||||||
const _sfc_main = {
|
|
||||||
__name: "error-404",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
props: {
|
|
||||||
appName: {
|
|
||||||
type: String,
|
|
||||||
default: "Nuxt"
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
type: Number,
|
|
||||||
default: 404
|
|
||||||
},
|
|
||||||
statusText: {
|
|
||||||
type: String,
|
|
||||||
default: "Page not found"
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: "Sorry, the page you are looking for could not be found."
|
|
||||||
},
|
|
||||||
backHome: {
|
|
||||||
type: String,
|
|
||||||
default: "Go back home"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(__props) {
|
|
||||||
const props = __props;
|
|
||||||
useHead({
|
|
||||||
title: `${props.status} - ${props.statusText} | ${props.appName}`,
|
|
||||||
script: [
|
|
||||||
{
|
|
||||||
innerHTML: `!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver(e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)}).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`
|
|
||||||
}
|
|
||||||
],
|
|
||||||
style: [
|
|
||||||
{
|
|
||||||
innerHTML: `*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1,h2{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}h1,h2,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
const _component_NuxtLink = __nuxt_component_0;
|
|
||||||
_push(`<div${ssrRenderAttrs(mergeProps({ class: "antialiased bg-white dark:bg-[#020420] dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-[#020420] tracking-wide" }, _attrs))} data-v-204d37bf><div class="max-w-520px text-center" data-v-204d37bf><h1 class="font-semibold leading-none mb-4 sm:text-[110px] tabular-nums text-[80px]" data-v-204d37bf>${ssrInterpolate(__props.status)}</h1><h2 class="font-semibold mb-2 sm:text-3xl text-2xl" data-v-204d37bf>${ssrInterpolate(__props.statusText)}</h2><p class="mb-4 px-2 text-[#64748B] text-md" data-v-204d37bf>${ssrInterpolate(__props.description)}</p><div class="flex items-center justify-center w-full" data-v-204d37bf>`);
|
|
||||||
_push(ssrRenderComponent(_component_NuxtLink, {
|
|
||||||
to: "/",
|
|
||||||
class: "font-medium hover:text-[#00DC82] text-sm underline underline-offset-3"
|
|
||||||
}, {
|
|
||||||
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
|
||||||
if (_push2) {
|
|
||||||
_push2(`${ssrInterpolate(__props.backHome)}`);
|
|
||||||
} else {
|
|
||||||
return [
|
|
||||||
createTextVNode(toDisplayString(__props.backHome), 1)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
_: 1
|
|
||||||
}, _parent));
|
|
||||||
_push(`</div></div></div>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const _sfc_setup = _sfc_main.setup;
|
|
||||||
_sfc_main.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("../node_modules/nuxt/dist/app/components/error-404.vue");
|
|
||||||
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const error404 = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-204d37bf"]]);
|
|
||||||
export {
|
|
||||||
error404 as default
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=error-404-BY_x-_oz.js.map
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-BY_x-_oz.js.map
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-BY_x-_oz.js.map
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-BY_x-_oz.js.map.json
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-BY_x-_oz.js.map.json
generated
vendored
File diff suppressed because one or more lines are too long
5
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-styles-1.mjs-Bae73Gon.js
generated
vendored
5
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-styles-1.mjs-Bae73Gon.js
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
const error404_vue_vue_type_style_index_0_scoped_204d37bf_lang = ".grid[data-v-204d37bf]{display:grid}.mb-2[data-v-204d37bf]{margin-bottom:.5rem}.mb-4[data-v-204d37bf]{margin-bottom:1rem}.max-w-520px[data-v-204d37bf]{max-width:520px}.min-h-screen[data-v-204d37bf]{min-height:100vh}.w-full[data-v-204d37bf]{width:100%}.flex[data-v-204d37bf]{display:flex}.place-content-center[data-v-204d37bf]{place-content:center}.items-center[data-v-204d37bf]{align-items:center}.justify-center[data-v-204d37bf]{justify-content:center}.overflow-hidden[data-v-204d37bf]{overflow:hidden}.bg-white[data-v-204d37bf]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-2[data-v-204d37bf]{padding-left:.5rem;padding-right:.5rem}.text-center[data-v-204d37bf]{text-align:center}.text-\\[80px\\][data-v-204d37bf]{font-size:80px}.text-2xl[data-v-204d37bf]{font-size:1.5rem;line-height:2rem}.text-sm[data-v-204d37bf]{font-size:.875rem;line-height:1.25rem}.text-\\[\\#020420\\][data-v-204d37bf]{--un-text-opacity:1;color:rgb(2 4 32/var(--un-text-opacity))}.text-\\[\\#64748B\\][data-v-204d37bf]{--un-text-opacity:1;color:rgb(100 116 139/var(--un-text-opacity))}.hover\\:text-\\[\\#00DC82\\][data-v-204d37bf]:hover{--un-text-opacity:1;color:rgb(0 220 130/var(--un-text-opacity))}.font-medium[data-v-204d37bf]{font-weight:500}.font-semibold[data-v-204d37bf]{font-weight:600}.leading-none[data-v-204d37bf]{line-height:1}.tracking-wide[data-v-204d37bf]{letter-spacing:.025em}.font-sans[data-v-204d37bf]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.tabular-nums[data-v-204d37bf]{--un-numeric-spacing:tabular-nums;font-variant-numeric:var(--un-ordinal) var(--un-slashed-zero) var(--un-numeric-figure) var(--un-numeric-spacing) var(--un-numeric-fraction)}.underline[data-v-204d37bf]{text-decoration-line:underline}.underline-offset-3[data-v-204d37bf]{text-underline-offset:3px}.antialiased[data-v-204d37bf]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(prefers-color-scheme:dark){.dark\\:bg-\\[\\#020420\\][data-v-204d37bf]{--un-bg-opacity:1;background-color:rgb(2 4 32/var(--un-bg-opacity))}.dark\\:text-white[data-v-204d37bf]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media(min-width:640px){.sm\\:text-\\[110px\\][data-v-204d37bf]{font-size:110px}.sm\\:text-3xl[data-v-204d37bf]{font-size:1.875rem;line-height:2.25rem}}";
|
|
||||||
export {
|
|
||||||
error404_vue_vue_type_style_index_0_scoped_204d37bf_lang as default
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=error-404-styles-1.mjs-Bae73Gon.js.map
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"error-404-styles-1.mjs-Bae73Gon.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"file":"error-404-styles-1.mjs-Bae73Gon.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
|
|
||||||
4
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-styles.DuDrf-v0.mjs
generated
vendored
4
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-404-styles.DuDrf-v0.mjs
generated
vendored
@@ -1,4 +0,0 @@
|
|||||||
import style_0 from "./error-404-styles-1.mjs-Bae73Gon.js";
|
|
||||||
export default [
|
|
||||||
style_0
|
|
||||||
]
|
|
||||||
69
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-B0qDQUop.js
generated
vendored
69
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-B0qDQUop.js
generated
vendored
@@ -1,69 +0,0 @@
|
|||||||
import { mergeProps, useSSRContext } from "vue";
|
|
||||||
import { ssrRenderAttrs, ssrInterpolate } from "vue/server-renderer";
|
|
||||||
import { _ as _export_sfc } from "../server.mjs";
|
|
||||||
import { u as useHead } from "./composables-Piy9capG.js";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/ofetch/dist/node.mjs";
|
|
||||||
import "#internal/nuxt/paths";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/hookable/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/unctx/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/h3/dist/index.mjs";
|
|
||||||
import "vue-router";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/defu/dist/defu.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/ufo/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/@unhead/vue/dist/index.mjs";
|
|
||||||
const _sfc_main = {
|
|
||||||
__name: "error-500",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
props: {
|
|
||||||
appName: {
|
|
||||||
type: String,
|
|
||||||
default: "Nuxt"
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
type: Number,
|
|
||||||
default: 500
|
|
||||||
},
|
|
||||||
statusText: {
|
|
||||||
type: String,
|
|
||||||
default: "Internal server error"
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: "This page is temporarily unavailable."
|
|
||||||
},
|
|
||||||
refresh: {
|
|
||||||
type: String,
|
|
||||||
default: "Refresh this page"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(__props) {
|
|
||||||
const props = __props;
|
|
||||||
useHead({
|
|
||||||
title: `${props.status} - ${props.statusText} | ${props.appName}`,
|
|
||||||
script: [
|
|
||||||
{
|
|
||||||
innerHTML: `!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver(e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)}).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`
|
|
||||||
}
|
|
||||||
],
|
|
||||||
style: [
|
|
||||||
{
|
|
||||||
innerHTML: `*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1,h2{font-size:inherit;font-weight:inherit}h1,h2,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
_push(`<div${ssrRenderAttrs(mergeProps({ class: "antialiased bg-white dark:bg-[#020420] dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-[#020420] tracking-wide" }, _attrs))} data-v-d349100d><div class="max-w-520px text-center" data-v-d349100d><h1 class="font-semibold leading-none mb-4 sm:text-[110px] tabular-nums text-[80px]" data-v-d349100d>${ssrInterpolate(__props.status)}</h1><h2 class="font-semibold mb-2 sm:text-3xl text-2xl" data-v-d349100d>${ssrInterpolate(__props.statusText)}</h2><p class="mb-4 px-2 text-[#64748B] text-md" data-v-d349100d>${ssrInterpolate(__props.description)}</p></div></div>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const _sfc_setup = _sfc_main.setup;
|
|
||||||
_sfc_main.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("../node_modules/nuxt/dist/app/components/error-500.vue");
|
|
||||||
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const error500 = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d349100d"]]);
|
|
||||||
export {
|
|
||||||
error500 as default
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=error-500-B0qDQUop.js.map
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-B0qDQUop.js.map
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-B0qDQUop.js.map
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-B0qDQUop.js.map.json
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-B0qDQUop.js.map.json
generated
vendored
File diff suppressed because one or more lines are too long
5
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-styles-1.mjs-DOrS-RIZ.js
generated
vendored
5
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-styles-1.mjs-DOrS-RIZ.js
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
const error500_vue_vue_type_style_index_0_scoped_d349100d_lang = ".grid[data-v-d349100d]{display:grid}.mb-2[data-v-d349100d]{margin-bottom:.5rem}.mb-4[data-v-d349100d]{margin-bottom:1rem}.max-w-520px[data-v-d349100d]{max-width:520px}.min-h-screen[data-v-d349100d]{min-height:100vh}.place-content-center[data-v-d349100d]{place-content:center}.overflow-hidden[data-v-d349100d]{overflow:hidden}.bg-white[data-v-d349100d]{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-2[data-v-d349100d]{padding-left:.5rem;padding-right:.5rem}.text-center[data-v-d349100d]{text-align:center}.text-\\[80px\\][data-v-d349100d]{font-size:80px}.text-2xl[data-v-d349100d]{font-size:1.5rem;line-height:2rem}.text-\\[\\#020420\\][data-v-d349100d]{--un-text-opacity:1;color:rgb(2 4 32/var(--un-text-opacity))}.text-\\[\\#64748B\\][data-v-d349100d]{--un-text-opacity:1;color:rgb(100 116 139/var(--un-text-opacity))}.font-semibold[data-v-d349100d]{font-weight:600}.leading-none[data-v-d349100d]{line-height:1}.tracking-wide[data-v-d349100d]{letter-spacing:.025em}.font-sans[data-v-d349100d]{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.tabular-nums[data-v-d349100d]{--un-numeric-spacing:tabular-nums;font-variant-numeric:var(--un-ordinal) var(--un-slashed-zero) var(--un-numeric-figure) var(--un-numeric-spacing) var(--un-numeric-fraction)}.antialiased[data-v-d349100d]{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(prefers-color-scheme:dark){.dark\\:bg-\\[\\#020420\\][data-v-d349100d]{--un-bg-opacity:1;background-color:rgb(2 4 32/var(--un-bg-opacity))}.dark\\:text-white[data-v-d349100d]{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media(min-width:640px){.sm\\:text-\\[110px\\][data-v-d349100d]{font-size:110px}.sm\\:text-3xl[data-v-d349100d]{font-size:1.875rem;line-height:2.25rem}}";
|
|
||||||
export {
|
|
||||||
error500_vue_vue_type_style_index_0_scoped_d349100d_lang as default
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=error-500-styles-1.mjs-DOrS-RIZ.js.map
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"version":3,"file":"error-500-styles-1.mjs-DOrS-RIZ.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"file":"error-500-styles-1.mjs-DOrS-RIZ.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
|
|
||||||
4
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-styles.8IYEHzz6.mjs
generated
vendored
4
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/error-500-styles.8IYEHzz6.mjs
generated
vendored
@@ -1,4 +0,0 @@
|
|||||||
import style_0 from "./error-500-styles-1.mjs-DOrS-RIZ.js";
|
|
||||||
export default [
|
|
||||||
style_0
|
|
||||||
]
|
|
||||||
409
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/index-BDcJu3_l.js
generated
vendored
409
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/index-BDcJu3_l.js
generated
vendored
@@ -1,409 +0,0 @@
|
|||||||
import { defineComponent, ref, mergeProps, unref, useSSRContext, readonly, computed } from "vue";
|
|
||||||
import { ssrRenderAttrs, ssrRenderList, ssrRenderAttr, ssrInterpolate, ssrRenderStyle, ssrRenderClass, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderComponent } from "vue/server-renderer";
|
|
||||||
import { _ as _export_sfc } from "../server.mjs";
|
|
||||||
import { u as useHead } from "./composables-Piy9capG.js";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/ofetch/dist/node.mjs";
|
|
||||||
import "#internal/nuxt/paths";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/hookable/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/unctx/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/h3/dist/index.mjs";
|
|
||||||
import "vue-router";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/defu/dist/defu.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/ufo/dist/index.mjs";
|
|
||||||
import "/home/bennet/source/vat-api/node_modules/@unhead/vue/dist/index.mjs";
|
|
||||||
const _sfc_main$7 = /* @__PURE__ */ defineComponent({
|
|
||||||
__name: "Navbar",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
setup(__props) {
|
|
||||||
const scrolled = ref(false);
|
|
||||||
const links = [
|
|
||||||
{ label: "Features", href: "#features" },
|
|
||||||
{ label: "Rates", href: "#rates" },
|
|
||||||
{ label: "API Docs", href: "#playground" }
|
|
||||||
];
|
|
||||||
const mobileOpen = ref(false);
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
_push(`<nav${ssrRenderAttrs(mergeProps({
|
|
||||||
class: ["fixed top-0 left-0 right-0 z-50 transition-all duration-300", unref(scrolled) ? "bg-white/80 backdrop-blur-xl shadow-soft border-b border-surface-border" : "bg-transparent"]
|
|
||||||
}, _attrs))}><div class="section-container flex items-center justify-between h-16"><a href="#" class="flex items-center gap-2 group"><span class="inline-flex items-center justify-center w-8 h-8 rounded-lg bg-eu-blue text-white font-heading font-bold text-sm transition-transform group-hover:scale-105"> V </span><span class="font-heading font-bold text-lg text-ink"> vat-api<span class="text-eu-blue">.eu</span></span></a><div class="hidden md:flex items-center gap-8"><!--[-->`);
|
|
||||||
ssrRenderList(links, (link) => {
|
|
||||||
_push(`<a${ssrRenderAttr("href", link.href)} class="text-sm font-medium text-ink-secondary hover:text-eu-blue transition-colors">${ssrInterpolate(link.label)}</a>`);
|
|
||||||
});
|
|
||||||
_push(`<!--]--><a href="#playground" class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-eu-blue text-white text-sm font-semibold hover:bg-eu-blue-dark transition-colors"> Try API <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3"></path></svg></a></div><button class="md:hidden p-2 -mr-2 text-ink-secondary hover:text-ink"${ssrRenderAttr("aria-label", unref(mobileOpen) ? "Close menu" : "Open menu")}>`);
|
|
||||||
if (!unref(mobileOpen)) {
|
|
||||||
_push(`<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" d="M4 6h16M4 12h16M4 18h16"></path></svg>`);
|
|
||||||
} else {
|
|
||||||
_push(`<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" d="M6 18 18 6M6 6l12 12"></path></svg>`);
|
|
||||||
}
|
|
||||||
_push(`</button></div>`);
|
|
||||||
if (unref(mobileOpen)) {
|
|
||||||
_push(`<div class="md:hidden bg-white/95 backdrop-blur-xl border-b border-surface-border"><div class="section-container py-4 flex flex-col gap-3"><!--[-->`);
|
|
||||||
ssrRenderList(links, (link) => {
|
|
||||||
_push(`<a${ssrRenderAttr("href", link.href)} class="text-sm font-medium text-ink-secondary hover:text-eu-blue py-2 transition-colors">${ssrInterpolate(link.label)}</a>`);
|
|
||||||
});
|
|
||||||
_push(`<!--]--></div></div>`);
|
|
||||||
} else {
|
|
||||||
_push(`<!---->`);
|
|
||||||
}
|
|
||||||
_push(`</nav>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const _sfc_setup$7 = _sfc_main$7.setup;
|
|
||||||
_sfc_main$7.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/Navbar.vue");
|
|
||||||
return _sfc_setup$7 ? _sfc_setup$7(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const __nuxt_component_0 = Object.assign(_sfc_main$7, { __name: "Navbar" });
|
|
||||||
const _sfc_main$6 = {};
|
|
||||||
function _sfc_ssrRender$1(_ctx, _push, _parent, _attrs) {
|
|
||||||
_push(`<section${ssrRenderAttrs(mergeProps({ class: "relative pt-32 pb-20 md:pt-40 md:pb-28 overflow-hidden" }, _attrs))}><div class="absolute inset-0 dot-grid opacity-[0.03]"></div><div class="absolute top-0 right-0 w-[600px] h-[600px] bg-eu-blue/[0.04] rounded-full blur-3xl -translate-y-1/2 translate-x-1/4"></div><div class="section-container relative"><div class="grid lg:grid-cols-2 gap-12 lg:gap-16 items-center"><div><div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-eu-blue-100 text-eu-blue text-xs font-semibold tracking-wide uppercase mb-6"><span class="w-1.5 h-1.5 rounded-full bg-eu-blue animate-pulse"></span> Free & Open </div><h1 class="text-display-sm md:text-display text-ink text-balance"> EU VAT Rates <span class="text-eu-blue">API</span></h1><p class="mt-4 text-subtitle text-ink-secondary font-heading"> Free. No API key. Always up-to-date. </p><p class="mt-4 text-base text-ink-muted leading-relaxed max-w-lg"> Get current VAT rates for all 27 EU member states with a single API call. No registration, no rate limits, no nonsense. </p><div class="flex flex-wrap gap-3 mt-8"><a href="#rates" class="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-eu-blue text-white font-semibold text-sm hover:bg-eu-blue-dark transition-all hover:shadow-glow"> View Rates <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3"></path></svg></a><a href="#playground" class="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg border-2 border-eu-blue/20 text-eu-blue font-semibold text-sm hover:border-eu-blue/40 hover:bg-eu-blue-100 transition-all"> Try the API <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z"></path></svg></a></div></div><div class="relative"><div class="absolute -inset-4 bg-eu-blue/[0.04] rounded-3xl blur-2xl"></div><div class="relative code-block p-6 shadow-card"><div class="flex items-center gap-3 mb-4 pb-4 border-b border-white/10"><div class="flex gap-1.5"><span class="w-3 h-3 rounded-full bg-[#ff5f57]"></span><span class="w-3 h-3 rounded-full bg-[#febc2e]"></span><span class="w-3 h-3 rounded-full bg-[#28c840]"></span></div><span class="text-xs text-[#8b949e] font-mono">GET /api/v1/rates/DE</span></div><pre class="text-[13px] leading-6"><span style="${ssrRenderStyle({ "color": "#8b949e" })}">// Response 200 OK</span>
|
|
||||||
<span style="${ssrRenderStyle({ "color": "#ff7b72" })}">{</span>
|
|
||||||
<span style="${ssrRenderStyle({ "color": "#79c0ff" })}">"country"</span>: <span style="${ssrRenderStyle({ "color": "#a5d6ff" })}">"Germany"</span>,
|
|
||||||
<span style="${ssrRenderStyle({ "color": "#79c0ff" })}">"country_code"</span>: <span style="${ssrRenderStyle({ "color": "#a5d6ff" })}">"DE"</span>,
|
|
||||||
<span style="${ssrRenderStyle({ "color": "#79c0ff" })}">"standard_rate"</span>: <span style="${ssrRenderStyle({ "color": "#79c0ff" })}">19</span>,
|
|
||||||
<span style="${ssrRenderStyle({ "color": "#79c0ff" })}">"reduced_rates"</span>: <span style="${ssrRenderStyle({ "color": "#ff7b72" })}">[</span><span style="${ssrRenderStyle({ "color": "#79c0ff" })}">7</span><span style="${ssrRenderStyle({ "color": "#ff7b72" })}">]</span>,
|
|
||||||
<span style="${ssrRenderStyle({ "color": "#79c0ff" })}">"currency"</span>: <span style="${ssrRenderStyle({ "color": "#a5d6ff" })}">"EUR"</span>
|
|
||||||
<span style="${ssrRenderStyle({ "color": "#ff7b72" })}">}</span></pre></div></div></div></div></section>`);
|
|
||||||
}
|
|
||||||
const _sfc_setup$6 = _sfc_main$6.setup;
|
|
||||||
_sfc_main$6.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/HeroSection.vue");
|
|
||||||
return _sfc_setup$6 ? _sfc_setup$6(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const __nuxt_component_1 = /* @__PURE__ */ Object.assign(_export_sfc(_sfc_main$6, [["ssrRender", _sfc_ssrRender$1]]), { __name: "HeroSection" });
|
|
||||||
const _sfc_main$5 = /* @__PURE__ */ defineComponent({
|
|
||||||
__name: "FeaturesGrid",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
setup(__props) {
|
|
||||||
const features = [
|
|
||||||
{
|
|
||||||
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-6 h-6"><circle cx="12" cy="12" r="10"/><path d="M12 6v2m0 8v2m-4.24-2.76 1.42-1.42m5.64-5.64 1.42-1.42M6 12h2m8 0h2m-2.76 4.24-1.42-1.42m-5.64-5.64L7.76 7.76"/></svg>`,
|
|
||||||
title: "All 27 EU Countries",
|
|
||||||
description: "Standard, reduced, and special VAT rates for every member state."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z"/></svg>`,
|
|
||||||
title: "No Authentication",
|
|
||||||
description: "No API keys, no sign-up. Just send a GET request."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182"/></svg>`,
|
|
||||||
title: "Always Current",
|
|
||||||
description: "Rates sourced and synced regularly from official EU data."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5"/></svg>`,
|
|
||||||
title: "Simple JSON",
|
|
||||||
description: "Clean, predictable JSON responses. Easy to integrate anywhere."
|
|
||||||
}
|
|
||||||
];
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
_push(`<section${ssrRenderAttrs(mergeProps({
|
|
||||||
id: "features",
|
|
||||||
class: "section-padding bg-surface-soft"
|
|
||||||
}, _attrs))}><div class="section-container"><div class="text-center mb-14 animate-on-scroll"><h2 class="text-title md:text-display-sm text-ink"> Built for developers </h2><p class="mt-3 text-ink-muted max-w-lg mx-auto"> A straightforward API that does one thing well — delivering EU VAT rates without friction. </p></div><div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-5"><!--[-->`);
|
|
||||||
ssrRenderList(features, (feature, i) => {
|
|
||||||
_push(`<div class="animate-on-scroll group relative bg-white rounded-2xl p-6 border border-surface-border hover:border-eu-blue/20 hover:shadow-card transition-all duration-300" style="${ssrRenderStyle({ transitionDelay: `${i * 80}ms` })}"><div class="w-10 h-10 rounded-xl bg-eu-blue-100 text-eu-blue flex items-center justify-center mb-4 group-hover:bg-eu-blue group-hover:text-white transition-colors duration-300">${feature.icon ?? ""}</div><h3 class="font-heading font-semibold text-ink mb-1.5">${ssrInterpolate(feature.title)}</h3><p class="text-sm text-ink-muted leading-relaxed">${ssrInterpolate(feature.description)}</p></div>`);
|
|
||||||
});
|
|
||||||
_push(`<!--]--></div></div></section>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const _sfc_setup$5 = _sfc_main$5.setup;
|
|
||||||
_sfc_main$5.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/FeaturesGrid.vue");
|
|
||||||
return _sfc_setup$5 ? _sfc_setup$5(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const __nuxt_component_2 = Object.assign(_sfc_main$5, { __name: "FeaturesGrid" });
|
|
||||||
const euVatRates = [
|
|
||||||
{ country: "Austria", code: "AT", flag: "🇦🇹", standard: 20, reduced: [10, 13] },
|
|
||||||
{ country: "Belgium", code: "BE", flag: "🇧🇪", standard: 21, reduced: [6, 12] },
|
|
||||||
{ country: "Bulgaria", code: "BG", flag: "🇧🇬", standard: 20, reduced: [9] },
|
|
||||||
{ country: "Croatia", code: "HR", flag: "🇭🇷", standard: 25, reduced: [5, 13] },
|
|
||||||
{ country: "Cyprus", code: "CY", flag: "🇨🇾", standard: 19, reduced: [5, 9] },
|
|
||||||
{ country: "Czech Republic", code: "CZ", flag: "🇨🇿", standard: 21, reduced: [12, 15] },
|
|
||||||
{ country: "Denmark", code: "DK", flag: "🇩🇰", standard: 25, reduced: [] },
|
|
||||||
{ country: "Estonia", code: "EE", flag: "🇪🇪", standard: 22, reduced: [9] },
|
|
||||||
{ country: "Finland", code: "FI", flag: "🇫🇮", standard: 25.5, reduced: [10, 14] },
|
|
||||||
{ country: "France", code: "FR", flag: "🇫🇷", standard: 20, reduced: [5.5, 10] },
|
|
||||||
{ country: "Germany", code: "DE", flag: "🇩🇪", standard: 19, reduced: [7] },
|
|
||||||
{ country: "Greece", code: "GR", flag: "🇬🇷", standard: 24, reduced: [6, 13] },
|
|
||||||
{ country: "Hungary", code: "HU", flag: "🇭🇺", standard: 27, reduced: [5, 18] },
|
|
||||||
{ country: "Ireland", code: "IE", flag: "🇮🇪", standard: 23, reduced: [9, 13.5] },
|
|
||||||
{ country: "Italy", code: "IT", flag: "🇮🇹", standard: 22, reduced: [5, 10] },
|
|
||||||
{ country: "Latvia", code: "LV", flag: "🇱🇻", standard: 21, reduced: [5, 12] },
|
|
||||||
{ country: "Lithuania", code: "LT", flag: "🇱🇹", standard: 21, reduced: [5, 9] },
|
|
||||||
{ country: "Luxembourg", code: "LU", flag: "🇱🇺", standard: 17, reduced: [8] },
|
|
||||||
{ country: "Malta", code: "MT", flag: "🇲🇹", standard: 18, reduced: [5, 7] },
|
|
||||||
{ country: "Netherlands", code: "NL", flag: "🇳🇱", standard: 21, reduced: [9] },
|
|
||||||
{ country: "Poland", code: "PL", flag: "🇵🇱", standard: 23, reduced: [5, 8] },
|
|
||||||
{ country: "Portugal", code: "PT", flag: "🇵🇹", standard: 23, reduced: [6, 13] },
|
|
||||||
{ country: "Romania", code: "RO", flag: "🇷🇴", standard: 19, reduced: [5, 9] },
|
|
||||||
{ country: "Slovakia", code: "SK", flag: "🇸🇰", standard: 23, reduced: [5, 10] },
|
|
||||||
{ country: "Slovenia", code: "SI", flag: "🇸🇮", standard: 22, reduced: [5, 9.5] },
|
|
||||||
{ country: "Spain", code: "ES", flag: "🇪🇸", standard: 21, reduced: [4, 10] },
|
|
||||||
{ country: "Sweden", code: "SE", flag: "🇸🇪", standard: 25, reduced: [6, 12] }
|
|
||||||
];
|
|
||||||
function useVatRates() {
|
|
||||||
const rates = readonly(ref(euVatRates));
|
|
||||||
function getRateByCode(code) {
|
|
||||||
return euVatRates.find((r) => r.code === code.toUpperCase());
|
|
||||||
}
|
|
||||||
function formatApiResponse(rate) {
|
|
||||||
return {
|
|
||||||
country: rate.country,
|
|
||||||
country_code: rate.code,
|
|
||||||
standard_rate: rate.standard,
|
|
||||||
reduced_rates: rate.reduced,
|
|
||||||
currency: "EUR"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function formatAllRatesResponse() {
|
|
||||||
return euVatRates.map(formatApiResponse);
|
|
||||||
}
|
|
||||||
return { rates, getRateByCode, formatApiResponse, formatAllRatesResponse };
|
|
||||||
}
|
|
||||||
const _sfc_main$4 = /* @__PURE__ */ defineComponent({
|
|
||||||
__name: "VatRateTable",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
setup(__props) {
|
|
||||||
const rates = ref([]);
|
|
||||||
const search = ref("");
|
|
||||||
const filtered = computed(() => {
|
|
||||||
const q = search.value.toLowerCase().trim();
|
|
||||||
if (!q) return rates.value;
|
|
||||||
return rates.value.filter(
|
|
||||||
(r) => r.country.toLowerCase().includes(q) || r.code.toLowerCase().includes(q)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
_push(`<section${ssrRenderAttrs(mergeProps({
|
|
||||||
id: "rates",
|
|
||||||
class: "section-padding"
|
|
||||||
}, _attrs))}><div class="section-container"><div class="text-center mb-10 animate-on-scroll"><h2 class="text-title md:text-display-sm text-ink"> VAT Rates across the EU </h2><p class="mt-3 text-ink-muted max-w-lg mx-auto"> Current standard and reduced rates for all 27 member states. </p></div><div class="max-w-sm mx-auto mb-8 animate-on-scroll"><div class="relative"><svg class="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-ink-faint" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"></path></svg><input${ssrRenderAttr("value", unref(search))} type="text" placeholder="Search countries..." class="w-full pl-10 pr-4 py-2.5 rounded-xl border border-surface-border bg-white text-sm text-ink placeholder:text-ink-faint focus:outline-none focus:ring-2 focus:ring-eu-blue/20 focus:border-eu-blue/40 transition-all"></div></div><div class="animate-on-scroll overflow-hidden rounded-2xl border border-surface-border bg-white shadow-soft"><div class="overflow-x-auto"><table class="w-full text-sm"><thead><tr class="bg-surface-soft border-b border-surface-border"><th class="text-left font-semibold text-ink-secondary px-5 py-3.5 font-heading">Country</th><th class="text-left font-semibold text-ink-secondary px-5 py-3.5 font-heading w-20">Code</th><th class="text-right font-semibold text-ink-secondary px-5 py-3.5 font-heading w-32">Standard</th><th class="text-right font-semibold text-ink-secondary px-5 py-3.5 font-heading w-40">Reduced</th></tr></thead><tbody><!--[-->`);
|
|
||||||
ssrRenderList(unref(filtered), (rate) => {
|
|
||||||
_push(`<tr class="border-b border-surface-border/60 last:border-0 hover:bg-eu-blue-100/40 transition-colors"><td class="px-5 py-3"><span class="inline-flex items-center gap-2.5"><span class="text-lg leading-none">${ssrInterpolate(rate.flag)}</span><span class="font-medium text-ink">${ssrInterpolate(rate.country)}</span></span></td><td class="px-5 py-3"><span class="inline-flex px-2 py-0.5 rounded bg-surface-muted font-mono text-xs font-medium text-ink-secondary">${ssrInterpolate(rate.code)}</span></td><td class="px-5 py-3 text-right"><span class="font-semibold text-ink tabular-nums">${ssrInterpolate(rate.standard)}%</span></td><td class="px-5 py-3 text-right">`);
|
|
||||||
if (rate.reduced.length) {
|
|
||||||
_push(`<span class="text-ink-muted tabular-nums">${ssrInterpolate(rate.reduced.map((r) => `${r}%`).join(", "))}</span>`);
|
|
||||||
} else {
|
|
||||||
_push(`<span class="text-ink-faint">—</span>`);
|
|
||||||
}
|
|
||||||
_push(`</td></tr>`);
|
|
||||||
});
|
|
||||||
_push(`<!--]-->`);
|
|
||||||
if (!unref(filtered).length) {
|
|
||||||
_push(`<tr><td colspan="4" class="px-5 py-10 text-center text-ink-muted"> No countries matching "${ssrInterpolate(unref(search))}" </td></tr>`);
|
|
||||||
} else {
|
|
||||||
_push(`<!---->`);
|
|
||||||
}
|
|
||||||
_push(`</tbody></table></div></div><p class="text-center text-xs text-ink-faint mt-4"> Data updated regularly from official EU sources. </p></div></section>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const _sfc_setup$4 = _sfc_main$4.setup;
|
|
||||||
_sfc_main$4.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/VatRateTable.vue");
|
|
||||||
return _sfc_setup$4 ? _sfc_setup$4(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const __nuxt_component_3 = Object.assign(_sfc_main$4, { __name: "VatRateTable" });
|
|
||||||
const _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
||||||
__name: "ApiPlayground",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
setup(__props) {
|
|
||||||
const { rates } = useVatRates();
|
|
||||||
const selectedCode = ref("DE");
|
|
||||||
const response = ref("");
|
|
||||||
const statusCode = ref(null);
|
|
||||||
const isLoading = ref(false);
|
|
||||||
const showAllRates = ref(false);
|
|
||||||
const endpointUrl = computed(
|
|
||||||
() => showAllRates.value ? "https://vat-api.eu/api/v1/rates" : `https://vat-api.eu/api/v1/rates/${selectedCode.value}`
|
|
||||||
);
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
_push(`<section${ssrRenderAttrs(mergeProps({
|
|
||||||
id: "playground",
|
|
||||||
class: "section-padding bg-surface-soft"
|
|
||||||
}, _attrs))}><div class="section-container"><div class="text-center mb-10 animate-on-scroll"><h2 class="text-title md:text-display-sm text-ink"> Try it out </h2><p class="mt-3 text-ink-muted max-w-lg mx-auto"> Build your request, send it, and see the response. </p></div><div class="animate-on-scroll max-w-3xl mx-auto"><div class="bg-white rounded-2xl border border-surface-border shadow-card overflow-hidden"><div class="p-5 border-b border-surface-border"><div class="flex flex-wrap gap-3 items-end"><div class="flex-shrink-0"><label class="block text-xs font-medium text-ink-muted mb-1.5">Endpoint</label><div class="flex rounded-lg border border-surface-border overflow-hidden text-sm"><button class="${ssrRenderClass([!unref(showAllRates) ? "bg-eu-blue text-white" : "bg-white text-ink-secondary hover:bg-surface-soft", "px-3 py-2 font-medium transition-colors"])}"> Single </button><button class="${ssrRenderClass([unref(showAllRates) ? "bg-eu-blue text-white" : "bg-white text-ink-secondary hover:bg-surface-soft", "px-3 py-2 font-medium transition-colors"])}"> All Rates </button></div></div>`);
|
|
||||||
if (!unref(showAllRates)) {
|
|
||||||
_push(`<div class="flex-shrink-0"><label class="block text-xs font-medium text-ink-muted mb-1.5">Country</label><select class="px-3 py-2 rounded-lg border border-surface-border bg-white text-sm text-ink focus:outline-none focus:ring-2 focus:ring-eu-blue/20 focus:border-eu-blue/40 transition-all"><!--[-->`);
|
|
||||||
ssrRenderList(unref(rates), (rate) => {
|
|
||||||
_push(`<option${ssrRenderAttr("value", rate.code)}${ssrIncludeBooleanAttr(Array.isArray(unref(selectedCode)) ? ssrLooseContain(unref(selectedCode), rate.code) : ssrLooseEqual(unref(selectedCode), rate.code)) ? " selected" : ""}>${ssrInterpolate(rate.flag)} ${ssrInterpolate(rate.country)} (${ssrInterpolate(rate.code)}) </option>`);
|
|
||||||
});
|
|
||||||
_push(`<!--]--></select></div>`);
|
|
||||||
} else {
|
|
||||||
_push(`<!---->`);
|
|
||||||
}
|
|
||||||
_push(`<button class="px-5 py-2 rounded-lg bg-eu-gold text-eu-blue-dark font-semibold text-sm hover:bg-eu-gold-dark transition-colors flex items-center gap-2"${ssrIncludeBooleanAttr(unref(isLoading)) ? " disabled" : ""}>`);
|
|
||||||
if (unref(isLoading)) {
|
|
||||||
_push(`<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg>`);
|
|
||||||
} else {
|
|
||||||
_push(`<!---->`);
|
|
||||||
}
|
|
||||||
_push(`<span>${ssrInterpolate(unref(isLoading) ? "Sending..." : "Send Request")}</span></button></div><div class="mt-3.5 flex items-center gap-2 px-3.5 py-2.5 rounded-lg bg-surface-muted font-mono text-sm overflow-x-auto"><span class="flex-shrink-0 text-xs font-semibold px-1.5 py-0.5 rounded bg-green-100 text-green-700">GET</span><span class="text-ink-secondary">${ssrInterpolate(unref(endpointUrl))}</span></div></div><div class="relative"><div class="flex items-center justify-between px-5 py-3 bg-[#0d1117] border-b border-white/5"><span class="text-xs text-[#8b949e] font-mono">Response</span>`);
|
|
||||||
if (unref(statusCode) === 200) {
|
|
||||||
_push(`<span class="text-xs font-mono px-2 py-0.5 rounded bg-green-500/20 text-green-400">200 OK</span>`);
|
|
||||||
} else if (unref(statusCode)) {
|
|
||||||
_push(`<span class="text-xs font-mono px-2 py-0.5 rounded bg-red-500/20 text-red-400">${ssrInterpolate(unref(statusCode))} Error</span>`);
|
|
||||||
} else {
|
|
||||||
_push(`<!---->`);
|
|
||||||
}
|
|
||||||
_push(`</div><div class="bg-[#0d1117] text-[#e6edf3] font-mono text-sm leading-relaxed p-5 max-h-80 overflow-y-auto">`);
|
|
||||||
if (unref(response)) {
|
|
||||||
_push(`<pre class="text-[13px] leading-6">${unref(response) ?? ""}</pre>`);
|
|
||||||
} else if (unref(isLoading)) {
|
|
||||||
_push(`<p class="text-[#8b949e] text-sm">Loading...</p>`);
|
|
||||||
} else {
|
|
||||||
_push(`<p class="text-[#8b949e] text-sm">Click "Send Request" to see a response.</p>`);
|
|
||||||
}
|
|
||||||
_push(`</div></div></div></div></div></section>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const _sfc_setup$3 = _sfc_main$3.setup;
|
|
||||||
_sfc_main$3.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/ApiPlayground.vue");
|
|
||||||
return _sfc_setup$3 ? _sfc_setup$3(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const __nuxt_component_4 = Object.assign(_sfc_main$3, { __name: "ApiPlayground" });
|
|
||||||
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
||||||
__name: "CodeExamples",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
setup(__props) {
|
|
||||||
const tabs = ["cURL", "JavaScript", "Python"];
|
|
||||||
const activeTab = ref("cURL");
|
|
||||||
const code = {
|
|
||||||
cURL: `# Get all EU VAT rates
|
|
||||||
curl https://vat-api.eu/api/v1/rates
|
|
||||||
|
|
||||||
# Get rates for a specific country
|
|
||||||
curl https://vat-api.eu/api/v1/rates/DE`,
|
|
||||||
JavaScript: `const response = await fetch('https://vat-api.eu/api/v1/rates/DE');
|
|
||||||
const data = await response.json();
|
|
||||||
console.log(data.standard_rate); // 19`,
|
|
||||||
Python: `import requests
|
|
||||||
|
|
||||||
response = requests.get('https://vat-api.eu/api/v1/rates/DE')
|
|
||||||
data = response.json()
|
|
||||||
print(data['standard_rate']) # 19`
|
|
||||||
};
|
|
||||||
const highlighted = {
|
|
||||||
cURL: highlightBash(code.cURL),
|
|
||||||
JavaScript: highlightJS(code.JavaScript),
|
|
||||||
Python: highlightPython(code.Python)
|
|
||||||
};
|
|
||||||
function highlightBash(src) {
|
|
||||||
return src.replace(/(#.*)/g, '<span style="color:#8b949e">$1</span>').replace(/(curl)\s/g, '<span style="color:#ff7b72">$1</span> ').replace(/(https?:\/\/[^\s]+)/g, '<span style="color:#a5d6ff">$1</span>');
|
|
||||||
}
|
|
||||||
function highlightJS(src) {
|
|
||||||
return src.replace(/(\/\/\s*\d+)/g, '<span style="color:#8b949e">$1</span>').replace(/(const|await)\s/g, '<span style="color:#ff7b72">$1</span> ').replace(/(fetch|json|log)\(/g, '<span style="color:#d2a8ff">$1</span>(').replace(/('https?:\/\/[^']*')/g, '<span style="color:#a5d6ff">$1</span>').replace(/(\.standard_rate)/g, '<span style="color:#79c0ff">$1</span>').replace(/(console)\./g, '<span style="color:#79c0ff">$1</span>.').replace(/(response)\./g, '<span style="color:#79c0ff">$1</span>.');
|
|
||||||
}
|
|
||||||
function highlightPython(src) {
|
|
||||||
return src.replace(/(#\s*\d+)/g, '<span style="color:#8b949e">$1</span>').replace(/(import|from)\s/g, '<span style="color:#ff7b72">$1</span> ').replace(/(requests)/g, '<span style="color:#79c0ff">$1</span>').replace(/('https?:\/\/[^']*')/g, '<span style="color:#a5d6ff">$1</span>').replace(/(\['standard_rate'\])/g, '<span style="color:#79c0ff">$1</span>').replace(/(print|get)\(/g, '<span style="color:#d2a8ff">$1</span>(').replace(/(response)\./g, '<span style="color:#79c0ff">$1</span>.');
|
|
||||||
}
|
|
||||||
const copied = ref(false);
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
_push(`<section${ssrRenderAttrs(mergeProps({
|
|
||||||
id: "examples",
|
|
||||||
class: "section-padding"
|
|
||||||
}, _attrs))}><div class="section-container"><div class="text-center mb-10 animate-on-scroll"><h2 class="text-title md:text-display-sm text-ink"> Quick start </h2><p class="mt-3 text-ink-muted max-w-lg mx-auto"> Integrate EU VAT rates into your project in seconds. </p></div><div class="animate-on-scroll max-w-2xl mx-auto"><div class="rounded-2xl border border-surface-border overflow-hidden shadow-card bg-white"><div class="flex items-center justify-between border-b border-surface-border bg-surface-soft px-1.5"><div class="flex"><!--[-->`);
|
|
||||||
ssrRenderList(tabs, (tab) => {
|
|
||||||
_push(`<button class="${ssrRenderClass([unref(activeTab) === tab ? "text-eu-blue" : "text-ink-muted hover:text-ink-secondary", "px-4 py-3 text-sm font-medium transition-colors relative"])}">${ssrInterpolate(tab)} `);
|
|
||||||
if (unref(activeTab) === tab) {
|
|
||||||
_push(`<span class="absolute bottom-0 left-2 right-2 h-0.5 bg-eu-blue rounded-full"></span>`);
|
|
||||||
} else {
|
|
||||||
_push(`<!---->`);
|
|
||||||
}
|
|
||||||
_push(`</button>`);
|
|
||||||
});
|
|
||||||
_push(`<!--]--></div><button class="flex items-center gap-1.5 px-3 py-1.5 mr-1.5 rounded-lg text-xs font-medium text-ink-muted hover:text-ink-secondary hover:bg-surface-muted transition-colors">`);
|
|
||||||
if (!unref(copied)) {
|
|
||||||
_push(`<svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9.75a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184"></path></svg>`);
|
|
||||||
} else {
|
|
||||||
_push(`<svg class="w-3.5 h-3.5 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"></path></svg>`);
|
|
||||||
}
|
|
||||||
_push(` ${ssrInterpolate(unref(copied) ? "Copied!" : "Copy")}</button></div><div class="code-block p-5"><pre class="text-[13px] leading-6">${highlighted[unref(activeTab)] ?? ""}</pre></div></div></div></div></section>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const _sfc_setup$2 = _sfc_main$2.setup;
|
|
||||||
_sfc_main$2.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/CodeExamples.vue");
|
|
||||||
return _sfc_setup$2 ? _sfc_setup$2(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const __nuxt_component_5 = Object.assign(_sfc_main$2, { __name: "CodeExamples" });
|
|
||||||
const _sfc_main$1 = {};
|
|
||||||
function _sfc_ssrRender(_ctx, _push, _parent, _attrs) {
|
|
||||||
_push(`<footer${ssrRenderAttrs(mergeProps({ class: "border-t border-surface-border bg-white" }, _attrs))}><div class="section-container py-10"><div class="flex flex-col md:flex-row items-center justify-between gap-6 text-sm text-ink-muted"><div class="flex items-center gap-2"><span class="inline-flex items-center justify-center w-6 h-6 rounded-md bg-eu-blue text-white font-heading font-bold text-[10px]">V</span><span class="font-heading font-semibold text-ink"> vat-api<span class="text-eu-blue">.eu</span></span><span class="hidden sm:inline text-ink-faint">— Free EU VAT Rate API</span></div><p class="text-ink-faint text-xs"> Free EU VAT rate data </p><div class="flex items-center gap-5"><a href="#" class="hover:text-eu-blue transition-colors">Imprint</a><a href="#" class="hover:text-eu-blue transition-colors">Privacy</a><a href="#" class="hover:text-eu-blue transition-colors flex items-center gap-1.5"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path></svg> GitHub </a></div></div></div></footer>`);
|
|
||||||
}
|
|
||||||
const _sfc_setup$1 = _sfc_main$1.setup;
|
|
||||||
_sfc_main$1.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/FooterSection.vue");
|
|
||||||
return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const __nuxt_component_6 = /* @__PURE__ */ Object.assign(_export_sfc(_sfc_main$1, [["ssrRender", _sfc_ssrRender]]), { __name: "FooterSection" });
|
|
||||||
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
||||||
__name: "index",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
setup(__props) {
|
|
||||||
useHead({
|
|
||||||
title: "vat-api.eu — Free EU VAT Rates API",
|
|
||||||
meta: [
|
|
||||||
{
|
|
||||||
name: "description",
|
|
||||||
content: "Get current VAT rates for all 27 EU member states with a free, open REST API. No API key required."
|
|
||||||
},
|
|
||||||
{ property: "og:title", content: "vat-api.eu — Free EU VAT Rates API" },
|
|
||||||
{
|
|
||||||
property: "og:description",
|
|
||||||
content: "Get current VAT rates for all 27 EU member states with a free, open REST API. No API key required."
|
|
||||||
},
|
|
||||||
{ property: "og:type", content: "website" },
|
|
||||||
{ property: "og:url", content: "https://vat-api.eu" },
|
|
||||||
{ name: "twitter:card", content: "summary_large_image" },
|
|
||||||
{ name: "twitter:title", content: "vat-api.eu — Free EU VAT Rates API" },
|
|
||||||
{
|
|
||||||
name: "twitter:description",
|
|
||||||
content: "Get current VAT rates for all 27 EU member states with a free, open REST API. No API key required."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
const _component_Navbar = __nuxt_component_0;
|
|
||||||
const _component_HeroSection = __nuxt_component_1;
|
|
||||||
const _component_FeaturesGrid = __nuxt_component_2;
|
|
||||||
const _component_VatRateTable = __nuxt_component_3;
|
|
||||||
const _component_ApiPlayground = __nuxt_component_4;
|
|
||||||
const _component_CodeExamples = __nuxt_component_5;
|
|
||||||
const _component_FooterSection = __nuxt_component_6;
|
|
||||||
_push(`<div${ssrRenderAttrs(mergeProps({ class: "min-h-screen" }, _attrs))}>`);
|
|
||||||
_push(ssrRenderComponent(_component_Navbar, null, null, _parent));
|
|
||||||
_push(`<main>`);
|
|
||||||
_push(ssrRenderComponent(_component_HeroSection, null, null, _parent));
|
|
||||||
_push(ssrRenderComponent(_component_FeaturesGrid, null, null, _parent));
|
|
||||||
_push(ssrRenderComponent(_component_VatRateTable, null, null, _parent));
|
|
||||||
_push(ssrRenderComponent(_component_ApiPlayground, null, null, _parent));
|
|
||||||
_push(ssrRenderComponent(_component_CodeExamples, null, null, _parent));
|
|
||||||
_push(`</main>`);
|
|
||||||
_push(ssrRenderComponent(_component_FooterSection, null, null, _parent));
|
|
||||||
_push(`</div>`);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const _sfc_setup = _sfc_main.setup;
|
|
||||||
_sfc_main.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("pages/index.vue");
|
|
||||||
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
export {
|
|
||||||
_sfc_main as default
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=index-BDcJu3_l.js.map
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/index-BDcJu3_l.js.map
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/index-BDcJu3_l.js.map
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/index-BDcJu3_l.js.map.json
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/_nuxt/index-BDcJu3_l.js.map.json
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/.cache/nuxt/.nuxt/dist/server/client.manifest.mjs
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/client.manifest.mjs
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/.cache/nuxt/.nuxt/dist/server/client.precomputed.mjs
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/client.precomputed.mjs
generated
vendored
File diff suppressed because one or more lines are too long
954
node_modules/.cache/nuxt/.nuxt/dist/server/server.mjs
generated
vendored
954
node_modules/.cache/nuxt/.nuxt/dist/server/server.mjs
generated
vendored
@@ -1,954 +0,0 @@
|
|||||||
import { hasInjectionContext, getCurrentInstance, shallowReactive, reactive, effectScope, getCurrentScope, inject, toRef, shallowRef, isReadonly, isRef, isShallow, isReactive, toRaw, defineComponent, provide, h, ref, Suspense, Fragment, useSSRContext, defineAsyncComponent, unref, mergeProps, onErrorCaptured, onServerPrefetch, createVNode, resolveDynamicComponent, createApp } from "vue";
|
|
||||||
import { $fetch } from "/home/bennet/source/vat-api/node_modules/ofetch/dist/node.mjs";
|
|
||||||
import { baseURL } from "#internal/nuxt/paths";
|
|
||||||
import { createHooks } from "/home/bennet/source/vat-api/node_modules/hookable/dist/index.mjs";
|
|
||||||
import { getContext, executeAsync } from "/home/bennet/source/vat-api/node_modules/unctx/dist/index.mjs";
|
|
||||||
import { sanitizeStatusCode, createError as createError$1 } from "/home/bennet/source/vat-api/node_modules/h3/dist/index.mjs";
|
|
||||||
import { START_LOCATION, createMemoryHistory, createRouter, RouterView } from "vue-router";
|
|
||||||
import { defu } from "/home/bennet/source/vat-api/node_modules/defu/dist/defu.mjs";
|
|
||||||
import { hasProtocol, joinURL, withQuery, parseURL, encodePath, decodePath, isScriptProtocol } from "/home/bennet/source/vat-api/node_modules/ufo/dist/index.mjs";
|
|
||||||
import { ssrRenderComponent, ssrRenderSuspense, ssrRenderVNode } from "vue/server-renderer";
|
|
||||||
if (!globalThis.$fetch) {
|
|
||||||
globalThis.$fetch = $fetch.create({
|
|
||||||
baseURL: baseURL()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!("global" in globalThis)) {
|
|
||||||
globalThis.global = globalThis;
|
|
||||||
}
|
|
||||||
const nuxtLinkDefaults = { "componentName": "NuxtLink" };
|
|
||||||
const appId = "nuxt-app";
|
|
||||||
function getNuxtAppCtx(id = appId) {
|
|
||||||
return getContext(id, {
|
|
||||||
asyncContext: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const NuxtPluginIndicator = "__nuxt_plugin";
|
|
||||||
function createNuxtApp(options) {
|
|
||||||
let hydratingCount = 0;
|
|
||||||
const nuxtApp = {
|
|
||||||
_id: options.id || appId || "nuxt-app",
|
|
||||||
_scope: effectScope(),
|
|
||||||
provide: void 0,
|
|
||||||
versions: {
|
|
||||||
get nuxt() {
|
|
||||||
return "4.3.1";
|
|
||||||
},
|
|
||||||
get vue() {
|
|
||||||
return nuxtApp.vueApp.version;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
payload: shallowReactive({
|
|
||||||
...options.ssrContext?.payload || {},
|
|
||||||
data: shallowReactive({}),
|
|
||||||
state: reactive({}),
|
|
||||||
once: /* @__PURE__ */ new Set(),
|
|
||||||
_errors: shallowReactive({})
|
|
||||||
}),
|
|
||||||
static: {
|
|
||||||
data: {}
|
|
||||||
},
|
|
||||||
runWithContext(fn) {
|
|
||||||
if (nuxtApp._scope.active && !getCurrentScope()) {
|
|
||||||
return nuxtApp._scope.run(() => callWithNuxt(nuxtApp, fn));
|
|
||||||
}
|
|
||||||
return callWithNuxt(nuxtApp, fn);
|
|
||||||
},
|
|
||||||
isHydrating: false,
|
|
||||||
deferHydration() {
|
|
||||||
if (!nuxtApp.isHydrating) {
|
|
||||||
return () => {
|
|
||||||
};
|
|
||||||
}
|
|
||||||
hydratingCount++;
|
|
||||||
let called = false;
|
|
||||||
return () => {
|
|
||||||
if (called) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
called = true;
|
|
||||||
hydratingCount--;
|
|
||||||
if (hydratingCount === 0) {
|
|
||||||
nuxtApp.isHydrating = false;
|
|
||||||
return nuxtApp.callHook("app:suspense:resolve");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
_asyncDataPromises: {},
|
|
||||||
_asyncData: shallowReactive({}),
|
|
||||||
_payloadRevivers: {},
|
|
||||||
...options
|
|
||||||
};
|
|
||||||
{
|
|
||||||
nuxtApp.payload.serverRendered = true;
|
|
||||||
}
|
|
||||||
if (nuxtApp.ssrContext) {
|
|
||||||
nuxtApp.payload.path = nuxtApp.ssrContext.url;
|
|
||||||
nuxtApp.ssrContext.nuxt = nuxtApp;
|
|
||||||
nuxtApp.ssrContext.payload = nuxtApp.payload;
|
|
||||||
nuxtApp.ssrContext.config = {
|
|
||||||
public: nuxtApp.ssrContext.runtimeConfig.public,
|
|
||||||
app: nuxtApp.ssrContext.runtimeConfig.app
|
|
||||||
};
|
|
||||||
}
|
|
||||||
nuxtApp.hooks = createHooks();
|
|
||||||
nuxtApp.hook = nuxtApp.hooks.hook;
|
|
||||||
{
|
|
||||||
const contextCaller = async function(hooks, args) {
|
|
||||||
for (const hook of hooks) {
|
|
||||||
await nuxtApp.runWithContext(() => hook(...args));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
nuxtApp.hooks.callHook = (name, ...args) => nuxtApp.hooks.callHookWith(contextCaller, name, ...args);
|
|
||||||
}
|
|
||||||
nuxtApp.callHook = nuxtApp.hooks.callHook;
|
|
||||||
nuxtApp.provide = (name, value) => {
|
|
||||||
const $name = "$" + name;
|
|
||||||
defineGetter(nuxtApp, $name, value);
|
|
||||||
defineGetter(nuxtApp.vueApp.config.globalProperties, $name, value);
|
|
||||||
};
|
|
||||||
defineGetter(nuxtApp.vueApp, "$nuxt", nuxtApp);
|
|
||||||
defineGetter(nuxtApp.vueApp.config.globalProperties, "$nuxt", nuxtApp);
|
|
||||||
const runtimeConfig = options.ssrContext.runtimeConfig;
|
|
||||||
nuxtApp.provide("config", runtimeConfig);
|
|
||||||
return nuxtApp;
|
|
||||||
}
|
|
||||||
function registerPluginHooks(nuxtApp, plugin2) {
|
|
||||||
if (plugin2.hooks) {
|
|
||||||
nuxtApp.hooks.addHooks(plugin2.hooks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function applyPlugin(nuxtApp, plugin2) {
|
|
||||||
if (typeof plugin2 === "function") {
|
|
||||||
const { provide: provide2 } = await nuxtApp.runWithContext(() => plugin2(nuxtApp)) || {};
|
|
||||||
if (provide2 && typeof provide2 === "object") {
|
|
||||||
for (const key in provide2) {
|
|
||||||
nuxtApp.provide(key, provide2[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function applyPlugins(nuxtApp, plugins2) {
|
|
||||||
const resolvedPlugins = /* @__PURE__ */ new Set();
|
|
||||||
const unresolvedPlugins = [];
|
|
||||||
const parallels = [];
|
|
||||||
let error = void 0;
|
|
||||||
let promiseDepth = 0;
|
|
||||||
async function executePlugin(plugin2) {
|
|
||||||
const unresolvedPluginsForThisPlugin = plugin2.dependsOn?.filter((name) => plugins2.some((p) => p._name === name) && !resolvedPlugins.has(name)) ?? [];
|
|
||||||
if (unresolvedPluginsForThisPlugin.length > 0) {
|
|
||||||
unresolvedPlugins.push([new Set(unresolvedPluginsForThisPlugin), plugin2]);
|
|
||||||
} else {
|
|
||||||
const promise = applyPlugin(nuxtApp, plugin2).then(async () => {
|
|
||||||
if (plugin2._name) {
|
|
||||||
resolvedPlugins.add(plugin2._name);
|
|
||||||
await Promise.all(unresolvedPlugins.map(async ([dependsOn, unexecutedPlugin]) => {
|
|
||||||
if (dependsOn.has(plugin2._name)) {
|
|
||||||
dependsOn.delete(plugin2._name);
|
|
||||||
if (dependsOn.size === 0) {
|
|
||||||
promiseDepth++;
|
|
||||||
await executePlugin(unexecutedPlugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}).catch((e) => {
|
|
||||||
if (!plugin2.parallel && !nuxtApp.payload.error) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
error ||= e;
|
|
||||||
});
|
|
||||||
if (plugin2.parallel) {
|
|
||||||
parallels.push(promise);
|
|
||||||
} else {
|
|
||||||
await promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const plugin2 of plugins2) {
|
|
||||||
if (nuxtApp.ssrContext?.islandContext && plugin2.env?.islands === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
registerPluginHooks(nuxtApp, plugin2);
|
|
||||||
}
|
|
||||||
for (const plugin2 of plugins2) {
|
|
||||||
if (nuxtApp.ssrContext?.islandContext && plugin2.env?.islands === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await executePlugin(plugin2);
|
|
||||||
}
|
|
||||||
await Promise.all(parallels);
|
|
||||||
if (promiseDepth) {
|
|
||||||
for (let i = 0; i < promiseDepth; i++) {
|
|
||||||
await Promise.all(parallels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
throw nuxtApp.payload.error || error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// @__NO_SIDE_EFFECTS__
|
|
||||||
function defineNuxtPlugin(plugin2) {
|
|
||||||
if (typeof plugin2 === "function") {
|
|
||||||
return plugin2;
|
|
||||||
}
|
|
||||||
const _name = plugin2._name || plugin2.name;
|
|
||||||
delete plugin2.name;
|
|
||||||
return Object.assign(plugin2.setup || (() => {
|
|
||||||
}), plugin2, { [NuxtPluginIndicator]: true, _name });
|
|
||||||
}
|
|
||||||
function callWithNuxt(nuxt, setup, args) {
|
|
||||||
const fn = () => setup();
|
|
||||||
const nuxtAppCtx = getNuxtAppCtx(nuxt._id);
|
|
||||||
{
|
|
||||||
return nuxt.vueApp.runWithContext(() => nuxtAppCtx.callAsync(nuxt, fn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function tryUseNuxtApp(id) {
|
|
||||||
let nuxtAppInstance;
|
|
||||||
if (hasInjectionContext()) {
|
|
||||||
nuxtAppInstance = getCurrentInstance()?.appContext.app.$nuxt;
|
|
||||||
}
|
|
||||||
nuxtAppInstance ||= getNuxtAppCtx(id).tryUse();
|
|
||||||
return nuxtAppInstance || null;
|
|
||||||
}
|
|
||||||
function useNuxtApp(id) {
|
|
||||||
const nuxtAppInstance = tryUseNuxtApp(id);
|
|
||||||
if (!nuxtAppInstance) {
|
|
||||||
{
|
|
||||||
throw new Error("[nuxt] instance unavailable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nuxtAppInstance;
|
|
||||||
}
|
|
||||||
// @__NO_SIDE_EFFECTS__
|
|
||||||
function useRuntimeConfig(_event) {
|
|
||||||
return useNuxtApp().$config;
|
|
||||||
}
|
|
||||||
function defineGetter(obj, key, val) {
|
|
||||||
Object.defineProperty(obj, key, { get: () => val });
|
|
||||||
}
|
|
||||||
const LayoutMetaSymbol = /* @__PURE__ */ Symbol("layout-meta");
|
|
||||||
const PageRouteSymbol = /* @__PURE__ */ Symbol("route");
|
|
||||||
import.meta.url.replace(/\/app\/.*$/, "/");
|
|
||||||
const useRouter = () => {
|
|
||||||
return useNuxtApp()?.$router;
|
|
||||||
};
|
|
||||||
const useRoute = () => {
|
|
||||||
if (hasInjectionContext()) {
|
|
||||||
return inject(PageRouteSymbol, useNuxtApp()._route);
|
|
||||||
}
|
|
||||||
return useNuxtApp()._route;
|
|
||||||
};
|
|
||||||
// @__NO_SIDE_EFFECTS__
|
|
||||||
function defineNuxtRouteMiddleware(middleware) {
|
|
||||||
return middleware;
|
|
||||||
}
|
|
||||||
const isProcessingMiddleware = () => {
|
|
||||||
try {
|
|
||||||
if (useNuxtApp()._processingMiddleware) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
const URL_QUOTE_RE = /"/g;
|
|
||||||
const navigateTo = (to, options) => {
|
|
||||||
to ||= "/";
|
|
||||||
const toPath = typeof to === "string" ? to : "path" in to ? resolveRouteObject(to) : useRouter().resolve(to).href;
|
|
||||||
const isExternalHost = hasProtocol(toPath, { acceptRelative: true });
|
|
||||||
const isExternal = options?.external || isExternalHost;
|
|
||||||
if (isExternal) {
|
|
||||||
if (!options?.external) {
|
|
||||||
throw new Error("Navigating to an external URL is not allowed by default. Use `navigateTo(url, { external: true })`.");
|
|
||||||
}
|
|
||||||
const { protocol } = new URL(toPath, "http://localhost");
|
|
||||||
if (protocol && isScriptProtocol(protocol)) {
|
|
||||||
throw new Error(`Cannot navigate to a URL with '${protocol}' protocol.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const inMiddleware = isProcessingMiddleware();
|
|
||||||
const router = useRouter();
|
|
||||||
const nuxtApp = useNuxtApp();
|
|
||||||
{
|
|
||||||
if (nuxtApp.ssrContext) {
|
|
||||||
const fullPath = typeof to === "string" || isExternal ? toPath : router.resolve(to).fullPath || "/";
|
|
||||||
const location2 = isExternal ? toPath : joinURL((/* @__PURE__ */ useRuntimeConfig()).app.baseURL, fullPath);
|
|
||||||
const redirect = async function(response) {
|
|
||||||
await nuxtApp.callHook("app:redirected");
|
|
||||||
const encodedLoc = location2.replace(URL_QUOTE_RE, "%22");
|
|
||||||
const encodedHeader = encodeURL(location2, isExternalHost);
|
|
||||||
nuxtApp.ssrContext["~renderResponse"] = {
|
|
||||||
statusCode: sanitizeStatusCode(options?.redirectCode || 302, 302),
|
|
||||||
body: `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`,
|
|
||||||
headers: { location: encodedHeader }
|
|
||||||
};
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
if (!isExternal && inMiddleware) {
|
|
||||||
router.afterEach((final) => final.fullPath === fullPath ? redirect(false) : void 0);
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
return redirect(!inMiddleware ? void 0 : (
|
|
||||||
/* abort route navigation */
|
|
||||||
false
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isExternal) {
|
|
||||||
nuxtApp._scope.stop();
|
|
||||||
if (options?.replace) {
|
|
||||||
(void 0).replace(toPath);
|
|
||||||
} else {
|
|
||||||
(void 0).href = toPath;
|
|
||||||
}
|
|
||||||
if (inMiddleware) {
|
|
||||||
if (!nuxtApp.isHydrating) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return new Promise(() => {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
const encodedTo = typeof to === "string" ? encodeRoutePath(to) : to;
|
|
||||||
return options?.replace ? router.replace(encodedTo) : router.push(encodedTo);
|
|
||||||
};
|
|
||||||
function resolveRouteObject(to) {
|
|
||||||
return withQuery(to.path || "", to.query || {}) + (to.hash || "");
|
|
||||||
}
|
|
||||||
function encodeURL(location2, isExternalHost = false) {
|
|
||||||
const url = new URL(location2, "http://localhost");
|
|
||||||
if (!isExternalHost) {
|
|
||||||
return url.pathname + url.search + url.hash;
|
|
||||||
}
|
|
||||||
if (location2.startsWith("//")) {
|
|
||||||
return url.toString().replace(url.protocol, "");
|
|
||||||
}
|
|
||||||
return url.toString();
|
|
||||||
}
|
|
||||||
function encodeRoutePath(url) {
|
|
||||||
const parsed = parseURL(url);
|
|
||||||
return encodePath(decodePath(parsed.pathname)) + parsed.search + parsed.hash;
|
|
||||||
}
|
|
||||||
const NUXT_ERROR_SIGNATURE = "__nuxt_error";
|
|
||||||
const useError = /* @__NO_SIDE_EFFECTS__ */ () => toRef(useNuxtApp().payload, "error");
|
|
||||||
const showError = (error) => {
|
|
||||||
const nuxtError = createError(error);
|
|
||||||
try {
|
|
||||||
const error2 = /* @__PURE__ */ useError();
|
|
||||||
if (false) ;
|
|
||||||
error2.value ||= nuxtError;
|
|
||||||
} catch {
|
|
||||||
throw nuxtError;
|
|
||||||
}
|
|
||||||
return nuxtError;
|
|
||||||
};
|
|
||||||
const isNuxtError = (error) => !!error && typeof error === "object" && NUXT_ERROR_SIGNATURE in error;
|
|
||||||
const createError = (error) => {
|
|
||||||
if (typeof error !== "string" && error.statusText) {
|
|
||||||
error.message ??= error.statusText;
|
|
||||||
}
|
|
||||||
const nuxtError = createError$1(error);
|
|
||||||
Object.defineProperty(nuxtError, NUXT_ERROR_SIGNATURE, {
|
|
||||||
value: true,
|
|
||||||
configurable: false,
|
|
||||||
writable: false
|
|
||||||
});
|
|
||||||
Object.defineProperty(nuxtError, "status", {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
||||||
get: () => nuxtError.statusCode,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
Object.defineProperty(nuxtError, "statusText", {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
||||||
get: () => nuxtError.statusMessage,
|
|
||||||
configurable: true
|
|
||||||
});
|
|
||||||
return nuxtError;
|
|
||||||
};
|
|
||||||
const unhead_k2P3m_ZDyjlr2mMYnoDPwavjsDN8hBlk9cFai0bbopU = /* @__PURE__ */ defineNuxtPlugin({
|
|
||||||
name: "nuxt:head",
|
|
||||||
enforce: "pre",
|
|
||||||
setup(nuxtApp) {
|
|
||||||
const head = nuxtApp.ssrContext.head;
|
|
||||||
nuxtApp.vueApp.use(head);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
function toArray(value) {
|
|
||||||
return Array.isArray(value) ? value : [value];
|
|
||||||
}
|
|
||||||
const matcher = (m, p) => {
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
const _routeRulesMatcher = (path) => defu({}, ...matcher().map((r) => r.data).reverse());
|
|
||||||
const routeRulesMatcher = _routeRulesMatcher;
|
|
||||||
function getRouteRules(arg) {
|
|
||||||
const path = typeof arg === "string" ? arg : arg.path;
|
|
||||||
try {
|
|
||||||
return routeRulesMatcher(path);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[nuxt] Error matching route rules.", e);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const _routes = [
|
|
||||||
{
|
|
||||||
name: "index",
|
|
||||||
path: "/",
|
|
||||||
component: () => import("./_nuxt/index-BDcJu3_l.js")
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const ROUTE_KEY_PARENTHESES_RE = /(:\w+)\([^)]+\)/g;
|
|
||||||
const ROUTE_KEY_SYMBOLS_RE = /(:\w+)[?+*]/g;
|
|
||||||
const ROUTE_KEY_NORMAL_RE = /:\w+/g;
|
|
||||||
function generateRouteKey(route) {
|
|
||||||
const source = route?.meta.key ?? route.path.replace(ROUTE_KEY_PARENTHESES_RE, "$1").replace(ROUTE_KEY_SYMBOLS_RE, "$1").replace(ROUTE_KEY_NORMAL_RE, (r) => route.params[r.slice(1)]?.toString() || "");
|
|
||||||
return typeof source === "function" ? source(route) : source;
|
|
||||||
}
|
|
||||||
function isChangingPage(to, from) {
|
|
||||||
if (to === from || from === START_LOCATION) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (generateRouteKey(to) !== generateRouteKey(from)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const areComponentsSame = to.matched.every(
|
|
||||||
(comp, index) => comp.components && comp.components.default === from.matched[index]?.components?.default
|
|
||||||
);
|
|
||||||
if (areComponentsSame) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const routerOptions0 = {
|
|
||||||
scrollBehavior(to, from, savedPosition) {
|
|
||||||
const nuxtApp = useNuxtApp();
|
|
||||||
const hashScrollBehaviour = useRouter().options?.scrollBehaviorType ?? "auto";
|
|
||||||
if (to.path.replace(/\/$/, "") === from.path.replace(/\/$/, "")) {
|
|
||||||
if (from.hash && !to.hash) {
|
|
||||||
return { left: 0, top: 0 };
|
|
||||||
}
|
|
||||||
if (to.hash) {
|
|
||||||
return { el: to.hash, top: _getHashElementScrollMarginTop(to.hash), behavior: hashScrollBehaviour };
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const routeAllowsScrollToTop = typeof to.meta.scrollToTop === "function" ? to.meta.scrollToTop(to, from) : to.meta.scrollToTop;
|
|
||||||
if (routeAllowsScrollToTop === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const hookToWait = nuxtApp._runningTransition ? "page:transition:finish" : "page:loading:end";
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
if (from === START_LOCATION) {
|
|
||||||
resolve(_calculatePosition(to, from, savedPosition, hashScrollBehaviour));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nuxtApp.hooks.hookOnce(hookToWait, () => {
|
|
||||||
requestAnimationFrame(() => resolve(_calculatePosition(to, from, savedPosition, hashScrollBehaviour)));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function _getHashElementScrollMarginTop(selector) {
|
|
||||||
try {
|
|
||||||
const elem = (void 0).querySelector(selector);
|
|
||||||
if (elem) {
|
|
||||||
return (Number.parseFloat(getComputedStyle(elem).scrollMarginTop) || 0) + (Number.parseFloat(getComputedStyle((void 0).documentElement).scrollPaddingTop) || 0);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
function _calculatePosition(to, from, savedPosition, defaultHashScrollBehaviour) {
|
|
||||||
if (savedPosition) {
|
|
||||||
return savedPosition;
|
|
||||||
}
|
|
||||||
const isPageNavigation = isChangingPage(to, from);
|
|
||||||
if (to.hash) {
|
|
||||||
return {
|
|
||||||
el: to.hash,
|
|
||||||
top: _getHashElementScrollMarginTop(to.hash),
|
|
||||||
behavior: isPageNavigation ? defaultHashScrollBehaviour : "instant"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
left: 0,
|
|
||||||
top: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const configRouterOptions = {
|
|
||||||
hashMode: false,
|
|
||||||
scrollBehaviorType: "auto"
|
|
||||||
};
|
|
||||||
const routerOptions = {
|
|
||||||
...configRouterOptions,
|
|
||||||
...routerOptions0
|
|
||||||
};
|
|
||||||
const validate = /* @__PURE__ */ defineNuxtRouteMiddleware(async (to, from) => {
|
|
||||||
let __temp, __restore;
|
|
||||||
if (!to.meta?.validate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result = ([__temp, __restore] = executeAsync(() => Promise.resolve(to.meta.validate(to))), __temp = await __temp, __restore(), __temp);
|
|
||||||
if (result === true) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const error = createError({
|
|
||||||
fatal: false,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
||||||
status: result && (result.status || result.statusCode) || 404,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
||||||
statusText: result && (result.statusText || result.statusMessage) || `Page Not Found: ${to.fullPath}`,
|
|
||||||
data: {
|
|
||||||
path: to.fullPath
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return error;
|
|
||||||
});
|
|
||||||
const manifest_45route_45rule = /* @__PURE__ */ defineNuxtRouteMiddleware((to) => {
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const globalMiddleware = [
|
|
||||||
validate,
|
|
||||||
manifest_45route_45rule
|
|
||||||
];
|
|
||||||
const namedMiddleware = {};
|
|
||||||
const plugin = /* @__PURE__ */ defineNuxtPlugin({
|
|
||||||
name: "nuxt:router",
|
|
||||||
enforce: "pre",
|
|
||||||
async setup(nuxtApp) {
|
|
||||||
let __temp, __restore;
|
|
||||||
let routerBase = (/* @__PURE__ */ useRuntimeConfig()).app.baseURL;
|
|
||||||
const history = routerOptions.history?.(routerBase) ?? createMemoryHistory(routerBase);
|
|
||||||
const routes = routerOptions.routes ? ([__temp, __restore] = executeAsync(() => routerOptions.routes(_routes)), __temp = await __temp, __restore(), __temp) ?? _routes : _routes;
|
|
||||||
let startPosition;
|
|
||||||
const router = createRouter({
|
|
||||||
...routerOptions,
|
|
||||||
scrollBehavior: (to, from, savedPosition) => {
|
|
||||||
if (from === START_LOCATION) {
|
|
||||||
startPosition = savedPosition;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (routerOptions.scrollBehavior) {
|
|
||||||
router.options.scrollBehavior = routerOptions.scrollBehavior;
|
|
||||||
if ("scrollRestoration" in (void 0).history) {
|
|
||||||
const unsub = router.beforeEach(() => {
|
|
||||||
unsub();
|
|
||||||
(void 0).history.scrollRestoration = "manual";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return routerOptions.scrollBehavior(to, START_LOCATION, startPosition || savedPosition);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
history,
|
|
||||||
routes
|
|
||||||
});
|
|
||||||
nuxtApp.vueApp.use(router);
|
|
||||||
const previousRoute = shallowRef(router.currentRoute.value);
|
|
||||||
router.afterEach((_to, from) => {
|
|
||||||
previousRoute.value = from;
|
|
||||||
});
|
|
||||||
Object.defineProperty(nuxtApp.vueApp.config.globalProperties, "previousRoute", {
|
|
||||||
get: () => previousRoute.value
|
|
||||||
});
|
|
||||||
const initialURL = nuxtApp.ssrContext.url;
|
|
||||||
const _route = shallowRef(router.currentRoute.value);
|
|
||||||
const syncCurrentRoute = () => {
|
|
||||||
_route.value = router.currentRoute.value;
|
|
||||||
};
|
|
||||||
router.afterEach((to, from) => {
|
|
||||||
if (to.matched.at(-1)?.components?.default === from.matched.at(-1)?.components?.default) {
|
|
||||||
syncCurrentRoute();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const route = { sync: syncCurrentRoute };
|
|
||||||
for (const key in _route.value) {
|
|
||||||
Object.defineProperty(route, key, {
|
|
||||||
get: () => _route.value[key],
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
nuxtApp._route = shallowReactive(route);
|
|
||||||
nuxtApp._middleware ||= {
|
|
||||||
global: [],
|
|
||||||
named: {}
|
|
||||||
};
|
|
||||||
const error = /* @__PURE__ */ useError();
|
|
||||||
if (!nuxtApp.ssrContext?.islandContext) {
|
|
||||||
router.afterEach(async (to, _from, failure) => {
|
|
||||||
delete nuxtApp._processingMiddleware;
|
|
||||||
if (failure) {
|
|
||||||
await nuxtApp.callHook("page:loading:end");
|
|
||||||
}
|
|
||||||
if (failure?.type === 4) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (to.redirectedFrom && to.fullPath !== initialURL) {
|
|
||||||
await nuxtApp.runWithContext(() => navigateTo(to.fullPath || "/"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (true) {
|
|
||||||
;
|
|
||||||
[__temp, __restore] = executeAsync(() => router.push(initialURL)), await __temp, __restore();
|
|
||||||
;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
[__temp, __restore] = executeAsync(() => router.isReady()), await __temp, __restore();
|
|
||||||
;
|
|
||||||
} catch (error2) {
|
|
||||||
[__temp, __restore] = executeAsync(() => nuxtApp.runWithContext(() => showError(error2))), await __temp, __restore();
|
|
||||||
}
|
|
||||||
const resolvedInitialRoute = router.currentRoute.value;
|
|
||||||
syncCurrentRoute();
|
|
||||||
if (nuxtApp.ssrContext?.islandContext) {
|
|
||||||
return { provide: { router } };
|
|
||||||
}
|
|
||||||
const initialLayout = nuxtApp.payload.state._layout;
|
|
||||||
router.beforeEach(async (to, from) => {
|
|
||||||
await nuxtApp.callHook("page:loading:start");
|
|
||||||
to.meta = reactive(to.meta);
|
|
||||||
if (nuxtApp.isHydrating && initialLayout && !isReadonly(to.meta.layout)) {
|
|
||||||
to.meta.layout = initialLayout;
|
|
||||||
}
|
|
||||||
nuxtApp._processingMiddleware = true;
|
|
||||||
if (!nuxtApp.ssrContext?.islandContext) {
|
|
||||||
const middlewareEntries = /* @__PURE__ */ new Set([...globalMiddleware, ...nuxtApp._middleware.global]);
|
|
||||||
for (const component of to.matched) {
|
|
||||||
const componentMiddleware = component.meta.middleware;
|
|
||||||
if (!componentMiddleware) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const entry2 of toArray(componentMiddleware)) {
|
|
||||||
middlewareEntries.add(entry2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const routeRules = getRouteRules({ path: to.path });
|
|
||||||
if (routeRules.appMiddleware) {
|
|
||||||
for (const key in routeRules.appMiddleware) {
|
|
||||||
if (routeRules.appMiddleware[key]) {
|
|
||||||
middlewareEntries.add(key);
|
|
||||||
} else {
|
|
||||||
middlewareEntries.delete(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const entry2 of middlewareEntries) {
|
|
||||||
const middleware = typeof entry2 === "string" ? nuxtApp._middleware.named[entry2] || await namedMiddleware[entry2]?.().then((r) => r.default || r) : entry2;
|
|
||||||
if (!middleware) {
|
|
||||||
throw new Error(`Unknown route middleware: '${entry2}'.`);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (false) ;
|
|
||||||
const result = await nuxtApp.runWithContext(() => middleware(to, from));
|
|
||||||
if (true) {
|
|
||||||
if (result === false || result instanceof Error) {
|
|
||||||
const error2 = result || createError({
|
|
||||||
status: 404,
|
|
||||||
statusText: `Page Not Found: ${initialURL}`
|
|
||||||
});
|
|
||||||
await nuxtApp.runWithContext(() => showError(error2));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result === true) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (result === false) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (result) {
|
|
||||||
if (isNuxtError(result) && result.fatal) {
|
|
||||||
await nuxtApp.runWithContext(() => showError(result));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const error2 = createError(err);
|
|
||||||
if (error2.fatal) {
|
|
||||||
await nuxtApp.runWithContext(() => showError(error2));
|
|
||||||
}
|
|
||||||
return error2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
router.onError(async () => {
|
|
||||||
delete nuxtApp._processingMiddleware;
|
|
||||||
await nuxtApp.callHook("page:loading:end");
|
|
||||||
});
|
|
||||||
router.afterEach((to) => {
|
|
||||||
if (to.matched.length === 0 && !error.value) {
|
|
||||||
return nuxtApp.runWithContext(() => showError(createError({
|
|
||||||
status: 404,
|
|
||||||
fatal: false,
|
|
||||||
statusText: `Page not found: ${to.fullPath}`,
|
|
||||||
data: {
|
|
||||||
path: to.fullPath
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
nuxtApp.hooks.hookOnce("app:created", async () => {
|
|
||||||
try {
|
|
||||||
if ("name" in resolvedInitialRoute) {
|
|
||||||
resolvedInitialRoute.name = void 0;
|
|
||||||
}
|
|
||||||
await router.replace({
|
|
||||||
...resolvedInitialRoute,
|
|
||||||
force: true
|
|
||||||
});
|
|
||||||
router.options.scrollBehavior = routerOptions.scrollBehavior;
|
|
||||||
} catch (error2) {
|
|
||||||
await nuxtApp.runWithContext(() => showError(error2));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { provide: { router } };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
function definePayloadReducer(name, reduce) {
|
|
||||||
{
|
|
||||||
useNuxtApp().ssrContext["~payloadReducers"][name] = reduce;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const reducers = [
|
|
||||||
["NuxtError", (data) => isNuxtError(data) && data.toJSON()],
|
|
||||||
["EmptyShallowRef", (data) => isRef(data) && isShallow(data) && !data.value && (typeof data.value === "bigint" ? "0n" : JSON.stringify(data.value) || "_")],
|
|
||||||
["EmptyRef", (data) => isRef(data) && !data.value && (typeof data.value === "bigint" ? "0n" : JSON.stringify(data.value) || "_")],
|
|
||||||
["ShallowRef", (data) => isRef(data) && isShallow(data) && data.value],
|
|
||||||
["ShallowReactive", (data) => isReactive(data) && isShallow(data) && toRaw(data)],
|
|
||||||
["Ref", (data) => isRef(data) && data.value],
|
|
||||||
["Reactive", (data) => isReactive(data) && toRaw(data)]
|
|
||||||
];
|
|
||||||
const revive_payload_server_MVtmlZaQpj6ApFmshWfUWl5PehCebzaBf2NuRMiIbms = /* @__PURE__ */ defineNuxtPlugin({
|
|
||||||
name: "nuxt:revive-payload:server",
|
|
||||||
setup() {
|
|
||||||
for (const [reducer, fn] of reducers) {
|
|
||||||
definePayloadReducer(reducer, fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const components_plugin_4kY4pyzJIYX99vmMAAIorFf3CnAaptHitJgf7JxiED8 = /* @__PURE__ */ defineNuxtPlugin({
|
|
||||||
name: "nuxt:global-components"
|
|
||||||
});
|
|
||||||
const plugins = [
|
|
||||||
unhead_k2P3m_ZDyjlr2mMYnoDPwavjsDN8hBlk9cFai0bbopU,
|
|
||||||
plugin,
|
|
||||||
revive_payload_server_MVtmlZaQpj6ApFmshWfUWl5PehCebzaBf2NuRMiIbms,
|
|
||||||
components_plugin_4kY4pyzJIYX99vmMAAIorFf3CnAaptHitJgf7JxiED8
|
|
||||||
];
|
|
||||||
const defineRouteProvider = (name = "RouteProvider") => defineComponent({
|
|
||||||
name,
|
|
||||||
props: {
|
|
||||||
route: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
vnode: Object,
|
|
||||||
vnodeRef: Object,
|
|
||||||
renderKey: String,
|
|
||||||
trackRootNodes: Boolean
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const previousKey = props.renderKey;
|
|
||||||
const previousRoute = props.route;
|
|
||||||
const route = {};
|
|
||||||
for (const key in props.route) {
|
|
||||||
Object.defineProperty(route, key, {
|
|
||||||
get: () => previousKey === props.renderKey ? props.route[key] : previousRoute[key],
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
provide(PageRouteSymbol, shallowReactive(route));
|
|
||||||
return () => {
|
|
||||||
if (!props.vnode) {
|
|
||||||
return props.vnode;
|
|
||||||
}
|
|
||||||
return h(props.vnode, { ref: props.vnodeRef });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const RouteProvider = defineRouteProvider();
|
|
||||||
const __nuxt_component_0 = defineComponent({
|
|
||||||
name: "NuxtPage",
|
|
||||||
inheritAttrs: false,
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
transition: {
|
|
||||||
type: [Boolean, Object],
|
|
||||||
default: void 0
|
|
||||||
},
|
|
||||||
keepalive: {
|
|
||||||
type: [Boolean, Object],
|
|
||||||
default: void 0
|
|
||||||
},
|
|
||||||
route: {
|
|
||||||
type: Object
|
|
||||||
},
|
|
||||||
pageKey: {
|
|
||||||
type: [Function, String],
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props, { attrs, slots, expose }) {
|
|
||||||
const nuxtApp = useNuxtApp();
|
|
||||||
const pageRef = ref();
|
|
||||||
inject(PageRouteSymbol, null);
|
|
||||||
expose({ pageRef });
|
|
||||||
inject(LayoutMetaSymbol, null);
|
|
||||||
nuxtApp.deferHydration();
|
|
||||||
return () => {
|
|
||||||
return h(RouterView, { name: props.name, route: props.route, ...attrs }, {
|
|
||||||
default: (routeProps) => {
|
|
||||||
return h(Suspense, { suspensible: true }, {
|
|
||||||
default() {
|
|
||||||
return h(RouteProvider, {
|
|
||||||
vnode: slots.default ? normalizeSlot(slots.default, routeProps) : routeProps.Component,
|
|
||||||
route: routeProps.route,
|
|
||||||
vnodeRef: pageRef
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
function normalizeSlot(slot, data) {
|
|
||||||
const slotContent = slot(data);
|
|
||||||
return slotContent.length === 1 ? h(slotContent[0]) : h(Fragment, void 0, slotContent);
|
|
||||||
}
|
|
||||||
const _export_sfc = (sfc, props) => {
|
|
||||||
const target = sfc.__vccOpts || sfc;
|
|
||||||
for (const [key, val] of props) {
|
|
||||||
target[key] = val;
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
const _sfc_main$2 = {};
|
|
||||||
function _sfc_ssrRender(_ctx, _push, _parent, _attrs) {
|
|
||||||
const _component_NuxtPage = __nuxt_component_0;
|
|
||||||
_push(ssrRenderComponent(_component_NuxtPage, _attrs, null, _parent));
|
|
||||||
}
|
|
||||||
const _sfc_setup$2 = _sfc_main$2.setup;
|
|
||||||
_sfc_main$2.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("app.vue");
|
|
||||||
return _sfc_setup$2 ? _sfc_setup$2(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const AppComponent = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["ssrRender", _sfc_ssrRender]]);
|
|
||||||
const _sfc_main$1 = {
|
|
||||||
__name: "nuxt-error-page",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
props: {
|
|
||||||
error: Object
|
|
||||||
},
|
|
||||||
setup(__props) {
|
|
||||||
const props = __props;
|
|
||||||
const _error = props.error;
|
|
||||||
const status = Number(_error.statusCode || 500);
|
|
||||||
const is404 = status === 404;
|
|
||||||
const statusText = _error.statusMessage ?? (is404 ? "Page Not Found" : "Internal Server Error");
|
|
||||||
const description = _error.message || _error.toString();
|
|
||||||
const stack = void 0;
|
|
||||||
const _Error404 = defineAsyncComponent(() => import("./_nuxt/error-404-BY_x-_oz.js"));
|
|
||||||
const _Error = defineAsyncComponent(() => import("./_nuxt/error-500-B0qDQUop.js"));
|
|
||||||
const ErrorTemplate = is404 ? _Error404 : _Error;
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
_push(ssrRenderComponent(unref(ErrorTemplate), mergeProps({ status: unref(status), statusText: unref(statusText), statusCode: unref(status), statusMessage: unref(statusText), description: unref(description), stack: unref(stack) }, _attrs), null, _parent));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const _sfc_setup$1 = _sfc_main$1.setup;
|
|
||||||
_sfc_main$1.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("../node_modules/nuxt/dist/app/components/nuxt-error-page.vue");
|
|
||||||
return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
const _sfc_main = {
|
|
||||||
__name: "nuxt-root",
|
|
||||||
__ssrInlineRender: true,
|
|
||||||
setup(__props) {
|
|
||||||
const IslandRenderer = () => null;
|
|
||||||
const nuxtApp = useNuxtApp();
|
|
||||||
nuxtApp.deferHydration();
|
|
||||||
nuxtApp.ssrContext.url;
|
|
||||||
const SingleRenderer = false;
|
|
||||||
provide(PageRouteSymbol, useRoute());
|
|
||||||
nuxtApp.hooks.callHookWith((hooks) => hooks.map((hook) => hook()), "vue:setup");
|
|
||||||
const error = /* @__PURE__ */ useError();
|
|
||||||
const abortRender = error.value && !nuxtApp.ssrContext.error;
|
|
||||||
onErrorCaptured((err, target, info) => {
|
|
||||||
nuxtApp.hooks.callHook("vue:error", err, target, info).catch((hookError) => console.error("[nuxt] Error in `vue:error` hook", hookError));
|
|
||||||
{
|
|
||||||
const p = nuxtApp.runWithContext(() => showError(err));
|
|
||||||
onServerPrefetch(() => p);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const islandContext = nuxtApp.ssrContext.islandContext;
|
|
||||||
return (_ctx, _push, _parent, _attrs) => {
|
|
||||||
ssrRenderSuspense(_push, {
|
|
||||||
default: () => {
|
|
||||||
if (unref(abortRender)) {
|
|
||||||
_push(`<div></div>`);
|
|
||||||
} else if (unref(error)) {
|
|
||||||
_push(ssrRenderComponent(unref(_sfc_main$1), { error: unref(error) }, null, _parent));
|
|
||||||
} else if (unref(islandContext)) {
|
|
||||||
_push(ssrRenderComponent(unref(IslandRenderer), { context: unref(islandContext) }, null, _parent));
|
|
||||||
} else if (unref(SingleRenderer)) {
|
|
||||||
ssrRenderVNode(_push, createVNode(resolveDynamicComponent(unref(SingleRenderer)), null, null), _parent);
|
|
||||||
} else {
|
|
||||||
_push(ssrRenderComponent(unref(AppComponent), null, null, _parent));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_: 1
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const _sfc_setup = _sfc_main.setup;
|
|
||||||
_sfc_main.setup = (props, ctx) => {
|
|
||||||
const ssrContext = useSSRContext();
|
|
||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("../node_modules/nuxt/dist/app/components/nuxt-root.vue");
|
|
||||||
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
|
|
||||||
};
|
|
||||||
let entry;
|
|
||||||
{
|
|
||||||
entry = async function createNuxtAppServer(ssrContext) {
|
|
||||||
const vueApp = createApp(_sfc_main);
|
|
||||||
const nuxt = createNuxtApp({ vueApp, ssrContext });
|
|
||||||
try {
|
|
||||||
await applyPlugins(nuxt, plugins);
|
|
||||||
await nuxt.hooks.callHook("app:created", vueApp);
|
|
||||||
} catch (error) {
|
|
||||||
await nuxt.hooks.callHook("app:error", error);
|
|
||||||
nuxt.payload.error ||= createError(error);
|
|
||||||
}
|
|
||||||
if (ssrContext && (ssrContext["~renderResponse"] || ssrContext._renderResponse)) {
|
|
||||||
throw new Error("skipping render");
|
|
||||||
}
|
|
||||||
return vueApp;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const entry_default = ((ssrContext) => entry(ssrContext));
|
|
||||||
export {
|
|
||||||
_export_sfc as _,
|
|
||||||
useNuxtApp as a,
|
|
||||||
useRuntimeConfig as b,
|
|
||||||
nuxtLinkDefaults as c,
|
|
||||||
entry_default as default,
|
|
||||||
encodeRoutePath as e,
|
|
||||||
navigateTo as n,
|
|
||||||
resolveRouteObject as r,
|
|
||||||
useRouter as u
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=server.mjs.map
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/dist/server/server.mjs.map
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/server.mjs.map
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/.cache/nuxt/.nuxt/dist/server/server.mjs.map.json
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/dist/server/server.mjs.map.json
generated
vendored
File diff suppressed because one or more lines are too long
7
node_modules/.cache/nuxt/.nuxt/dist/server/styles.mjs
generated
vendored
7
node_modules/.cache/nuxt/.nuxt/dist/server/styles.mjs
generated
vendored
@@ -1,7 +0,0 @@
|
|||||||
const interopDefault = r => r.default || r || []
|
|
||||||
export default {
|
|
||||||
"../node_modules/nuxt/dist/app/components/error-404.vue": () => import('./_nuxt/error-404-styles.DuDrf-v0.mjs').then(interopDefault),
|
|
||||||
"../node_modules/nuxt/dist/app/components/error-500.vue": () => import('./_nuxt/error-500-styles.8IYEHzz6.mjs').then(interopDefault),
|
|
||||||
"../node_modules/nuxt/dist/app/components/error-404.vue?vue&type=style&index=0&scoped=204d37bf&lang.css": () => import('./_nuxt/error-404-styles.DuDrf-v0.mjs').then(interopDefault),
|
|
||||||
"../node_modules/nuxt/dist/app/components/error-500.vue?vue&type=style&index=0&scoped=d349100d&lang.css": () => import('./_nuxt/error-500-styles.8IYEHzz6.mjs').then(interopDefault)
|
|
||||||
}
|
|
||||||
1
node_modules/.cache/nuxt/.nuxt/manifest/latest.json
generated
vendored
1
node_modules/.cache/nuxt/.nuxt/manifest/latest.json
generated
vendored
@@ -1 +0,0 @@
|
|||||||
{"id":"6324e146-4419-48fd-9473-f44ec3cf0d83","timestamp":1771016374643}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"id":"6324e146-4419-48fd-9473-f44ec3cf0d83","timestamp":1771016374643,"prerendered":[]}
|
|
||||||
2
node_modules/.cache/nuxt/.nuxt/nuxt.d.ts
generated
vendored
2
node_modules/.cache/nuxt/.nuxt/nuxt.d.ts
generated
vendored
@@ -1,7 +1,7 @@
|
|||||||
/// <reference types="@nuxtjs/tailwindcss" />
|
/// <reference types="@nuxtjs/tailwindcss" />
|
||||||
/// <reference types="@nuxt/telemetry" />
|
|
||||||
/// <reference types="@nuxtjs/google-fonts" />
|
/// <reference types="@nuxtjs/google-fonts" />
|
||||||
/// <reference types="@nuxt/devtools" />
|
/// <reference types="@nuxt/devtools" />
|
||||||
|
/// <reference types="@nuxt/telemetry" />
|
||||||
/// <reference path="types/nitro-layouts.d.ts" />
|
/// <reference path="types/nitro-layouts.d.ts" />
|
||||||
/// <reference path="types/builder-env.d.ts" />
|
/// <reference path="types/builder-env.d.ts" />
|
||||||
/// <reference path="types/plugins.d.ts" />
|
/// <reference path="types/plugins.d.ts" />
|
||||||
|
|||||||
2
node_modules/.cache/nuxt/.nuxt/nuxt.node.d.ts
generated
vendored
2
node_modules/.cache/nuxt/.nuxt/nuxt.node.d.ts
generated
vendored
@@ -1,7 +1,7 @@
|
|||||||
/// <reference types="@nuxtjs/tailwindcss" />
|
/// <reference types="@nuxtjs/tailwindcss" />
|
||||||
/// <reference types="@nuxt/telemetry" />
|
|
||||||
/// <reference types="@nuxtjs/google-fonts" />
|
/// <reference types="@nuxtjs/google-fonts" />
|
||||||
/// <reference types="@nuxt/devtools" />
|
/// <reference types="@nuxt/devtools" />
|
||||||
|
/// <reference types="@nuxt/telemetry" />
|
||||||
/// <reference path="types/nitro-layouts.d.ts" />
|
/// <reference path="types/nitro-layouts.d.ts" />
|
||||||
/// <reference path="types/modules.d.ts" />
|
/// <reference path="types/modules.d.ts" />
|
||||||
/// <reference path="types/runtime-config.d.ts" />
|
/// <reference path="types/runtime-config.d.ts" />
|
||||||
|
|||||||
17
node_modules/.cache/nuxt/.nuxt/schema/nuxt.schema.d.ts
generated
vendored
17
node_modules/.cache/nuxt/.nuxt/schema/nuxt.schema.d.ts
generated
vendored
@@ -1,17 +0,0 @@
|
|||||||
export interface NuxtCustomSchema {
|
|
||||||
|
|
||||||
}
|
|
||||||
export type CustomAppConfig = Exclude<NuxtCustomSchema['appConfig'], undefined>
|
|
||||||
type _CustomAppConfig = CustomAppConfig
|
|
||||||
|
|
||||||
declare module '@nuxt/schema' {
|
|
||||||
interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
|
||||||
interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
|
||||||
interface CustomAppConfig extends _CustomAppConfig {}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'nuxt/schema' {
|
|
||||||
interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
|
||||||
interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
|
|
||||||
interface CustomAppConfig extends _CustomAppConfig {}
|
|
||||||
}
|
|
||||||
3
node_modules/.cache/nuxt/.nuxt/schema/nuxt.schema.json
generated
vendored
3
node_modules/.cache/nuxt/.nuxt/schema/nuxt.schema.json
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "#"
|
|
||||||
}
|
|
||||||
2
node_modules/.cache/nuxt/.nuxt/tailwind/postcss.mjs
generated
vendored
2
node_modules/.cache/nuxt/.nuxt/tailwind/postcss.mjs
generated
vendored
@@ -1,4 +1,4 @@
|
|||||||
// generated by the @nuxtjs/tailwindcss <https://github.com/nuxt-modules/tailwindcss> module at 2/13/2026, 9:59:34 PM
|
// generated by the @nuxtjs/tailwindcss <https://github.com/nuxt-modules/tailwindcss> module at 2/14/2026, 10:10:49 AM
|
||||||
import "@nuxtjs/tailwindcss/config-ctx"
|
import "@nuxtjs/tailwindcss/config-ctx"
|
||||||
import configMerger from "@nuxtjs/tailwindcss/merger";
|
import configMerger from "@nuxtjs/tailwindcss/merger";
|
||||||
|
|
||||||
|
|||||||
2
node_modules/.cache/nuxt/.nuxt/tsconfig.app.json
generated
vendored
2
node_modules/.cache/nuxt/.nuxt/tsconfig.app.json
generated
vendored
@@ -104,7 +104,7 @@
|
|||||||
"./imports"
|
"./imports"
|
||||||
],
|
],
|
||||||
"#app-manifest": [
|
"#app-manifest": [
|
||||||
"./manifest/meta/6324e146-4419-48fd-9473-f44ec3cf0d83.json"
|
"./manifest/meta/6d3b88f2-3db7-437b-87fe-690483f96918.json"
|
||||||
],
|
],
|
||||||
"#components": [
|
"#components": [
|
||||||
"./components"
|
"./components"
|
||||||
|
|||||||
2
node_modules/.cache/nuxt/.nuxt/tsconfig.json
generated
vendored
2
node_modules/.cache/nuxt/.nuxt/tsconfig.json
generated
vendored
@@ -104,7 +104,7 @@
|
|||||||
"./imports"
|
"./imports"
|
||||||
],
|
],
|
||||||
"#app-manifest": [
|
"#app-manifest": [
|
||||||
"./manifest/meta/6324e146-4419-48fd-9473-f44ec3cf0d83.json"
|
"./manifest/meta/6d3b88f2-3db7-437b-87fe-690483f96918.json"
|
||||||
],
|
],
|
||||||
"#components": [
|
"#components": [
|
||||||
"./components"
|
"./components"
|
||||||
|
|||||||
2
node_modules/.cache/nuxt/.nuxt/tsconfig.shared.json
generated
vendored
2
node_modules/.cache/nuxt/.nuxt/tsconfig.shared.json
generated
vendored
@@ -104,7 +104,7 @@
|
|||||||
"./imports"
|
"./imports"
|
||||||
],
|
],
|
||||||
"#app-manifest": [
|
"#app-manifest": [
|
||||||
"./manifest/meta/6324e146-4419-48fd-9473-f44ec3cf0d83.json"
|
"./manifest/meta/6d3b88f2-3db7-437b-87fe-690483f96918.json"
|
||||||
],
|
],
|
||||||
"#build": [
|
"#build": [
|
||||||
"."
|
"."
|
||||||
|
|||||||
12
node_modules/.cache/nuxt/.nuxt/types/nitro-imports.d.ts
generated
vendored
12
node_modules/.cache/nuxt/.nuxt/types/nitro-imports.d.ts
generated
vendored
@@ -15,11 +15,14 @@ declare global {
|
|||||||
const callNodeListener: typeof import('../../../../h3').callNodeListener
|
const callNodeListener: typeof import('../../../../h3').callNodeListener
|
||||||
const clearResponseHeaders: typeof import('../../../../h3').clearResponseHeaders
|
const clearResponseHeaders: typeof import('../../../../h3').clearResponseHeaders
|
||||||
const clearSession: typeof import('../../../../h3').clearSession
|
const clearSession: typeof import('../../../../h3').clearSession
|
||||||
|
const closeDb: typeof import('../../../../../server/utils/mongodb').closeDb
|
||||||
|
const closeRedis: typeof import('../../../../../server/utils/redis').closeRedis
|
||||||
const createApp: typeof import('../../../../h3').createApp
|
const createApp: typeof import('../../../../h3').createApp
|
||||||
const createAppEventHandler: typeof import('../../../../h3').createAppEventHandler
|
const createAppEventHandler: typeof import('../../../../h3').createAppEventHandler
|
||||||
const createError: typeof import('../../../../h3').createError
|
const createError: typeof import('../../../../h3').createError
|
||||||
const createEvent: typeof import('../../../../h3').createEvent
|
const createEvent: typeof import('../../../../h3').createEvent
|
||||||
const createEventStream: typeof import('../../../../h3').createEventStream
|
const createEventStream: typeof import('../../../../h3').createEventStream
|
||||||
|
const createRedisClient: typeof import('../../../../../server/utils/redis').createRedisClient
|
||||||
const createRouter: typeof import('../../../../h3').createRouter
|
const createRouter: typeof import('../../../../h3').createRouter
|
||||||
const defaultContentType: typeof import('../../../../h3').defaultContentType
|
const defaultContentType: typeof import('../../../../h3').defaultContentType
|
||||||
const defineAppConfig: typeof import('../../../../@nuxt/nitro-server/dist/runtime/utils/config').defineAppConfig
|
const defineAppConfig: typeof import('../../../../@nuxt/nitro-server/dist/runtime/utils/config').defineAppConfig
|
||||||
@@ -47,12 +50,14 @@ declare global {
|
|||||||
const fromWebHandler: typeof import('../../../../h3').fromWebHandler
|
const fromWebHandler: typeof import('../../../../h3').fromWebHandler
|
||||||
const getAllRates: typeof import('../../../../../server/utils/vatRates').getAllRates
|
const getAllRates: typeof import('../../../../../server/utils/vatRates').getAllRates
|
||||||
const getCookie: typeof import('../../../../h3').getCookie
|
const getCookie: typeof import('../../../../h3').getCookie
|
||||||
|
const getDb: typeof import('../../../../../server/utils/mongodb').getDb
|
||||||
const getHeader: typeof import('../../../../h3').getHeader
|
const getHeader: typeof import('../../../../h3').getHeader
|
||||||
const getHeaders: typeof import('../../../../h3').getHeaders
|
const getHeaders: typeof import('../../../../h3').getHeaders
|
||||||
const getMethod: typeof import('../../../../h3').getMethod
|
const getMethod: typeof import('../../../../h3').getMethod
|
||||||
const getProxyRequestHeaders: typeof import('../../../../h3').getProxyRequestHeaders
|
const getProxyRequestHeaders: typeof import('../../../../h3').getProxyRequestHeaders
|
||||||
const getQuery: typeof import('../../../../h3').getQuery
|
const getQuery: typeof import('../../../../h3').getQuery
|
||||||
const getRateByCode: typeof import('../../../../../server/utils/vatRates').getRateByCode
|
const getRateByCode: typeof import('../../../../../server/utils/vatRates').getRateByCode
|
||||||
|
const getRedis: typeof import('../../../../../server/utils/redis').getRedis
|
||||||
const getRequestFingerprint: typeof import('../../../../h3').getRequestFingerprint
|
const getRequestFingerprint: typeof import('../../../../h3').getRequestFingerprint
|
||||||
const getRequestHeader: typeof import('../../../../h3').getRequestHeader
|
const getRequestHeader: typeof import('../../../../h3').getRequestHeader
|
||||||
const getRequestHeaders: typeof import('../../../../h3').getRequestHeaders
|
const getRequestHeaders: typeof import('../../../../h3').getRequestHeaders
|
||||||
@@ -83,6 +88,7 @@ declare global {
|
|||||||
const isStream: typeof import('../../../../h3').isStream
|
const isStream: typeof import('../../../../h3').isStream
|
||||||
const isWebResponse: typeof import('../../../../h3').isWebResponse
|
const isWebResponse: typeof import('../../../../h3').isWebResponse
|
||||||
const lazyEventHandler: typeof import('../../../../h3').lazyEventHandler
|
const lazyEventHandler: typeof import('../../../../h3').lazyEventHandler
|
||||||
|
const logRequest: typeof import('../../../../../server/utils/requestLogger').logRequest
|
||||||
const nitroPlugin: typeof import('../../../../nitropack/dist/runtime/internal/plugin').nitroPlugin
|
const nitroPlugin: typeof import('../../../../nitropack/dist/runtime/internal/plugin').nitroPlugin
|
||||||
const parseCookies: typeof import('../../../../h3').parseCookies
|
const parseCookies: typeof import('../../../../h3').parseCookies
|
||||||
const promisifyNodeListener: typeof import('../../../../h3').promisifyNodeListener
|
const promisifyNodeListener: typeof import('../../../../h3').promisifyNodeListener
|
||||||
@@ -135,6 +141,9 @@ declare global {
|
|||||||
export type { EventHandler, EventHandlerRequest, EventHandlerResponse, EventHandlerObject, H3EventContext } from '../../../../h3'
|
export type { EventHandler, EventHandlerRequest, EventHandlerResponse, EventHandlerObject, H3EventContext } from '../../../../h3'
|
||||||
import('../../../../h3')
|
import('../../../../h3')
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
export type { RequestLogEntry } from '../../../../../server/utils/requestLogger'
|
||||||
|
import('../../../../../server/utils/requestLogger')
|
||||||
|
// @ts-ignore
|
||||||
export type { VatRateResponse } from '../../../../../server/utils/vatRates'
|
export type { VatRateResponse } from '../../../../../server/utils/vatRates'
|
||||||
import('../../../../../server/utils/vatRates')
|
import('../../../../../server/utils/vatRates')
|
||||||
}
|
}
|
||||||
@@ -152,4 +161,7 @@ export { defineTask, runTask } from 'nitropack/runtime/internal/task';
|
|||||||
export { defineNitroErrorHandler } from 'nitropack/runtime/internal/error/utils';
|
export { defineNitroErrorHandler } from 'nitropack/runtime/internal/error/utils';
|
||||||
export { buildAssetsURL as __buildAssetsURL, publicAssetsURL as __publicAssetsURL } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths';
|
export { buildAssetsURL as __buildAssetsURL, publicAssetsURL as __publicAssetsURL } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths';
|
||||||
export { defineAppConfig } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/config';
|
export { defineAppConfig } from '/home/bennet/source/vat-api/node_modules/@nuxt/nitro-server/dist/runtime/utils/config';
|
||||||
|
export { getDb, closeDb } from '/home/bennet/source/vat-api/server/utils/mongodb';
|
||||||
|
export { getRedis, createRedisClient, closeRedis } from '/home/bennet/source/vat-api/server/utils/redis';
|
||||||
|
export { logRequest } from '/home/bennet/source/vat-api/server/utils/requestLogger';
|
||||||
export { getAllRates, getRateByCode } from '/home/bennet/source/vat-api/server/utils/vatRates';
|
export { getAllRates, getRateByCode } from '/home/bennet/source/vat-api/server/utils/vatRates';
|
||||||
9
node_modules/.cache/nuxt/.nuxt/types/nitro-routes.d.ts
generated
vendored
9
node_modules/.cache/nuxt/.nuxt/types/nitro-routes.d.ts
generated
vendored
@@ -3,6 +3,15 @@ import type { Serialize, Simplify } from "nitropack/types";
|
|||||||
declare module "nitropack/types" {
|
declare module "nitropack/types" {
|
||||||
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
|
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
|
||||||
interface InternalApi {
|
interface InternalApi {
|
||||||
|
'/api/admin/login': {
|
||||||
|
'post': Simplify<Serialize<Awaited<ReturnType<typeof import('../../../../../server/api/admin/login.post').default>>>>
|
||||||
|
}
|
||||||
|
'/api/admin/requests': {
|
||||||
|
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../../../../server/api/admin/requests.get').default>>>>
|
||||||
|
}
|
||||||
|
'/api/admin/stats': {
|
||||||
|
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../../../../server/api/admin/stats.get').default>>>>
|
||||||
|
}
|
||||||
'/api/v1/rates/:code': {
|
'/api/v1/rates/:code': {
|
||||||
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../../../../server/api/v1/rates/[code].get').default>>>>
|
'get': Simplify<Serialize<Awaited<ReturnType<typeof import('../../../../../server/api/v1/rates/[code].get').default>>>>
|
||||||
}
|
}
|
||||||
|
|||||||
4
node_modules/.cache/nuxt/.nuxt/types/runtime-config.d.ts
generated
vendored
4
node_modules/.cache/nuxt/.nuxt/types/runtime-config.d.ts
generated
vendored
@@ -12,6 +12,10 @@ import { RuntimeConfig as UserRuntimeConfig, PublicRuntimeConfig as UserPublicRu
|
|||||||
|
|
||||||
vatstackApiKey: string,
|
vatstackApiKey: string,
|
||||||
|
|
||||||
|
db: string,
|
||||||
|
|
||||||
|
adminPassword: string,
|
||||||
|
|
||||||
nitro: {
|
nitro: {
|
||||||
envPrefix: string,
|
envPrefix: string,
|
||||||
},
|
},
|
||||||
|
|||||||
12
node_modules/.cache/vite/client/deps/_metadata.json
generated
vendored
12
node_modules/.cache/vite/client/deps/_metadata.json
generated
vendored
@@ -1,25 +1,25 @@
|
|||||||
{
|
{
|
||||||
"hash": "5d0f798f",
|
"hash": "e7d1c9fa",
|
||||||
"configHash": "0abaf243",
|
"configHash": "0abaf243",
|
||||||
"lockfileHash": "19c8bc86",
|
"lockfileHash": "00c2deb1",
|
||||||
"browserHash": "a321d57f",
|
"browserHash": "46083ee2",
|
||||||
"optimized": {
|
"optimized": {
|
||||||
"errx": {
|
"errx": {
|
||||||
"src": "../../../../errx/dist/index.js",
|
"src": "../../../../errx/dist/index.js",
|
||||||
"file": "errx.js",
|
"file": "errx.js",
|
||||||
"fileHash": "5dd32b92",
|
"fileHash": "7d74e3df",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@vue/devtools-core": {
|
"@vue/devtools-core": {
|
||||||
"src": "../../../../@vue/devtools-core/dist/index.js",
|
"src": "../../../../@vue/devtools-core/dist/index.js",
|
||||||
"file": "@vue_devtools-core.js",
|
"file": "@vue_devtools-core.js",
|
||||||
"fileHash": "307e8ff7",
|
"fileHash": "1702c1a1",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@vue/devtools-kit": {
|
"@vue/devtools-kit": {
|
||||||
"src": "../../../../@vue/devtools-kit/dist/index.js",
|
"src": "../../../../@vue/devtools-kit/dist/index.js",
|
||||||
"file": "@vue_devtools-kit.js",
|
"file": "@vue_devtools-kit.js",
|
||||||
"fileHash": "90e11ab0",
|
"fileHash": "257d05dc",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
139
node_modules/.package-lock.json
generated
vendored
139
node_modules/.package-lock.json
generated
vendored
@@ -753,6 +753,14 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
|
"version": "1.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz",
|
||||||
|
"integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
||||||
@@ -1883,6 +1891,19 @@
|
|||||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/webidl-conversions": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/whatwg-url": {
|
||||||
|
"version": "13.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
|
||||||
|
"integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/webidl-conversions": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@unhead/vue": {
|
"node_modules/@unhead/vue": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.4.tgz",
|
||||||
@@ -2915,6 +2936,14 @@
|
|||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bson": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/buffer": {
|
"node_modules/buffer": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
@@ -4883,7 +4912,6 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.3.tgz",
|
||||||
"integrity": "sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==",
|
"integrity": "sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ioredis/commands": "1.5.0",
|
"@ioredis/commands": "1.5.0",
|
||||||
"cluster-key-slot": "^1.1.0",
|
"cluster-key-slot": "^1.1.0",
|
||||||
@@ -5713,6 +5741,11 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/memory-pager": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
|
||||||
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
@@ -5898,6 +5931,94 @@
|
|||||||
"integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
|
"integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/mongodb": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-kMfnKunbolQYwCIyrkxNJFB4Ypy91pYqua5NargS/f8ODNSJxT03ZU3n1JqL4mCzbSih8tvmMEMLpKTT7x5gCg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@mongodb-js/saslprep": "^1.3.0",
|
||||||
|
"bson": "^7.1.1",
|
||||||
|
"mongodb-connection-string-url": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@aws-sdk/credential-providers": "^3.806.0",
|
||||||
|
"@mongodb-js/zstd": "^7.0.0",
|
||||||
|
"gcp-metadata": "^7.0.1",
|
||||||
|
"kerberos": "^7.0.0",
|
||||||
|
"mongodb-client-encryption": ">=7.0.0 <7.1.0",
|
||||||
|
"snappy": "^7.3.2",
|
||||||
|
"socks": "^2.8.6"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@aws-sdk/credential-providers": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mongodb-js/zstd": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"gcp-metadata": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"kerberos": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongodb-client-encryption": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"snappy": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"socks": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/whatwg-url": "^13.0.0",
|
||||||
|
"whatwg-url": "^14.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/tr46": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
|
||||||
|
"version": "14.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||||
|
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "^5.1.0",
|
||||||
|
"webidl-conversions": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mrmime": {
|
"node_modules/mrmime": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||||
@@ -7462,6 +7583,14 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/quansync": {
|
"node_modules/quansync": {
|
||||||
"version": "0.2.11",
|
"version": "0.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
||||||
@@ -8228,6 +8357,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sparse-bitfield": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"memory-pager": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/speakingurl": {
|
"node_modules/speakingurl": {
|
||||||
"version": "14.0.1",
|
"version": "14.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
vatstackApiKey: '',
|
vatstackApiKey: '',
|
||||||
|
db: '',
|
||||||
|
redisUrl: '',
|
||||||
|
adminPassword: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
app: {
|
app: {
|
||||||
|
|||||||
141
package-lock.json
generated
141
package-lock.json
generated
@@ -8,6 +8,8 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-parser/binding-linux-x64-gnu": "^0.112.0",
|
"@oxc-parser/binding-linux-x64-gnu": "^0.112.0",
|
||||||
|
"ioredis": "^5.9.3",
|
||||||
|
"mongodb": "^7.1.0",
|
||||||
"nuxt": "^4.0.0",
|
"nuxt": "^4.0.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0"
|
||||||
@@ -1169,6 +1171,14 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
|
"version": "1.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz",
|
||||||
|
"integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
||||||
@@ -3682,6 +3692,19 @@
|
|||||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/webidl-conversions": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/whatwg-url": {
|
||||||
|
"version": "13.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
|
||||||
|
"integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/webidl-conversions": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@unhead/vue": {
|
"node_modules/@unhead/vue": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.4.tgz",
|
||||||
@@ -4714,6 +4737,14 @@
|
|||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bson": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/buffer": {
|
"node_modules/buffer": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
@@ -6696,7 +6727,6 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.3.tgz",
|
||||||
"integrity": "sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==",
|
"integrity": "sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ioredis/commands": "1.5.0",
|
"@ioredis/commands": "1.5.0",
|
||||||
"cluster-key-slot": "^1.1.0",
|
"cluster-key-slot": "^1.1.0",
|
||||||
@@ -7526,6 +7556,11 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/memory-pager": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
|
||||||
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
@@ -7711,6 +7746,94 @@
|
|||||||
"integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
|
"integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/mongodb": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-kMfnKunbolQYwCIyrkxNJFB4Ypy91pYqua5NargS/f8ODNSJxT03ZU3n1JqL4mCzbSih8tvmMEMLpKTT7x5gCg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@mongodb-js/saslprep": "^1.3.0",
|
||||||
|
"bson": "^7.1.1",
|
||||||
|
"mongodb-connection-string-url": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@aws-sdk/credential-providers": "^3.806.0",
|
||||||
|
"@mongodb-js/zstd": "^7.0.0",
|
||||||
|
"gcp-metadata": "^7.0.1",
|
||||||
|
"kerberos": "^7.0.0",
|
||||||
|
"mongodb-client-encryption": ">=7.0.0 <7.1.0",
|
||||||
|
"snappy": "^7.3.2",
|
||||||
|
"socks": "^2.8.6"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@aws-sdk/credential-providers": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mongodb-js/zstd": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"gcp-metadata": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"kerberos": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongodb-client-encryption": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"snappy": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"socks": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/whatwg-url": "^13.0.0",
|
||||||
|
"whatwg-url": "^14.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/tr46": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mongodb-connection-string-url/node_modules/whatwg-url": {
|
||||||
|
"version": "14.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||||
|
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "^5.1.0",
|
||||||
|
"webidl-conversions": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mrmime": {
|
"node_modules/mrmime": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||||
@@ -9275,6 +9398,14 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/punycode": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/quansync": {
|
"node_modules/quansync": {
|
||||||
"version": "0.2.11",
|
"version": "0.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
||||||
@@ -10041,6 +10172,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sparse-bitfield": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"memory-pager": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/speakingurl": {
|
"node_modules/speakingurl": {
|
||||||
"version": "14.0.1",
|
"version": "14.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-parser/binding-linux-x64-gnu": "^0.112.0",
|
"@oxc-parser/binding-linux-x64-gnu": "^0.112.0",
|
||||||
|
"ioredis": "^5.9.3",
|
||||||
|
"mongodb": "^7.1.0",
|
||||||
"nuxt": "^4.0.0",
|
"nuxt": "^4.0.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0"
|
||||||
|
|||||||
28
server/api/admin/login.post.ts
Normal file
28
server/api/admin/login.post.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { signToken } from "../../middleware/adminAuth";
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody<{ password?: string }>(event);
|
||||||
|
const adminPassword = useRuntimeConfig().adminPassword;
|
||||||
|
|
||||||
|
if (!adminPassword) {
|
||||||
|
throw createError({ statusCode: 500, statusMessage: "Admin password not configured" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!body?.password || body.password !== adminPassword) {
|
||||||
|
throw createError({ statusCode: 401, statusMessage: "Invalid password" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = Date.now().toString(36);
|
||||||
|
const signature = signToken(payload);
|
||||||
|
const token = `${payload}.${signature}`;
|
||||||
|
|
||||||
|
setCookie(event, "admin-session", token, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: process.env.NODE_ENV === "production",
|
||||||
|
sameSite: "strict",
|
||||||
|
maxAge: 60 * 60 * 24, // 24 hours
|
||||||
|
path: "/",
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
});
|
||||||
31
server/api/admin/requests.get.ts
Normal file
31
server/api/admin/requests.get.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const query = getQuery(event);
|
||||||
|
const page = Math.max(1, parseInt(query.page as string) || 1);
|
||||||
|
const limit = Math.min(100, Math.max(1, parseInt(query.limit as string) || 50));
|
||||||
|
const ip = (query.ip as string) || undefined;
|
||||||
|
|
||||||
|
const db = await getDb();
|
||||||
|
const collection = db.collection("requests");
|
||||||
|
|
||||||
|
const filter: Record<string, unknown> = {};
|
||||||
|
if (ip) filter.ip = ip;
|
||||||
|
|
||||||
|
const [requests, total] = await Promise.all([
|
||||||
|
collection
|
||||||
|
.find(filter)
|
||||||
|
.sort({ timestamp: -1 })
|
||||||
|
.skip((page - 1) * limit)
|
||||||
|
.limit(limit)
|
||||||
|
.project({ _id: 0 })
|
||||||
|
.toArray(),
|
||||||
|
collection.countDocuments(filter),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
requests,
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
totalPages: Math.ceil(total / limit),
|
||||||
|
};
|
||||||
|
});
|
||||||
54
server/api/admin/stats.get.ts
Normal file
54
server/api/admin/stats.get.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
export default defineEventHandler(async () => {
|
||||||
|
const db = await getDb();
|
||||||
|
const collection = db.collection("requests");
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const last24h = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
const last7d = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||||
|
const last30d = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
const [totalRequests24h, totalRequests7d, totalRequests30d, uniqueIPs, topIPs, requestsByHour] =
|
||||||
|
await Promise.all([
|
||||||
|
collection.countDocuments({ timestamp: { $gte: last24h.toISOString() } }),
|
||||||
|
collection.countDocuments({ timestamp: { $gte: last7d.toISOString() } }),
|
||||||
|
collection.countDocuments({ timestamp: { $gte: last30d.toISOString() } }),
|
||||||
|
collection
|
||||||
|
.distinct("ip", { timestamp: { $gte: last24h.toISOString() } })
|
||||||
|
.then((ips) => ips.length),
|
||||||
|
collection
|
||||||
|
.aggregate([
|
||||||
|
{ $match: { timestamp: { $gte: last24h.toISOString() } } },
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: "$ip",
|
||||||
|
count: { $sum: 1 },
|
||||||
|
lastSeen: { $max: "$timestamp" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ $sort: { count: -1 } },
|
||||||
|
{ $limit: 20 },
|
||||||
|
{ $project: { _id: 0, ip: "$_id", count: 1, lastSeen: 1 } },
|
||||||
|
])
|
||||||
|
.toArray(),
|
||||||
|
collection
|
||||||
|
.aggregate([
|
||||||
|
{ $match: { timestamp: { $gte: last24h.toISOString() } } },
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: { $substr: ["$timestamp", 11, 2] },
|
||||||
|
count: { $sum: 1 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ $sort: { _id: 1 } },
|
||||||
|
{ $project: { _id: 0, hour: "$_id", count: 1 } },
|
||||||
|
])
|
||||||
|
.toArray(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalRequests: { last24h: totalRequests24h, last7d: totalRequests7d, last30d: totalRequests30d },
|
||||||
|
uniqueIPs,
|
||||||
|
topIPs,
|
||||||
|
requestsByHour,
|
||||||
|
};
|
||||||
|
});
|
||||||
33
server/middleware/adminAuth.ts
Normal file
33
server/middleware/adminAuth.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
||||||
|
|
||||||
|
function getSecret(): string {
|
||||||
|
return useRuntimeConfig().adminPassword || "changeme";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function signToken(payload: string): string {
|
||||||
|
return createHmac("sha256", getSecret()).update(payload).digest("hex");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verifyToken(payload: string, signature: string): boolean {
|
||||||
|
const expected = signToken(payload);
|
||||||
|
try {
|
||||||
|
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineEventHandler((event) => {
|
||||||
|
const path = getRequestURL(event).pathname;
|
||||||
|
if (!path.startsWith("/api/admin/") || path === "/api/admin/login") return;
|
||||||
|
|
||||||
|
const cookie = getCookie(event, "admin-session");
|
||||||
|
if (!cookie) {
|
||||||
|
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [payload, signature] = cookie.split(".");
|
||||||
|
if (!payload || !signature || !verifyToken(payload, signature)) {
|
||||||
|
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
||||||
|
}
|
||||||
|
});
|
||||||
19
server/middleware/requestLogger.ts
Normal file
19
server/middleware/requestLogger.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export default defineEventHandler((event) => {
|
||||||
|
const path = getRequestURL(event).pathname;
|
||||||
|
if (!path.startsWith("/api/v1/rates")) return;
|
||||||
|
|
||||||
|
const ip = getRequestIP(event, { xForwardedFor: true }) ?? "127.0.0.1";
|
||||||
|
const method = event.method;
|
||||||
|
const userAgent = getRequestHeader(event, "user-agent") ?? "";
|
||||||
|
|
||||||
|
event.node.res.on("finish", () => {
|
||||||
|
logRequest({
|
||||||
|
ip,
|
||||||
|
path,
|
||||||
|
method,
|
||||||
|
statusCode: event.node.res.statusCode,
|
||||||
|
userAgent,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
66
server/plugins/logWorker.ts
Normal file
66
server/plugins/logWorker.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { createRedisClient, closeRedis } from "../utils/redis";
|
||||||
|
import { getDb, closeDb } from "../utils/mongodb";
|
||||||
|
|
||||||
|
export default defineNitroPlugin((nitro) => {
|
||||||
|
let running = true;
|
||||||
|
let workerRedis: ReturnType<typeof createRedisClient> | null = null;
|
||||||
|
|
||||||
|
const worker = async () => {
|
||||||
|
try {
|
||||||
|
workerRedis = createRedisClient();
|
||||||
|
const db = await getDb();
|
||||||
|
const collection = db.collection("requests");
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
const batch: Record<string, unknown>[] = [];
|
||||||
|
const deadline = Date.now() + 2000;
|
||||||
|
|
||||||
|
// Blocking pop — waits up to 5s for an item
|
||||||
|
const item = await workerRedis.brpop("request-logs", 5);
|
||||||
|
if (item) {
|
||||||
|
try {
|
||||||
|
batch.push(JSON.parse(item[1]));
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drain up to 49 more items non-blocking
|
||||||
|
while (batch.length < 50) {
|
||||||
|
const next = await workerRedis.rpop("request-logs");
|
||||||
|
if (!next) break;
|
||||||
|
try {
|
||||||
|
batch.push(JSON.parse(next));
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.length > 0) {
|
||||||
|
try {
|
||||||
|
await collection.insertMany(batch);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[logWorker] MongoDB insert failed:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we haven't hit the deadline and batch was small, wait a bit
|
||||||
|
const remaining = deadline - Date.now();
|
||||||
|
if (remaining > 0 && batch.length < 10) {
|
||||||
|
await new Promise((r) => setTimeout(r, remaining));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[logWorker] Worker error:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start worker in background
|
||||||
|
worker();
|
||||||
|
|
||||||
|
nitro.hooks.hook("close", async () => {
|
||||||
|
running = false;
|
||||||
|
if (workerRedis) {
|
||||||
|
await workerRedis.quit();
|
||||||
|
workerRedis = null;
|
||||||
|
}
|
||||||
|
await closeRedis();
|
||||||
|
await closeDb();
|
||||||
|
});
|
||||||
|
});
|
||||||
30
server/utils/mongodb.ts
Normal file
30
server/utils/mongodb.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { MongoClient, type Db } from "mongodb";
|
||||||
|
|
||||||
|
let client: MongoClient | null = null;
|
||||||
|
let db: Db | null = null;
|
||||||
|
|
||||||
|
export async function getDb(): Promise<Db> {
|
||||||
|
if (db) return db;
|
||||||
|
|
||||||
|
const uri = useRuntimeConfig().db;
|
||||||
|
if (!uri) throw new Error("NUXT_DB is not configured");
|
||||||
|
|
||||||
|
client = new MongoClient(uri);
|
||||||
|
await client.connect();
|
||||||
|
db = client.db("vat-api");
|
||||||
|
|
||||||
|
// Create indexes for the requests collection
|
||||||
|
const requests = db.collection("requests");
|
||||||
|
await requests.createIndex({ timestamp: -1 });
|
||||||
|
await requests.createIndex({ ip: 1, timestamp: -1 });
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function closeDb(): Promise<void> {
|
||||||
|
if (client) {
|
||||||
|
await client.close();
|
||||||
|
client = null;
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
server/utils/redis.ts
Normal file
24
server/utils/redis.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import Redis from "ioredis";
|
||||||
|
|
||||||
|
let redis: Redis | null = null;
|
||||||
|
|
||||||
|
function getRedisUrl(): string {
|
||||||
|
return useRuntimeConfig().redisUrl || "redis://localhost:6379";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRedis(): Redis {
|
||||||
|
if (redis) return redis;
|
||||||
|
redis = new Redis(getRedisUrl(), { maxRetriesPerRequest: null });
|
||||||
|
return redis;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRedisClient(): Redis {
|
||||||
|
return new Redis(getRedisUrl(), { maxRetriesPerRequest: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function closeRedis(): Promise<void> {
|
||||||
|
if (redis) {
|
||||||
|
await redis.quit();
|
||||||
|
redis = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
server/utils/requestLogger.ts
Normal file
17
server/utils/requestLogger.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export interface RequestLogEntry {
|
||||||
|
ip: string;
|
||||||
|
path: string;
|
||||||
|
method: string;
|
||||||
|
statusCode: number;
|
||||||
|
userAgent: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logRequest(data: RequestLogEntry): void {
|
||||||
|
try {
|
||||||
|
const redis = getRedis();
|
||||||
|
redis.lpush("request-logs", JSON.stringify(data)).catch(() => {});
|
||||||
|
} catch {
|
||||||
|
// Never let logging break API responses
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user