🎉 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