V5 updates

This commit is contained in:
Martin Donnelly 2024-10-28 17:26:42 +00:00
parent b4b1de6c21
commit 3d9eeecf82
105 changed files with 1436 additions and 32 deletions

40
Makefile Normal file
View File

@ -0,0 +1,40 @@
PROJECT = go-traintimes
VERSION=`git describe --tags`
BUILD=`date +%FT%T%z`
ECR_REPO = git.caliban.io/martin
APP_IMAGE = $(ECR_REPO)/$(PROJECT):$(VERSION)
NO_CACHE = true
LDFLAGS=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD}"
.PHONY: build
build:
#CC=/usr/local/musl/bin/musl-gcc go build --ldflags '-linkmode external -extldflags "-static"' server.go
GCO_ENABLED=0 GOOS=linux go build ${LDFLAGS} -o ${PROJECT} main.go
# go build ${LDFLAGS} -o ${PROJECT} server.go
# docker build ./docker/. -t $(APP_IMAGE) --build-arg VERSION=$(VERSION) --no-cache=$(NO_CACHE) --compress=true
docker build --platform linux/amd64 --no-cache -force-rm --tag ${APP_IMAGE} --file ./docker/Dockerfile .
#push docker image to registry
.PHONY: push
push: build
docker push $(APP_IMAGE)
#push docker image to registry
.PHONY: run
run: build
docker run $(APP_IMAGE)
ver:
@echo '$(VERSION)'
#echo $ERSION
.PHONY: ver

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/img/mstile-150x150.png"/>
<TileColor>#2b5797</TileColor>
</tile>
</msapplication>
</browserconfig>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,321 @@
@font-face {
font-family: 'Fujicons';
font-style: normal;
font-weight: 400;
src: url(fujicons.ttf) format('truetype');
unicode-range: U+0-10FFFF;
}
.fa {
display: inline-block;
font: normal normal normal 14px/1 Fujicons;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* makes the font 33% larger relative to the icon container */
.fa-lg {
font-size: 1.33333333em;
line-height: 0.75em;
vertical-align: -15%;
}
.fa-2x {
font-size: 2em;
}
.fa-3x {
font-size: 3em;
}
.fa-4x {
font-size: 4em;
}
.fa-5x {
font-size: 5em;
}
.fa-fw {
width: 1.28571429em;
text-align: center;
}
.fa-ul {
padding-left: 0;
margin-left: 2.14285714em;
list-style-type: none;
}
.fa-ul > li {
position: relative;
}
.fa-li {
position: absolute;
left: -2.14285714em;
width: 2.14285714em;
top: 0.14285714em;
text-align: center;
}
.fa-li.fa-lg {
left: -1.85714286em;
}
.fa-border {
padding: .2em .25em .15em;
border: solid 0.08em #eeeeee;
border-radius: .1em;
}
.fa-pull-left {
float: left;
}
.fa-pull-right {
float: right;
}
.fa.fa-pull-left {
margin-right: .3em;
}
.fa.fa-pull-right {
margin-left: .3em;
}
/* Deprecated as of 4.4.0 */
.pull-right {
float: right;
}
.pull-left {
float: left;
}
.fa.pull-left {
margin-right: .3em;
}
.fa.pull-right {
margin-left: .3em;
}
.fa-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear;
}
.fa-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8);
}
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
.fa-rotate-90 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.fa-rotate-180 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
.fa-rotate-270 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}
.fa-flip-horizontal {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
-ms-transform: scale(-1, 1);
transform: scale(-1, 1);
}
.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
-ms-transform: scale(1, -1);
transform: scale(1, -1);
}
:root .fa-rotate-90,
:root .fa-rotate-180,
:root .fa-rotate-270,
:root .fa-flip-horizontal,
:root .fa-flip-vertical {
filter: none;
}
.fa-stack {
position: relative;
display: inline-block;
width: 2em;
height: 2em;
line-height: 2em;
vertical-align: middle;
}
.fa-stack-1x,
.fa-stack-2x {
position: absolute;
left: 0;
width: 100%;
text-align: center;
}
.fa-stack-1x {
line-height: inherit;
}
.fa-stack-2x {
font-size: 2em;
}
.fa-inverse {
color: #ffffff;
}
.fa-back:before {
content: "";
}
.fa-back-chevron:before {
content: "\E91F"
}
.fa-forward:before {
content: "";
}
.fa-globe:before {
content: "\EA12"
}
.fa-up:before {
content: "\E925"
}
.fa-down:before {
content: "\E922"
}
.fa-work:before {
content: "\E998"
}
.fa-home:before {
content: "\EA1E"
}
.fa-refresh:before {
content: "\EA88"
}
.fa-menu:before {
content: "\EA68"
}
.fa-star:before {
content: "\EAB3"
}
.fa-star-filled:before {
content: "\EAB2"
}
.fa-thick-arrow:before {
content: "\EA08"
}
.fa-tick:before {
content: "\E9A4"
}
.fa-cross:before {
content: "\E951"
}
.fa-cross-circle:before {
content: "\E9D2"
}
.fa-cross-circle-filled:before {
content: "\E9D1"
}
.fa-alert:before {
content: "\E9ED"
}
.fa-alert-filled:before {
content: "\E9EC"
}
.fa-info:before {
content: "\E9EF"
}
.fa-info-filled:before {
content: "\E9EE"
}
.fa-bookmark:before {
content: "\E951"
}
.fa-bookmark-filled:before {
content: "\E950"
}
.fa-clockface:before {
content: "\E9AB"
}
.fa-clockface-filled:before {
content: "\E9AA"
}
.fa-twitter:before {
content: "\EAE5"
}
.fa-cog:before {
content: "\EA9E"
}
.fa-cog-solid:before {
content: "\EA11"
}
.fa-foursquare:before {
content: "\EA0B"
}
/*full list*/
.fa-binoculars-solid:before {
content: "\E900"
}
.fa-binoculars:before {
content: "\E901"
}
.fa-check-circle-solid:before {
content: "\E902"
}
.fa-check-circle:before {
content: "\E903"
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,39 @@
@font-face {
font-family: 'Gotham';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Book.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Light';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Light.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Medium';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Medium.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Bold';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Bold.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Black';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Black.otf) format('opentype');
unicode-range: U+0-10FFFF;
}

