mirror of
https://github.com/Myzel394/amiopen.now.git
synced 2025-06-18 15:35:27 +02:00
fix: Improvements
This commit is contained in:
parent
ce52341ad8
commit
82774d656d
29
.github/workflows/release.yaml
vendored
29
.github/workflows/release.yaml
vendored
@ -1,22 +1,21 @@
|
|||||||
name: Build and release
|
name: Build and release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Add SSH identity key
|
- name: Add SSH identity key
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||||
chmod 600 ~/.ssh/id_rsa
|
chmod 600 ~/.ssh/id_rsa
|
||||||
ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts
|
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: Build & upload image
|
||||||
|
run: ./upload_to_server.sh ${{ secrets.SERVER_IP }} ${{ secrets.SSH_USER }}
|
||||||
|
15
Dockerfile
Normal file
15
Dockerfile
Normal 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
61
flake.lock
generated
Normal 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
23
flake.nix
Normal 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
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
@ -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"]
|
|
||||||
|
|
@ -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
14
src/index.ts
Normal 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,
|
||||||
|
});
|
23
src/middlewares/presentation.ts
Normal file
23
src/middlewares/presentation.ts
Normal 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;
|
@ -1,9 +1,9 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { getConnInfo } from "hono/bun";
|
|
||||||
import connectToAddress from "../utils/connect-to-address";
|
import connectToAddress from "../utils/connect-to-address";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import * as IP from "ip";
|
import * as IP from "ip";
|
||||||
import realIP from "../middlewares/real-ip";
|
import realIP from "../middlewares/real-ip";
|
||||||
|
import presentation from "../middlewares/presentation";
|
||||||
|
|
||||||
export const portRoute = new Hono();
|
export const portRoute = new Hono();
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ const schema = z.object({
|
|||||||
.pipe(z.number().min(100).max(60_000)),
|
.pipe(z.number().min(100).max(60_000)),
|
||||||
});
|
});
|
||||||
|
|
||||||
portRoute.get("/:port", realIP, async context => {
|
portRoute.get("/:port", realIP, presentation, async context => {
|
||||||
const rawData = {
|
const rawData = {
|
||||||
ip: context.get("ip"),
|
ip: context.get("ip"),
|
||||||
port: context.req.param("port"),
|
port: context.req.param("port"),
|
12
src/routes/root.ts
Normal file
12
src/routes/root.ts
Normal 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
46
src/utils/renderer.ts
Normal 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
1
static/copy.svg
Normal 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
140
templates/index.html
Normal 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"><port></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"><ip address></span>/<span class="input"><port></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"><port></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
1
templates/index.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ip}}
|
Loading…
x
Reference in New Issue
Block a user