add static file handler. add logo, rendered icons, and manifest for browser favicon and home screen launcher. add 3 lines of js and an empty serviceworker.js so it works as a PWA.

+13
files/head.tmpl.html
··· 4 4 <head> 5 5 <meta charset="utf-8"> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"> 7 + 8 + <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> 9 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> 10 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> 11 + <link rel='shortcut icon' href='/favicon.ico'> 12 + <link rel="manifest" href="/manifest.json"> 13 + 14 + <script> 15 + if ('serviceWorker' in navigator) { 16 + navigator.serviceWorker.register("/serviceworker.js"); 17 + } 18 + </script> 19 + 7 20 <title>{{ .Title }}</title> 8 21 <style> 9 22 {{ .StyleSheet }}
files/static/android-chrome-192x192.png

This is a binary file and will not be displayed.

files/static/android-chrome-512x512.png

This is a binary file and will not be displayed.

files/static/apple-touch-icon.png

This is a binary file and will not be displayed.

files/static/favicon-16x16.png

This is a binary file and will not be displayed.

files/static/favicon-32x32.png

This is a binary file and will not be displayed.

files/static/favicon.ico

This is a binary file and will not be displayed.

+22
files/static/manifest.json
··· 1 + { 2 + "name": "vore", 3 + "short_name": "vore", 4 + "start_url": "https://vore.website", 5 + "display": "standalone", 6 + "icons": [ 7 + { 8 + "src": "/android-chrome-192x192.png", 9 + "sizes": "192x192", 10 + "type": "image/png", 11 + "purpose": "any maskable monochrome" 12 + }, 13 + { 14 + "src": "/android-chrome-512x512.png", 15 + "sizes": "512x512", 16 + "type": "image/png", 17 + "purpose": "any maskable monochrome" 18 + } 19 + ], 20 + "theme_color": "#ffffff", 21 + "background_color": "#ffffff" 22 + }
files/static/serviceworker.js

This is a binary file and will not be displayed.

