Merge branch 'main' into rework/cards

This commit is contained in:
Neshura 2023-03-15 18:44:55 +01:00
commit 3f077029c6
Signed by: Neshura
GPG key ID: B6983AAA6B9A7A6C
13 changed files with 166 additions and 172 deletions

1
.gitignore vendored
View file

@ -17,3 +17,4 @@ yarn-error.log*
/build/ /build/
/data/ /data/
/confs/ /confs/
/private/

View file

@ -1,45 +0,0 @@
## INIT STEP
# Install dependencies only when needed
FROM node:18-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Copy the files needed to install deps
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
## BUILD STEP
# Rebuild the source code only when needed
FROM node:18-alpine AS builder
WORKDIR /app
# Copy node_modules installed by the deps step
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
## RUN STEP
FROM node:18-alpine AS runner
LABEL author="neshura@proton.me"
WORKDIR /usr/src/app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/node_modules ./node_modules
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
# expose port 3000
ENV PORT 3000
EXPOSE 3000
CMD [ "yarn", "start" ]

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# Main Page Repository
Release URL: [www.neshweb.net](https://www.neshweb.net)
Development URL: [wip.neshweb.net](https://wip.neshweb.net)

View file

@ -48,24 +48,6 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
defer src='https://static.cloudflareinsights.com/beacon.min.js' defer src='https://static.cloudflareinsights.com/beacon.min.js'
data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'> data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'>
</Script> </Script>
<Script id="matomo_analytics">
{`
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
_paq.push(["setCookieDomain", "www.neshweb.net"]);
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//tracking.neshweb.net/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '2']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
`}
</Script>
<PageNavbar mobile={isMobile}/> <PageNavbar mobile={isMobile}/>
<Main> <Main>

View file

@ -35,6 +35,7 @@ export enum Status {
export enum ServiceLocation { export enum ServiceLocation {
brr7_4800u = "brr7-4800u", brr7_4800u = "brr7-4800u",
tower_0 = "tower-0",
other = "" other = ""
} }

View file

@ -0,0 +1,11 @@
import { ServiceLocation } from './CardTypes';
export interface DockerInfo {
name: string,
status: DockerStatus,
id: string
location: ServiceLocation,
}
export enum DockerStatus {
running = "running",
}

View file

@ -3,10 +3,10 @@
"version": "0.2.0", "version": "0.2.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev:debug": "NODE_OPTIONS='--inspect' next dev -p 4040", "dev:debug": "NODE_OPTIONS='--inspect' next dev -H :: -p 8001",
"dev": "next dev -p 4040", "dev": "next dev -H :: -p 8001",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start -H :: -p 8001",
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {

View file

@ -14,7 +14,8 @@ export default function About() {
About About
</PageTitle> </PageTitle>
<PageDescription> <PageDescription>
This website is primarily for managing my game servers in one spot I&apos;m currently expanding what I want to do with this site.
Currently a list of available services and servers is available via the respective navbar entry.
</PageDescription> </PageDescription>
</> </>
) )

View file

@ -1,13 +1,29 @@
import Docker from 'dockerode' import Docker from 'dockerode'
import ApiSecret from '../../private/portainer_api_secret.json'
import { DockerInfo } from '../../interfaces/DockerStatus';
import { ServiceLocation } from '../../interfaces/CardTypes';
export default async function ContainersAPI(req: any, res: any) { export default async function ContainersAPI(req: any, res: any) {
const token = JSON.parse(JSON.stringify(ApiSecret.token));
try { try {
const options = { const res1 = await fetch('https://portainer.neshweb.net/api/endpoints/2/docker/containers/json', {
socketPath: '/var/run/docker.sock', method: "GET",
path: '/v1.41/containers/json' headers: {"X-API-Key": token}
}; });
var docker = new Docker({ socketPath: options.socketPath });
const list = await docker.listContainers({ all: true }) const unparsed = await res1.json();
let list: DockerInfo[] = [];
unparsed.forEach((entry: any) => {
let newEntry = {} as DockerInfo;
newEntry.name = entry.Names[0].substring(1);
newEntry.status = entry.State;
newEntry.id = entry.Id;
newEntry.location = ServiceLocation.tower_0;
list.push(newEntry);
});
res.status(200).json(list); res.status(200).json(list);
} }

View file

@ -1,7 +1,7 @@
import Head from 'next/head' import Head from 'next/head'
import { Game } from '../interfaces/CardTypes'; import { Game } from '../interfaces/CardTypes';
import { PageContentBox, PageDescription, PageTitle, CardContentGame } from '../components/styles/content' import { PageContentBox, PageDescription, PageTitle, CardContentGame } from '../components/styles/content'
import GameList from '../public/pages.json'; import GameList from '../public/data/pages.json';
function Servers() { function Servers() {
// TODO: unuggly this shit // TODO: unuggly this shit
@ -9,7 +9,7 @@ function Servers() {
return ( return (
<> <>
<Head> <Head>
<title>Neshweb - Games</title> <title>Neshweb - Servers</title>
<meta charSet='utf-8' /> <meta charSet='utf-8' />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>

View file

@ -3,10 +3,9 @@ import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/Car
import Dockerode from 'dockerode'; import Dockerode from 'dockerode';
import { ReactElement } from 'react' import { ReactElement } from 'react'
import useSWR from 'swr'; import useSWR from 'swr';
import { CardContentService, PageContentBoxNew as PageContentBox, PageDescription, PageTitle } from '../components/styles/content'; import ServiceList from '../public/data/pages.json';
import { ServiceCardMobile } from '../components/styles/cards/mobile'; import { DockerInfo } from '../interfaces/DockerStatus';
import ServiceList from '../public/pages.json'; import { CardContentService, PageContentBox, PageDescription, PageTitle } from '../components/styles/content';
import useWindowSize from '../components/windowsize';
const fetcher = (url: string) => fetch(url).then((res) => res.json()) const fetcher = (url: string) => fetch(url).then((res) => res.json())
@ -79,12 +78,10 @@ function Services() {
) )
} }
async function getStatus(entry: Service, containers: Dockerode.ContainerInfo[]) { async function getStatus(entry: Service, containers: DockerInfo[]) {
// Currently the only location supporting different fetching depending on type is brr7-4800u // Currently the only location supporting different fetching depending on type is brr7-4800u
// Others to follow but low prio as this is currently the only location used // Others to follow but low prio as this is currently the only location used
// Location BRR7-4800U
if (entry.location === ServiceLocation.brr7_4800u) {
// Type APP // Type APP
if (entry.type === ServiceType.app) { if (entry.type === ServiceType.app) {
await fetch(entry.href) await fetch(entry.href)
@ -116,20 +113,23 @@ async function getStatus(entry: Service, containers: Dockerode.ContainerInfo[])
for (let i = 0; i < containers.length; i++) { for (let i = 0; i < containers.length; i++) {
const container = containers[i]; const container = containers[i];
// Docker API returns container names with / prepended // Docker API returns container names with / prepended
if (containers[i].Names.includes("/" + entry.docker_container_name)) { if (container.name === entry.docker_container_name) {
if (container.location === entry.location) {
// so far only "running" is properly implemented, mroe cases to follow as needed // so far only "running" is properly implemented, mroe cases to follow as needed
switch (container.State) { switch (container.status) {
case "running": case "running":
entry.status = Status.online; entry.status = Status.online;
break; break;
default: default:
console.log("Container Status " + container.State + " has no case implemented"); console.log("Container Status " + container.status + " has no case implemented");
entry.status = Status.offline; entry.status = Status.offline;
} }
found = true; found = true;
// cancel the for // cancel the for
break; break;
} }
}
// If container name is not missing the container is set to offline // If container name is not missing the container is set to offline
else { else {
entry.status = Status.offline; entry.status = Status.offline;
@ -150,42 +150,10 @@ async function getStatus(entry: Service, containers: Dockerode.ContainerInfo[])
console.warn("Service Type for Service " + entry.name + " not specified or invalid"); console.warn("Service Type for Service " + entry.name + " not specified or invalid");
entry.status = Status.error; entry.status = Status.error;
} }
}
// Location Other
// TODO: implement docker type for other locations
else if (entry.location === ServiceLocation.other) {
// Currently uses the same handling as app type for the other location
await fetch(entry.href)
.then((response) => {
if (response.ok) {
switch (response.status) {
case 200:
case 301:
case 302:
entry.status = Status.online;
break;
default:
entry.status = Status.offline;
}
}
else {
entry.status = Status.offline;
}
})
.catch((error) => {
console.error("Error pinging Website: ", error);
entry.status = Status.error;
})
}
// If no Location matches
else {
console.warn("Service Location for Service " + entry.name + " not specified");
entry.status = Status.error;
}
return entry; return entry;
} }
const fetchFullDataArray = (containerData: Dockerode.ContainerInfo[], dataSet: Service[]) => { const fetchFullDataArray = (containerData: DockerInfo[], dataSet: Service[]) => {
const fetchStatus = (entry: Service) => getStatus(entry, containerData); const fetchStatus = (entry: Service) => getStatus(entry, containerData);
return Promise.all(dataSet.map(fetchStatus)); return Promise.all(dataSet.map(fetchStatus));
} }

View file

@ -9,8 +9,8 @@
"href": "/about" "href": "/about"
}, },
{ {
"name": "Games", "name": "Servers",
"href": "/games" "href": "/servers"
}, },
{ {
"name": "Services", "name": "Services",

View file

@ -3,26 +3,39 @@
{ {
"name": "Nextcloud", "name": "Nextcloud",
"icon": "/icons/nextcloud-logo.svg", "icon": "/icons/nextcloud-logo.svg",
<<<<<<< HEAD:public/pages.json
"href": "https://nextcloud.neshweb.net/", "href": "https://nextcloud.neshweb.net/",
"desc1": "Cloud Storage", "desc1": "Cloud Storage",
"desc": "Self-hosted Cloud Storage Service but longer and hopefully with wrap as well as some extra just to make sure", "desc": "Self-hosted Cloud Storage Service but longer and hopefully with wrap as well as some extra just to make sure",
=======
"href": "https://nextcloud.neshweb.net",
"desc": "Self-hosted Cloud Storage Service",
>>>>>>> main:public/data/pages.json
"warn": "Note: Registration requires approval", "warn": "Note: Registration requires approval",
"extLink": "https://qwant.com", "extLink": "https://qwant.com",
"extName": "Qwant", "extName": "Qwant",
"type": "docker", "type": "docker",
"docker_container_name": "nextcloud", "docker_container_name": "nextcloud",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
<<<<<<< HEAD:public/pages.json
"name": "Komga", "name": "Komga",
"icon": "/icons/komga-logo.png", "icon": "/icons/komga-logo.png",
"href": "https://komga.neshweb.net/", "href": "https://komga.neshweb.net/",
"desc1": "Manga/Comics", "desc1": "Manga/Comics",
"desc": "Self-hosted Comic Library", "desc": "Self-hosted Comic Library",
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
=======
"name": "Kavita",
"icon": "/icons/kavita-logo.svg",
"href": "https://kavita.neshweb.net",
"desc": "Self-hosted Manga Library",
"warn": "Registration via Admin invite",
>>>>>>> main:public/data/pages.json
"type": "docker", "type": "docker",
"docker_container_name": "komga", "docker_container_name": "kavita",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Calibre Web", "name": "Calibre Web",
@ -31,30 +44,50 @@
"desc1": "eBooks", "desc1": "eBooks",
"desc": "Self-hosted Ebook Library Service", "desc": "Self-hosted Ebook Library Service",
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "app", "type": "docker",
"location": "brr7-4800u" "docker_container_name": "calibre-web",
"location": "tower-0"
}, },
{ {
"name": "PeerTube", "name": "PeerTube",
"icon": "/icons/peertube-logo.svg", "icon": "/icons/peertube-logo.svg",
<<<<<<< HEAD:public/pages.json
"href": "https://tube.neshweb.net/", "href": "https://tube.neshweb.net/",
"desc1": "Video Platform", "desc1": "Video Platform",
=======
"href": "https://tube.neshweb.net",
>>>>>>> main:public/data/pages.json
"desc": "Self-hosted PeerTube Instance", "desc": "Self-hosted PeerTube Instance",
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
"docker_container_name": "peertube", "docker_container_name": "peertube",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Mastodon", "name": "Mastodon",
"icon": "/icons/mastodon-logo.svg", "icon": "/icons/mastodon-logo.svg",
<<<<<<< HEAD:public/pages.json
"href": "https://mastodon.neshweb.net/", "href": "https://mastodon.neshweb.net/",
"desc1": "Twitter Alternative", "desc1": "Twitter Alternative",
=======
"href": "https://mastodon.neshweb.net",
>>>>>>> main:public/data/pages.json
"desc": "Self-hosted Mastodon Instance", "desc": "Self-hosted Mastodon Instance",
"warn": "Note: Registration requires approval", "warn": "Note: Registration requires approval",
"type": "docker", "type": "docker",
"docker_container_name": "mastodon-web", "docker_container_name": "mastodon-web",
"location": "brr7-4800u" "location": "tower-0"
},
{
"name": "Vaultwarden",
"icon": "/icons/vaultwarden-logo.svg",
"href": "https://vault.neshweb.net",
"desc": "Self-hosted Password Manager",
"warn": "Note: Invite only",
"type": "docker",
"docker_container_name": "vaultwarden",
"location": "tower-0"
}, },
{ {
"name": "File Browser", "name": "File Browser",
@ -64,7 +97,7 @@
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
"docker_container_name": "filebrowser", "docker_container_name": "filebrowser",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Jellyfin", "name": "Jellyfin",
@ -75,7 +108,7 @@
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
"docker_container_name": "jellyfin", "docker_container_name": "jellyfin",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Navidrome", "name": "Navidrome",
@ -86,7 +119,7 @@
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
"docker_container_name": "navidrome", "docker_container_name": "navidrome",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Picard", "name": "Picard",
@ -96,7 +129,7 @@
"warn": "Note: Access only via Admin", "warn": "Note: Access only via Admin",
"type": "docker", "type": "docker",
"docker_container_name": "picard", "docker_container_name": "picard",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Gitlab", "name": "Gitlab",
@ -107,7 +140,7 @@
"warn": "Note: Registration only via Admin", "warn": "Note: Registration only via Admin",
"type": "docker", "type": "docker",
"docker_container_name": "gitlab", "docker_container_name": "gitlab",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Portainer", "name": "Portainer",
@ -118,7 +151,7 @@
"warn": "Note: Admin Only", "warn": "Note: Admin Only",
"type": "docker", "type": "docker",
"docker_container_name": "portainer", "docker_container_name": "portainer",
"location": "brr7-4800u" "location": "tower-0"
}, },
{ {
"name": "Nginx", "name": "Nginx",
@ -129,6 +162,7 @@
"warn": "Note: Admin Only", "warn": "Note: Admin Only",
"type": "docker", "type": "docker",
"docker_container_name": "nginx-prox", "docker_container_name": "nginx-prox",
<<<<<<< HEAD:public/pages.json
"location": "brr7-4800u" "location": "brr7-4800u"
}, },
{ {
@ -174,6 +208,18 @@
"type": "docker", "type": "docker",
"docker_container_name": "Debug", "docker_container_name": "Debug",
"location": "brr7-4800u" "location": "brr7-4800u"
=======
"location": "tower-0"
},
{
"name": "Proxmox",
"icon": "/icons/proxmox-logo.png",
"href": "https://proxmox.neshweb.net/",
"desc": "Hypervisor Webinterface",
"warn": "Note: Admin Only",
"type": "app",
"location": ""
>>>>>>> main:public/data/pages.json
} }
], ],
"games": { "games": {
@ -194,6 +240,14 @@
"icon": "/icons/zomboid-logo.png", "icon": "/icons/zomboid-logo.png",
"ip": "91.13.248.30", "ip": "91.13.248.30",
"status": "Online" "status": "Online"
},
"factorio": {
"name": "Factorio",
"status": "Online"
},
"space_engineers": {
"name": "Space Engineers",
"status": "Online"
} }
} }
} }