Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a web UI #4

Merged
merged 70 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
166f55e
Switch to gin
brackendawson Sep 7, 2024
6491d97
A web page that can load a raw ics
brackendawson Sep 7, 2024
d242f5c
Mastering the flex box
brackendawson Sep 8, 2024
886bd2c
Add an empty calendar
brackendawson Sep 8, 2024
3556cb9
Improve the skellington
brackendawson Sep 9, 2024
b56fd57
Use CSS variables
brackendawson Sep 9, 2024
27a5b63
main is a better content container
brackendawson Sep 10, 2024
b68fa31
Render the calender in the user's time zone
brackendawson Sep 10, 2024
f94e03e
Remove unnecessary loops
brackendawson Sep 10, 2024
5c14e73
Remove unnecessary divs, classes, and funcs
brackendawson Sep 11, 2024
93d493d
left initial calendar div open
brackendawson Sep 11, 2024
48200da
cleaner space in day classes
brackendawson Sep 11, 2024
fe4d980
Refactor tests and remove global variables
brackendawson Sep 11, 2024
1775a26
Test the calendar handler
brackendawson Sep 11, 2024
c5ea4ff
Somewhat working webcal load into UI
brackendawson Sep 12, 2024
e707a49
Allow 10 redirects
brackendawson Sep 12, 2024
85aea53
Move some funcs
brackendawson Sep 12, 2024
e13aa09
Add a user agent
brackendawson Sep 12, 2024
d249d97
Hide events when there are too many
brackendawson Sep 13, 2024
c109c11
Cache the calendar in the client
brackendawson Sep 14, 2024
f4f6256
Make sure validation is done up front
brackendawson Sep 14, 2024
89810cf
Add a loading indicator
brackendawson Sep 14, 2024
234d897
Sort out the error handling
brackendawson Sep 14, 2024
2337acb
Restructure module
brackendawson Sep 14, 2024
edb7954
Show events in users' local time
brackendawson Sep 14, 2024
8a665df
this was unused
brackendawson Sep 15, 2024
56a800e
Show URL for user to copy
brackendawson Sep 15, 2024
ed1a694
Bootstrapify notifications
brackendawson Sep 15, 2024
56b32e3
Hide the copy button when loading
brackendawson Sep 15, 2024
f55e247
Remove redundant CSS
brackendawson Sep 15, 2024
1b16293
stop using z-index
brackendawson Sep 15, 2024
44664bb
Fix host in client URL
brackendawson Sep 15, 2024
e7fb44c
Cleanup: keep the gin context in the handlers
brackendawson Sep 15, 2024
18b13c3
Support proxy path
brackendawson Sep 15, 2024
4f90e42
Don't insist on a trailing slash
brackendawson Sep 15, 2024
1c57eaf
Fix regressions in webcal handling
brackendawson Sep 15, 2024
7198541
Fix try button
brackendawson Sep 15, 2024
74fed75
fix nginx snippet formatting
brackendawson Sep 15, 2024
22ce3ea
Add the merge option
brackendawson Sep 15, 2024
adc6b03
Add arbitrary matchers to the form
brackendawson Sep 19, 2024
5a3bcba
If the page URL has our args pre-fill the form
brackendawson Sep 20, 2024
dc57945
Bug: Only add template matcher to form if there are 0 inc and 0 exc p…
brackendawson Sep 20, 2024
6a67b3c
Allow month to be selected
brackendawson Sep 20, 2024
d0deacc
refactor: move form templated to form.html
brackendawson Sep 20, 2024
a791eec
Fix log format strings
brackendawson Sep 20, 2024
dcf6d89
Add a today button
brackendawson Sep 20, 2024
108d201
Use bootstrap alerts
brackendawson Sep 21, 2024
fd7945b
Fix paths and nginx snippet
brackendawson Sep 21, 2024
0da7b98
Stop checking upstream content-type
brackendawson Sep 21, 2024
16413b0
Improve layout on mobile
brackendawson Sep 22, 2024
f8631af
Add expanded modal events
brackendawson Sep 22, 2024
76f8d35
Call to action alert for no calendar
brackendawson Sep 22, 2024
ec493b5
modal for spill days should be opaque
brackendawson Sep 22, 2024
5b7ca5e
I want to clean up the script src
brackendawson Sep 22, 2024
d98b06e
Add github link
brackendawson Sep 22, 2024
74216e9
Be consistent with what a webcal feed is called
brackendawson Sep 22, 2024
72ac413
It's not today every year
brackendawson Sep 22, 2024
01b17a4
Show multi-day events in all their days
brackendawson Sep 24, 2024
577564a
Fix summary closing tag
brackendawson Sep 24, 2024
d9684ed
Remove empty name attr from matcher arg
brackendawson Sep 24, 2024
fee87c7
Cleanup webcal URL placeholder
brackendawson Sep 24, 2024
c6adfbd
Remove inline javascript
brackendawson Sep 25, 2024
d41eccb
Only need to default includes once
brackendawson Sep 25, 2024
9312c2c
Clean up calendar
brackendawson Sep 25, 2024
57b7624
Fix panic when matching unset property
brackendawson Sep 25, 2024
e6b001c
Fix notification, only ever display one
brackendawson Sep 25, 2024
79e8eaa
Fix year picker delay
brackendawson Sep 25, 2024
3b5bfdc
Add some more log fields
brackendawson Sep 25, 2024
96da6e8
Don't log warnings for events with no end time
brackendawson Sep 26, 2024
fe09bd1
Make fixtures match file names
brackendawson Sep 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
[![Go workflow](https://github.com/brackendawson/webcal-proxy/actions/workflows/go.yml/badge.svg)](https://github.com/brackendawson/webcal-proxy/actions/workflows/go.yml)

# webcal-proxy
A simple server to proxy and filter webcal links.
# ![wp logo](assets/img/favicon.ico) webcal-proxy
A simple server to filter events from webcal feeds.

[![Try it out](doc/try-it-out.png)](https://bracken.cc/webcal-proxy)

## Usage
### Server
#### Arguments
Usage of webcal-proxy:
* -addr string
local address:port to bind to (default ":80")
local address:port to bind to (default ":8080")
* -logfile string
File to log to
* -loglevel string
* -log-level string
log level (default "info")
* -max-conns maximum total upstream connections
* -dev disables security policies that prevent http://localhost from working

#### TLS
The server should be run behind a reverse proxy which terminates TLS because the webcal:// protocol requires valid TLS. The web interface will also not function on http without the -dev argument, even then some things will not work, such as clipboard interaction.

#### Proxy Path
If the reverse proxy uses a path then provide it in the `X-Forwarded-URI` header. Example nginx config:
```nginx
location /webcal-proxy/ {
proxy_pass http://127.0.0.1:8080;
rewrite ^/webcal-proxy/(.*)$ /$1 break;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-URI /webcal-proxy;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_buffering off;
}
```

### Client
Enter the URL into your webcal client:
Expand All @@ -27,5 +49,5 @@ Where:

eg:
```
webcal://example.com/webcal-proxy?cal=webcal://example.com/my/calendar&exc=SUMMARY=Boring%20Events
webcal://webcal-proxy.example.com/webcal-proxy?cal=webcal://example.com/my/calendar&exc=SUMMARY=Boring%20Events
```
40 changes: 40 additions & 0 deletions assets/assets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package assets

import (
"embed"
"fmt"
"html/template"
)

var (
//go:embed js css img webfonts
Assets embed.FS
//go:embed html
templates embed.FS

funcs template.FuncMap = template.FuncMap{
"errorf": func(format string, args ...any) (any, error) {
return nil, fmt.Errorf(format, args...)
},
"dict": dict,
}
)

func Templates() *template.Template {
return template.Must(template.New("_all").Funcs(funcs).ParseFS(templates, "html/*.html"))
}

func dict(kv ...any) (map[string]any, error) {
if len(kv)%2 != 0 {
return nil, fmt.Errorf("dict must have even number arguments, got: %d", len(kv))
}
m := make(map[string]any, len(kv)/2)
for i := 0; i < len(kv); i += 2 {
k, ok := kv[i].(string)
if !ok {
return nil, fmt.Errorf("dict argument %d must be string, got %T", i, kv[i])
}
m[k] = kv[i+1]
}
return m, nil
}
6 changes: 6 additions & 0 deletions assets/css/bootstrap.min.css

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions assets/css/fontawesome.min.css

Large diffs are not rendered by default.

256 changes: 256 additions & 0 deletions assets/css/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
:root {
--max-width: 60rem;
--min-width: 20rem;
--light-bg: #ffaa00;
--dark-bg: #ff6600;
--light-grey: #eeeeee;
--med-grey: #cccccc;
--light-blue: #6599ff;
--med-blue: #0066ff;
--light-red: #ffaacc;
--light-green: #92fe92;
}


html,
body {
height: 100%;
}

body {
display: flex;
flex-direction: column;
align-items: center;
min-width: var(--min-width);
}

header {
width: 100%;
padding: 2rem;
padding-bottom: 1rem;
text-align: center;
color: white;
background: var(--light-bg);
background-image: linear-gradient(var(--light-bg), var(--dark-bg) 100%);
}

main {
flex-grow: 1;
padding: 1rem;
max-width: var(--max-width);
width: 100%;
}

footer {
width: 100%;
box-sizing: border-box;
padding-inline: calc(50% - var(--max-width) / 2);
padding-block: 1rem;
color: white;
background: var(--light-bg);
background-image: linear-gradient(var(--dark-bg), var(--light-bg) 100%);
}

footer span {
padding-left: 1rem;
}

footer a {
text-decoration: inherit;
color: inherit;
}

#calendar {
display: flex;
flex-wrap: wrap;
margin-block: 1rem;
font-size: 0.8rem;
}

#calendar * {
overflow: hidden;
text-overflow: ellipsis;
}

.calendar-title {
width: 100%;
text-align: center;
}

.calendar-dow {
width: calc(100%/7);
text-align: center;
}

.day {
position: relative;
min-height: 6.3em;
width: calc(100%/7);
border: solid 0.05rem;
border-color: var(--light-grey);
}

.day > a {
text-decoration: inherit;
color: inherit;
}

.day-head {
background: var(--light-grey);
padding-left: 0.25em;
}

.day-event {
white-space: nowrap;
border-radius: 0.25em;
background: var(--med-blue);
padding-left: 0.25em;
margin-bottom: 0.1em;
}

.day-event > .event-end-time,
.day-event > .event-location,
.day-event > .event-description {
display: none;
}

.event-start-time-continued:after {
content: "...";
}

.modal-body > p {
margin-block: 0;
}

.event-modal-start-time:before {
content: "Start: ";
color: grey;
font-style: italic;
}

.event-modal-end-time:before {
content: "End: ";
color: grey;
font-style: italic;
}

.event-modal-location:before {
content: "Location: ";
color: grey;
font-style: italic;
}

.event-modal-description:before {
content: "Description: ";
color: grey;
font-style: italic;
}

.day-saturday .day-head,
.day-sunday .day-head {
background: var(--med-grey);
}

.day-today .day-head {
background: var(--light-bg);
color: white;
}

.day-spill .day-event,
.day-spill .day-head {
opacity: 25%;
}

.input-group {
margin-block: 1rem;
}

#url-label {
background: var(--light-green);
}

#loading {
display: none;
}

.htmx-request #loading {
display: flex;
animation: pulse 1s linear 0s infinite;
}

.htmx-request #notification {
display: none;
}

@keyframes pulse {
50% {
opacity: 0%;
}

100% {
opacity: 100%;
}
}

.property-select {
text-align: right;
}

.match-select {
text-align: center;
}

.input-group > .matcher-property {
flex: 0 0 fit-content;
}

.input-group > .matcher-operator {
flex: 0 0 fit-content;
}

.input-group > .matcher-value {
flex: 1 0 auto;
}

.del-matcher > * {
pointer-events: none;
}

.matcher-group:nth-child(1 of .matcher-group) .del-matcher {
display: none;
}

#date-picker {
width: auto;
justify-content: center;
}

#date-picker * {
flex: 0 0 fit-content;
}

#date-pick-year {
flex: 0 0;
min-width: 4em;
}

#date-pick-prev-month::after {
content: '<';
}

#date-pick-next-month:after {
content: '>';
}

#date-pick-prev-year:after {
content: '-';
}

#date-pick-next-year:after {
content: '+';
}

.alert {
margin-block: 1rem;
padding-block: 0.38rem;
}
Loading
Loading