mirror of
https://github.com/DI0IK/homepage-plus.git
synced 2025-07-15 17:30:30 +00:00
first public source commit
This commit is contained in:
parent
1a4fbb9d42
commit
3914fee775
65 changed files with 4697 additions and 312 deletions
18
src/pages/_app.js
Normal file
18
src/pages/_app.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { SWRConfig } from "swr";
|
||||
import "styles/globals.css";
|
||||
import "styles/weather-icons.css";
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
<SWRConfig
|
||||
value={{
|
||||
fetcher: (resource, init) =>
|
||||
fetch(resource, init).then((res) => res.json()),
|
||||
}}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</SWRConfig>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp;
|
24
src/pages/_document.js
Normal file
24
src/pages/_document.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://fonts.gstatic.com"
|
||||
crossOrigin="true"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400;1,500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</Head>
|
||||
<body className="w-full h-full bg-theme-50 dark:bg-theme-800 transition duration-150 ease-in-out">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
27
src/pages/api/bookmarks.js
Normal file
27
src/pages/api/bookmarks.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import yaml from "js-yaml";
|
||||
import checkAndCopyConfig from "utils/config";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
checkAndCopyConfig("bookmarks.yaml");
|
||||
|
||||
const bookmarksYaml = path.join(process.cwd(), "config", "bookmarks.yaml");
|
||||
const fileContents = await fs.readFile(bookmarksYaml, "utf8");
|
||||
const bookmarks = yaml.load(fileContents);
|
||||
|
||||
// map easy to write YAML objects into easy to consume JS arrays
|
||||
const bookmarksArray = bookmarks.map((group) => {
|
||||
return {
|
||||
name: Object.keys(group)[0],
|
||||
bookmarks: group[Object.keys(group)[0]].map((entries) => {
|
||||
return {
|
||||
name: Object.keys(entries)[0],
|
||||
...entries[Object.keys(entries)[0]][0],
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
res.send(bookmarksArray);
|
||||
}
|
49
src/pages/api/docker/stats/[...service].js
Normal file
49
src/pages/api/docker/stats/[...service].js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import Docker from "dockerode";
|
||||
import getDockerArguments from "utils/docker";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { service } = req.query;
|
||||
const [containerName, containerServer] = service;
|
||||
|
||||
if (!containerName && !containerServer) {
|
||||
res.status(400).send({
|
||||
error: "docker query parameters are required",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const docker = new Docker(await getDockerArguments(containerServer));
|
||||
const containers = await docker.listContainers();
|
||||
|
||||
// bad docker connections can result in a <Buffer ...> object?
|
||||
// in any case, this ensures the result is the expected array
|
||||
if (!Array.isArray(containers)) {
|
||||
return res.status(500).send({
|
||||
error: "query failed",
|
||||
});
|
||||
}
|
||||
|
||||
const containerNames = containers.map((container) => {
|
||||
return container.Names[0].replace(/^\//, "");
|
||||
});
|
||||
const containerExists = containerNames.includes(containerName);
|
||||
|
||||
if (!containerExists) {
|
||||
return res.status(404).send({
|
||||
error: "not found",
|
||||
});
|
||||
}
|
||||
|
||||
const container = docker.getContainer(containerName);
|
||||
const stats = await container.stats({ stream: false });
|
||||
|
||||
return res.status(200).json({
|
||||
stats: stats,
|
||||
});
|
||||
} catch {
|
||||
return res.status(500).send({
|
||||
error: "unknown error",
|
||||
});
|
||||
}
|
||||
}
|
48
src/pages/api/docker/status/[...service].js
Normal file
48
src/pages/api/docker/status/[...service].js
Normal file
|
@ -0,0 +1,48 @@
|
|||
import Docker from "dockerode";
|
||||
import getDockerArguments from "utils/docker";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { service } = req.query;
|
||||
const [containerName, containerServer] = service;
|
||||
|
||||
if (!containerName && !containerServer) {
|
||||
return res.status(400).send({
|
||||
error: "docker query parameters are required",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const docker = new Docker(await getDockerArguments(containerServer));
|
||||
const containers = await docker.listContainers();
|
||||
|
||||
// bad docker connections can result in a <Buffer ...> object?
|
||||
// in any case, this ensures the result is the expected array
|
||||
if (!Array.isArray(containers)) {
|
||||
return res.status(500).send({
|
||||
error: "query failed",
|
||||
});
|
||||
}
|
||||
|
||||
const containerNames = containers.map((container) => {
|
||||
return container.Names[0].replace(/^\//, "");
|
||||
});
|
||||
const containerExists = containerNames.includes(containerName);
|
||||
|
||||
if (!containerExists) {
|
||||
return res.status(404).send({
|
||||
error: "not found",
|
||||
});
|
||||
}
|
||||
|
||||
const container = docker.getContainer(containerName);
|
||||
const info = await container.inspect();
|
||||
|
||||
return res.status(200).json({
|
||||
status: info.State.Status,
|
||||
});
|
||||
} catch {
|
||||
return res.status(500).send({
|
||||
error: "unknown error",
|
||||
});
|
||||
}
|
||||
}
|
29
src/pages/api/proxy.js
Normal file
29
src/pages/api/proxy.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
function pick(object, keys) {
|
||||
return;
|
||||
}
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const headers = ["X-API-Key", "Content-Type", "Authorization"].reduce((obj, key) => {
|
||||
if (req.headers && req.headers.hasOwnProperty(key.toLowerCase())) {
|
||||
obj[key] = req.headers[key.toLowerCase()];
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
try {
|
||||
const result = await fetch(req.query.url, {
|
||||
strictSSL: false,
|
||||
rejectUnhauthorized: false,
|
||||
method: req.method,
|
||||
headers: headers,
|
||||
body: req.method == "GET" || req.method == "HEAD" ? null : req.body,
|
||||
}).then((res) => res);
|
||||
|
||||
const forward = await result.text();
|
||||
return res.status(result.status).send(forward);
|
||||
} catch {
|
||||
return res.status(500).send({
|
||||
error: "query failed",
|
||||
});
|
||||
}
|
||||
}
|
27
src/pages/api/services/index.js
Normal file
27
src/pages/api/services/index.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import yaml from "js-yaml";
|
||||
import checkAndCopyConfig from "utils/config";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
checkAndCopyConfig("services.yaml");
|
||||
|
||||
const servicesYaml = path.join(process.cwd(), "config", "services.yaml");
|
||||
const fileContents = await fs.readFile(servicesYaml, "utf8");
|
||||
const services = yaml.load(fileContents);
|
||||
|
||||
// map easy to write YAML objects into easy to consume JS arrays
|
||||
const servicesArray = services.map((group) => {
|
||||
return {
|
||||
name: Object.keys(group)[0],
|
||||
services: group[Object.keys(group)[0]].map((entries) => {
|
||||
return {
|
||||
name: Object.keys(entries)[0],
|
||||
...entries[Object.keys(entries)[0]],
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
res.send(servicesArray);
|
||||
}
|
22
src/pages/api/widgets/index.js
Normal file
22
src/pages/api/widgets/index.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import yaml from "js-yaml";
|
||||
import checkAndCopyConfig from "utils/config";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
checkAndCopyConfig("widgets.yaml");
|
||||
|
||||
const widgetsYaml = path.join(process.cwd(), "config", "widgets.yaml");
|
||||
const fileContents = await fs.readFile(widgetsYaml, "utf8");
|
||||
const widgets = yaml.load(fileContents);
|
||||
|
||||
// map easy to write YAML objects into easy to consume JS arrays
|
||||
const widgetsArray = widgets.map((group) => {
|
||||
return {
|
||||
type: Object.keys(group)[0],
|
||||
options: { ...group[Object.keys(group)[0]] },
|
||||
};
|
||||
});
|
||||
|
||||
res.send(widgetsArray);
|
||||
}
|
14
src/pages/api/widgets/resources.js
Normal file
14
src/pages/api/widgets/resources.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { cpu, drive, mem, netstat } from "node-os-utils";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { disk } = req.query;
|
||||
|
||||
res.send({
|
||||
cpu: {
|
||||
usage: await cpu.usage(),
|
||||
load: cpu.loadavgTime(5),
|
||||
},
|
||||
drive: await drive.info(disk || "/"),
|
||||
memory: await mem.info(),
|
||||
});
|
||||
}
|
9
src/pages/api/widgets/weather.js
Normal file
9
src/pages/api/widgets/weather.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import cachedFetch from "utils/cached-fetch";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { lat, lon, apiKey, duration } = req.query;
|
||||
|
||||
const api_url = `http://api.weatherapi.com/v1/current.json?q=${lat},${lon}&key=${apiKey}`;
|
||||
|
||||
res.send(await cachedFetch(api_url, duration));
|
||||
}
|
58
src/pages/index.js
Normal file
58
src/pages/index.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
import useSWR from "swr";
|
||||
import Head from "next/head";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import { ThemeProvider } from "utils/theme-context";
|
||||
|
||||
import ServicesGroup from "components/services/group";
|
||||
import BookmarksGroup from "components/bookmarks/group";
|
||||
import Widget from "components/widget";
|
||||
|
||||
const ThemeToggle = dynamic(() => import("components/theme-toggle"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function Home() {
|
||||
const { data: services, error: servicesError } = useSWR("/api/services");
|
||||
const { data: bookmarks, error: bookmarksError } = useSWR("/api/bookmarks");
|
||||
const { data: widgets, error: widgetsError } = useSWR("/api/widgets");
|
||||
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<Head>
|
||||
<title>Welcome</title>
|
||||
</Head>
|
||||
<div className="w-full container m-auto flex flex-col h-screen justify-between">
|
||||
<div className="flex flex-wrap m-8 pb-4 mt-10 border-b-2 border-theme-800 dark:border-theme-200">
|
||||
{widgets && (
|
||||
<>
|
||||
{widgets.map((widget) => (
|
||||
<Widget key={widget.type} widget={widget} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{services && (
|
||||
<div className="flex flex-wrap p-8 items-start">
|
||||
{services.map((group) => (
|
||||
<ServicesGroup key={group.name} services={group} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{bookmarks && (
|
||||
<div className="grow flex flex-wrap pt-0 p-8">
|
||||
{bookmarks.map((group) => (
|
||||
<BookmarksGroup key={group.name} group={group} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="rounded-full flex p-8 w-full justify-end">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue