Nuxt 3 component (SSR)
<template>
<a
class="inline-flex gap-2 items-center px-3 py-1 text-sm rounded-md border border-border text-white/70 hover:bg-muted hover:text-white max-w-fit"
:href="statusSiteUrl"
target="_blank"
rel="noreferrer"
>
<span class="flex relative w-2 h-2">
<span
v-if="status.label === 'Operational'"
class="inline-flex absolute w-full h-full rounded-full opacity-75 duration-1000 animate-ping"
:class="status.color"
/>
<span
class="inline-flex relative w-2 h-2 rounded-full"
:class="status.color"
/>
</span>
{{ status.label }}
</a>
</template>
<script setup lang="ts">
import * as z from "zod";
const config = useRuntimeConfig();
const statusSiteUrl = config.statusSiteSlug;
const status = ref({
label: "checking",
color: undefined,
});
const statusEnum = z.enum([
"operational",
"degraded_performance",
"partial_outage",
"major_outage",
"under_maintenance",
"unknown",
]);
const statusSchema = z.object({ status: statusEnum });
const dictionary = {
operational: {
label: "Operational",
color: "bg-green-500",
},
degraded_performance: {
label: "Degraded Performance",
color: "bg-yellow-500",
},
partial_outage: {
label: "Partial Outage",
color: "bg-yellow-500",
},
major_outage: {
label: "Major Outage",
color: "bg-red-500",
},
unknown: {
label: "Unknown",
color: "bg-gray-500",
},
under_maintenance: {
label: "Under Maintenance",
color: "bg-gray-500",
},
} as const;
const { data } = await useFetch("/api/site-status");
const parsed = statusSchema.safeParse(data);
const key = !parsed.success ? "unknown" : parsed.data.status;
status.value = dictionary[key];
</script>
Nuxt 2 Component (No TS, No SSR)
<template>
<a
class="inline-flex gap-2 items-center px-3 py-1 text-sm rounded-md border border-border text-white/70 hover:bg-muted hover:text-white max-w-fit"
:href="statusSiteUrl"
target="_blank"
rel="noreferrer"
>
<span class="flex relative w-2 h-2">
<span
v-if="status.label === 'Operational'"
class="inline-flex absolute w-full h-full rounded-full opacity-75 duration-1000 animate-ping"
:class="status.color"
/>
<span
class="inline-flex relative w-2 h-2 rounded-full"
:class="status.color"
/>
</span>
{{ status.label }}
</a>
</template>
<script>
const statusEnum = [
"operational",
"degraded_performance",
"partial_outage",
"major_outage",
"under_maintenance",
"unknown",
];
const dictionary = {
operational: {
label: "Operational",
color: "bg-green-500",
},
degraded_performance: {
label: "Degraded Performance",
color: "bg-yellow-500",
},
partial_outage: {
label: "Partial Outage",
color: "bg-yellow-500",
},
major_outage: {
label: "Major Outage",
color: "bg-red-500",
},
unknown: {
label: "Unknown",
color: "bg-gray-500",
},
under_maintenance: {
label: "Under Maintenance",
color: "bg-gray-500",
},
};
export default {
data() {
return {
status: {
label: "Checking",
color: "",
},
statusSiteUrl: "read-from-runtime-config",
};
},
mounted() {
this.fetchSiteStatus();
},
methods: {
async fetchSiteStatus() {
const { data } = await $fetch((`/api/site-status`);
const key = statusEnum.includes(data.status) ? data.status : "unknown";
this.status = dictionary[key];
},
},
};
</script>
Serverless function to fetch status from Openstatus public API
export default defineEventHandler((event) => {
const config = useRuntimeConfig();
const resp = await $fetch(
`https://api.openstatus.dev/public/status/${config.statusSiteSlug}`,
{
next: { revalidate: 60 }, // cache request for 60 seconds
}
);
return response.status(200).json(resp.data);
})