diff --git a/backend/main.go b/backend/main.go index f7e8a0fe1bfa562fcbb3b5b160e77b2f7700b9a7..b7826b162204b5d43e410cf1e0d24b4506deb96f 100644 --- a/backend/main.go +++ b/backend/main.go @@ -7,12 +7,10 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" - "golang.org/x/sys/unix" "gopkg.in/gographics/imagick.v3/imagick" "log" "net/http" "os" - "os/signal" ) var imageProcessDuration = promauto.NewCounterVec(prometheus.CounterOpts{ @@ -28,7 +26,7 @@ var imageProcessDurationWrite = imageProcessDuration.WithLabelValues("write") func main() { configFile, err := os.Open("config.yaml") if err != nil { - log.Fatalf("Could not open config file: %s", err.Error()) + log.Fatalf("error opening config file: %s", err.Error()) } config := shared.LoadConfigFromFile(configFile) @@ -53,25 +51,17 @@ func main() { Handler: metricsMux, } - done := make(chan struct{}) go func() { if err := metrics.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Printf("Error: metrics server error: %s", err.Error()) + log.Printf("error in metrics server: %s", err.Error()) } - close(done) + srv.Shutdown() }() if err := srv.Run(mux); err != nil { - log.Fatal(err) + log.Printf("error in asynq server: %s", err.Error()) } - - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, unix.SIGTERM, unix.SIGINT) - <-sigs - - srv.Shutdown() if err := metrics.Shutdown(context.Background()); err != nil { - log.Printf("Error: metrics shutdown error: %s", err.Error()) + log.Printf("error shutting down metrics server: %s", err.Error()) } - <-done } diff --git a/frontend/assets/css/fonts.css b/frontend/assets/css/fonts.css deleted file mode 100644 index b2c0f8a8d213cda12525152661a7cc01b8eb1742..0000000000000000000000000000000000000000 --- a/frontend/assets/css/fonts.css +++ /dev/null @@ -1 +0,0 @@ -@font-face{font-family:'Lato';src:url("/assets/fonts/Lato-Regular.eot");src:url("/assets/fonts/Lato-Regular.eot?#iefix") format("embedded-opentype"),url("/assets/fonts/Lato-Regular.woff2") format("woff2"),url("/assets/fonts/Lato-Regular.woff") format("woff"),url("/assets/fonts/Lato-Regular.ttf") format("truetype");font-style:normal;font-weight:normal;text-rendering:optimizeLegibility}@font-face{font-family:'Lato';src:url("/assets/fonts/Lato-Bold.eot");src:url("/assets/fonts/Lato-Bold.eot?#iefix") format("embedded-opentype"),url("/assets/fonts/Lato-Bold.woff2") format("woff2"),url("/assets/fonts/Lato-Bold.woff") format("woff"),url("/assets/fonts/Lato-Bold.ttf") format("truetype");font-style:normal;font-weight:bold;text-rendering:optimizeLegibility}@font-face{font-family:'Lato';src:url("/assets/fonts/Lato-BoldItalic.eot");src:url("/assets/fonts/Lato-BoldItalic.eot?#iefix") format("embedded-opentype"),url("/assets/fonts/Lato-BoldItalic.woff2") format("woff2"),url("/assets/fonts/Lato-BoldItalic.woff") format("woff"),url("/assets/fonts/Lato-BoldItalic.ttf") format("truetype");font-style:italic;font-weight:bold;text-rendering:optimizeLegibility}@font-face{font-family:'Lato';src:url("/assets/fonts/Lato-Italic.eot");src:url("/assets/fonts/Lato-Italic.eot?#iefix") format("embedded-opentype"),url("/assets/fonts/Lato-Italic.woff2") format("woff2"),url("/assets/fonts/Lato-Italic.woff") format("woff"),url("/assets/fonts/Lato-Italic.ttf") format("truetype");font-style:italic;font-weight:normal;text-rendering:optimizeLegibility} diff --git a/frontend/assets/css/style.css b/frontend/assets/css/style.css deleted file mode 100644 index c5636cdab3259e1b2d480c168d74264aadcaed44..0000000000000000000000000000000000000000 --- a/frontend/assets/css/style.css +++ /dev/null @@ -1 +0,0 @@ -*{margin:0;padding:0}*:focus{outline:none}*::-moz-focus-inner{border:0}html{height:100%}body{background:#282828;font-family:'Lato', sans-serif;font-size:81.25%;min-height:100%;display:flex;flex-direction:column}.container.centered{flex-grow:1;display:flex;margin:0 auto 64px auto;padding:0 16px;flex-direction:column;justify-content:center;width:100%;max-width:640px}.button{display:block;background:#FFC107;padding:4px 16px;border-radius:2px;box-shadow:0 1px 2px rgba(0,0,0,0.1);color:#282828;text-decoration:none;line-height:24px;cursor:pointer;min-width:40px;text-align:center}.button:hover,.button:focus{background:#FFD54F;box-shadow:0 2px 4px rgba(0,0,0,0.2)}.button[aria-disabled=true],.button:disabled{cursor:default;background:#838383;box-shadow:0 -1px 2px rgba(0,0,0,0.1)}nav.navigation{position:sticky;top:0;background:#333333;box-shadow:0 2px 4px rgba(0,0,0,0.2);padding:0 16px;z-index:100}nav.navigation ul{display:flex;max-width:1024px;margin:0 auto;height:56px;align-items:center}nav.navigation ul li{display:block}@media (max-width: 640px){nav.navigation ul li.images,nav.navigation ul li.albums{display:none}}nav.navigation ul li.title a{display:inline-block;line-height:56px}nav.navigation ul li.title a img{width:32px;height:32px;vertical-align:middle}nav.navigation ul li.spacer{flex-grow:1}nav.navigation ul li:not(.spacer){margin:0 8px}nav.navigation ul li:not(.spacer):first-child{margin-left:0}nav.navigation ul li:not(.spacer):last-child{margin-right:0}.page.upload{flex-grow:1;display:flex;flex-direction:column}.page.upload .alert{padding:16px;margin:16px 0;box-shadow:0 2px 4px rgba(33,33,33,0.2);text-decoration:none;border-radius:2px}.page.upload .alert.success{background:#DCEDC8;color:#689F38;border-color:#689F38}.page.upload .alert.success a{color:#33691E}.page.upload .alert.error{background:#FFEBEE;color:#F44336;border-color:#F44336}.page.upload .alert.error a{color:#D32F2F}.page.upload form.upload{padding:96px 0;box-shadow:0 2px 4px rgba(0,0,0,0.2);text-decoration:none;border-radius:2px;text-align:center;background:#333333}.page.upload form.upload .upload-label{font-size:18pt;color:#fff}.page.upload form.upload label{position:relative;display:inline-block;overflow:hidden}.page.upload form.upload label span.text{position:relative;display:inline-block;background:#FFC107;padding:4px 16px;border-radius:2px;box-shadow:0 1px 2px rgba(0,0,0,0.1);line-height:24px;color:#282828;cursor:pointer;z-index:1}.page.upload form.upload label span.text:hover,.page.upload form.upload label span.text:focus{background:#FFD54F;box-shadow:0 2px 4px rgba(0,0,0,0.2)}.page.upload form.upload label input[type=file]{position:absolute;left:0;right:0;top:0;bottom:0;opacity:0}.page.upload .uploading-images{display:flex;flex-direction:row}.page.upload .uploading-images .images{display:flex;flex-direction:column;align-items:stretch;flex-grow:1}.page.upload .uploading-images .images .detail{margin-bottom:32px}.page.upload .uploading-images .images .detail .image{position:relative}.page.upload .uploading-images .images .detail .image .progress{position:absolute;top:-4px;left:0;right:0;height:4px;display:block;background-color:rgba(255,193,7,0.2);overflow:hidden;transition:opacity 400ms}.page.upload .uploading-images .images .detail .image .progress .indeterminate{background-color:rgba(255,193,7,0.8)}.page.upload .uploading-images .images .detail .image .progress .indeterminate::before{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;animation:indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite}.page.upload .uploading-images .images .detail .image .progress .indeterminate::after{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;animation:indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;animation-delay:1.125s}.page.upload .uploading-images .images .detail:not(.uploading) .progress{opacity:0}.page.upload.submitted .container.centered{display:none}.page.upload:not(.submitted) .uploading-images{display:none}@keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}100%{left:100%;right:-90%}}@keyframes indeterminate-short{0%{left:-200%;right:100%}60%{left:107%;right:-8%}100%{left:107%;right:-8%}}.page.image.detail,.page.upload .uploading-images{display:flex;flex-direction:row;align-items:start;align-self:center;padding:32px 32px 128px;justify-content:center}@media (max-width: 1024px){.page.image.detail,.page.upload .uploading-images{flex-direction:column;padding:32px 0;align-items:stretch;justify-content:start}}.page.image.detail .sidebar,.page.upload .uploading-images .sidebar{width:250px;max-width:640px;margin-left:32px}.page.image.detail .sidebar .url,.page.upload .uploading-images .sidebar .url{background:#333333;border-radius:4px;box-shadow:0 2px 4px rgba(0,0,0,0.4);padding:8px;margin-bottom:8px}@media (max-width: 1024px){.page.image.detail .sidebar .url,.page.upload .uploading-images .sidebar .url{padding:16px;margin-bottom:32px}}.page.image.detail .sidebar .url p,.page.upload .uploading-images .sidebar .url p{color:#ffffff}.page.image.detail .sidebar .url div,.page.upload .uploading-images .sidebar .url div{display:flex;flex-direction:row}.page.image.detail .sidebar .url div input,.page.upload .uploading-images .sidebar .url div input{background:none;color:#fff;opacity:0.4;border:none;flex-shrink:1;display:block;flex-grow:1;width:0;text-overflow:ellipsis}.page.image.detail .sidebar .url div button.copy,.page.upload .uploading-images .sidebar .url div button.copy{display:block;background:#FFC107;padding:4px 16px;border-radius:2px;box-shadow:0 1px 2px rgba(0,0,0,0.1);border:none;font-family:'Lato', sans-serif;font-size:10pt;line-height:24px;margin-left:8px;cursor:pointer}.page.image.detail .sidebar .url div button.copy:hover,.page.image.detail .sidebar .url div button.copy:focus,.page.upload .uploading-images .sidebar .url div button.copy:hover,.page.upload .uploading-images .sidebar .url div button.copy:focus{background:#FFD54F;box-shadow:0 2px 4px rgba(0,0,0,0.2)}.page.image.detail .sidebar .actions,.page.upload .uploading-images .sidebar .actions{background:#333333;border-radius:4px;box-shadow:0 2px 4px rgba(0,0,0,0.4);display:flex;flex-direction:row;margin-bottom:8px}@media (max-width: 1024px){.page.image.detail .sidebar .actions,.page.upload .uploading-images .sidebar .actions{margin-bottom:32px}}.page.image.detail .sidebar .actions .delete-form,.page.image.detail .sidebar .actions .update-form,.page.upload .uploading-images .sidebar .actions .delete-form,.page.upload .uploading-images .sidebar .actions .update-form{display:flex;flex-grow:1;flex-basis:0}.page.image.detail .sidebar .actions .delete-form input[type=submit],.page.image.detail .sidebar .actions .update-form input[type=submit],.page.upload .uploading-images .sidebar .actions .delete-form input[type=submit],.page.upload .uploading-images .sidebar .actions .update-form input[type=submit]{display:block;background:#FFC107;padding:4px 16px;border-radius:2px;box-shadow:0 1px 2px rgba(0,0,0,0.1);border:none;font-family:'Lato', sans-serif;font-size:10pt;line-height:24px;margin:8px;flex-grow:1;cursor:pointer}@media (max-width: 1024px){.page.image.detail .sidebar .actions .delete-form input[type=submit],.page.image.detail .sidebar .actions .update-form input[type=submit],.page.upload .uploading-images .sidebar .actions .delete-form input[type=submit],.page.upload .uploading-images .sidebar .actions .update-form input[type=submit]{margin:16px}}.page.image.detail .sidebar .actions .delete-form input[type=submit]:hover,.page.image.detail .sidebar .actions .delete-form input[type=submit]:focus,.page.image.detail .sidebar .actions .update-form input[type=submit]:hover,.page.image.detail .sidebar .actions .update-form input[type=submit]:focus,.page.upload .uploading-images .sidebar .actions .delete-form input[type=submit]:hover,.page.upload .uploading-images .sidebar .actions .delete-form input[type=submit]:focus,.page.upload .uploading-images .sidebar .actions .update-form input[type=submit]:hover,.page.upload .uploading-images .sidebar .actions .update-form input[type=submit]:focus{background:#FFD54F;box-shadow:0 2px 4px rgba(0,0,0,0.2)}@media (max-width: 1024px){.page.image.detail .sidebar,.page.upload .uploading-images .sidebar{width:auto;margin-left:0;margin-top:32px}}.page.image.detail .detail,.page.upload .uploading-images .detail{max-width:640px;width:100%;background:#333333;border-radius:4px;box-shadow:0 2px 4px rgba(0,0,0,0.4)}.page.image.detail .detail .title,.page.upload .uploading-images .detail .title{line-height:1.25;padding:16px;vertical-align:middle;font-size:14pt;font-weight:normal;color:#eee;background:none;border:none;width:100%;box-sizing:border-box}.page.image.detail .detail .title:not(.fake-input):empty,.page.upload .uploading-images .detail .title:not(.fake-input):empty{display:none}.page.image.detail .detail .title:not(.fake-input):empty+.image,.page.upload .uploading-images .detail .title:not(.fake-input):empty+.image{border-top-left-radius:4px;border-top-right-radius:4px}.page.image.detail .detail .image,.page.upload .uploading-images .detail .image{background:#000;display:flex;flex-direction:row;justify-content:center;align-items:start}.page.image.detail .detail .image img,.page.upload .uploading-images .detail .image img{max-width:100%}.page.image.detail .detail .description,.page.upload .uploading-images .detail .description{line-height:1.25;padding:16px;vertical-align:middle;font-size:11pt;font-weight:normal;color:#eee;background:none;border:none;width:100%;box-sizing:border-box;font-family:'Lato', sans-serif;resize:vertical;white-space:pre-line}.page.image.detail .detail .description:not(.fake-input):empty,.page.upload .uploading-images .detail .description:not(.fake-input):empty{display:none}.page.image.detail .fake-input[contenteditable]:empty:before,.page.upload .uploading-images .fake-input[contenteditable]:empty:before{opacity:0.4;content:attr(placeholder)}.page.image.list{max-width:1024px;margin:8px auto 48px;display:grid;grid-template-columns:repeat(auto-fill, minmax(180px, 1fr));grid-gap:8px;align-self:stretch;width:calc(100% - 16px)}@media (max-width: 600px){.page.image.list{grid-template-columns:repeat(auto-fill, minmax(160px, 1fr))}}.page.image.list .image{padding:8px;position:relative;box-shadow:0 2px 4px rgba(0,0,0,0.2);transition:all 200ms;text-decoration:none;background:#333333;border-radius:2px;will-change:transform}.page.image.list .image:hover,.page.image.list .image:focus{transform:translate(0, -2px);box-shadow:0 4px 6px rgba(0,0,0,0.4)}.page.image.list .image .image-container{display:flex;justify-content:stretch;align-content:stretch;justify-items:stretch;align-items:stretch;flex-direction:column;background:#000000}.page.image.list .image .image-container img{aspect-ratio:1;object-fit:contain}.page.image.list .image .info{display:block;z-index:1;color:#eeeeee;line-height:1.25;font-size:10pt;padding-top:12px}.page.image.list .image .info p{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.page.image.list .image .info p.title{font-weight:600}.page.image.list .image .info p.title span.placeholder{opacity:0.4}ul.pagination{display:flex;flex-direction:row;background:#333;box-shadow:0 2px 4px rgba(0,0,0,0.2);padding:4px 8px;color:#fff;justify-self:center;grid-column-start:1;grid-column-end:-1;max-width:480px;width:calc(100% - 16px)}ul.pagination li.page{appearance:none;list-style:none;margin:4px;padding:0;line-height:24px}ul.pagination li.page.current{display:block;padding:4px 16px;flex-grow:1;text-align:center} diff --git a/frontend/assets/favicon.svg b/frontend/assets/favicon.svg index 15701aabcf0d0f3f014f96832f794fd80e01b80b..50cb23016f1ec61f97df09f92a39b012716761d1 100644 --- a/frontend/assets/favicon.svg +++ b/frontend/assets/favicon.svg @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1024 1024"> <defs> - <linearGradient id="linearGradient861" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="1024"> - <stop stop-color="#FFD54F" offset="0"/> - <stop stop-color="#FFC107" offset="1"/> - </linearGradient> - <linearGradient id="linearGradient859" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="1024" y2="0"> + <linearGradient id="background" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="1024" y2="0"> <stop stop-color="#4c4c4c" offset="0"/> <stop stop-color="#1a1a1a" offset="1"/> </linearGradient> + <linearGradient id="foreground" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2="1024"> + <stop stop-color="#FFD54F" offset="0"/> + <stop stop-color="#FFC107" offset="1"/> + </linearGradient> </defs> <path d="m 188.74089,189.79585 662.14201,29.73874 c 15.33377,0.68868 27.31132,13.69238 26.61678,28.89711 l -31.56963,691.1239 c -0.69457,15.20473 -13.80866,27.08144 -29.14246,26.39277 L 154.64562,936.2096 c -15.33382,-0.68869 -27.3114,-13.69234 -26.61688,-28.89705 l 31.5697,-691.12389 c 0.69454,-15.20473 13.80865,-27.08149 29.14245,-26.39281 z" - fill="url(#linearGradient859)"/> + fill="url(#background)"/> <path d="m 366.39565,8.0213922 c -0.26427,0 -0.56628,0 -0.83056,0.037434 C 315.01465,13.037623 264.53971,27.37506 192.20614,57.547318 c -5.28534,2.208639 -8.23003,7.861258 -6.94644,13.439008 67.84104,299.102154 120.12809,595.958244 23.55749,930.024374 -2.49166,8.7222 5.39859,16.8829 14.30816,14.6742 90.86998,-22.49812 101.59166,-28.71226 188.04462,-44.77169 5.09657,-0.93585 8.98507,-5.05366 9.58911,-10.1822 14.9877,-124.95656 17.59261,-250.92386 13.10008,-376.10505 109.3309,120.20238 176.37914,231.30817 243.42738,343.27495 2.26514,3.78088 6.49341,6.02696 10.98595,5.72749 65.04735,-4.15524 98.00518,-13.40158 187.7426,-11.56728 9.66462,0.18716 15.51624,-10.51911 10.04213,-18.45525 C 804.73859,786.32339 726.28913,679.14824 616.5807,555.01523 L 854.88652,302.77319 c 5.47409,-5.87722 3.54872,-15.34817 -3.77523,-18.71728 -36.2423,-16.47121 -73.84368,-33.3542 -143.1193,-42.15131 0,0 -7.51274,0.82355 -10.11763,3.55628 L 430.95223,522.25999 c -9.24934,-170.40214 -30.23967,-338.7828 -51.68302,-500.31294 0,-0.224608 -0.0378,-0.48665 -0.11326,-0.748692 l -0.75505,-3.743456 c -1.13257,-5.615184 -6.15363,-9.6206826 -11.9675,-9.4709444 z" - fill="url(#linearGradient861)"/> + fill="url(#foreground)"/> </svg> diff --git a/frontend/assets/sass/_error.scss b/frontend/assets/sass/_error.scss new file mode 100644 index 0000000000000000000000000000000000000000..9cabd0747fbb8d9a2697fef7e00852b886580e55 --- /dev/null +++ b/frontend/assets/sass/_error.scss @@ -0,0 +1,21 @@ +.container.fatal { + padding: 16px; + margin: 16px auto; + box-shadow: 0 2px 4px rgba(33, 33, 33, 0.2); + text-decoration: none; + border-radius: 2px; + background: #FFEBEE; + color: #F44336; + border-color: #F44336; + font-size: 14px; + + ins { + text-decoration: none; + opacity: 0.75; + } + + code:last-child { + display: block; + margin-top: 16px; + } +} diff --git a/frontend/assets/sass/_navigation.sass b/frontend/assets/sass/_navigation.sass deleted file mode 100644 index 191378da92c5ce48404bd40ea7d0bbe1e4cec595..0000000000000000000000000000000000000000 --- a/frontend/assets/sass/_navigation.sass +++ /dev/null @@ -1,34 +0,0 @@ -nav.navigation - position: sticky - top: 0 - background: #333333 - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - padding: 0 16px - z-index: 100 - ul - display: flex - max-width: 1024px - margin: 0 auto - height: 56px - align-items: center - li - display: block - &.images, &.albums - @media (max-width: 640px) - display: none - &.title - a - display: inline-block - line-height: 56px - img - width: 32px - height: 32px - vertical-align: middle - &.spacer - flex-grow: 1 - &:not(.spacer) - margin: 0 8px - &:first-child - margin-left: 0 - &:last-child - margin-right: 0 diff --git a/frontend/assets/sass/_navigation.scss b/frontend/assets/sass/_navigation.scss new file mode 100644 index 0000000000000000000000000000000000000000..d91ee734db041511de9e2d5d1904f6cd439a6068 --- /dev/null +++ b/frontend/assets/sass/_navigation.scss @@ -0,0 +1,56 @@ +nav.navigation { + position: sticky; + top: 0; + background: #333333; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + padding: 0 16px; + z-index: 100; + + ul { + display: flex; + max-width: 1024px; + margin: 0 auto; + height: 56px; + align-items: center; + + li { + display: block; + + &.images, + &.albums { + @media (max-width: 640px) { + display: none; + } + } + + &.title { + a { + display: inline-block; + line-height: 56px; + + img { + width: 32px; + height: 32px; + vertical-align: middle; + } + } + } + + &.spacer { + flex-grow: 1; + } + + &:not(.spacer) { + margin: 0 8px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + } + } +} diff --git a/frontend/assets/sass/_page_image_detail.sass b/frontend/assets/sass/_page_image_detail.sass deleted file mode 100644 index e1bccc7cc4069478b5aedff2730040af91321639..0000000000000000000000000000000000000000 --- a/frontend/assets/sass/_page_image_detail.sass +++ /dev/null @@ -1,139 +0,0 @@ -.page.image.detail, .page.upload .uploading-images - display: flex - flex-direction: row - align-items: start - align-self: center - padding: 32px 32px 128px - justify-content: center - @media (max-width: 1024px) - flex-direction: column - padding: 32px 0 - align-items: stretch - justify-content: start - .sidebar - width: 250px - max-width: 640px - margin-left: 32px - .url - background: #333333 - border-radius: 4px - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4) - padding: 8px - margin-bottom: 8px - @media (max-width: 1024px) - padding: 16px - margin-bottom: 32px - p - color: #ffffff - div - display: flex - flex-direction: row - input - background: none - color: #fff - opacity: 0.4 - border: none - flex-shrink: 1 - display: block - flex-grow: 1 - width: 0 - text-overflow: ellipsis - button.copy - display: block - background: #FFC107 - padding: 4px 16px - border-radius: 2px - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) - border: none - font-family: 'Lato', sans-serif - font-size: 10pt - line-height: 24px - margin-left: 8px - cursor: pointer - &:hover, &:focus - background: #FFD54F - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - .actions - background: #333333 - border-radius: 4px - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4) - display: flex - flex-direction: row - margin-bottom: 8px - @media (max-width: 1024px) - margin-bottom: 32px - .delete-form, .update-form - display: flex - flex-grow: 1 - flex-basis: 0 - input[type=submit] - display: block - background: #FFC107 - padding: 4px 16px - border-radius: 2px - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) - border: none - font-family: 'Lato', sans-serif - font-size: 10pt - line-height: 24px - margin: 8px - flex-grow: 1 - cursor: pointer - @media (max-width: 1024px) - margin: 16px - &:hover, &:focus - background: #FFD54F - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - @media (max-width: 1024px) - width: auto - margin-left: 0 - margin-top: 32px - .detail - max-width: 640px - width: 100% - background: #333333 - border-radius: 4px - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4) - .title - line-height: 1.25 - padding: 16px - vertical-align: middle - font-size: 14pt - font-weight: normal - color: #eee - background: none - border: none - width: 100% - box-sizing: border-box - &:not(.fake-input):empty - display: none - & + .image - border-top-left-radius: 4px - border-top-right-radius: 4px - .image - background: #000 - display: flex - flex-direction: row - justify-content: center - align-items: start - img - max-width: 100% - .description - line-height: 1.25 - padding: 16px - vertical-align: middle - font-size: 11pt - font-weight: normal - color: #eee - background: none - border: none - width: 100% - box-sizing: border-box - font-family: 'Lato', sans-serif - resize: vertical - white-space: pre-line - &:not(.fake-input):empty - display: none - .fake-input[contenteditable]:empty:before - opacity: 0.4 - content: attr(placeholder) \ No newline at end of file diff --git a/frontend/assets/sass/_page_image_detail.scss b/frontend/assets/sass/_page_image_detail.scss new file mode 100644 index 0000000000000000000000000000000000000000..5cd28778f330796a1dfdf660bb258a72a6d2f9d2 --- /dev/null +++ b/frontend/assets/sass/_page_image_detail.scss @@ -0,0 +1,189 @@ +.page.image.detail, .page.upload .uploading-images { + display: flex; + flex-direction: row; + align-items: start; + align-self: center; + padding: 32px 32px 128px; + justify-content: center; + @media (max-width: 1024px) { + flex-direction: column; + padding: 32px 0; + align-items: stretch; + justify-content: start; + } + + .sidebar { + width: 250px; + max-width: 640px; + margin-left: 32px; + + .url { + background: #333333; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); + padding: 8px; + margin-bottom: 8px; + @media (max-width: 1024px) { + padding: 16px; + margin-bottom: 32px; + } + + p { + color: #ffffff; + } + + div { + display: flex; + flex-direction: row; + + input { + background: none; + color: #fff; + opacity: 0.4; + border: none; + flex-shrink: 1; + display: block; + flex-grow: 1; + width: 0; + text-overflow: ellipsis; + } + + button.copy { + display: block; + background: #FFC107; + padding: 4px 16px; + border-radius: 2px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + border: none; + font-family: 'Lato', sans-serif; + font-size: 10pt; + line-height: 24px; + margin-left: 8px; + cursor: pointer; + + &:hover, + &:focus { + background: #FFD54F; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + } + } + } + } + + .actions { + background: #333333; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: row; + margin-bottom: 8px; + @media (max-width: 1024px) { + margin-bottom: 32px; + } + + .delete-form, + .update-form { + display: flex; + flex-grow: 1; + flex-basis: 0; + + input[type=submit] { + display: block; + background: #FFC107; + padding: 4px 16px; + border-radius: 2px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + border: none; + font-family: 'Lato', sans-serif; + font-size: 10pt; + line-height: 24px; + margin: 8px; + flex-grow: 1; + cursor: pointer; + @media (max-width: 1024px) { + margin: 16px; + } + + &:hover, + &:focus { + background: #FFD54F; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + } + } + } + } + + @media (max-width: 1024px) { + width: auto; + margin-left: 0; + margin-top: 32px; + } + } + + .detail { + max-width: 640px; + width: 100%; + background: #333333; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); + + .title { + line-height: 1.25; + padding: 16px; + vertical-align: middle; + font-size: 14pt; + font-weight: normal; + color: #eee; + background: none; + border: none; + width: 100%; + box-sizing: border-box; + + &:not(.fake-input):empty { + display: none; + + & + .image { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } + } + } + + .image { + background: #000; + display: flex; + flex-direction: row; + justify-content: center; + align-items: start; + + img { + max-width: 100%; + } + } + + .description { + line-height: 1.25; + padding: 16px; + vertical-align: middle; + font-size: 11pt; + font-weight: normal; + color: #eee; + background: none; + border: none; + width: 100%; + box-sizing: border-box; + font-family: 'Lato', sans-serif; + resize: vertical; + white-space: pre-line; + + &:not(.fake-input):empty { + display: none; + } + } + } + + .fake-input[contenteditable]:empty:before { + opacity: 0.4; + content: attr(placeholder); + } +} diff --git a/frontend/assets/sass/_page_image_list.sass b/frontend/assets/sass/_page_image_list.sass deleted file mode 100644 index e367c712c7802a8beed80def31f18065d5f000b0..0000000000000000000000000000000000000000 --- a/frontend/assets/sass/_page_image_list.sass +++ /dev/null @@ -1,55 +0,0 @@ -.page.image.list - max-width: 1024px - margin: 8px auto 48px - display: grid - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)) - grid-gap: 8px - align-self: stretch - width: calc(100% - 16px) - - @media (max-width: 600px) - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)) - - .image - padding: 8px - position: relative - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - transition: all 200ms - text-decoration: none - background: #333333 - border-radius: 2px - will-change: transform - - &:hover, &:focus - transform: translate(0, -2px) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4) - .image-container - display: flex - justify-content: stretch - align-content: stretch - justify-items: stretch - align-items: stretch - flex-direction: column - background: #000000 - - img - aspect-ratio: 1 - object-fit: contain - .info - display: block - z-index: 1 - color: #eeeeee - line-height: 1.25 - font-size: 10pt - padding-top: 12px - - p - white-space: nowrap - text-overflow: ellipsis - overflow: hidden - - &.title - font-weight: 600 - - span.placeholder - opacity: 0.4 diff --git a/frontend/assets/sass/_page_image_list.scss b/frontend/assets/sass/_page_image_list.scss new file mode 100644 index 0000000000000000000000000000000000000000..7dbe2be907eed72ee821f1d65d3e8890e5e8a564 --- /dev/null +++ b/frontend/assets/sass/_page_image_list.scss @@ -0,0 +1,68 @@ +.page.image.list { + max-width: 1024px; + margin: 8px auto 48px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + grid-gap: 8px; + align-self: stretch; + width: calc(100% - 16px); + + @media (max-width: 600px) { + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + } + + .image { + padding: 8px; + position: relative; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all 200ms; + text-decoration: none; + background: #333333; + border-radius: 2px; + will-change: transform; + + &:hover, + &:focus { + transform: translate(0, -2px); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4); + } + + .image-container { + display: flex; + justify-content: stretch; + align-content: stretch; + justify-items: stretch; + align-items: stretch; + flex-direction: column; + background: #000000; + + img { + aspect-ratio: 1; + object-fit: contain; + } + } + + .info { + display: block; + z-index: 1; + color: #eeeeee; + line-height: 1.25; + font-size: 10pt; + padding-top: 12px; + + p { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + + &.title { + font-weight: 600; + + span.placeholder { + opacity: 0.4; + } + } + } + } + } +} diff --git a/frontend/assets/sass/_page_upload.sass b/frontend/assets/sass/_page_upload.sass deleted file mode 100644 index a82a5c84600fcbc426eec2f3c6cd5af71c2802c3..0000000000000000000000000000000000000000 --- a/frontend/assets/sass/_page_upload.sass +++ /dev/null @@ -1,130 +0,0 @@ -.page.upload - flex-grow: 1 - display: flex - flex-direction: column - - .alert - padding: 16px - margin: 16px 0 - box-shadow: 0 2px 4px rgba(33, 33, 33, 0.2) - text-decoration: none - border-radius: 2px - &.success - background: #DCEDC8 - color: #689F38 - border-color: #689F38 - a - color: #33691E - &.error - background: #FFEBEE - color: #F44336 - border-color: #F44336 - a - color: #D32F2F - - form.upload - padding: 96px 0 - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - text-decoration: none - border-radius: 2px - text-align: center - background: #333333 - .upload-label - font-size: 18pt - color: #fff - label - position: relative - display: inline-block - overflow: hidden - span.text - position: relative - display: inline-block - background: #FFC107 - padding: 4px 16px - border-radius: 2px - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) - line-height: 24px - color: #282828 - cursor: pointer - z-index: 1 - &:hover, &:focus - background: #FFD54F - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - input[type=file] - position: absolute - left: 0 - right: 0 - top: 0 - bottom: 0 - opacity: 0 - .uploading-images - display: flex - flex-direction: row - .images - display: flex - flex-direction: column - align-items: stretch - flex-grow: 1 - .detail - margin-bottom: 32px - .image - position: relative - .progress - position: absolute - top: -4px - left: 0 - right: 0 - height: 4px - display: block - background-color: rgba(255, 193, 7, 0.2) - overflow: hidden - transition: opacity 400ms - .indeterminate - background-color: rgba(255, 193, 7, 0.8) - &::before - content: '' - position: absolute - background-color: inherit - top: 0 - left: 0 - bottom: 0 - will-change: left, right - animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite - &::after - content: '' - position: absolute - background-color: inherit - top: 0 - left: 0 - bottom: 0 - will-change: left, right - animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite - animation-delay: 1.125s - &:not(.uploading) .progress - opacity: 0 - &.submitted .container.centered - display: none - &:not(.submitted) .uploading-images - display: none - -@keyframes indeterminate - 0% - left: -35% - right: 100% - 60% - left: 100% - right: -90% - 100% - left: 100% - right: -90% - -@keyframes indeterminate-short - 0% - left: -200% - right: 100% - 60% - left: 107% - right: -8% - 100% - left: 107% - right: -8% \ No newline at end of file diff --git a/frontend/assets/sass/_page_upload.scss b/frontend/assets/sass/_page_upload.scss new file mode 100644 index 0000000000000000000000000000000000000000..c1353c79cdf93a9c434324b27fcb6fc65d097b85 --- /dev/null +++ b/frontend/assets/sass/_page_upload.scss @@ -0,0 +1,184 @@ +.page.upload { + flex-grow: 1; + display: flex; + flex-direction: column; + + .alert { + padding: 16px; + margin: 16px 0; + box-shadow: 0 2px 4px rgba(33, 33, 33, 0.2); + text-decoration: none; + border-radius: 2px; + + &.success { + background: #DCEDC8; + color: #689F38; + border-color: #689F38; + + a { + color: #33691E; + } + } + + &.error { + background: #FFEBEE; + color: #F44336; + border-color: #F44336; + + a { + color: #D32F2F; + } + } + } + + form.upload { + padding: 96px 0; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + text-decoration: none; + border-radius: 2px; + text-align: center; + background: #333333; + + .upload-label { + font-size: 18pt; + color: #fff; + } + + label { + position: relative; + display: inline-block; + overflow: hidden; + + span.text { + position: relative; + display: inline-block; + background: #FFC107; + padding: 4px 16px; + border-radius: 2px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + line-height: 24px; + color: #282828; + cursor: pointer; + z-index: 1; + + &:hover, + &:focus { + background: #FFD54F; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + } + } + + input[type=file] { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + opacity: 0; + } + } + } + + .uploading-images { + display: flex; + flex-direction: row; + + .images { + display: flex; + flex-direction: column; + align-items: stretch; + flex-grow: 1; + + .detail { + margin-bottom: 32px; + + .image { + position: relative; + + .progress { + position: absolute; + top: -4px; + left: 0; + right: 0; + height: 4px; + display: block; + background-color: rgba(255, 193, 7, 0.2); + overflow: hidden; + transition: opacity 400ms; + + .indeterminate { + background-color: rgba(255, 193, 7, 0.8); + + &::before { + content: ''; + position: absolute; + background-color: inherit; + top: 0; + left: 0; + bottom: 0; + will-change: left, right; + animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite; + } + + &::after { + content: ''; + position: absolute; + background-color: inherit; + top: 0; + left: 0; + bottom: 0; + will-change: left, right; + animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite; + animation-delay: 1.125s; + } + } + } + } + + &:not(.uploading) .progress { + opacity: 0; + } + } + } + } + + &.submitted .container.centered { + display: none; + } + + &:not(.submitted) .uploading-images { + display: none; + } +} + +@keyframes indeterminate { + 0% { + left: -35%; + right: 100%; + } + 60% { + left: 100%; + right: -90%; + } + 100% { + left: 100%; + right: -90%; + } +} + +@keyframes indeterminate-short { + 0% { + left: -200%; + right: 100%; + } + 60% { + left: 107%; + right: -8%; + } + 100% { + left: 107%; + right: -8%; + } +} + +; diff --git a/frontend/assets/sass/_pagination.sass b/frontend/assets/sass/_pagination.sass deleted file mode 100644 index 9d206c6029197de3b8b1cb500d7d34f4f7dc64f4..0000000000000000000000000000000000000000 --- a/frontend/assets/sass/_pagination.sass +++ /dev/null @@ -1,25 +0,0 @@ -ul.pagination - display: flex - flex-direction: row - background: #333 - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - padding: 4px 8px - color: #fff - justify-self: center - grid-column-start: 1 - grid-column-end: -1 - max-width: 480px - width: calc(100% - 16px) - - li.page - appearance: none - list-style: none - margin: 4px - padding: 0 - line-height: 24px - - &.current - display: block - padding: 4px 16px - flex-grow: 1 - text-align: center diff --git a/frontend/assets/sass/_pagination.scss b/frontend/assets/sass/_pagination.scss new file mode 100644 index 0000000000000000000000000000000000000000..51cd7c291e354cfb5c726fd64c1d224f6200a409 --- /dev/null +++ b/frontend/assets/sass/_pagination.scss @@ -0,0 +1,28 @@ +ul.pagination { + display: flex; + flex-direction: row; + background: #333; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + padding: 4px 8px; + color: #fff; + justify-self: center; + grid-column-start: 1; + grid-column-end: -1; + max-width: 480px; + width: calc(100% - 16px); + + li.page { + appearance: none; + list-style: none; + margin: 4px; + padding: 0; + line-height: 24px; + + &.current { + display: block; + padding: 4px 16px; + flex-grow: 1; + text-align: center; + } + } +} diff --git a/frontend/assets/sass/fonts.sass b/frontend/assets/sass/fonts.scss similarity index 50% rename from frontend/assets/sass/fonts.sass rename to frontend/assets/sass/fonts.scss index e0b855b982e0cf6f578c7dba58d98a41cf01e640..5fefcf5dbd449ebef0e788896e05c1fa333e92a1 100644 --- a/frontend/assets/sass/fonts.sass +++ b/frontend/assets/sass/fonts.scss @@ -1,31 +1,37 @@ -@font-face - font-family: 'Lato' - src: url('/assets/fonts/Lato-Regular.eot') - src: url('/assets/fonts/Lato-Regular.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-Regular.woff2') format('woff2'), url('/assets/fonts/Lato-Regular.woff') format('woff'), url('/assets/fonts/Lato-Regular.ttf') format('truetype') - font-style: normal - font-weight: normal - text-rendering: optimizeLegibility +@font-face { + font-family: 'Lato'; + src: url('/assets/fonts/Lato-Regular.eot'); + src: url('/assets/fonts/Lato-Regular.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-Regular.woff2') format('woff2'), url('/assets/fonts/Lato-Regular.woff') format('woff'), url('/assets/fonts/Lato-Regular.ttf') format('truetype'); + font-style: normal; + font-weight: normal; + text-rendering: optimizeLegibility; +} -@font-face - font-family: 'Lato' - src: url('/assets/fonts/Lato-Bold.eot') - src: url('/assets/fonts/Lato-Bold.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-Bold.woff2') format('woff2'), url('/assets/fonts/Lato-Bold.woff') format('woff'), url('/assets/fonts/Lato-Bold.ttf') format('truetype') - font-style: normal - font-weight: bold - text-rendering: optimizeLegibility +@font-face { + font-family: 'Lato'; + src: url('/assets/fonts/Lato-Bold.eot'); + src: url('/assets/fonts/Lato-Bold.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-Bold.woff2') format('woff2'), url('/assets/fonts/Lato-Bold.woff') format('woff'), url('/assets/fonts/Lato-Bold.ttf') format('truetype'); + font-style: normal; + font-weight: bold; + text-rendering: optimizeLegibility; +} -@font-face - font-family: 'Lato' - src: url('/assets/fonts/Lato-BoldItalic.eot') - src: url('/assets/fonts/Lato-BoldItalic.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-BoldItalic.woff2') format('woff2'), url('/assets/fonts/Lato-BoldItalic.woff') format('woff'), url('/assets/fonts/Lato-BoldItalic.ttf') format('truetype') - font-style: italic - font-weight: bold - text-rendering: optimizeLegibility +@font-face { + font-family: 'Lato'; + src: url('/assets/fonts/Lato-BoldItalic.eot'); + src: url('/assets/fonts/Lato-BoldItalic.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-BoldItalic.woff2') format('woff2'), url('/assets/fonts/Lato-BoldItalic.woff') format('woff'), url('/assets/fonts/Lato-BoldItalic.ttf') format('truetype'); + font-style: italic; + font-weight: bold; + text-rendering: optimizeLegibility; +} -@font-face - font-family: 'Lato' - src: url('/assets/fonts/Lato-Italic.eot') - src: url('/assets/fonts/Lato-Italic.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-Italic.woff2') format('woff2'), url('/assets/fonts/Lato-Italic.woff') format('woff'), url('/assets/fonts/Lato-Italic.ttf') format('truetype') - font-style: italic - font-weight: normal - text-rendering: optimizeLegibility \ No newline at end of file +@font-face { + font-family: 'Lato'; + src: url('/assets/fonts/Lato-Italic.eot'); + src: url('/assets/fonts/Lato-Italic.eot?#iefix') format('embedded-opentype'), url('/assets/fonts/Lato-Italic.woff2') format('woff2'), url('/assets/fonts/Lato-Italic.woff') format('woff'), url('/assets/fonts/Lato-Italic.ttf') format('truetype'); + font-style: italic; + font-weight: normal; + text-rendering: optimizeLegibility; +} + +; diff --git a/frontend/assets/sass/style.sass b/frontend/assets/sass/style.sass deleted file mode 100644 index b715ca5c808cf9b282ea6612e61540bd7575c0eb..0000000000000000000000000000000000000000 --- a/frontend/assets/sass/style.sass +++ /dev/null @@ -1,58 +0,0 @@ -* - margin: 0 - padding: 0 - -*:focus - outline: none - -*::-moz-focus-inner - border: 0 - -html - height: 100% - -body - background: #282828 - font-family: 'Lato', sans-serif - font-size: 81.25% - min-height: 100% - display: flex - flex-direction: column - -.container.centered - flex-grow: 1 - display: flex - margin: 0 auto 64px auto - padding: 0 16px - flex-direction: column - justify-content: center - width: 100% - max-width: 640px - -.button - display: block - background: #FFC107 - padding: 4px 16px - border-radius: 2px - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) - color: #282828 - text-decoration: none - line-height: 24px - cursor: pointer - min-width: 40px - text-align: center - - &:hover, &:focus - background: #FFD54F - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - - &[aria-disabled=true], &:disabled - cursor: default - background: #838383 - box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.1) - -@import "navigation" -@import "page_upload" -@import "page_image_detail" -@import "page_image_list" -@import "pagination" diff --git a/frontend/assets/sass/style.scss b/frontend/assets/sass/style.scss new file mode 100644 index 0000000000000000000000000000000000000000..e6d37ba05d97ee5864fca7b68abbca4592e8fa05 --- /dev/null +++ b/frontend/assets/sass/style.scss @@ -0,0 +1,70 @@ +* { + margin: 0; + padding: 0; +} + +*:focus { + outline: none; +} + +*::-moz-focus-inner { + border: 0; +} + +html { + height: 100%; +} + +body { + background: #282828; + font-family: 'Lato', sans-serif; + font-size: 81.25%; + min-height: 100%; + display: flex; + flex-direction: column; +} + +.container.centered { + flex-grow: 1; + display: flex; + margin: 0 auto 64px auto; + padding: 0 16px; + flex-direction: column; + justify-content: center; + width: 100%; + max-width: 640px; +} + +.button { + display: block; + background: #FFC107; + padding: 4px 16px; + border-radius: 2px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + color: #282828; + text-decoration: none; + line-height: 24px; + cursor: pointer; + min-width: 40px; + text-align: center; + + &:hover, + &:focus { + background: #FFD54F; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + } + + &[aria-disabled=true], + &:disabled { + cursor: default; + background: #838383; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.1); + } +} + +@import "navigation"; +@import "page_upload"; +@import "page_image_detail"; +@import "page_image_list"; +@import "pagination"; +@import "error"; diff --git a/frontend/errors.go b/frontend/errors.go index 29c96ff8fddb13183833358a260bb30bed16fca8..e8d868d8da131eb04e8586b78cc319d6c5574f7a 100644 --- a/frontend/errors.go +++ b/frontend/errors.go @@ -19,8 +19,8 @@ type errorDto struct { } func formatError(w http.ResponseWriter, data ErrorData, format string) { - if data.Code != 0 { - data.Code = 500 + if data.Code == 0 { + data.Code = http.StatusInternalServerError } log.Printf( "A type %d error occured for user %s while accessing %s: %s", @@ -32,7 +32,7 @@ func formatError(w http.ResponseWriter, data ErrorData, format string) { w.WriteHeader(data.Code) if format == "html" { if err := formatTemplate(w, "error.html", data); err != nil { - log.Printf("Error while serving html error for %s", data.URL.Path) + log.Printf("error serving html error for %s", data.URL.Path) return } } else if format == "json" { @@ -40,7 +40,7 @@ func formatError(w http.ResponseWriter, data ErrorData, format string) { data.URL.Path, data.Error.Error(), }); err != nil { - log.Printf("Error while serving json error for %s", data.URL.Path) + log.Printf("error serving json error for %s", data.URL.Path) return } } diff --git a/frontend/main.go b/frontend/main.go index 28d6751e5e34c57b0eead624c128e411dfc0b009..df215d81395220fea8f6a7831a469effafb07ca6 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -14,13 +14,13 @@ import ( func main() { configFile, err := os.Open("config.yaml") if err != nil { - log.Fatalf("Could not open config file: %s", err.Error()) + log.Fatalf("error opening config file: %s", err.Error()) } config := shared.LoadConfigFromFile(configFile) db, err := sql.Open(config.Database.Format, config.Database.Url) if err != nil { - panic(err) + log.Fatalf("error connecting to database: %s", err.Error()) } pageContext := PageContext{ @@ -46,8 +46,7 @@ func main() { w.Write([]byte("OK")) }) - err = http.ListenAndServe(":8080", nil) - if err != nil { - panic(err) + if err := http.ListenAndServe(":8080", nil); err != nil { + log.Fatalf("error in http server: %s", err.Error()) } } diff --git a/frontend/page_album_detail.go b/frontend/page_album_detail.go index 94d4683964a011e47794b333d99ea78f2adbf5cf..4308e3ec4de0d33472cf1e64623a673df10f5086 100644 --- a/frontend/page_album_detail.go +++ b/frontend/page_album_detail.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "net/http" "path" ) @@ -28,7 +27,8 @@ func pageAlbumDetail(ctx PageContext) http.Handler { WHERE id = $1 `, albumId) if err != nil { - panic(err) + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") + return } var info Album @@ -36,7 +36,8 @@ func pageAlbumDetail(ctx PageContext) http.Handler { var owner string err := result.Scan(&info.Id, &owner, &info.Title, &info.Description, &info.CreatedAt) if err != nil { - panic(err) + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") + return } result, err := ctx.Database.Query(` @@ -50,14 +51,16 @@ func pageAlbumDetail(ctx PageContext) http.Handler { ORDER BY position ASC `, albumId) if err != nil { - panic(err) + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") + return } for result.Next() { var image AlbumImage err := result.Scan(&image.Id, &owner, &image.Title, &image.Description, &image.Position) if err != nil { - panic(err) + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") + return } info.Images = append(info.Images, image) @@ -68,13 +71,16 @@ func pageAlbumDetail(ctx PageContext) http.Handler { info, owner == user.Id, }); err != nil { - panic(err) + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") + return } return } - w.WriteHeader(http.StatusNotFound) - fmt.Fprint(w, "Album not found") + if err := returnError(w, http.StatusNotFound, "Image Not Found"); err != nil { + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") + return + } }) } diff --git a/frontend/page_image_detail.go b/frontend/page_image_detail.go index e14ff3f85243ed5dd8ff0e5a73b1e9bd3a67132a..81bd483e9097df38505e84ad5f012c8129697f35 100644 --- a/frontend/page_image_detail.go +++ b/frontend/page_image_detail.go @@ -33,7 +33,7 @@ func pageImageDetail(ctx PageContext) http.Handler { WHERE id = $1 `, imageId) if err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } @@ -42,7 +42,7 @@ func pageImageDetail(ctx PageContext) http.Handler { if result.Next() { var owner string if err := result.Scan(&info.Id, &owner, &info.Title, &info.Description, &info.CreatedAt, &info.OriginalName, &info.MimeType); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } @@ -55,12 +55,12 @@ func pageImageDetail(ctx PageContext) http.Handler { info.Id, user.Id, ); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } if r.PostFormValue("from_js") == "true" { if err := returnJson(w, true); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } } else { @@ -69,12 +69,12 @@ func pageImageDetail(ctx PageContext) http.Handler { return case "delete": if _, err := ctx.Database.Exec("DELETE FROM images WHERE id = $1 AND owner = $2", info.Id, user.Id); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } for _, definition := range ctx.Config.Sizes { if err := os.Remove(path.Join(ctx.Config.TargetFolder, fmt.Sprintf("%s%s", info.Id, definition.Suffix))); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } } @@ -87,14 +87,14 @@ func pageImageDetail(ctx PageContext) http.Handler { info, owner == user.Id, }); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } return } if err := returnError(w, http.StatusNotFound, "Image Not Found"); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } }) diff --git a/frontend/page_image_list.go b/frontend/page_image_list.go index 41771004525eff1237d84fc237bc1eb43cf64224..aa0ab6d7a4504becead8d91fecd94de41e5c2c72 100644 --- a/frontend/page_image_list.go +++ b/frontend/page_image_list.go @@ -68,7 +68,7 @@ func pageImageList(ctx PageContext) http.Handler { PageSize, ) if err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } @@ -76,7 +76,7 @@ func pageImageList(ctx PageContext) http.Handler { for result.Next() { var info shared.Image if err := result.Scan(&info.Id, &info.Title, &info.Description, &info.CreatedAt, &info.OriginalName, &info.MimeType); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } images = append(images, info) @@ -89,7 +89,7 @@ func pageImageList(ctx PageContext) http.Handler { pageNumber, pageNumber + 1, }); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } }) diff --git a/frontend/page_upload.go b/frontend/page_upload.go index eae13b58867fd7c25434ed30fe785f59d38847da..0387c251f25f1aa75a3e6723f96fd5191cec08f1 100644 --- a/frontend/page_upload.go +++ b/frontend/page_upload.go @@ -80,23 +80,23 @@ func pageUpload(ctx PageContext) http.Handler { err := r.ParseMultipartForm(32 << 20) if err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } file, header, err := r.FormFile("file") if err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } image, err := createImage(ctx.Config, file, header) if err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } if _, err = ctx.Database.Exec("INSERT INTO images (id, owner, created_at, updated_at, original_name, type) VALUES ($1, $2, $3, $4, $5, $6)", image.Id, user.Id, image.CreatedAt, image.CreatedAt, image.OriginalName, image.MimeType); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } @@ -104,25 +104,25 @@ func pageUpload(ctx PageContext) http.Handler { t, err := shared.NewImageResizeTask(image.Id) fmt.Printf("Submitted task %s at %d\n", image.Id, time.Now().Unix()) if err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } info, err := ctx.Async.Enqueue(t) if err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } if err := waitOnTask(info, ctx.UploadTimeout); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } var result shared.Result if err := json.Unmarshal(info.Result, &result); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } if err = returnJson(w, result); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "json") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "json") return } return @@ -131,7 +131,7 @@ func pageUpload(ctx PageContext) http.Handler { if err := formatTemplate(w, "upload.html", IndexData{ user, }); err != nil { - formatError(w, ErrorData{500, user, r.URL, err}, "html") + formatError(w, ErrorData{http.StatusInternalServerError, user, r.URL, err}, "html") return } } diff --git a/go.mod b/go.mod index e79686c0015aa75dd366fddb77a79d68d8b781e9..a47887b505b089205226453424fc28298f538745 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.kuschku.de/justjanne/imghost-frontend +module git.kuschku.de/justjanne/imghost go 1.15 diff --git a/shared/config.go b/shared/config.go index 8348eb5ec58231bf9798b21acda5d3109b4ea1a8..25928b89941c092dd68e9e723249a7155930a1c1 100644 --- a/shared/config.go +++ b/shared/config.go @@ -39,7 +39,7 @@ type Config struct { func LoadConfigFromFile(file *os.File) Config { var config Config if err := yaml.NewDecoder(file).Decode(&config); err != nil { - log.Fatalf("Could not load config, %s", err.Error()) + log.Fatalf("error loading config, %s", err.Error()) } return config } @@ -47,7 +47,7 @@ func LoadConfigFromFile(file *os.File) Config { func (config Config) UploadTimeoutDuration() time.Duration { duration, err := time.ParseDuration(config.UploadTimeout) if err != nil { - log.Fatalf("Could not load config: Could not parse upload timeout, %s", err.Error()) + log.Fatalf("error loading config: error parsing upload timeout, %s", err.Error()) } return duration }