diff --git a/.gitignore b/.gitignore index 67d0346..3751439 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ yarn-error.log* # production /build/ /data/ -/confs/ \ No newline at end of file +/confs/ +/private/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index a90b352..0000000 --- a/Dockerfile +++ /dev/null @@ -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" ] diff --git a/README.md b/README.md new file mode 100644 index 0000000..844adaf --- /dev/null +++ b/README.md @@ -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) diff --git a/components/layout.tsx b/components/layout.tsx index 6a2f966..9205de4 100644 --- a/components/layout.tsx +++ b/components/layout.tsx @@ -48,24 +48,6 @@ const Layout = ({ children }: { children: React.ReactNode }) => { defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "826fc083aa86417890c0ceb3e0a597fa"}'> -
diff --git a/interfaces/CardTypes.ts b/interfaces/CardTypes.ts index e2a9b74..21c5ec2 100644 --- a/interfaces/CardTypes.ts +++ b/interfaces/CardTypes.ts @@ -35,6 +35,7 @@ export enum Status { export enum ServiceLocation { brr7_4800u = "brr7-4800u", + tower_0 = "tower-0", other = "" } diff --git a/interfaces/DockerStatus.ts b/interfaces/DockerStatus.ts new file mode 100644 index 0000000..910f222 --- /dev/null +++ b/interfaces/DockerStatus.ts @@ -0,0 +1,11 @@ +import { ServiceLocation } from './CardTypes'; +export interface DockerInfo { + name: string, + status: DockerStatus, + id: string + location: ServiceLocation, +} + +export enum DockerStatus { + running = "running", +} diff --git a/package.json b/package.json index dbf4333..bc21cd6 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "0.2.0", "private": true, "scripts": { - "dev:debug": "NODE_OPTIONS='--inspect' next dev -p 4040", - "dev": "next dev -p 4040", + "dev:debug": "NODE_OPTIONS='--inspect' next dev -H :: -p 8001", + "dev": "next dev -H :: -p 8001", "build": "next build", - "start": "next start", + "start": "next start -H :: -p 8001", "lint": "next lint" }, "dependencies": { diff --git a/pages/about.tsx b/pages/about.tsx index 4874605..89768c9 100644 --- a/pages/about.tsx +++ b/pages/about.tsx @@ -14,7 +14,8 @@ export default function About() { About - This website is primarily for managing my game servers in one spot + I'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. ) diff --git a/pages/api/containers.tsx b/pages/api/containers.tsx index 6f75e6e..7f6a6d2 100644 --- a/pages/api/containers.tsx +++ b/pages/api/containers.tsx @@ -1,13 +1,29 @@ 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) { + const token = JSON.parse(JSON.stringify(ApiSecret.token)); + try { - const options = { - socketPath: '/var/run/docker.sock', - path: '/v1.41/containers/json' - }; - var docker = new Docker({ socketPath: options.socketPath }); - const list = await docker.listContainers({ all: true }) + const res1 = await fetch('https://portainer.neshweb.net/api/endpoints/2/docker/containers/json', { + method: "GET", + headers: {"X-API-Key": token} + }); + + 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); } diff --git a/pages/games.tsx b/pages/servers.tsx similarity index 87% rename from pages/games.tsx rename to pages/servers.tsx index f57ba1b..c193a49 100644 --- a/pages/games.tsx +++ b/pages/servers.tsx @@ -1,7 +1,7 @@ import Head from 'next/head' import { Game } from '../interfaces/CardTypes'; import { PageContentBox, PageDescription, PageTitle, CardContentGame } from '../components/styles/content' -import GameList from '../public/pages.json'; +import GameList from '../public/data/pages.json'; function Servers() { // TODO: unuggly this shit @@ -9,7 +9,7 @@ function Servers() { return ( <> - Neshweb - Games + Neshweb - Servers diff --git a/pages/services.tsx b/pages/services.tsx index 2ae5794..de5b066 100644 --- a/pages/services.tsx +++ b/pages/services.tsx @@ -3,10 +3,9 @@ import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/Car import Dockerode from 'dockerode'; import { ReactElement } from 'react' import useSWR from 'swr'; -import { CardContentService, PageContentBoxNew as PageContentBox, PageDescription, PageTitle } from '../components/styles/content'; -import { ServiceCardMobile } from '../components/styles/cards/mobile'; -import ServiceList from '../public/pages.json'; -import useWindowSize from '../components/windowsize'; +import ServiceList from '../public/data/pages.json'; +import { DockerInfo } from '../interfaces/DockerStatus'; +import { CardContentService, PageContentBox, PageDescription, PageTitle } from '../components/styles/content'; const fetcher = (url: string) => fetch(url).then((res) => res.json()) @@ -79,82 +78,12 @@ 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 // 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 - if (entry.type === ServiceType.app) { - 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; - }) - } - // Type Docker - else if (entry.type === ServiceType.docker) { - if (entry.name !== null) { - let found = false; - for (let i = 0; i < containers.length; i++) { - const container = containers[i]; - // Docker API returns container names with / prepended - if (containers[i].Names.includes("/" + entry.docker_container_name)) { - // so far only "running" is properly implemented, mroe cases to follow as needed - switch (container.State) { - case "running": - entry.status = Status.online; - break; - default: - console.log("Container Status " + container.State + " has no case implemented"); - entry.status = Status.offline; - } - found = true; - // cancel the for - break; - } - // If container name is not missing the container is set to offline - else { - entry.status = Status.offline; - } - } - if (!found) { - console.warn("Container for " + entry.name + " could not be found"); - } - } - // if name is null do not enter for loop - else { - console.error("Container Name not specified"); - entry.status = Status.error; - } - } - // If no Type matches - else { - console.warn("Service Type for Service " + entry.name + " not specified or invalid"); - 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 + // Type APP + if (entry.type === ServiceType.app) { await fetch(entry.href) .then((response) => { if (response.ok) { @@ -177,15 +106,54 @@ async function getStatus(entry: Service, containers: Dockerode.ContainerInfo[]) entry.status = Status.error; }) } - // If no Location matches + // Type Docker + else if (entry.type === ServiceType.docker) { + if (entry.name !== null) { + let found = false; + for (let i = 0; i < containers.length; i++) { + const container = containers[i]; + // Docker API returns container names with / prepended + 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 + switch (container.status) { + case "running": + entry.status = Status.online; + break; + default: + console.log("Container Status " + container.status + " has no case implemented"); + entry.status = Status.offline; + } + found = true; + // cancel the for + break; + } + } + // If container name is not missing the container is set to offline + else { + entry.status = Status.offline; + } + } + if (!found) { + console.warn("Container for " + entry.name + " could not be found"); + } + } + // if name is null do not enter for loop + else { + console.error("Container Name not specified"); + entry.status = Status.error; + } + } + // If no Type matches else { - console.warn("Service Location for Service " + entry.name + " not specified"); + console.warn("Service Type for Service " + entry.name + " not specified or invalid"); entry.status = Status.error; } return entry; } -const fetchFullDataArray = (containerData: Dockerode.ContainerInfo[], dataSet: Service[]) => { +const fetchFullDataArray = (containerData: DockerInfo[], dataSet: Service[]) => { const fetchStatus = (entry: Service) => getStatus(entry, containerData); return Promise.all(dataSet.map(fetchStatus)); } @@ -207,4 +175,4 @@ function useServices() { }; } -export default Services \ No newline at end of file +export default Services diff --git a/public/data/navbar.json b/public/data/navbar.json index 613e960..cf2227b 100644 --- a/public/data/navbar.json +++ b/public/data/navbar.json @@ -9,8 +9,8 @@ "href": "/about" }, { - "name": "Games", - "href": "/games" + "name": "Servers", + "href": "/servers" }, { "name": "Services", diff --git a/public/pages.json b/public/data/pages.json similarity index 75% rename from public/pages.json rename to public/data/pages.json index 3ecb7f6..3ddbd0e 100644 --- a/public/pages.json +++ b/public/data/pages.json @@ -3,26 +3,39 @@ { "name": "Nextcloud", "icon": "/icons/nextcloud-logo.svg", +<<<<<<< HEAD:public/pages.json "href": "https://nextcloud.neshweb.net/", "desc1": "Cloud Storage", "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", "extLink": "https://qwant.com", "extName": "Qwant", "type": "docker", "docker_container_name": "nextcloud", - "location": "brr7-4800u" + "location": "tower-0" }, { +<<<<<<< HEAD:public/pages.json "name": "Komga", "icon": "/icons/komga-logo.png", "href": "https://komga.neshweb.net/", "desc1": "Manga/Comics", "desc": "Self-hosted Comic Library", "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", - "docker_container_name": "komga", - "location": "brr7-4800u" + "docker_container_name": "kavita", + "location": "tower-0" }, { "name": "Calibre Web", @@ -31,30 +44,50 @@ "desc1": "eBooks", "desc": "Self-hosted Ebook Library Service", "warn": "Note: Registration only via Admin", - "type": "app", - "location": "brr7-4800u" + "type": "docker", + "docker_container_name": "calibre-web", + "location": "tower-0" }, { "name": "PeerTube", "icon": "/icons/peertube-logo.svg", +<<<<<<< HEAD:public/pages.json "href": "https://tube.neshweb.net/", "desc1": "Video Platform", +======= + "href": "https://tube.neshweb.net", +>>>>>>> main:public/data/pages.json "desc": "Self-hosted PeerTube Instance", "warn": "Note: Registration only via Admin", "type": "docker", "docker_container_name": "peertube", - "location": "brr7-4800u" + "location": "tower-0" }, { "name": "Mastodon", "icon": "/icons/mastodon-logo.svg", +<<<<<<< HEAD:public/pages.json "href": "https://mastodon.neshweb.net/", "desc1": "Twitter Alternative", +======= + "href": "https://mastodon.neshweb.net", +>>>>>>> main:public/data/pages.json "desc": "Self-hosted Mastodon Instance", "warn": "Note: Registration requires approval", "type": "docker", "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", @@ -64,7 +97,7 @@ "warn": "Note: Registration only via Admin", "type": "docker", "docker_container_name": "filebrowser", - "location": "brr7-4800u" + "location": "tower-0" }, { "name": "Jellyfin", @@ -75,7 +108,7 @@ "warn": "Note: Registration only via Admin", "type": "docker", "docker_container_name": "jellyfin", - "location": "brr7-4800u" + "location": "tower-0" }, { "name": "Navidrome", @@ -86,7 +119,7 @@ "warn": "Note: Registration only via Admin", "type": "docker", "docker_container_name": "navidrome", - "location": "brr7-4800u" + "location": "tower-0" }, { "name": "Picard", @@ -96,7 +129,7 @@ "warn": "Note: Access only via Admin", "type": "docker", "docker_container_name": "picard", - "location": "brr7-4800u" + "location": "tower-0" }, { "name": "Gitlab", @@ -107,7 +140,7 @@ "warn": "Note: Registration only via Admin", "type": "docker", "docker_container_name": "gitlab", - "location": "brr7-4800u" + "location": "tower-0" }, { "name": "Portainer", @@ -118,7 +151,7 @@ "warn": "Note: Admin Only", "type": "docker", "docker_container_name": "portainer", - "location": "brr7-4800u" + "location": "tower-0" }, { "name": "Nginx", @@ -129,6 +162,7 @@ "warn": "Note: Admin Only", "type": "docker", "docker_container_name": "nginx-prox", +<<<<<<< HEAD:public/pages.json "location": "brr7-4800u" }, { @@ -174,6 +208,18 @@ "type": "docker", "docker_container_name": "Debug", "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": { @@ -194,6 +240,14 @@ "icon": "/icons/zomboid-logo.png", "ip": "91.13.248.30", "status": "Online" + }, + "factorio": { + "name": "Factorio", + "status": "Online" + }, + "space_engineers": { + "name": "Space Engineers", + "status": "Online" } } }