fix: Improvements

This commit is contained in:
Myzel394 2024-11-20 16:21:41 +01:00
parent ce52341ad8
commit 82774d656d
No known key found for this signature in database
GPG Key ID: ED20A1D1D423AF3F
20 changed files with 352 additions and 43 deletions

View File

@ -1,22 +1,21 @@
name: Build and release
on:
release:
types: [published]
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Add SSH identity key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts
- name: Build & upload image
run: ./upload_to_server.sh ${{ secrets.SERVER_IP }} ${{ secrets.SSH_USER }}
- name: Add SSH identity key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts
- name: Build & upload image
run: ./upload_to_server.sh ${{ secrets.SERVER_IP }} ${{ secrets.SSH_USER }}

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM oven/bun
WORKDIR /app
COPY . /app
# Absolutely no idea why, but piping the output directly to the file makes it empty.
RUN mkdir -p /tmp/templates && mv /app/templates/*.html /tmp/templates/
RUN bunx html-minifier --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js true /tmp/templates/index.html > /app/templates/index.html
RUN bun install --production
EXPOSE 3000
ENTRYPOINT ["bun", "run", "start"]

Binary file not shown.

61
flake.lock generated Normal file
View File

@ -0,0 +1,61 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1731676054,
"narHash": "sha256-OZiZ3m8SCMfh3B6bfGC/Bm4x3qc1m2SVEAlkV6iY7Yg=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "5e4fbfb6b3de1aa2872b76d49fafc942626e2add",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

23
flake.nix Normal file
View File

@ -0,0 +1,23 @@
{
description = "A very basic flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, utils }: utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
html-minifier
bun
vscode-langservers-extracted
];
};
}
);
}

View File

@ -1,14 +0,0 @@
FROM oven/bun:alpine
WORKDIR /app
COPY package.json ./
COPY bun.lockb ./
COPY src ./src
RUN bun install
EXPOSE 3000
ENTRYPOINT ["bun", "run", "start"]

View File

@ -1,12 +0,0 @@
import { Hono } from "hono";
import { portRoute } from "./routes/port";
import realIP from "./middlewares/real-ip";
const app = new Hono();
app.route("/", portRoute);
Bun.serve({
...app,
idleTimeout: 90,
});

14
src/index.ts Normal file
View File

@ -0,0 +1,14 @@
import { Hono } from "hono";
import { portRoute } from "./routes/port";
import { rootRoute } from "./routes/root";
import { serveStatic } from "hono/bun";
const app = new Hono()
.use("/static/*", serveStatic({ root: "./" }))
.route("/", rootRoute)
.route("/", portRoute);
Bun.serve({
...app,
idleTimeout: 90,
});

View File

@ -0,0 +1,23 @@
import { createMiddleware } from "hono/factory";
export type PresentationType = "terminal" | "browser";
const TERMINAL_USER_AGENT = /^(curl|wget|python-urllib|pycurl|java)/i;
const presentation = createMiddleware<{
Variables: {
presentation: PresentationType;
};
}>(async (context, next) => {
const userAgent = context.req.header("User-Agent") || "";
if (TERMINAL_USER_AGENT.test(userAgent)) {
context.set("presentation", "terminal");
} else {
context.set("presentation", "browser");
}
await next();
});
export default presentation;

View File

@ -1,9 +1,9 @@
import { Hono } from "hono";
import { getConnInfo } from "hono/bun";
import connectToAddress from "../utils/connect-to-address";
import { z } from "zod";
import * as IP from "ip";
import realIP from "../middlewares/real-ip";
import presentation from "../middlewares/presentation";
export const portRoute = new Hono();
@ -34,7 +34,7 @@ const schema = z.object({
.pipe(z.number().min(100).max(60_000)),
});
portRoute.get("/:port", realIP, async context => {
portRoute.get("/:port", realIP, presentation, async context => {
const rawData = {
ip: context.get("ip"),
port: context.req.param("port"),

12
src/routes/root.ts Normal file
View File

@ -0,0 +1,12 @@
import { Hono } from "hono";
import presentation from "../middlewares/presentation";
import realIP from "../middlewares/real-ip";
import render from "../utils/renderer";
export const rootRoute = new Hono();
rootRoute.get("/", realIP, presentation, context => {
return render(context, "index", {
ip: context.get("ip"),
});
});

46
src/utils/renderer.ts Normal file
View File

@ -0,0 +1,46 @@
import { Context } from "hono";
import { PresentationType } from "../middlewares/presentation";
export default async function render(
request: Context,
templateName: "index",
ctx: Record<string, any>,
) {
const presentation = request.get("presentation");
let content = await getTemplate(templateName, presentation);
for (const [key, value] of Object.entries(ctx)) {
content = content.replaceAll(`{{${key}}}`, value);
}
switch (presentation) {
case "browser": {
return request.html(content);
}
case "terminal": {
return request.text(content);
}
}
}
const _templateCache: Record<string, string> = {};
async function getTemplate(
templateName: "index",
presentation: PresentationType,
): Promise<string> {
const extension = {
browser: ".html",
terminal: ".txt",
}[presentation];
const key = templateName + extension;
if (_templateCache[key]) {
return _templateCache[key];
}
const currentPath = await Bun.resolve(`./templates/${key}`, process.cwd());
_templateCache[key] = await Bun.file(currentPath).text();
return _templateCache[key];
}

1
static/copy.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /></svg>

After

Width:  |  Height:  |  Size: 201 B

140
templates/index.html Normal file
View File

@ -0,0 +1,140 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>amiopen.now</title>
<style type="text/css">
* {
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
background-color: #161318;
color: #eee;
margin: 0;
padding: 0;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
}
main {
background-color: #212126;
width: 100%;
max-width: 40em;
margin: 0 auto;
border-radius: 1em;
padding: 3em;
}
h2 {
margin-top: 0;
text-align: left;
}
code {
background-color: #2f2f2f;
color: #eee;
padding: 1em;
border-radius: 0.5em;
width: 100%;
display: block;
}
code .command {
color: #598eb2;
}
code .input {
color: #b27b59;
}
code .second-input {
color: #b2596e;
}
code .secondary {
color: #b2a559;
}
code .comment {
color: #5c6370;
}
.copy {
cursor: pointer;
border: none;
border-radius: 0.4em;
aspect-ratio: 1;
background: transparent;
vertical-align: middle;
transition: background-color 0.2s;
}
.copy:hover {
background: #ffffff40;
}
.copy img {
width: 1rem;
color: #fff;
filter: invert(1);
}
</style>
</head>
<body>
<main>
<h2>Am I Open</h2>
<p>
Your IP address: <strong>{{ip}}</strong>
<button class="copy" id="copy-ip">
<img width="1em" src="/static/copy.svg" alt="Copy" />
</button>
</p>
<strong> Check if your port is reachable: </strong>
<pre><code><span class="command">curl</span> amiopen.now/<span class="input">&lt;port&gt;</span>
<span class="comment">// Example</span>
<span class="command">curl</span> amiopen.now/<span class="input">80</span>
> open</code></pre>
<strong> Check if an IP address is reachable: </strong>
<pre><code><span class="command">curl</span> amiopen.now/<span class="second-input">&lt;ip address&gt;</span>/<span class="input">&lt;port&gt;</span>
<span class="comment">// Example</span>
<span class="comment">$</span> <span class="command">curl</span> amiopen.now/<span class="second-input">1.1.1.1</span>/<span class="input">53</span>
> open</code></pre>
<strong> Check if your ISP is blocking a port: </strong>
<pre><code><span class="command">telnet</span> amiopen.now <span class="input">&lt;port&gt;</span>
<span class="comment">// Example</span>
<span class="command">telnet</span> amiopen.now <span class="input">80</span></code></pre>
<i>
Hint: You can also check if you can access SSH by using:
<pre><code><span class="command">ssh</span> <span class="secondary">hello</span>@amiopen.now</code></pre>
</i>
</main>
<script defer>
const $copyButton = document.getElementById("copy-ip");
const ip = "{{ip}}";
copyButton.addEventListener("click", () => {
navigator.clipboard.writeText(ip);
});
</script>
</body>
</html>

1
templates/index.txt Normal file
View File

@ -0,0 +1 @@
{{ip}}