BIN
OLD_pb_public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,32 @@
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(Roboto-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(Roboto_Condensed-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
src: url(Roboto_Mono-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Roboto Slab';
font-style: normal;
font-weight: 400;
src: url(Roboto_Slab-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}

View File

@ -0,0 +1,209 @@
@font-face {
font-family: 'Fujicons';
font-style: normal;
font-weight: 400;
src: url(fujicons.ttf) format('truetype');
unicode-range: U+0-10FFFF;
}
.fa {
display: inline-block;
font: normal normal normal 14px/1 Fujicons;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* makes the font 33% larger relative to the icon container */
.fa-lg {
font-size: 1.33333333em;
line-height: 0.75em;
vertical-align: -15%;
}
.fa-2x {
font-size: 2em;
}
.fa-3x {
font-size: 3em;
}
.fa-4x {
font-size: 4em;
}
.fa-5x {
font-size: 5em;
}
.fa-fw {
width: 1.28571429em;
text-align: center;
}
.fa-ul {
padding-left: 0;
margin-left: 2.14285714em;
list-style-type: none;
}
.fa-ul > li {
position: relative;
}
.fa-li {
position: absolute;
left: -2.14285714em;
width: 2.14285714em;
top: 0.14285714em;
text-align: center;
}
.fa-li.fa-lg {
left: -1.85714286em;
}
.fa-border {
padding: .2em .25em .15em;
border: solid 0.08em #eeeeee;
border-radius: .1em;
}
.fa-pull-left {
float: left;
}
.fa-pull-right {
float: right;
}
.fa.fa-pull-left {
margin-right: .3em;
}
.fa.fa-pull-right {
margin-left: .3em;
}
/* Deprecated as of 4.4.0 */
.pull-right {
float: right;
}
.pull-left {
float: left;
}
.fa.pull-left {
margin-right: .3em;
}
.fa.pull-right {
margin-left: .3em;
}
.fa-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear;
}
.fa-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8);
}
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
.fa-rotate-90 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.fa-rotate-180 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
.fa-rotate-270 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}
.fa-flip-horizontal {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
-ms-transform: scale(-1, 1);
transform: scale(-1, 1);
}
.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
-ms-transform: scale(1, -1);
transform: scale(1, -1);
}
:root .fa-rotate-90,
:root .fa-rotate-180,
:root .fa-rotate-270,
:root .fa-flip-horizontal,
:root .fa-flip-vertical {
filter: none;
}
.fa-stack {
position: relative;
display: inline-block;
width: 2em;
height: 2em;
line-height: 2em;
vertical-align: middle;
}
.fa-stack-1x,
.fa-stack-2x {
position: absolute;
left: 0;
width: 100%;
text-align: center;
}
.fa-stack-1x {
line-height: inherit;
}
.fa-stack-2x {
font-size: 2em;
}
.fa-inverse {
color: #ffffff;
}
.fa-back:before {
content: "";
}
.fa-forward:before {
content: "";
}
.fa-globe:before {
content: "\EA12"
}
.fa-up:before {
content: "\E925"
}
.fa-down:before {
content: "\E922"
}
.fa-work:before {
content: "\E998"
}
.fa-home:before {
content: "\EA1E"
}
.fa-refresh:before {
content: "\EA88"
}