+61
logo/original.svg
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <!-- Created with Inkscape (http://www.inkscape.org/) --> 3 + 4 + <svg 5 + width="300" 6 + height="300" 7 + viewBox="0 0 300 300" 8 + version="1.1" 9 + id="svg5" 10 + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" 11 + sodipodi:docname="original.svg" 12 + inkscape:export-filename="../files/static/apple-touch-icon.png" 13 + inkscape:export-xdpi="57.599998" 14 + inkscape:export-ydpi="57.599998" 15 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 16 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 17 + xmlns="http://www.w3.org/2000/svg" 18 + xmlns:svg="http://www.w3.org/2000/svg"> 19 + <sodipodi:namedview 20 + id="namedview7" 21 + pagecolor="#ffffff" 22 + bordercolor="#ffffff" 23 + borderopacity="1" 24 + inkscape:showpageshadow="0" 25 + inkscape:pageopacity="0" 26 + inkscape:pagecheckerboard="1" 27 + inkscape:deskcolor="#505050" 28 + inkscape:document-units="mm" 29 + showgrid="false" 30 + showborder="false" 31 + inkscape:zoom="2.7433333" 32 + inkscape:cx="150" 33 + inkscape:cy="150" 34 + inkscape:current-layer="layer1" /> 35 + <defs 36 + id="defs2" /> 37 + <g 38 + inkscape:label="Layer 1" 39 + inkscape:groupmode="layer" 40 + id="layer1" 41 + transform="translate(-88.429247,-252.219)"> 42 + <rect 43 + style="fill:#ffffff;stroke:none;stroke-width:32;stroke-linecap:round;paint-order:stroke fill markers" 44 + id="rect1062" 45 + width="300" 46 + height="300" 47 + x="88.429245" 48 + y="252.21899" /> 49 + <ellipse 50 + style="display:none;fill:#ffffff;stroke:none;stroke-width:177.123;stroke-linecap:round;paint-order:stroke fill markers" 51 + id="path1008" 52 + cx="238.42923" 53 + cy="402.21899" 54 + rx="120.00002" 55 + ry="120" /> 56 + <path 57 + style="color:#000000;fill:#000000;stroke:none;stroke-width:22.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" 58 + d="m 199.67882,390.54935 a 5.60056,5.60056 0 0 0 -3.34414,2.13145 l -20.49961,27.45996 c -13.20644,-7.14236 -20.24258,-14.0123 -20.24258,-14.0123 a 5.6,5.6 0 0 0 -7.92012,0.0328 5.6,5.6 0 0 0 0.0342,7.92011 c 0,0 9.2309,9.00163 26.40586,17.62441 a 5.6,5.6 0 0 0 0.0342,0.0574 5.6,5.6 0 0 0 0.16953,0.0423 c 15.75889,7.88039 38.13726,15.41504 66.43438,15.41504 29.63099,0 51.71276,-7.75469 66.36602,-15.59961 14.65325,-7.84491 22.2373,-16.1082 22.2373,-16.1082 a 5.6,5.6 0 0 0 -0.38008,-7.91055 5.6,5.6 0 0 0 -7.90918,0.37871 c 0,0 -5.9871,6.67444 -19.23359,13.76621 -13.24649,7.09178 -33.39531,14.27344 -61.08047,14.27344 -7.42782,0 -14.35756,-0.57039 -20.85781,-1.50528 l -13.77032,-40.29511 a 5.60056,5.60056 0 0 0 -6.44355,-3.6709 z m -0.72051,17.34005 8.26191,24.17734 c -7.92572,-1.90722 -14.97001,-4.32617 -21.11894,-6.95625 z" 59 + id="path734" /> 60 + </g> 61 + </svg>
+58
logo/small.svg
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <!-- Created with Inkscape (http://www.inkscape.org/) --> 3 + 4 + <svg 5 + width="300" 6 + height="300" 7 + viewBox="0 0 300 300" 8 + version="1.1" 9 + id="svg5" 10 + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" 11 + sodipodi:docname="small.svg" 12 + inkscape:export-filename="../files/static/favicon-16x16.png" 13 + inkscape:export-xdpi="5.1199999" 14 + inkscape:export-ydpi="5.1199999" 15 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 16 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 17 + xmlns="http://www.w3.org/2000/svg" 18 + xmlns:svg="http://www.w3.org/2000/svg"> 19 + <sodipodi:namedview 20 + id="namedview7" 21 + pagecolor="#ffffff" 22 + bordercolor="#ffffff" 23 + borderopacity="1" 24 + inkscape:showpageshadow="0" 25 + inkscape:pageopacity="0" 26 + inkscape:pagecheckerboard="1" 27 + inkscape:deskcolor="#505050" 28 + inkscape:document-units="mm" 29 + showgrid="false" 30 + showborder="false" 31 + inkscape:zoom="2.351237" 32 + inkscape:cx="75.492178" 33 + inkscape:cy="99.309428" 34 + inkscape:current-layer="layer1" /> 35 + <defs 36 + id="defs2" /> 37 + <g 38 + inkscape:label="Layer 1" 39 + inkscape:groupmode="layer" 40 + id="layer1" 41 + transform="translate(-88.429247,-252.219)"> 42 + <g 43 + id="g3911" 44 + transform="translate(2.1288773,110.05368)"> 45 + <ellipse 46 + style="display:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:16;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" 47 + id="path1933" 48 + cx="177.51703" 49 + cy="312.58838" 50 + rx="10.493162" 51 + ry="11.11914" /> 52 + <path 53 + style="color:#000000;fill:#000000;stroke:#ffffff;stroke-width:32;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" 54 + d="m 180.94284,268.37715 a 8.0008,8.0008 0 0 0 -4.77735,3.04492 l -29.28515,39.22852 c -18.86635,-10.20337 -28.91797,-20.01758 -28.91797,-20.01758 a 8,8 0 0 0 -11.31446,0.0469 8,8 0 0 0 0.0488,11.31445 c 0,0 13.187,12.85947 37.72266,25.17773 a 8,8 0 0 0 0.0488,0.082 8,8 0 0 0 0.24218,0.0605 c 22.5127,11.2577 54.4818,22.02149 94.90625,22.02149 42.32999,0 73.87538,-11.07814 94.8086,-22.28516 20.93322,-11.20702 31.76758,-23.01172 31.76758,-23.01172 a 8,8 0 0 0 -0.54297,-11.30078 8,8 0 0 0 -11.29883,0.54101 c 0,0 -8.553,9.53491 -27.47656,19.66602 -18.92356,10.13111 -47.70758,20.39063 -87.25782,20.39063 -10.61116,0 -20.51079,-0.81485 -29.79687,-2.1504 l -19.67188,-57.56445 a 8.0008,8.0008 0 0 0 -9.20507,-5.24414 z m -1.0293,24.77149 11.80273,34.53906 c -11.32247,-2.7246 -21.38574,-6.18024 -30.16992,-9.9375 z" 55 + id="path734" /> 56 + </g> 57 + </g> 58 + </svg>
+10 -1
site.go
··· 1 1 package main 2 2 3 3 import ( 4 + "errors" 4 5 "fmt" 5 6 "io/ioutil" 6 7 "log" 7 8 "math/rand" 8 9 "net/http" 9 10 "net/url" 11 + "os" 10 12 "path/filepath" 11 13 "strings" 12 14 "text/template" ··· 45 47 // rootHandler is our "wildcard handler", so in addition to 46 48 // serving /, it also acts as a router for a few arbitrary 47 49 // patterns that can't be registered at starttime 48 - // this includes /<username> and 404 50 + // this includes /<username>, static files, and 404 49 51 func (s *Site) rootHandler(w http.ResponseWriter, r *http.Request) { 50 52 if r.URL.Path == "/" { 51 53 s.indexHandler(w, r) ··· 56 58 s.userHandler(w, r) 57 59 return 58 60 } 61 + // handles static files 62 + file := filepath.Join("files", "static", strings.TrimPrefix(r.URL.Path, "/")) 63 + if _, err := os.Stat(file); !errors.Is(err, os.ErrNotExist) { 64 + http.ServeFile(w, r, file) 65 + return 66 + } 67 + // 404 59 68 http.NotFound(w, r) 60 69 } 61 70