🎉 Init
This commit is contained in:
commit
f116fb6206
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal 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
17
Dockerfile
Normal 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
37
Jenkinsfile
vendored
Normal 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
75
README.md
Normal 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
5
app/api/Api.ts
Normal 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
3
app/app.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<NuxtPage></NuxtPage>
|
||||||
|
</template>
|
||||||
256
app/assets/base-style.css
Normal file
256
app/assets/base-style.css
Normal 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
21
app/assets/style.css
Normal 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;
|
||||||
|
}
|
||||||
27
app/components/targets/Target.ts
Normal file
27
app/components/targets/Target.ts
Normal 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
74
app/pages/index.vue
Normal 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"> </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
9
nuxt.config.ts
Normal 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
10043
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
package.json
Normal file
18
package.json
Normal 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
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
2
public/robots.txt
Normal file
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-Agent: *
|
||||||
|
Disallow:
|
||||||
18
tsconfig.json
Normal file
18
tsconfig.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user