BIN
OLD_pb_public/fonts/fujicons.ttf Executable file

Binary file not shown.

View File

@ -0,0 +1,39 @@
@font-face {
font-family: 'Gotham';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Book.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Light';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Light.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Medium';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Medium.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Bold';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Bold.otf) format('opentype');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Gotham Black';
font-style: normal;
font-weight: 400;
src: url(GothamSSm-Black.otf) format('opentype');
unicode-range: U+0-10FFFF;
}

0
OLD_pb_public/global.css Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

View File

@ -0,0 +1,33 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="192.000000pt" height="192.000000pt" viewBox="0 0 192.000000 192.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,192.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M440 1789 c-80 -4 -104 -10 -149 -35 -63 -34 -117 -94 -145 -162 -19
-45 -20 -74 -20 -637 l0 -590 25 -56 c32 -67 91 -128 156 -160 l48 -23 605 -1
605 0 53 28 c69 35 118 86 151 156 l26 56 0 585 c-1 534 -2 590 -18 637 -33
94 -133 180 -231 199 -47 8 -969 11 -1106 3z m389 -79 c21 0 51 -44 51 -74 l0
-34 80 -1 c79 -1 80 -1 80 23 0 40 21 74 51 85 57 20 112 -21 112 -83 0 -17 5
-26 16 -26 68 0 143 -50 172 -115 17 -37 19 -74 19 -455 0 -265 -4 -429 -11
-454 -6 -23 -28 -55 -55 -81 l-44 -43 85 -89 c91 -94 101 -118 63 -153 -41
-38 -61 -28 -194 105 l-125 125 -168 0 -167 0 -128 -125 c-136 -134 -157 -145
-196 -103 -35 38 -26 60 65 155 l85 88 -35 30 c-19 16 -44 48 -55 70 -18 38
-19 65 -19 468 -1 404 1 431 19 467 32 63 105 110 171 110 11 0 16 8 14 23 -3
48 53 104 93 91 8 -2 17 -4 21 -4z"/>
<path d="M819 1532 c-48 -15 -65 -75 -31 -106 16 -14 43 -16 172 -17 169 0
190 7 190 62 0 25 -21 57 -38 60 -22 4 -282 4 -293 1z"/>
<path d="M735 1342 c-93 -5 -94 -6 -94 -192 0 -143 2 -160 19 -175 18 -16 48
-18 301 -18 l282 -1 18 23 c17 20 19 42 19 176 0 133 -2 154 -18 168 -15 14
-52 17 -242 19 -124 2 -252 2 -285 0z"/>
<path d="M685 741 c-35 -22 -50 -67 -35 -107 12 -32 55 -57 93 -56 44 1 88 50
81 89 -9 52 -15 63 -43 78 -34 17 -63 16 -96 -4z"/>
<path d="M1138 745 c-49 -27 -58 -97 -18 -137 55 -54 139 -31 154 43 7 38 -9
71 -44 92 -34 21 -56 21 -92 2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

