port to Astro
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6e1c163a147b14b92a5f6ae4de6a87206a6a6964
This commit is contained in:
parent
69e9d63be7
commit
f25cfa3e7e
14 changed files with 5125 additions and 200 deletions
46
.gitignore
vendored
46
.gitignore
vendored
|
|
@ -1,42 +1,4 @@
|
|||
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
||||
#
|
||||
# If you find yourself ignoring temporary files generated by your text editor
|
||||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile '~/.gitignore_global'
|
||||
|
||||
# Ignore bundler config.
|
||||
/.bundle
|
||||
|
||||
# Ignore all environment files (except templates).
|
||||
/.env*
|
||||
!/.env*.erb
|
||||
|
||||
# Ignore all logfiles and tempfiles.
|
||||
/log/*
|
||||
/tmp/*
|
||||
!/log/.keep
|
||||
!/tmp/.keep
|
||||
|
||||
# Ignore pidfiles, but keep the directory.
|
||||
/tmp/pids/*
|
||||
!/tmp/pids/
|
||||
!/tmp/pids/.keep
|
||||
|
||||
# Ignore storage (uploaded files in development and any SQLite databases).
|
||||
/storage/*
|
||||
!/storage/.keep
|
||||
/tmp/storage/*
|
||||
!/tmp/storage/
|
||||
!/tmp/storage/.keep
|
||||
|
||||
/public/assets
|
||||
|
||||
# Ignore master key for decrypting credentials and more.
|
||||
/config/master.key
|
||||
|
||||
# JetBrains can you not
|
||||
.idea/*
|
||||
/tmp
|
||||
|
||||
# VScode can you not
|
||||
.vscode/*
|
||||
/.astro
|
||||
/.direnv
|
||||
dist/
|
||||
result*
|
||||
|
|
|
|||
34
astro.config.ts
Normal file
34
astro.config.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { defineConfig, fontProviders } from 'astro/config';
|
||||
import node from '@astrojs/node';
|
||||
import icon from 'astro-icon';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: node({
|
||||
mode: 'standalone',
|
||||
}),
|
||||
|
||||
server: {
|
||||
host: true,
|
||||
},
|
||||
|
||||
integrations: [icon()],
|
||||
|
||||
fonts: [
|
||||
{
|
||||
name: 'Fira Code',
|
||||
cssVariable: '--font-fira-code',
|
||||
provider: fontProviders.local(),
|
||||
options: {
|
||||
variants: [
|
||||
{
|
||||
src: ['./src/assets/fonts/FiraCode-Regular.woff2'],
|
||||
weight: 'normal',
|
||||
style: 'normal',
|
||||
},
|
||||
],
|
||||
fallbacks: ['monospace'],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
146
index.php
146
index.php
|
|
@ -1,146 +0,0 @@
|
|||
<?php
|
||||
|
||||
function getUsers()
|
||||
{
|
||||
$usernames = array();
|
||||
|
||||
// Get all users in /home directory
|
||||
$users = glob("/home/*");
|
||||
|
||||
foreach ($users as $user) {
|
||||
if (is_dir($user)) {
|
||||
if (file_exists("$user/public_html")) {
|
||||
$username = basename($user);
|
||||
$usernames[] = $username;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $usernames;
|
||||
}
|
||||
|
||||
// Execute the function and display results
|
||||
$usernames = getUsers();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>frzn.dev</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/assets/master.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="header">
|
||||
<pre>
|
||||
_______ _____
|
||||
| ___|.----.-----.-----.-----.-----.| \.-----.--.--.
|
||||
| ___|​| _| _ |-- __| -__| |​| -- | -__| | |
|
||||
|___| |__| |_____|_____|_____|__|__|​|_____/|_____|\___/</pre>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div>
|
||||
<div class="section tor-notice">
|
||||
<pre>
|
||||
+----------------------------------------------------------------+
|
||||
| This site is accessible via Tor! |
|
||||
| <a href="http://frzndev32nhnla77oozhxhz5yzo4abldr6zbc4qkdh5hcyanizlxs2ad.onion/">frzndev32nhnla77oozhxhz5yzo4abldr6zbc4qkdh5hcyanizlxs2ad.onion</a> |
|
||||
+----------------------------------------------------------------+
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<p>Members</p>
|
||||
<ul class="members">
|
||||
<?php if (count($usernames) > 0) { ?>
|
||||
<?php foreach ($usernames as $username) { ?>
|
||||
<li><a href="/~<?= $username ?>">~<?= $username ?></a><?= $username == "amr" ? "*" : "" ?></li>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<li class="error">Error fetching members!</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<small>
|
||||
* owner & admin
|
||||
</small>
|
||||
</div>
|
||||
<div class="section">
|
||||
<p>Services</p>
|
||||
<table class="services">
|
||||
<tr>
|
||||
<td><a href="https://git.frzn.dev">git.frzn.dev</a></td>
|
||||
<td>Forgejo<sup>1</sup></td>
|
||||
<td>A lightweight git server (which is better than Gogs)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://p.frzn.dev">p.frzn.dev</a></td>
|
||||
<td>fiche</td>
|
||||
<td>A command line pastebin (similar to termbin)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://pb.frzn.dev">pb.frzn.dev</a></td>
|
||||
<td>PrivateBin</td>
|
||||
<td>A minimalist, open source online pastebin</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://bitwarden.frzn.dev/">bitwarden.frzn.dev</a></td>
|
||||
<td>Vaultwarden<sup>1</sup></td>
|
||||
<td>A Bitwarden-compatible server written in Rust</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://snowflake.torproject.org/">Snowflake</a></td>
|
||||
<td></td>
|
||||
<td>A web proxy server</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://crypt.frzn.dev">crypt.frzn.dev</a></td>
|
||||
<td>CryptPad</td>
|
||||
<td>An open-source web-based encrypted suite of realtime collaborative editors</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>frzn.dev:64738</td>
|
||||
<td>Mumble<sup>1,2</sup></td>
|
||||
<td>A VoIP server</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://irc.frozenelectronics.ca:8088/">frzn.dev:6697/6667</a></td>
|
||||
<td>ZNC<sup>1</sup></td>
|
||||
<td>An IRC bouncer</td>
|
||||
</tr>
|
||||
</table>
|
||||
<small>
|
||||
<sup>1</sup> Only available for existing members<br>
|
||||
<sup>2</sup> Not running 24/7
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<div class="section">
|
||||
<p>System Info</p>
|
||||
<table class="system-info">
|
||||
<tr>
|
||||
<td>time:</td>
|
||||
<td><?= gmdate("l, j F Y g:i:s A (e)") ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>os:</td>
|
||||
<td>Ubuntu GNU/Linux 24.04 (LTS)</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<hr>
|
||||
(c) frzn.dev 2018 - <?= date('Y') ?> / design and "backend" by <a href="/~roscoe">~roscoe</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
23
package.json
Normal file
23
package.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "frzn.dev",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"fmt": "prettier --write .",
|
||||
"fmt:check": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^10.0.4",
|
||||
"@iconify-json/fa": "^1.2.2",
|
||||
"@iconify-json/fa-solid": "^1.2.2",
|
||||
"astro": "^6.1.5",
|
||||
"astro-icon": "^1.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.8.1"
|
||||
}
|
||||
}
|
||||
4785
pnpm-lock.yaml
generated
Normal file
4785
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
33
src/components/MemberList.astro
Normal file
33
src/components/MemberList.astro
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
interface Props {
|
||||
members: string[];
|
||||
}
|
||||
|
||||
const { members } = Astro.props;
|
||||
|
||||
const owner = "amr";
|
||||
const admins = ["raf", "rocoe"];
|
||||
---
|
||||
|
||||
<div class="section">
|
||||
<p>Members</p>
|
||||
<ul class="members">
|
||||
{members.length > 0 ? (
|
||||
members.map(username => (
|
||||
<li>
|
||||
<a href={`/~${username}`}>~{username}</a>
|
||||
{username === owner && (
|
||||
<Icon name="fa-solid:crown" class="member-icon owner" title="Owner" />
|
||||
)}
|
||||
{admins.includes(username) && username !== owner && (
|
||||
<Icon name="fa-solid:shield-halved" class="member-icon admin" title="Admin" />
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<li class="error">Error fetching members!</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
32
src/components/ServicesTable.astro
Normal file
32
src/components/ServicesTable.astro
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
interface Service {
|
||||
url: string;
|
||||
name: string;
|
||||
desc: string;
|
||||
requiresMember: boolean;
|
||||
special?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
services: Service[];
|
||||
}
|
||||
|
||||
const { services } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="section">
|
||||
<p>Services</p>
|
||||
<table class="services">
|
||||
{services.map(service => (
|
||||
<tr>
|
||||
<td>{service.url ? <a href={service.url}>{service.special || service.url}</a> : service.special}</td>
|
||||
<td>{service.name}</td>
|
||||
<td>{service.desc}</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
<small>
|
||||
<sup>1</sup> Only available for existing members<br>
|
||||
<sup>2</sup> Not running 24/7
|
||||
</small>
|
||||
</div>
|
||||
21
src/components/SystemInfo.astro
Normal file
21
src/components/SystemInfo.astro
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
interface Props {
|
||||
currentDate: string;
|
||||
}
|
||||
|
||||
const { currentDate } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="section">
|
||||
<p>System Info</p>
|
||||
<table class="system-info">
|
||||
<tr>
|
||||
<td>time:</td>
|
||||
<td>{currentDate}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>os:</td>
|
||||
<td>Ubuntu GNU/Linux 24.04 (LTS)</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
12
src/components/TorNotice.astro
Normal file
12
src/components/TorNotice.astro
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="section tor-notice">
|
||||
<pre>
|
||||
+----------------------------------------------------------------+
|
||||
| This site is accessible via Tor! |
|
||||
| <a href="http://frzndev32nhnla77oozhxhz5yzo4abldr6zbc4qkdh5hcyanizlxs2ad.onion/">frzndev32nhnla77oozhxhz5yzo4abldr6zbc4qkdh5hcyanizlxs2ad.onion</a> |
|
||||
+----------------------------------------------------------------+
|
||||
</pre>
|
||||
</div>
|
||||
45
src/layouts/Layout.astro
Normal file
45
src/layouts/Layout.astro
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
import { Font } from "astro:assets";
|
||||
import "@/styles/global.css";
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
currentYear?: number;
|
||||
}
|
||||
|
||||
const {
|
||||
title = "frzn.dev",
|
||||
currentYear = new Date().getFullYear()
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>{title}</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<Font cssVariable="--font-fira-code" preload />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="header">
|
||||
<pre>
|
||||
_______ _____
|
||||
| ___|.----.-----.-----.-----.-----.| \.-----.--.--.
|
||||
| ___|| _| _ |-- __| -__| || -- | -__| | |
|
||||
|___| |__| |_____|_____|_____|__|__||_____/|_____|\___/</pre>
|
||||
</div>
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="footer">
|
||||
<hr>
|
||||
(c) frzn.dev 2018 - {currentYear} / design and "backend" by <a href="/~roscoe">~roscoe</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
79
src/pages/index.astro
Normal file
79
src/pages/index.astro
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import TorNotice from '../components/TorNotice.astro';
|
||||
import MemberList from '../components/MemberList.astro';
|
||||
import ServicesTable from '../components/ServicesTable.astro';
|
||||
import SystemInfo from '../components/SystemInfo.astro';
|
||||
import { readdirSync, statSync } from "node:fs";
|
||||
|
||||
function getUsers(): string[] {
|
||||
const usernames: string[] = [];
|
||||
|
||||
let users;
|
||||
try {
|
||||
users = readdirSync("/home", { withFileTypes: true });
|
||||
} catch {
|
||||
return usernames;
|
||||
}
|
||||
|
||||
for (const user of users) {
|
||||
if (user.isDirectory()) {
|
||||
try {
|
||||
statSync(`/home/${user.name}/public_html`);
|
||||
usernames.push(user.name);
|
||||
} catch {
|
||||
// public_html doesn't exist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return usernames;
|
||||
}
|
||||
|
||||
const users = getUsers();
|
||||
|
||||
// Services data
|
||||
const services = [
|
||||
{ url: "https://git.frzn.dev", name: "Forgejo", desc: "A lightweight git server (which is better than Gogs)", requiresMember: true },
|
||||
{ url: "https://p.frzn.dev", name: "fiche", desc: "A command line pastebin (similar to termbin)", requiresMember: false },
|
||||
{ url: "https://pb.frzn.dev", name: "PrivateBin", desc: "A minimalist, open source online pastebin", requiresMember: false },
|
||||
{ url: "https://bitwarden.frzn.dev/", name: "Vaultwarden", desc: "A Bitwarden-compatible server written in Rust", requiresMember: true },
|
||||
{ url: "https://snowflake.torproject.org/", name: "", desc: "A web proxy server", requiresMember: false },
|
||||
{ url: "https://crypt.frzn.dev", name: "CryptPad", desc: "An open-source web-based encrypted suite of realtime collaborative editors", requiresMember: false },
|
||||
{ url: "", name: "Mumble", desc: "A VoIP server", requiresMember: true, special: "frzn.dev:64738 (Not running 24/7)" },
|
||||
{ url: "https://irc.frozenelectronics.ca:8088/", name: "ZNC", desc: "An IRC bouncer", requiresMember: true, special: "frzn.dev:6697/6667" },
|
||||
];
|
||||
|
||||
function formatDate(date: Date): string {
|
||||
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||
|
||||
const dayName = days[date.getUTCDay()];
|
||||
const day = date.getUTCDate();
|
||||
const monthName = months[date.getUTCMonth()];
|
||||
const year = date.getUTCFullYear();
|
||||
|
||||
let hours = date.getUTCHours();
|
||||
const ampm = hours >= 12 ? "PM" : "AM";
|
||||
hours = hours % 12 || 12;
|
||||
|
||||
const minutes = date.getUTCMinutes().toString().padStart(2, "0");
|
||||
const seconds = date.getUTCSeconds().toString().padStart(2, "0");
|
||||
|
||||
return `${dayName}, ${day} ${monthName} ${year} ${hours}:${minutes}:${seconds} ${ampm} (UTC)`;
|
||||
}
|
||||
|
||||
const currentDate = formatDate(new Date());
|
||||
const currentYear = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<Layout title="frzn.dev" currentYear={currentYear}>
|
||||
<div>
|
||||
<TorNotice />
|
||||
<MemberList members={users} />
|
||||
<ServicesTable services={services} />
|
||||
</div>
|
||||
<div>
|
||||
<SystemInfo currentDate={currentDate} />
|
||||
</div>
|
||||
</Layout>
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
@font-face {
|
||||
font-family: "FiraCode";
|
||||
src: url("/assets/FiraCode-Regular.woff2") format("woff2");
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: #11111b;
|
||||
--foreground: #cdd6f4;
|
||||
--links: #89b4fa;
|
||||
--font-fira-code: 'Fira Code', monospace;
|
||||
}
|
||||
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
height: 98%;
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +22,7 @@ body {
|
|||
|
||||
body,
|
||||
pre {
|
||||
font-family: "FiraCode", monospace;
|
||||
font-family: var(--font-fira-code);
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
@ -49,7 +46,7 @@ li {
|
|||
}
|
||||
|
||||
li:before {
|
||||
content: "-";
|
||||
content: '-';
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +67,7 @@ pre {
|
|||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 0px;
|
||||
grid-row-gap: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
min-width: 20em;
|
||||
|
|
@ -90,11 +87,35 @@ ul.members li:before {
|
|||
}
|
||||
|
||||
ul.members li.error {
|
||||
color: lightcoral
|
||||
color: lightcoral;
|
||||
}
|
||||
|
||||
ul.members li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
ul.members li a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.member-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.member-icon.owner {
|
||||
color: #f9e2af;
|
||||
}
|
||||
|
||||
.member-icon.admin {
|
||||
color: #a6e3a1;
|
||||
}
|
||||
|
||||
ul.sidebar-links li:before {
|
||||
content: ">";
|
||||
content: '>';
|
||||
}
|
||||
|
||||
body > div {
|
||||
|
|
@ -122,7 +143,7 @@ table.services tr td:first-child {
|
|||
}
|
||||
|
||||
table.services tr td:nth-child(2)::before {
|
||||
content: "- ";
|
||||
content: '- ';
|
||||
}
|
||||
|
||||
table.services tr td:last-child {
|
||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"outDir": "dist/",
|
||||
"declaration": true,
|
||||
"composite": true,
|
||||
"noEmit": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react",
|
||||
"verbatimModuleSyntax": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "@astrojs/ts-plugin"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"include": [".astro/types.d.ts", "src/**/*.d.ts"],
|
||||
"exclude": ["dist", "result", "node_modules"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue