diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | web/renderer/index.js | 94 | ||||
| -rw-r--r-- | web/renderer/package.json | 15 | ||||
| -rwxr-xr-x | web/renderer/run.sh | 4 | ||||
| -rw-r--r-- | web/renderer/server.sh | 3 | ||||
| -rw-r--r-- | web/src/blog.tsx | 18 | ||||
| -rw-r--r-- | web/src/index-page.tsx | 3 | ||||
| -rw-r--r-- | web/src/index.html | 7 | ||||
| -rw-r--r-- | web/src/jsx.tsx | 20 | ||||
| -rw-r--r-- | web/static/main.css | 79 | ||||
| -rwxr-xr-x | web/update-remote.sh | 11 |
11 files changed, 229 insertions, 28 deletions
@@ -2,3 +2,6 @@ web/dist web/node_modules web/package-lock.json +web/renderer/cache +web/renderer/node_modules +web/renderer/package-lock.json diff --git a/web/renderer/index.js b/web/renderer/index.js new file mode 100644 index 0000000..03caa1c --- /dev/null +++ b/web/renderer/index.js @@ -0,0 +1,94 @@ +const http = require( 'http' ); +const fs = require( 'fs' ); +const path = require( 'path' ); +const crypto = require( 'crypto' ); +const puppeteer = require( 'puppeteer' ); +const express = require( 'express' ); + +const PORT = 6860; +const CACHE_DIR = path.join( __dirname, 'cache' ); + +const host = "https://networkheaven.net"; +const app = express(); + +if( !fs.existsSync( CACHE_DIR ) ) fs.mkdirSync( CACHE_DIR ); + +function hash( str ) { + return crypto.createHash( 'sha1' ).update( str ).digest( 'hex' ); +} + +let browser = null; +let cache = async ( req, res ) => { + const { url } = req; + console.log( `incoming req from ${req.headers['user-agent']} for ${url}` ); + + const urlObj = new URL( req.url, host ); + const target = urlObj.href; + + const key = hash( target ); + const file = path.join( CACHE_DIR, key + '.html' ); + + if( fs.existsSync( file ) ) { + const html = fs.readFileSync( file ); + res.writeHead( 200, { 'content-type': 'text/html' } ); + return res.end( html ); + } + + try { + const page = await browser.newPage(); + + const r = await page.goto( target, { + waitUntil: 'networkidle2', + timeout: 30000 + } ); + + const html = await page.content(); + console.log( "caching page: " + target ); + fs.writeFileSync( file, html ); + + await page.close(); + const headers = r.headers(); + + res.writeHead( 200, { + 'content-type': headers['content-type'], + 'date': headers['date'], + 'etag': headers['etag'], + } ); + res.end( html ); + + } catch( e ) { + res.writeHead( 500, { 'content-type': 'text/plain' } ); + res.end( 'render failed: ' + e.message ); + } +} + +app.get( "/", cache ); +app.get( /\/$/, cache ); +app.get( /#$/, cache ); +app.get( /^\/[^.]*$/, cache ); +app.use( async ( req, res ) => { + let url = new URL( req.url, host ); + const r = await fetch( url.href ); + + if( r.ok ) { + const html = await r.text(); + const headers = []; + for( const [k, v] of r.headers.entries() ) + headers[k] = v; + + res.writeHead( 200, { + 'content-type': headers['content-type'], + 'date': headers['date'], + 'etag': headers['etag'], + } ); + res.end( html ); + } +} ); + +(async () => { + browser = await puppeteer.launch( { headless: 'new' } ); + app.listen( PORT, async () => { + + console.log( "listening on http://localhost:" + PORT ); + } ); +})(); diff --git a/web/renderer/package.json b/web/renderer/package.json new file mode 100644 index 0000000..1b92952 --- /dev/null +++ b/web/renderer/package.json @@ -0,0 +1,15 @@ +{ + "name": "renderer", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "express": "^5.2.1", + "puppeteer": "^24.37.2" + } +} diff --git a/web/renderer/run.sh b/web/renderer/run.sh new file mode 100755 index 0000000..b67ca91 --- /dev/null +++ b/web/renderer/run.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +npm i +screen -m -d node index.js diff --git a/web/renderer/server.sh b/web/renderer/server.sh new file mode 100644 index 0000000..293c5fb --- /dev/null +++ b/web/renderer/server.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +node index.js diff --git a/web/src/blog.tsx b/web/src/blog.tsx index bfef434..1fcfec2 100644 --- a/web/src/blog.tsx +++ b/web/src/blog.tsx @@ -8,12 +8,9 @@ let reqErr = 0; let hljs = null; let hljs_imported = 0; -async function importHlJs() { - if( hljs ) return; - if( hljs_imported ) return; - hljs_imported = 1; - hljs = ( await import( 'highlight.js' ) ).default; +async function highlightCode() { + if( !hljs || !hljs_imported ) return setTimeout( highlightCode, 500 ); const elements = $( "code" ); elements.each( ( _, e ) => { @@ -21,6 +18,14 @@ async function importHlJs() { } ); } +async function importHlJs() { + if( hljs ) return; + if( hljs_imported ) return; + + hljs_imported = 1; + hljs = ( await import( 'highlight.js' ) ).default; +} + function urlForHref( href: string, isdir: boolean ) { const url = new URL( window.location.href ); let path = url.pathname; @@ -105,19 +110,20 @@ async function populatePost() { $( "title" ).html( title ); } - importHlJs(); $( "#package-entries" ).find( ".spinner" ).remove(); $( "#package-entries" ).css( "display", "none" ); $( "#blog-entry" ).html( text ); $( "#blog-entry" ).css( "display", "flex" ); $( "#back-btn" ).css( "display", "block" ); + setTimeout( highlightCode ); } catch( e ) { return populateEntries(); } } export default function Blog() { + importHlJs(); entries = []; reqErr = 0; setTimeout( async () => { diff --git a/web/src/index-page.tsx b/web/src/index-page.tsx index a754ef6..71a2fdc 100644 --- a/web/src/index-page.tsx +++ b/web/src/index-page.tsx @@ -3,6 +3,8 @@ import Home from "./home"; import Blog from "./blog"; import Pkgs from "./pkg"; +JSX.setDefaultTitle( "networkheaven.net" ); + JSX.addRoute( "/", () => <Home /> ); JSX.addRoute( "/blog", () => <Blog /> ); JSX.addRoute( "/blog/*", () => <Blog /> ); @@ -10,6 +12,7 @@ JSX.addRoute( "/pkg", () => <Pkgs /> ); JSX.addRoute( "/pkg/*", () => <Pkgs /> ); window.onpopstate = JSX.onPopState; +document.head.appendChild( <link rel="shortcut icon" href="/static/nh.ico" /> ); const url = new URL( window.location.href ); JSX.navigateParams( url.pathname, url.searchParams.entries() ); diff --git a/web/src/index.html b/web/src/index.html index b14f5b5..68eba57 100644 --- a/web/src/index.html +++ b/web/src/index.html @@ -3,11 +3,14 @@ <head> <meta charset="UTF-8"> <meta name="description" content="hi i'm aura and this is my website about stuff."> - <meta name="viewport" content="width=device-width,user-scalable=yes"> + <meta name="viewport" content="user-scalable=yes"> <title>networkheaven.net</title> <link rel="stylesheet" href="/static/main.css"> <link href="/static/highlight.css" rel="preload" as="style" onload="this.rel='stylesheet'"> - <link rel="shortcut icon" href="/static/nh.ico" type="image"> + <link rel="icon" href="data:image/png;base64,iVBORw0KGgo="> + <noscript> + <link rel="shortcut icon" href="/static/nh.ico" type="image"> + </noscript> </head> <body> <div id="moneyjsx-root"> diff --git a/web/src/jsx.tsx b/web/src/jsx.tsx index a92fc11..749e93f 100644 --- a/web/src/jsx.tsx +++ b/web/src/jsx.tsx @@ -11,8 +11,9 @@ export interface Route { const routes: Route[] = []; let err404page = "/"; let rootId = "moneyjsx-root"; -let onprenavigate: Function = () => {}; -let onpostnavigate: Function = () => {}; +let defaultTitle = ""; +export let onprenavigate: Function = () => {}; +export let onpostnavigate: Function = () => {}; function routeForPath( route: string ) : Function | null { if( !routes[route] ) { @@ -37,6 +38,10 @@ export function setRootId( rootId: string ) { rootId = rootId; } +export function setDefaultTitle( title: string ) { + defaultTitle = title; +} + /** * adds a route component to the routes list * the component function must return either a jquery or a DOM element @@ -87,6 +92,7 @@ export function navigate( route: string ) { window.history.pushState( {}, null, url.href ); onprenavigate(); + setTitle(); const el = $( cb() ); curRoute = cb; @@ -111,6 +117,7 @@ export function navigateParams( route: string, params: any ) { window.history.pushState( {}, null, url.href ); onprenavigate(); + setTitle(); const el = $( cb() ); curRoute = cb; @@ -143,6 +150,7 @@ export function navigateParamsSilent( route: string, params: any ) { return navigateSilent( err404page ); onprenavigate(); + setTitle(); const el = $( cb() ); curRoute = cb; @@ -162,6 +170,7 @@ export function navigateSilent( route: string ) { return navigateSilent( err404page ); onprenavigate(); + setTitle(); const el = $( cb() ); curRoute = cb; @@ -185,6 +194,7 @@ export function onPopState() { return; onprenavigate(); + setTitle(); const el = $( cb() ); curRoute = cb; @@ -251,7 +261,7 @@ export function createElement( tag: any, props: any, ...children: any ) { const lowercaseName = name.toLowerCase(); if( lowercaseName in window ) { - element.addEventListener( lowercaseName.substring(2 ), value ); + element.addEventListener( lowercaseName.substring( 2 ), value ); continue; } } @@ -288,3 +298,7 @@ export function createElement( tag: any, props: any, ...children: any ) { export function createFragment( props: any ) { return props.children; } + +function setTitle() { + document.title = defaultTitle; +} diff --git a/web/static/main.css b/web/static/main.css index 79f28f4..dd00386 100644 --- a/web/static/main.css +++ b/web/static/main.css @@ -44,6 +44,7 @@ margin: 0; font-weight: normal; } + input[type="radio"] { appearance: none; background-color: var( --back ); @@ -95,8 +96,8 @@ input[type="radio"]:checked::before { } code { - font-family: Code; - font-size: 13px; + font-family: Terminal; + font-size: 18px; white-space: pre-wrap; background: #212325; padding: 5px 7px !important; @@ -354,7 +355,7 @@ h5 { } #blog-entry ul { - padding-left: 20px; + padding-left: 30px; } @media( max-width: 1100px ) { @@ -375,7 +376,6 @@ h5 { } .page-title { - height: 35px; } @@ -402,8 +402,8 @@ h5 { } #sidebar h4 { - font-family: JPN12 !important; - font-size: 13px !important; + font-family: JPN12; + font-size: 13px; } #package-entries table { @@ -429,7 +429,7 @@ h5 { } #sidebar h4 { - font-size: 10px !important; + font-size: 10px; margin-bottom: 0px; margin-top: 0px; } @@ -447,18 +447,15 @@ h5 { margin-top: 7px; } - .page-title { - height: 30px; - } - #sidebar h3 { - font-size: 20px !important; + font-size: 20px; } .sidebar-row { margin-bottom: 10px; } + #package-entries table { font-family: JPN12 !important; font-size: 8px !important; @@ -477,9 +474,9 @@ h5 { } -@media( max-width: 460px ) { +@media( max-width: 500px ) { #sidebar { - width: 170px !important; + width: 150px; margin-left: 5px; margin-right: 5px; } @@ -492,18 +489,32 @@ h5 { padding-top: 10px; } + #blog-entry ul { + padding-left: 20px; + } + + #blog-entry h4 { + font-size: 13px; + } + + .page-title h3 { + font-size: 17px; + margin-top: 8px; + margin-bottom: 0px; + } + #ascii-art { font-size: 4px; } #sidebar h4 { - font-size: 8px !important; + font-size: 8px; margin-bottom: 0px; margin-top: 0px; } #sidebar h3 { - font-size: 17px !important; + font-size: 17px; } h5 { @@ -512,12 +523,46 @@ h5 { font-weight: normal; margin-top: 4px; } + + #blog-entry { + font-size: 10px; + } + + #blog-entry code { + font-size: 10px; + } } @media( max-width: 350px ) { - #ascii-art { + #ascii-art { font-size: 3px; } + + #sidebar h4 { + font-size: 6px; + margin-bottom: 0px; + margin-top: 0px; + } + + #sidebar h3 { + font-size: 14px; + } + + #blog-entry { + font-size: 7px; + } + + #blog-entry code { + font-size: 7px; + } + + .sidebar-row { + margin-bottom: 13px; + } + + #sidebar { + width: 90px; + } } diff --git a/web/update-remote.sh b/web/update-remote.sh new file mode 100755 index 0000000..a861b60 --- /dev/null +++ b/web/update-remote.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +REPLY=0 +read -p "are you sure you want to update the remote website? (y/n) " REPLY + +if [[ "$REPLY" == "y" ]]; then + for i in $(ls -1 ./dist/); do + echo "copying $i" + scp -r ./dist/$i root@networkheaven.net:/var/www/html/ + done +fi |
