funny curl
This commit is contained in:
@@ -1,4 +1,25 @@
|
|||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
import curlResponse from './src/curl-response.ts';
|
||||||
|
|
||||||
|
const curlPreview = {
|
||||||
|
name: 'curl-preview',
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use(async (request, response, next) => {
|
||||||
|
const userAgent = request.headers['user-agent'] ?? '';
|
||||||
|
const path = request.url?.split('?', 1)[0];
|
||||||
|
|
||||||
|
if (request.method !== 'GET' || path !== '/' || !/^curl(?:\/|$)/i.test(userAgent)) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const terminalResponse = curlResponse();
|
||||||
|
response.statusCode = terminalResponse.status;
|
||||||
|
terminalResponse.headers.forEach((value, name) => response.setHeader(name, value));
|
||||||
|
response.end(await terminalResponse.text());
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://rummage.cc',
|
site: 'https://rummage.cc',
|
||||||
@@ -7,4 +28,7 @@ export default defineConfig({
|
|||||||
port: 4321,
|
port: 4321,
|
||||||
},
|
},
|
||||||
devToolbar: { enabled: false },
|
devToolbar: { enabled: false },
|
||||||
|
vite: {
|
||||||
|
plugins: [curlPreview],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import curlResponse from "../src/curl-response.ts";
|
||||||
|
|
||||||
|
interface PagesContext {
|
||||||
|
request: Request;
|
||||||
|
next(): Promise<Response>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onRequest(context: PagesContext): Response | Promise<Response> {
|
||||||
|
const { request } = context;
|
||||||
|
const userAgent = request.headers.get("user-agent") ?? "";
|
||||||
|
const pathname = new URL(request.url).pathname;
|
||||||
|
|
||||||
|
if (
|
||||||
|
request.method === "GET" &&
|
||||||
|
pathname === "/" &&
|
||||||
|
/^curl(?:\/|$)/i.test(userAgent)
|
||||||
|
) {
|
||||||
|
return curlResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.next();
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import { practice } from "./data/practice.ts";
|
||||||
|
import { projects, websites } from "./data/projects.ts";
|
||||||
|
import { tools } from "./data/tools.ts";
|
||||||
|
import { work } from "./data/work.ts";
|
||||||
|
|
||||||
|
const width = 86;
|
||||||
|
const esc = "\x1b";
|
||||||
|
const color = {
|
||||||
|
reset: `${esc}[0m`,
|
||||||
|
bold: `${esc}[1m`,
|
||||||
|
purple: `${esc}[38;5;141m`,
|
||||||
|
blue: `${esc}[38;5;117m`,
|
||||||
|
amber: `${esc}[38;5;215m`,
|
||||||
|
green: `${esc}[38;5;114m`,
|
||||||
|
text: `${esc}[38;5;252m`,
|
||||||
|
muted: `${esc}[38;5;245m`,
|
||||||
|
border: `${esc}[38;5;240m`,
|
||||||
|
};
|
||||||
|
|
||||||
|
type DisplayLine = string | { text: string; style?: string };
|
||||||
|
|
||||||
|
function hyperlink(label: string, url: string): string {
|
||||||
|
return `${esc}]8;;${url}${esc}\\${color.blue}${esc}[4m${label}${esc}[24m${esc}]8;;${esc}\\`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function quietHyperlink(label: string, url: string): string {
|
||||||
|
return `${esc}]8;;${url}${esc}\\${label}${esc}]8;;${esc}\\`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decorateLinks(text: string): string {
|
||||||
|
return text.replace(
|
||||||
|
/https?:\/\/[^\s]+|[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}/g,
|
||||||
|
(value) => hyperlink(value, value.includes("@") ? `mailto:${value}` : value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrap(text: string, lineWidth = width): string[] {
|
||||||
|
const words = text.split(/\s+/);
|
||||||
|
const lines: string[] = [];
|
||||||
|
let line = "";
|
||||||
|
|
||||||
|
for (const word of words) {
|
||||||
|
if (`${line} ${word}`.trim().length > lineWidth) {
|
||||||
|
lines.push(line);
|
||||||
|
line = word;
|
||||||
|
} else {
|
||||||
|
line = `${line} ${word}`.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line) lines.push(line);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
function section(title: string, lines: DisplayLine[]): string {
|
||||||
|
const label = ` ${title} `;
|
||||||
|
const top = `┌─${label}${"─".repeat(width - label.length - 1)}┐`;
|
||||||
|
const body = lines
|
||||||
|
.flatMap((entry) => {
|
||||||
|
const line = typeof entry === "string" ? entry : entry.text;
|
||||||
|
const style = typeof entry === "string" ? color.text : entry.style;
|
||||||
|
return (line ? wrap(line, width - 4) : [""]).map((text) => ({
|
||||||
|
text,
|
||||||
|
style,
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.map(({ text, style }) => {
|
||||||
|
const content = decorateLinks(text.padEnd(width - 2));
|
||||||
|
return `${color.border}│${color.reset} ${style ?? ""}${content}${color.reset} ${color.border}│${color.reset}`;
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const styledTop = top.replace(label, `${color.bold}${color.purple}${label}${color.reset}${color.border}`);
|
||||||
|
return `${color.border}${styledTop}${color.reset}\n${body}\n${color.border}└${"─".repeat(width)}┘${color.reset}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const experience = work.flatMap((role, index) => [
|
||||||
|
{ text: role.title.toUpperCase(), style: `${color.bold}${color.purple}` },
|
||||||
|
{ text: `${role.company} · ${role.years}`, style: color.amber },
|
||||||
|
{ text: role.description, style: color.text },
|
||||||
|
...(index < work.length - 1 ? [""] : []),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const projectList = [...projects, ...websites].flatMap((project, index, all) => [
|
||||||
|
{ text: project.name.toUpperCase(), style: `${color.bold}${color.purple}` },
|
||||||
|
{ text: `${project.type} · ${project.url}`, style: color.amber },
|
||||||
|
{ text: project.description, style: color.text },
|
||||||
|
...(index < all.length - 1 ? [""] : []),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const competencies = practice.flatMap((area, index) => [
|
||||||
|
{ text: area.title.toUpperCase(), style: `${color.bold}${color.purple}` },
|
||||||
|
{ text: area.body, style: color.text },
|
||||||
|
...(index < practice.length - 1 ? [""] : []),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const proficiencyOrder = [
|
||||||
|
"Advanced",
|
||||||
|
"Proficient",
|
||||||
|
"Working knowledge",
|
||||||
|
"Familiar",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const skills = proficiencyOrder.flatMap((level, index) => {
|
||||||
|
const names = tools
|
||||||
|
.filter((tool) => tool.proficiency === level)
|
||||||
|
.map((tool) => tool.name)
|
||||||
|
.join(" · ");
|
||||||
|
return [
|
||||||
|
{ text: level.toUpperCase(), style: `${color.bold}${color.purple}` },
|
||||||
|
{ text: names, style: color.text },
|
||||||
|
...(index < proficiencyOrder.length - 1 ? [""] : []),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = `
|
||||||
|
${color.purple} ▄████▄ ▄▄▄▄▄▄▄▄
|
||||||
|
▄██▀▀██▄ ███▀▀▀███ ${color.bold}${color.text}OWEN RUMMAGE${color.reset}
|
||||||
|
${color.purple} ███ ███ ███▄▄▄██▀ ${color.amber}Systems engineer${color.reset}
|
||||||
|
${color.purple} ▀██▄▄██▀ ███ ▀██▄ ${color.muted}Nashville, Tennessee${color.reset}
|
||||||
|
${color.purple} ▀████▀ ███ ███
|
||||||
|
${color.border} ──────────────────────── ${color.green}Linux · networks · cloud · automation${color.reset}
|
||||||
|
|
||||||
|
${color.text} Systems engineer specializing in Linux infrastructure, network engineering,
|
||||||
|
cloud platforms, and automation. Based in Nashville, Tennessee.${color.reset}
|
||||||
|
|
||||||
|
${hyperlink("hello@rummage.cc", "mailto:hello@rummage.cc")} ${hyperlink("github.com/owenrummage", "https://github.com/owenrummage")}
|
||||||
|
${hyperlink("linkedin.com/in/orummage", "https://linkedin.com/in/orummage")} ${hyperlink("rummage.cc", "https://rummage.cc")}
|
||||||
|
|
||||||
|
${section("EXPERIENCE", experience)}
|
||||||
|
|
||||||
|
${section("EDUCATION", [
|
||||||
|
{ text: "ASSOCIATE OF APPLIED SCIENCE IN COMPUTER INFORMATION TECHNOLOGY", style: `${color.bold}${color.purple}` },
|
||||||
|
{ text: "Nashville State Community College · 2023 - 2026", style: color.amber },
|
||||||
|
{ text: "Concentration in Systems Administration and Management. Graduated May 2026.", style: color.text },
|
||||||
|
])}
|
||||||
|
|
||||||
|
${section("PROJECTS", projectList)}
|
||||||
|
|
||||||
|
${section("CORE COMPETENCIES", competencies)}
|
||||||
|
|
||||||
|
${section("TECHNICAL SKILLS", skills)}
|
||||||
|
|
||||||
|
${section("APPROACH", [
|
||||||
|
{ text: "I build systems that can be understood and maintained by someone other than me.", style: `${color.bold}${color.green}` },
|
||||||
|
])}
|
||||||
|
|
||||||
|
${color.muted} $${color.reset} ${color.text}curl rummage.cc${color.reset} ${color.muted}Get this page${color.reset}
|
||||||
|
${color.muted} $${color.reset} ${color.text}open ${hyperlink("https://rummage.cc", "https://rummage.cc")}${color.reset} ${color.muted}Visit the web version${color.reset}
|
||||||
|
\n${color.muted} Inspired by Dave Eddy's ${quietHyperlink("ysap.sh", "https://ysap.sh")}.${color.reset}
|
||||||
|
${color.reset}`;
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return new Response(page, {
|
||||||
|
headers: {
|
||||||
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
||||||
|
"Content-Type": "text/plain; charset=utf-8",
|
||||||
|
Vary: "User-Agent",
|
||||||
|
"X-Content-Type-Options": "nosniff",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user