diff --git a/server/server.go b/server/server.go index 8d3526d3..0122c825 100644 --- a/server/server.go +++ b/server/server.go @@ -40,10 +40,14 @@ import ( - v.user --> see publishSyncEventAsync() test payments: - - delete messages + reserved topics on ResetTier delete attachments in access.go - reconciliation + delete messages + reserved topics on ResetTier delete attachments in access.go + account deletion should delete messages and reservations and attachments + Limits & rate limiting: + rate limiting weirdness. wth is going on? + bandwidth limit must be in tier users without tier: should the stats be persisted? are they meaningful? -> test that the visitor is based on the IP address! login/account endpoints when ResetStats() is run, reset messagesLimiter (and others)? @@ -52,7 +56,8 @@ import ( Make sure account endpoints make sense for admins UI: - - revert home page change + - reservation icons + - reservation table delete button: dialog "keep or delete messages?" - flicker of upgrade banner - JS constants Sync: @@ -61,6 +66,7 @@ import ( Tests: - Payment endpoints (make mocks) - Message rate limiting and reset tests + - Bandwidth limit test - test that the visitor is based on the IP address when a user has no tier */ diff --git a/web/public/index.html b/web/public/index.html index 0fe7170f..4dd8ef21 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -4,8 +4,6 @@ ntfy web - - @@ -31,7 +29,8 @@ - + + diff --git a/web/public/static/css/app.css b/web/public/static/css/app.css new file mode 100644 index 00000000..12b105a2 --- /dev/null +++ b/web/public/static/css/app.css @@ -0,0 +1,10 @@ +/* web app styling overrides */ + +a, a:visited { + color: #338574; +} + +a:hover { + text-decoration: none; + color: #317f6f; +} diff --git a/web/public/static/css/home.css b/web/public/static/css/home.css index a71fa467..feeaa7ee 100644 --- a/web/public/static/css/home.css +++ b/web/public/static/css/home.css @@ -1,6 +1,6 @@ /* general styling */ -#site { +html, body { font-family: 'Roboto', sans-serif; font-weight: 400; font-size: 1.1em; @@ -9,16 +9,22 @@ padding: 0; } -#site a, a:visited { +html { + /* prevent scrollbar from repositioning website: + * https://www.w3docs.com/snippets/css/how-to-prevent-scrollbar-from-repositioning-web-page.html */ + overflow-y: scroll; +} + +a, a:visited { color: #338574; } -#site a:hover { +a:hover { text-decoration: none; color: #317f6f; } -#site h1 { +h1 { margin-top: 35px; margin-bottom: 30px; font-size: 2.5em; @@ -28,7 +34,7 @@ color: #666; } -#site h2 { +h2 { margin-top: 30px; margin-bottom: 5px; font-size: 1.8em; @@ -36,7 +42,7 @@ color: #333; } -#site h3 { +h3 { margin-top: 25px; margin-bottom: 5px; font-size: 1.3em; @@ -44,28 +50,28 @@ color: #333; } -#site p { +p { margin-top: 10px; margin-bottom: 20px; line-height: 160%; font-weight: 400; } -#site p.smallMarginBottom { +p.smallMarginBottom { margin-bottom: 10px; } -#site b { +b { font-weight: 500; } -#site tt { +tt { background: #eee; padding: 2px 7px; border-radius: 3px; } -#site code { +code { display: block; background: #eee; font-family: monospace; @@ -79,18 +85,18 @@ /* Main page */ -#site #main { +#main { max-width: 900px; margin: 0 auto 50px auto; padding: 0 10px; } -#site #error { +#error { color: darkred; font-style: italic; } -#site #ironicCenterTagDontFreakOut { +#ironicCenterTagDontFreakOut { color: #666; } @@ -114,22 +120,22 @@ /* Figures */ -#site figure { +figure { text-align: center; } -#site figure img, figure video { +figure img, figure video { filter: drop-shadow(3px 3px 3px #ccc); border-radius: 7px; max-width: 100%; } -#site figure video { +figure video { width: 100%; max-height: 450px; } -#site figcaption { +figcaption { text-align: center; font-style: italic; padding-top: 10px; @@ -137,18 +143,18 @@ /* Screenshots */ -#site #screenshots { +#screenshots { text-align: center; } -#site #screenshots img { +#screenshots img { height: 190px; margin: 3px; border-radius: 5px; filter: drop-shadow(2px 2px 2px #ddd); } -#site #screenshots .nowrap { +#screenshots .nowrap { white-space: nowrap; } @@ -214,60 +220,52 @@ /* Header */ -#site #header { +#header { background: #338574; - background: linear-gradient(150deg, rgba(51,133,116,1) 0%, rgba(86,189,168,1) 100%); filter: drop-shadow(0 5px 10px #ccc); - height: 70px; + height: 130px; } -#site #header #headerBox { +#header #headerBox { max-width: 900px; margin: 0 auto; padding: 0 10px; } -#site #header #logo { - margin-top: 14px; - width: 48px; +#header #logo { + margin-top: 23px; float: left; } -#site #header #name { +#header #name { float: left; color: white; - font-size: 1.7em; - font-weight: 400; - margin: 12px 0 0 10px; + font-size: 2.6em; + font-weight: 300; + margin: 35px 0 0 20px; } -#site #header #menu { +#header ol { list-style-type: none; float: right; - margin-top: 16px; + margin-top: 80px; } -#site #header #menu li { +#header ol li { display: inline-block; - padding: 3px 10px; + margin: 0 10px; font-weight: 400; - border-radius: 5px; } -#site #header #menu li { - font-size: 1em; -} - -#site #header #menu li a, -#site #header #menu li a:visited { +#header ol li a, nav ol li a:visited { color: white; text-decoration: none; } -#site #header #menu li:hover { - background: #3f9a86; +#header ol li a:hover { + text-decoration: underline; } -#site li { +li { padding: 4px 0; margin: 4px 0; font-size: 0.9em; @@ -276,7 +274,7 @@ /* Hide top menu SMALL SCREEN */ @media only screen and (max-width: 780px) { - #header #menu { + #header ol { display: none; } } diff --git a/web/src/components/App.js b/web/src/components/App.js index 970f111c..fc2d7fad 100644 --- a/web/src/components/App.js +++ b/web/src/components/App.js @@ -22,12 +22,9 @@ import PublishDialog from "./PublishDialog"; import Messaging from "./Messaging"; import "./i18n"; // Translations! import {Backdrop, CircularProgress} from "@mui/material"; -import Home from "./Home"; import Login from "./Login"; -import Pricing from "./Pricing"; import Signup from "./Signup"; import Account from "./Account"; -import accountApi from "../app/AccountApi"; export const AccountContext = createContext(null); @@ -41,8 +38,6 @@ const App = () => { - }/> - }/> }/> }/> }> diff --git a/web/src/components/Home.js b/web/src/components/Home.js deleted file mode 100644 index 20a41b88..00000000 --- a/web/src/components/Home.js +++ /dev/null @@ -1,152 +0,0 @@ -import * as React from 'react'; -import SiteLayout from "./SiteLayout"; - -const Home = () => { - return ( - -

Send push notifications to your phone or desktop via PUT/POST

-

- ntfy (pronounce: notify) is a simple HTTP-based pub-sub notification - service. - It allows you to send notifications to your phone or desktop via scripts from any computer, - entirely without signup, cost or setup. It's also open source if you want to run your own. -

-
- - - - - - - -
- -

Publishing messages

-

- Publishing messages can be done via PUT or POST. Topics are created on - the fly by subscribing or publishing to them. - Because there is no sign-up, the topic is essentially a password, so pick something that's - not easily guessable. -

-

- Here's an example showing how to publish a message using a POST request (via curl -d): -

- - curl -d "Backup successful 😀" ntfy.sh/mytopic - -

- There are more features related to publishing messages: You can set a - notification priority, a title, - and tag messages. - Here's an example using some of them together: -

- - curl \
-   -H "Title: Unauthorized access detected" \
-   -H "Priority: urgent" \
-   -H "Tags: warning,skull" \
-   -d "Remote access to $(hostname) detected. Act right away." \
-   ntfy.sh/mytopic -
-

- Here's what that looks like in the Android app: -

-
- -
Urgent notification with pop-over
-
- -

Subscribe to a topic

-

- You can create and subscribe to a topic either using your phone, - in this web UI, or in your own app by subscribing via the API. -

- -

Subscribe from your phone

-

- Simply get the app and start publishing messages. To learn more about - the app, - check out the documentation. -

-

- - - -

-

- Here's a video showing the app in action: -

-
- -
Sending push notifications to your Android phone
-
- -

Subscribe via web app

-

- Subscribe to topics in the web app and receive messages as desktop - notification. - It is available at ntfy.sh/app. -

-
- -
ntfy web app, available at ntfy.sh/app
-
- -

Subscribe using the API

-

- There's a super simple API that you can use to integrate your own app. You can consume - a JSON stream, - an SSE/EventSource stream, - a plain text stream, - or via WebSockets. -

-

- Here's an example for JSON. The connection stays open, so you can retrieve messages as they - come in: -

- - $ curl -s ntfy.sh/mytopic/json
- {`{"id":"SLiKI64DOt","time":1635528757,"event":"open","topic":"mytopic"}`}
- {`{"id":"hwQ2YpKdmg","time":1635528741,"event":"message","topic":"mytopic","message":"Hi!"}`}
- {`{"id":"DGUDShMCsc","time":1635528787,"event":"keepalive","topic":"mytopic"}`}
- ... -
-

- Here's a short video demonstrating it in action: -

-
- -
Subscribing to the JSON stream with curl
-
- -

Check out the docs!

-

- ntfy has so many more features and you can learn about all of them in the - documentation - (I tried my very best to make it the best docs ever 😉, not sure if I succeeded, hehe). -

-
- -
Check out the documentation
-
- -

100% open source & forever free

-

- I love free software, and I'm doing this because it's fun. I have no bad intentions, and I will - never monetize or sell your information. This service will always stay - free and open. - You can read more in the FAQs and in the privacy - policy. -

- -
Made with ❤️ by Philipp C. Heckel
-
- ); -}; - -export default Home; diff --git a/web/src/components/Pricing.js b/web/src/components/Pricing.js deleted file mode 100644 index 24532613..00000000 --- a/web/src/components/Pricing.js +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from 'react'; -import SiteLayout from "./SiteLayout"; - -const Pricing = () => { - return ( - - pricing - - ); -}; - -export default Pricing; diff --git a/web/src/components/SiteLayout.js b/web/src/components/SiteLayout.js deleted file mode 100644 index bc73f4d0..00000000 --- a/web/src/components/SiteLayout.js +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from 'react'; -import {NavLink} from "react-router-dom"; -import routes from "./routes"; -import CloudOutlinedIcon from '@mui/icons-material/CloudOutlined'; -import GitHubIcon from '@mui/icons-material/GitHub'; -import {Link} from "@mui/material"; - -const SiteLayout = (props) => { - return ( -
- -
- {props.children} -
-
- ); -}; - -export default SiteLayout; diff --git a/web/src/components/UpgradeDialog.js b/web/src/components/UpgradeDialog.js index 5e2a068a..f907542d 100644 --- a/web/src/components/UpgradeDialog.js +++ b/web/src/components/UpgradeDialog.js @@ -74,7 +74,7 @@ const UpgradeDialog = (props) => { // Exceptional conditions if (loading) { submitAction = null; - } else if (newTier?.code && account?.reservations.length > newTier?.limits.reservations) { + } else if (newTier?.code && account?.reservations?.length > newTier?.limits?.reservations) { submitAction = null; banner = Banner.RESERVATIONS_WARNING; } diff --git a/web/src/components/routes.js b/web/src/components/routes.js index a7a2e762..5e0a54a2 100644 --- a/web/src/components/routes.js +++ b/web/src/components/routes.js @@ -4,8 +4,6 @@ import {shortUrl} from "../app/utils"; // Remember to also update the "disallowedTopics" list! const routes = { - home: "/", - pricing: "/pricing", login: "/login", signup: "/signup", resetPassword: "/reset-password", // Not used (yet)