30
OLD_pb_public/index.html Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Traintimes</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/img/safari-pinned-tab.svg" color="#5bbad5">
<meta name="apple-mobile-web-app-title" content="Train Times">
<meta name="application-name" content="Train Times">
<meta name="theme-color" content="#ffffff">
<!--<link rel='stylesheet' href='/global.c
ss'>-->
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,46 @@
{
"name": "Train Times",
"short_name": "Train Times",
"icons": [
{
"src": "/img/Icon-36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "/img/Icon-48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/img/Icon-72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/img/Icon-96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/img/Icon-144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/img/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"start_url": ".",
"imgdisplay": "standalone",
"display": "standalone"
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/img/mstile-150x150.png"/>
<TileColor>#2b5797</TileColor>
</tile>
</msapplication>
</browserconfig>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
OLD_pb_public/public/favicon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

View File

@ -0,0 +1,33 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="192.000000pt" height="192.000000pt" viewBox="0 0 192.000000 192.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,192.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M440 1789 c-80 -4 -104 -10 -149 -35 -63 -34 -117 -94 -145 -162 -19
-45 -20 -74 -20 -637 l0 -590 25 -56 c32 -67 91 -128 156 -160 l48 -23 605 -1
605 0 53 28 c69 35 118 86 151 156 l26 56 0 585 c-1 534 -2 590 -18 637 -33
94 -133 180 -231 199 -47 8 -969 11 -1106 3z m389 -79 c21 0 51 -44 51 -74 l0
-34 80 -1 c79 -1 80 -1 80 23 0 40 21 74 51 85 57 20 112 -21 112 -83 0 -17 5
-26 16 -26 68 0 143 -50 172 -115 17 -37 19 -74 19 -455 0 -265 -4 -429 -11
-454 -6 -23 -28 -55 -55 -81 l-44 -43 85 -89 c91 -94 101 -118 63 -153 -41
-38 -61 -28 -194 105 l-125 125 -168 0 -167 0 -128 -125 c-136 -134 -157 -145
-196 -103 -35 38 -26 60 65 155 l85 88 -35 30 c-19 16 -44 48 -55 70 -18 38
-19 65 -19 468 -1 404 1 431 19 467 32 63 105 110 171 110 11 0 16 8 14 23 -3
48 53 104 93 91 8 -2 17 -4 21 -4z"/>
<path d="M819 1532 c-48 -15 -65 -75 -31 -106 16 -14 43 -16 172 -17 169 0
190 7 190 62 0 25 -21 57 -38 60 -22 4 -282 4 -293 1z"/>
<path d="M735 1342 c-93 -5 -94 -6 -94 -192 0 -143 2 -160 19 -175 18 -16 48
-18 301 -18 l282 -1 18 23 c17 20 19 42 19 176 0 133 -2 154 -18 168 -15 14
-52 17 -242 19 -124 2 -252 2 -285 0z"/>
<path d="M685 741 c-35 -22 -50 -67 -35 -107 12 -32 55 -57 93 -56 44 1 88 50
81 89 -9 52 -15 63 -43 78 -34 17 -63 16 -96 -4z"/>
<path d="M1138 745 c-49 -27 -58 -97 -18 -137 55 -54 139 -31 154 43 7 38 -9
71 -44 92 -34 21 -56 21 -92 2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

30
OLD_pb_public/public/index.html Executable file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Traintimes</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/img/safari-pinned-tab.svg" color="#5bbad5">
<meta name="apple-mobile-web-app-title" content="Train Times">
<meta name="application-name" content="Train Times">
<meta name="theme-color" content="#ffffff">
<!--<link rel='stylesheet' href='/global.c
ss'>-->
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,46 @@
{
"name": "Train Times",
"short_name": "Train Times",
"icons": [
{
"src": "/img/Icon-36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "/img/Icon-48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/img/Icon-72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/img/Icon-96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/img/Icon-144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/img/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"start_url": ".",
"imgdisplay": "standalone",
"display": "standalone"
}

View File

@ -0,0 +1,110 @@
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const CACHE_VERSION = 8;
const dataCacheName = `traintimesData-v${CACHE_VERSION}`;
const cacheName = `traintimePWA-final-${CACHE_VERSION}`;
const filesToCache = [
'/',
'/index.html',
'/service-worker.js',
'/manifest.json',
'/favicon.png',
'/browserconfig.xml',
'/build/bundle.css',
'/build/bundle.js',
'/build/fonts/fujicons.css',
'/build/fonts/fujicons.ttf',
'/img/Icon-36.png',
'/img/Icon-48.png',
'/img/Icon-72.png',
'/img/Icon-96.png',
'/img/Icon-144.png',
'/img/Icon-192.png',
'/img/Icon-512.png'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName && key !== dataCacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
/*
* Fixes a corner case in which the app wasn't returning the latest data.
* You can reproduce the corner case by commenting out the line below and
* then doing the following steps: 1) load app for first time so that the
* initial New York City data is shown 2) press the refresh button on the
* app 3) go offline 4) reload the app. You expect to see the newer NYC
* data, but you actually see the initial data. This happens because the
* service worker is not yet activated. The code below essentially lets
* you activate the service worker faster.
*/
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.warn('[Service Worker] Fetch', e.request.url);
const dataUrl = '/getnexttraintimes?';
if (e.request.url.indexOf(dataUrl) > -1) {
console.log('!');
/*
* When the request URL contains dataUrl, the app is asking for fresh
* weather data. In this case, the service worker always goes to the
* network and then caches the response. This is called the "Cache then
* network" strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-then-network
*/
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response) {
cache.put(e.request.url, response.clone());
return response;
});
})
);
}
else
/*
* The app is asking for app shell files. In this scenario the app uses the
* "Cache, falling back to the network" offline strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network
*/
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});

View File

@ -0,0 +1,110 @@
// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const CACHE_VERSION = 8;
const dataCacheName = `traintimesData-v${CACHE_VERSION}`;
const cacheName = `traintimePWA-final-${CACHE_VERSION}`;
const filesToCache = [
'/',
'/index.html',
'/service-worker.js',
'/manifest.json',
'/favicon.png',
'/browserconfig.xml',
'/build/bundle.css',
'/build/bundle.js',
'/build/fonts/fujicons.css',
'/build/fonts/fujicons.woff',
'/img/Icon-36.png',
'/img/Icon-48.png',
'/img/Icon-72.png',
'/img/Icon-96.png',
'/img/Icon-144.png',
'/img/Icon-192.png',
'/img/Icon-512.png'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName && key !== dataCacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
/*
* Fixes a corner case in which the app wasn't returning the latest data.
* You can reproduce the corner case by commenting out the line below and
* then doing the following steps: 1) load app for first time so that the
* initial New York City data is shown 2) press the refresh button on the
* app 3) go offline 4) reload the app. You expect to see the newer NYC
* data, but you actually see the initial data. This happens because the
* service worker is not yet activated. The code below essentially lets
* you activate the service worker faster.
*/
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.warn('[Service Worker] Fetch', e.request.url);
const dataUrl = '/getnexttraintimes?';
if (e.request.url.indexOf(dataUrl) > -1) {
console.log('!');
/*
* When the request URL contains dataUrl, the app is asking for fresh
* weather data. In this case, the service worker always goes to the
* network and then caches the response. This is called the "Cache then
* network" strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-then-network
*/
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response) {
cache.put(e.request.url, response.clone());
return response;
});
})
);
}
else
/*
* The app is asking for app shell files. In this scenario the app uses the
* "Cache, falling back to the network" offline strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network
*/
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});

9
docker-compose.yml Normal file
View File

@ -0,0 +1,9 @@
version: '3.5'
services:
trainserver:
container_name: trainserver
image: git.caliban.io/martin/go-traintimes:5.0.0
restart: always
ports:
- "8100:8100"

29
docker/Dockerfile Normal file
View File

@ -0,0 +1,29 @@
FROM debian:12-slim
# FROM alpine:latest
WORKDIR /app
RUN mkdir -p /app/dist
COPY ./go-traintimes /app/
COPY ./pb_data /app/pb_data
COPY ./pb_public /app/pb_public
RUN apt-get update && apt-get install -y ca-certificates
# Need the following to get a go app to run inside a docker container
# as per: https://www.fairlyusefulcode.co.uk/post/go-alpine-linux/ -- DEAD!!!
# RUN apk upgrade musl
# RUN apk add gcompat
# RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
# RUN ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
RUN chmod +x /app/go-traintimes
EXPOSE 8100
CMD [ "/app/go-traintimes", "serve", "--http=0.0.0.0:8100", "--dev"]

