🎉 Init

This commit is contained in:
Andreas Dinauer 2026-02-07 11:47:29 +01:00
commit f116fb6206
16 changed files with 10629 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM node:22.9.0-alpine
WORKDIR /usr/app
COPY . .
ARG VERSION
RUN npm install
RUN npx nuxi cleanup
RUN npx nuxi build
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]

37
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,37 @@
pipeline {
agent any
stages {
stage('Set Image Name') {
steps {
script {
env.IMAGE = "harbor.dinauer.dev/pingo/frontend:${env.BUILD_NUMBER}";
}
}
}
stage('Build Image') {
steps {
script {
sh "docker build --no-cache -t ${env.IMAGE} ."
}
}
}
stage('Push Image to Docker Hub') {
steps {
script {
withCredentials([usernamePassword(credentialsId: 'harbor', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
sh 'echo ${PASSWORD} | docker login harbor.dinauer.dev -u ${USERNAME} --password-stdin'
sh "docker push ${env.IMAGE}"
sh "docker logout harbor.dinauer.dev"
}
}
}
}
stage('Remove image from host') {
steps {
script {
sh "docker image rm --force ${env.IMAGE}"
}
}
}
}
}

75
README.md Normal file
View File

@ -0,0 +1,75 @@
# Nuxt Minimal Starter
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

5
app/api/Api.ts Normal file
View File

@ -0,0 +1,5 @@
import axios, {type AxiosInstance} from "axios";
export const Api: AxiosInstance = axios.create({
baseURL: 'http://localhost:8080'
});

3
app/app.vue Normal file
View File

@ -0,0 +1,3 @@
<template>
<NuxtPage></NuxtPage>
</template>

256
app/assets/base-style.css Normal file
View File

@ -0,0 +1,256 @@
.center {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.left-center {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 0.5rem;
}
.right-center {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.5rem;
}
.stretch-center {
display: flex;
align-items: center;
justify-content: stretch;
gap: 0.5rem;
}
.left-top {
display: flex;
align-items: flex-start;
justify-content: flex-start;
gap: 0.5rem;
}
.center-top {
display: flex;
align-items: flex-start;
justify-content: center;
}
.center-bottom {
display: flex;
align-items: flex-end;
justify-content: center;
}
.top-center {
display: flex;
align-items: center;
justify-content: flex-start;
}
.spaced-top {
display: flex;
align-items: start;
justify-content: space-between;
}
.spaced-center {
display: flex;
align-items: center;
justify-content: space-between;
}
.pointer {
cursor: pointer;
}
.expand {
height: 100%;
width: 100%;
}
.content, *[class^='content-'], *[class*=' content-'] {
display: flex;
flex-direction: column;
gap: 1rem;
width: 100%;
}
.content, *[class^='content-'], *[class*=' content-'] > *:is(div, form, header, footer) {
width: 100%;
justify-self: stretch;
}
.content .full {
width: 100%;
}
.content-s {
gap: 0.25rem;
}
.content-m {
gap: 0.5rem;
}
.content-l {
gap: 1rem;
}
.content-xl {
display: grid;
gap: 2rem;
}
.content-2xl {
gap: 3rem;
}
.content-3xl {
gap: 4rem;
}
.padding-m {
padding: 0.5rem;
}
.padding-l {
padding: 1rem;
}
.padding-xl {
padding: 2rem;
}
.narrow {
width: min(100%, 1340px);
padding: 0 0.5rem;
}
.narrow.s {
width: min(100%, 320px);
}
.narrow.m {
width: min(100%, 540px);
}
.narrow-b {
width: min(100%, 740px);
}
.tile, *[class^='tile-'], *[class*=' tile-'] {
background-color: var(--tile-color);
border-radius: 0.25rem;
overflow: hidden;
width: 100%;
border: 1px solid #cddaff;
}
.tile-s {
padding: 0.25rem;
}
.tile-m {
padding: 0.5rem;
}
.tile-l {
padding: 1rem;
}
.tile-xl {
padding: 2rem;
}
.col-2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.col-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.col-4 {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.col-5 {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 1rem;
}
.grayed-out {
color: #777777;
font-size: 0.85rem;
}
.row_auto-1fr {
display: grid;
grid-template-rows: auto 1fr;
height: 100%;
}
.height_100 {
height: 100%;
}
.gap-0 {
gap: 0;
}
.gap-m {
gap: 0.5rem;
}
.gap-l {
gap: 1rem;
}
.column {
flex-direction: column;
}
@media (max-width: 768px) {
.col-1-m {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
.col-2-m {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.col-3-m {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
.center-m {
display: flex;
justify-content: center;
align-items: center;
}
}
.full_20rem {
display: grid;
grid-template-columns: 1fr 20rem;
gap: 1rem;
}
.base-shape {
height: 2.25rem;
padding: 0.5rem;
}
.width-6rem {
width: 6rem;
}
.nowrap {
white-space: nowrap
}

21
app/assets/style.css Normal file
View File

@ -0,0 +1,21 @@
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap');
* {
padding: 0;
margin: 0;
font-family: "Outfit", sans-serif;
font-optical-sizing: auto;
box-sizing: border-box;
text-decoration: none;
color: white;
}
html, body, #__nuxt {
height: 100vh;
width: 100vw;
}
html {
background-color: #151515;
}

View File

@ -0,0 +1,27 @@
import {Api} from "~/api/Api";
export class Target
{
constructor (
public key: string,
public display: string,
public status: string,
public pings: Ping[],
) {}
static get(onSuccess: (targets: Target[]) => void)
{
Api.get<Target[]>("/targets")
.then((response) => {
onSuccess(response.data)
});
}
}
export class Ping
{
constructor (
public result: string
) {}
}

74
app/pages/index.vue Normal file
View File

@ -0,0 +1,74 @@
<template>
<div class="app">
<div class="page">
<h1>Uptime Dashboard</h1>
<div v-for="target in targets" class="target">
<p class="healthcheck"><span class="indicator">99.86%</span> {{ target.display }}</p>
<div class="ping-container">
<div style="user-select: none" v-for="ping in target.pings" :class="ping.result">&nbsp;</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {Target} from "~/components/targets/Target";
const targets: Ref<Target[] | undefined> = ref();
onMounted(() => {
Target.get((_targets: Target[]) => {
targets.value = _targets;
})
})
</script>
<style scoped>
.app {
display: flex;
justify-content: center;
padding: 2rem;
}
.page {
width: min(1140px, 100%);
background-color: #222222;
border-radius: 1rem;
padding: 2rem;
display: grid;
gap: 1.5rem;
}
.target {
display: grid;
grid-template-columns: 55% 45%;
}
.ping-container {
display: grid;
gap: 0.25rem;
grid-template-columns: repeat(50, 1fr);
justify-content: end;
direction: rtl;
}
.healthcheck {
font-size: 1.125rem;
}
.UP {
background-color: #74f474;
border-radius: 0.25rem;
}
.DOWN {
background-color: #ff4534;
border-radius: 0.25rem;
}
.indicator {
font-size: 0.90rem;
font-weight: 550;
width: 3.75rem;
display: inline-flex;
justify-content: center;
background-color: #74f474;
color: black;
margin-right: 0.5rem;
border-radius: 1rem;
}
</style>

9
nuxt.config.ts Normal file
View File

@ -0,0 +1,9 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
css: [
'@/assets/style.css',
'@/assets/base-style.css'
]
})

10043
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "frontend",
"type": "module",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"axios": "^1.13.4",
"nuxt": "^4.3.0",
"vue": "^3.5.27",
"vue-router": "^4.6.4"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

2
public/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-Agent: *
Disallow:

18
tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"files": [],
"references": [
{
"path": "./.nuxt/tsconfig.app.json"
},
{
"path": "./.nuxt/tsconfig.server.json"
},
{
"path": "./.nuxt/tsconfig.shared.json"
},
{
"path": "./.nuxt/tsconfig.node.json"
}
]
}