Merge branch 'main' into rework/cards
This commit is contained in:
commit
3f077029c6
13 changed files with 166 additions and 172 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -16,4 +16,5 @@ yarn-error.log*
|
||||||
# production
|
# production
|
||||||
/build/
|
/build/
|
||||||
/data/
|
/data/
|
||||||
/confs/
|
/confs/
|
||||||
|
/private/
|
45
Dockerfile
45
Dockerfile
|
@ -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
5
README.md
Normal 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)
|
|
@ -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>
|
||||||
|
|
|
@ -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 = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
interfaces/DockerStatus.ts
Normal file
11
interfaces/DockerStatus.ts
Normal 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",
|
||||||
|
}
|
|
@ -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": {
|
||||||
|
|
|
@ -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'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>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
@ -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,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
|
// 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
|
// Type APP
|
||||||
if (entry.location === ServiceLocation.brr7_4800u) {
|
if (entry.type === ServiceType.app) {
|
||||||
// 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
|
|
||||||
await fetch(entry.href)
|
await fetch(entry.href)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
@ -177,15 +106,54 @@ async function getStatus(entry: Service, containers: Dockerode.ContainerInfo[])
|
||||||
entry.status = Status.error;
|
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 {
|
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;
|
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));
|
||||||
}
|
}
|
||||||
|
@ -207,4 +175,4 @@ function useServices() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Services
|
export default Services
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
"href": "/about"
|
"href": "/about"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Games",
|
"name": "Servers",
|
||||||
"href": "/games"
|
"href": "/servers"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Services",
|
"name": "Services",
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue