Compare commits
5 commits
master
...
notashelf/
| Author | SHA1 | Date | |
|---|---|---|---|
|
cd86aa4bd4 |
|||
|
c208da2ec5 |
|||
|
e81736426f |
|||
|
91119c8774 |
|||
|
f25cfa3e7e |
18 changed files with 5210 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.
|
/.astro
|
||||||
#
|
/.direnv
|
||||||
# If you find yourself ignoring temporary files generated by your text editor
|
dist/
|
||||||
# or operating system, you probably want to add a global ignore instead:
|
result*
|
||||||
# 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/*
|
|
||||||
|
|
|
||||||
28
.prettierignore
Normal file
28
.prettierignore
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Debugging
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Things handled by other linters
|
||||||
|
*.md
|
||||||
|
*.yaml
|
||||||
|
*.yml
|
||||||
|
*.json
|
||||||
|
!.prettierrc
|
||||||
|
!.package.json
|
||||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 100,
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
37
README.md
Normal file
37
README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# frzn.dev
|
||||||
|
|
||||||
|
Hackerspace portal & our glorious homepage.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Node.js 22+ (any recent version should just work)
|
||||||
|
- pnpm
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install all dependencies
|
||||||
|
$ pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Common tasks
|
||||||
|
$ pnpm dev # Start development server (http://localhost:4321)
|
||||||
|
$ pnpm build # Build for production
|
||||||
|
$ pnpm preview # Preview production build
|
||||||
|
$ pnpm fmt # Format code
|
||||||
|
$ pnpm fmt:check # Check formatting
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
[Astro]: https://astro.build/
|
||||||
|
[@astrojs/node]: https://docs.astro.build/en/guides/integrations-guide/node/
|
||||||
|
|
||||||
|
This site is built with [Astro], using Astro 6.x with server-side rendering
|
||||||
|
features powered by [@astrojs/node]. The stack is relatively simple, and should
|
||||||
|
be easily extensible without sacrificing from maintainability in the long term.
|
||||||
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
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
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>
|
||||||
25
src/components/TorNotice.astro
Normal file
25
src/components/TorNotice.astro
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
torUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { torUrl } = Astro.props;
|
||||||
|
|
||||||
|
const headerText = "This site is accessible via Tor!";
|
||||||
|
const maxLine = torUrl.length > headerText.length ? torUrl.length : headerText.length;
|
||||||
|
|
||||||
|
// Account for padding and borders: +2 for borders, +2 for padding spaces
|
||||||
|
const tableWidth = maxLine + 4;
|
||||||
|
|
||||||
|
const borderTop = "+" + "-".repeat(tableWidth) + "+";
|
||||||
|
const borderBottom = "+" + "-".repeat(tableWidth) + "+";
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="section tor-notice">
|
||||||
|
<pre>
|
||||||
|
{borderTop}
|
||||||
|
|{" ".repeat(Math.floor((tableWidth - headerText.length) / 2))}{headerText}{" ".repeat(Math.ceil((tableWidth - headerText.length) / 2))}|
|
||||||
|
|{" ".repeat(Math.floor((tableWidth - torUrl.length) / 2))}<a href={torUrl}>{torUrl}</a>{" ".repeat(Math.ceil((tableWidth - torUrl.length) / 2))}|
|
||||||
|
{borderBottom}
|
||||||
|
</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 torUrl="http://frzndev32nhnla77oozhxhz5yzo4abldr6zbc4qkdh5hcyanizlxs2ad.onion/" />
|
||||||
|
<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 {
|
:root {
|
||||||
--background: #11111b;
|
--background: #11111b;
|
||||||
--foreground: #cdd6f4;
|
--foreground: #cdd6f4;
|
||||||
--links: #89b4fa;
|
--links: #89b4fa;
|
||||||
|
--font-fira-code: 'Fira Code', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
height: 98%;
|
height: 98%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,7 +22,7 @@ body {
|
||||||
|
|
||||||
body,
|
body,
|
||||||
pre {
|
pre {
|
||||||
font-family: "FiraCode", monospace;
|
font-family: var(--font-fira-code);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
|
@ -49,7 +46,7 @@ li {
|
||||||
}
|
}
|
||||||
|
|
||||||
li:before {
|
li:before {
|
||||||
content: "-";
|
content: '-';
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +67,7 @@ pre {
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
grid-column-gap: 0px;
|
grid-column-gap: 0px;
|
||||||
grid-row-gap: 0px;
|
grid-row-gap: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
min-width: 20em;
|
min-width: 20em;
|
||||||
|
|
@ -90,11 +87,35 @@ ul.members li:before {
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.members li.error {
|
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 {
|
ul.sidebar-links li:before {
|
||||||
content: ">";
|
content: '>';
|
||||||
}
|
}
|
||||||
|
|
||||||
body > div {
|
body > div {
|
||||||
|
|
@ -122,7 +143,7 @@ table.services tr td:first-child {
|
||||||
}
|
}
|
||||||
|
|
||||||
table.services tr td:nth-child(2)::before {
|
table.services tr td:nth-child(2)::before {
|
||||||
content: "- ";
|
content: '- ';
|
||||||
}
|
}
|
||||||
|
|
||||||
table.services tr td:last-child {
|
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