BIN
go-traintimes Executable file

Binary file not shown.

2
go.mod
View File

@ -4,6 +4,7 @@ go 1.22.4
require (
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
github.com/pocketbase/dbx v1.10.1
github.com/pocketbase/pocketbase v0.22.14
)
@ -50,7 +51,6 @@ require (
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pocketbase/dbx v1.10.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect

129
main.go
View File

@ -32,11 +32,25 @@ type GetTrainsRec struct {
Hash string `db:"hash" json:"hash"`
}
type GetTrainTimesRec struct {
Hash string `db:"hash" json:"hash"`
Body string `db:"body" json:"body"`
Ts int64 `db:"ts" json:"ts"`
}
const host = "https://huxley2.azurewebsites.net"
var (
Version string
Build string
tr = &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client = &http.Client{Transport: tr}
gmtTimeLoc = time.FixedZone("GMT", 0)
timeFormat = "Mon, 2 Jan 2006 15:04:05 GMT"
)
func main() {
@ -58,7 +72,7 @@ func main() {
e.Router.GET("/getnexttraintimes", func(c echo.Context) error {
apis.ActivityLogger(app)
return GetNextTrainTimes(c)
return GetNextTrainTimes(c, app)
})
scheduler.MustAdd("cleanup", "*/10 * * * *", func() {
@ -120,7 +134,7 @@ func getTrains(c echo.Context, app *pocketbase.PocketBase) error {
if dberr == nil {
// handle error
log.Printf("-- Cache hit %+v,%+v\n", recentTrain.From, recentTrain.To)
log.Printf("-- Cache hit train %+v\n", recentTrain.Hash)
return c.String(200, recentTrain.Body)
}
@ -129,12 +143,12 @@ func getTrains(c echo.Context, app *pocketbase.PocketBase) error {
log.Printf("-- url: %+v\n", url)
tr := &http.Transport{
/*tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
}*/
// client := &http.Client{Transport: tr}
log.Printf("-- full url: %+v\n", host+url)
resp, err := client.Get(host + url)
@ -174,7 +188,7 @@ func getTrains(c echo.Context, app *pocketbase.PocketBase) error {
return c.JSON(http.StatusAccepted, dummy)
}
func GetNextTrainTimes(c echo.Context) error {
func GetNextTrainTimes(c echo.Context, app *pocketbase.PocketBase) error {
params := c.QueryParams()
@ -185,6 +199,10 @@ func GetNextTrainTimes(c echo.Context) error {
To := c.QueryParamDefault("to", "")
ts := time.Now().String()
tsUnix := time.Now().Unix()
now := time.Now()
then := now.Add(-2 * time.Minute)
log.Printf("-- %+v\n", ts)
log.Printf("-- From: %+v\n", From)
@ -192,6 +210,21 @@ func GetNextTrainTimes(c echo.Context) error {
dummy := DummyRecord{Ts: ts}
recentTraintimes := GetTrainTimesRec{}
hash := fmt.Sprintf("%s%s", From, To)
dberr := app.Dao().DB().NewQuery("SELECT * FROM traintimes WHERE hash = {:hash} and ts >= {:ts}").Bind(dbx.Params{
"ts": tsUnix - 120,
"hash": hash,
}).One(&recentTraintimes)
if dberr == nil {
// handle error
log.Printf("-- Cache hit traintimes %+v\n", recentTraintimes.Hash)
return c.String(200, recentTraintimes.Body)
}
if From != "" && To != "" {
log.Println("-- Got something to search for")
@ -200,19 +233,18 @@ func GetNextTrainTimes(c echo.Context) error {
log.Printf("-- url: %+v\n", url)
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
log.Printf("-- full url: %+v\n", host+url)
resp, err := client.Get(host + url)
req, err := http.NewRequest("GET", host+url, nil)
if err != nil {
// handle error
}
req.Header.Add("If-Modified-Since", then.In(gmtTimeLoc).Format(timeFormat))
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
@ -241,6 +273,20 @@ func GetNextTrainTimes(c echo.Context) error {
}
var nTrainTimesRec GetTrainTimesRec
nTrainTimesRec.Hash = hash
nTrainTimesRec.Ts = tsUnix
nTrainTimesRec.Body = string(departJson)
go func() {
err := saveTraintimesRec(app, nTrainTimesRec)
if err != nil {
log.Println(err)
}
}()
// log.Println("-- body: %+v\n", string(body))
// c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@ -306,7 +352,7 @@ func saveTrainRec(app *pocketbase.PocketBase, trainRecord GetTrainsRec) error {
}
if len(record) == 0 {
log.Printf("-- Insert new\n")
log.Printf("-- Insert new train\n")
record := models.NewRecord(collection)
@ -321,7 +367,7 @@ func saveTrainRec(app *pocketbase.PocketBase, trainRecord GetTrainsRec) error {
return err
}
} else {
log.Printf("-- Update\n")
log.Printf("-- Update train\n")
rec := record[0]
rec.Set("body", trainRecord.Body)
@ -371,6 +417,57 @@ func saveTrainRec(app *pocketbase.PocketBase, trainRecord GetTrainsRec) error {
return nil
}
func saveTraintimesRec(app *pocketbase.PocketBase, traintimesRecord GetTrainTimesRec) error {
log.Println("$$:saveTraintimesRec")
// foundTrain := GetTrainsRec{}
collection, err := app.Dao().FindCollectionByNameOrId("traintimes")
if err != nil {
log.Println(err)
return err
}
record, recErr := app.Dao().FindRecordsByExpr("traintimes", dbx.NewExp("hash = {:hash}", dbx.Params{
"hash": traintimesRecord.Hash,
}))
if recErr != nil {
log.Println(recErr)
return recErr
}
if len(record) == 0 {
log.Printf("-- Insert new traintimes\n")
record := models.NewRecord(collection)
record.Set("body", traintimesRecord.Body)
record.Set("ts", traintimesRecord.Ts)
record.Set("hash", traintimesRecord.Hash)
if err := app.Dao().SaveRecord(record); err != nil {
log.Println(err)
return err
}
} else {
log.Printf("-- Update traintimes\n")
rec := record[0]
rec.Set("body", traintimesRecord.Body)
rec.Set("ts", traintimesRecord.Ts)
if err := app.Dao().SaveRecord(rec); err != nil {
log.Println(err)
return err
}
}
return nil
}
func CleanupDB(app *pocketbase.PocketBase) {
ts := time.Now().Unix() - 600

Binary file not shown.

Binary file not shown.

0
pb_public/browserconfig.xml Normal file → Executable file
View File

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
pb_public/build/fonts/Roboto-normal-400.woff Normal file → Executable file

Binary file not shown.

BIN
pb_public/build/fonts/Roboto_Condensed-normal-400.woff Normal file → Executable file

Binary file not shown.

0
pb_public/build/fonts/Roboto_Mono-normal-400.woff Normal file → Executable file
View File

BIN
pb_public/build/fonts/Roboto_Slab-normal-400.woff Normal file → Executable file

Binary file not shown.

View File

@ -2,7 +2,7 @@
font-family: 'Fujicons';
font-style: normal;
font-weight: 400;
src: url(fujicons.ttf) format('truetype');
src: url(./fujicons.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@ -180,10 +180,6 @@
content: "";
}
.fa-back-chevron:before {
content: "\E91F"
}
.fa-forward:before {
content: "";
}
@ -212,6 +208,7 @@
content: "\EA88"
}
.fa-menu:before {
content: "\EA68"
}
@ -319,3 +316,8 @@
content: "\E903"
}
.fa-back-chevron:before {
content: "\E91F"
}

View File

@ -0,0 +1,7 @@
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(./Roboto_Condensed-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}

View File

@ -0,0 +1,32 @@
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(Roboto-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: url(Roboto_Condensed-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
font-weight: 400;
src: url(Roboto_Mono-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}
@font-face {
font-family: 'Roboto Slab';
font-style: normal;
font-weight: 400;
src: url(Roboto_Slab-normal-400.woff) format('woff');
unicode-range: U+0-10FFFF;
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More