mirror of
https://github.com/mvvasilev/findtheti.me.git
synced 2025-04-19 13:39:52 +03:00
Add SSL support, add not found page
This commit is contained in:
parent
30919f7700
commit
6f0913816e
15 changed files with 361 additions and 49 deletions
14
.env.example
14
.env.example
|
@ -1,3 +1,11 @@
|
||||||
DATABASE_URL= // the url to the database ( example local postgres: postgres://postgres:postgres@localhost/findthetime )
|
DATABASE_URL= # the url to the database ( example local postgres: postgres://postgres:postgres@localhost/findthetime )
|
||||||
EVENT_UID_SIZE= // size of snowflake ids for events ( 20 )
|
EVENT_UID_SIZE= # size of snowflake ids for events ( 20 )
|
||||||
LOG_LEVEL= // see https://docs.rs/tracing-subscriber/0.3.15/tracing_subscriber/struct.EnvFilter.html#. Default is info
|
LOG_LEVEL= # see https://docs.rs/tracing-subscriber/0.3.15/tracing_subscriber/struct.EnvFilter.html#. Default is info
|
||||||
|
|
||||||
|
HTTP_PORT= # 8080 by default
|
||||||
|
|
||||||
|
SSL_ENABLED= # false by default
|
||||||
|
SSL_REDIRECT= # true by default
|
||||||
|
SSL_PORT= # 8443 by default
|
||||||
|
SSL_CERT_PATH= # no default, if SSL_ENABLED is true, this must be configured, or the backend will panic. In a docker environment, this is configured by default ( see Dockerfile )
|
||||||
|
SSL_KEY_PATH= # no default, if SSL_ENABLED is true, this must be configured, or the backend will panic. In a docker environment, this is configured by default ( see Dockerfile )
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
.env
|
.env
|
||||||
|
.self-signed
|
110
Cargo.lock
generated
110
Cargo.lock
generated
|
@ -60,6 +60,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arc-swap"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.77"
|
version = "0.1.77"
|
||||||
|
@ -164,6 +170,29 @@ dependencies = [
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-server"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1ad46c3ec4e12f4a4b6835e173ba21c25e484c9d02b49770bf006ce5367c036"
|
||||||
|
dependencies = [
|
||||||
|
"arc-swap",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
@ -426,9 +455,10 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "findtheti-me"
|
name = "findtheti-me"
|
||||||
version = "0.1.4"
|
version = "0.1.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
|
"axum-server",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
@ -1308,6 +1338,20 @@ version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
"spin 0.9.8",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
@ -1347,6 +1391,44 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.21.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring",
|
||||||
|
"rustls-webpki",
|
||||||
|
"sct",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.101.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
|
@ -1365,6 +1447,16 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sct"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.195"
|
version = "1.0.195"
|
||||||
|
@ -1861,6 +1953,16 @@ dependencies = [
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.24.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
|
@ -2049,6 +2151,12 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "findtheti-me"
|
name = "findtheti-me"
|
||||||
version = "0.1.4"
|
version = "0.1.7"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.7.3", features = ["macros", "tokio"] }
|
axum = { version = "0.7.3", features = ["macros", "tokio"] }
|
||||||
|
axum-server = { version = "0.6.0", features = ["tls-rustls"] }
|
||||||
chrono = { version = "0.4.31", features = ["serde"] }
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
env_logger = "0.10.1"
|
env_logger = "0.10.1"
|
||||||
|
@ -16,6 +17,6 @@ serde = { version = "1.0.195", features = ["derive"] }
|
||||||
serde_json = "1.0.111"
|
serde_json = "1.0.111"
|
||||||
sqlx = { version = "0.7.3", features = ["runtime-tokio", "postgres", "chrono"] }
|
sqlx = { version = "0.7.3", features = ["runtime-tokio", "postgres", "chrono"] }
|
||||||
tokio = {version = "1.35.1", features = ["macros", "rt-multi-thread", "rt", "net"]}
|
tokio = {version = "1.35.1", features = ["macros", "rt-multi-thread", "rt", "net"]}
|
||||||
tower-http = { version = "0.5.0", features = ["fs", "trace"] }
|
tower-http = { version = "0.5.0", features = ["fs", "trace", "cors"] }
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "std"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "std"] }
|
||||||
|
|
12
Dockerfile
12
Dockerfile
|
@ -54,8 +54,14 @@ USER appuser
|
||||||
ENV LOG_LEVEL=info
|
ENV LOG_LEVEL=info
|
||||||
ENV EVENT_UID_SIZE=20
|
ENV EVENT_UID_SIZE=20
|
||||||
|
|
||||||
|
ENV HTTP_PORT=8080
|
||||||
|
|
||||||
|
ENV SSL_ENABLED=false
|
||||||
|
ENV SSL_REDIRECT=true
|
||||||
|
ENV SSL_PORT=8443
|
||||||
|
ENV SSL_CERT_PATH=/etc/findtheti-me/certs/server.cert
|
||||||
|
ENV SSL_KEY_PATH=/etc/findtheti-me/certs/server.key
|
||||||
|
|
||||||
WORKDIR ./findtheti-me
|
WORKDIR ./findtheti-me
|
||||||
|
|
||||||
ENTRYPOINT ["./findtheti-me"]
|
CMD ["./findtheti-me"]
|
||||||
|
|
||||||
EXPOSE 8080/tcp
|
|
46
README.md
46
README.md
|
@ -9,15 +9,27 @@ Also, it is only compatible with PostgreSQL at the moment. It is required to hav
|
||||||
|
|
||||||
### Simple (With Docker)
|
### Simple (With Docker)
|
||||||
|
|
||||||
To use `findtheti.me` with docker, simply run
|
#### Without SSL
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run
|
docker run
|
||||||
-e DATABASE_URL='postgresql://{postgres user}:{postgres password}@{postgres host}/{postgres database}'
|
-e DATABASE_URL='postgresql://{postgres user}:{postgres password}@{postgres host}/{postgres database}'
|
||||||
-p {port to run on}:8080
|
-p {port to run on}:8080
|
||||||
mvv97/findthetime
|
mvv97/findthetime:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Example docker-compose.yml
|
#### With SSL
|
||||||
|
```sh
|
||||||
|
docker run
|
||||||
|
-e DATABASE_URL='postgresql://{postgres user}:{postgres password}@{postgres host}/{postgres database}'
|
||||||
|
-e SSL_ENABLED='true'
|
||||||
|
-v /data/findtheti-me/certs:/etc/findtheti-me/certs # Place your cert files in /data/findtheti-me/certs and ensure they have permissions of at least 644
|
||||||
|
-p {http port to run on}:8080 # if SSL_REDIRECT=false, this can be skipped
|
||||||
|
-p {ssl port to run on}:8443
|
||||||
|
mvv97/findthetime:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example docker-compose.yml
|
||||||
```yml
|
```yml
|
||||||
version: "3.4"
|
version: "3.4"
|
||||||
|
|
||||||
|
@ -38,10 +50,20 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: "postgres://${PG_USER:-findthetime}:${PG_PASS}@postgresql/${PG_DB:-findthetime}"
|
DATABASE_URL: "postgres://${PG_USER:-findthetime}:${PG_PASS}@postgresql/${PG_DB:-findthetime}"
|
||||||
|
SSL_ENABLED: 'true' # when this is set to false ( default ), the ssl port is not listened to.
|
||||||
|
SSL_REDIRECT: 'true'
|
||||||
|
SSL_PORT: '8443'
|
||||||
|
SSL_CERT_PATH: '/etc/findtheti-me/certs/server.cert'
|
||||||
|
SSL_KEY_PATH: '/etc/findtheti-me/certs/server.key'
|
||||||
|
volumes:
|
||||||
|
- '/data/findtheti-me/certs:/etc/findtheti-me/certs'
|
||||||
ports:
|
ports:
|
||||||
- '8080:8080'
|
- '8080:8080'
|
||||||
|
- '8443:8443'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Ensure you have the necessary environment variables configured: `PG_DB`, `PG_USER` and `PG_PASS`.
|
||||||
|
|
||||||
### Advanced (Without Docker)
|
### Advanced (Without Docker)
|
||||||
|
|
||||||
1. Compile Backend (`cargo build --release`)
|
1. Compile Backend (`cargo build --release`)
|
||||||
|
@ -60,6 +82,21 @@ installationDir/
|
||||||
|
|
||||||
Finally, run `./findtheti-me` in the root, and the application should start.
|
Finally, run `./findtheti-me` in the root, and the application should start.
|
||||||
|
|
||||||
|
### Enable SSL
|
||||||
|
|
||||||
|
In order to enable SSL, configure `SSL_ENABLED=true`, `SSL_PORT` with the desired port ( `8443` by default ), and `SSL_CERT_PATH` and `SSL_KEY_PATH`
|
||||||
|
with the paths to your certificate and key files ( `/etc/letsencrypt/live/your.domain/cert.pem` and `/etc/letsencrypt/live/your.domain/key.pem`, for example ).
|
||||||
|
Ensure the permissions of these files are at least `644`, as the container user will need to be able to read them.
|
||||||
|
|
||||||
|
**Note that there is currently no support for encrypted private keys ( those that start with `-----BEGIN ENCRYPTED PRIVATE KEY-----`).
|
||||||
|
Attempting to use such will be met with the error:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Unable to use files configured in 'SSL_CERT_PATH' or 'SSL_KEY_PATH': Custom { kind: Other, error: "private key format not supported" }
|
||||||
|
```
|
||||||
|
`findtheti.me` will automatically register a listener at the configured `HTTP_PORT` to redirect
|
||||||
|
to the configured `SSL_PORT`. To disable this, configure `SSL_REDIRECT=false` ( `true` by default ).
|
||||||
|
|
||||||
## Setup For Development
|
## Setup For Development
|
||||||
### Backend
|
### Backend
|
||||||
1. Create a PostgreSQL database
|
1. Create a PostgreSQL database
|
||||||
|
@ -69,7 +106,8 @@ Finally, run `./findtheti-me` in the root, and the application should start.
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
1. `yarn install`
|
1. `yarn install`
|
||||||
2. `yarn dev` ( or `yarn build`/`yarn preview` )
|
2. If using SSL on the backend, change the proxy in `vite.config.ts` to reflect that.
|
||||||
|
3. `yarn dev` ( or `yarn build`/`yarn preview` )
|
||||||
|
|
||||||
### Docker Build Image
|
### Docker Build Image
|
||||||
1. Do Backend and Frontend setups first
|
1. Do Backend and Frontend setups first
|
||||||
|
|
|
@ -3,6 +3,7 @@ version: "3.4"
|
||||||
services:
|
services:
|
||||||
|
|
||||||
postgresql:
|
postgresql:
|
||||||
|
container_name: db
|
||||||
image: "docker.io/library/postgres:16-alpine"
|
image: "docker.io/library/postgres:16-alpine"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -13,9 +14,17 @@ services:
|
||||||
POSTGRES_DB: ${PG_DB:-findthetime}
|
POSTGRES_DB: ${PG_DB:-findthetime}
|
||||||
|
|
||||||
findthetime:
|
findthetime:
|
||||||
image: "docker.io/mvv97/findthetime:latest"
|
build: .
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: "postgres://${PG_USER:-findthetime}:${PG_PASS}@postgresql/${PG_DB:-findthetime}"
|
DATABASE_URL: "postgres://${PG_USER:-findthetime}:${PG_PASS}@db/${PG_DB:-findthetime}"
|
||||||
|
SSL_ENABLED: 'true' # when this is set to false ( default ), the ssl port is not listened to.
|
||||||
|
SSL_REDIRECT: 'true'
|
||||||
|
SSL_PORT: '8443'
|
||||||
|
SSL_CERT_PATH: '/etc/findtheti-me/certs/server.cert'
|
||||||
|
SSL_KEY_PATH: '/etc/findtheti-me/certs/server.key'
|
||||||
|
volumes:
|
||||||
|
- '/home/haedhutner/Workspace/findtheti-me/.self-signed:/etc/findtheti-me/certs'
|
||||||
ports:
|
ports:
|
||||||
- '8080:8080'
|
- '8080:8080'
|
||||||
|
- '8443:8443'
|
|
@ -4,6 +4,7 @@ import RootLayout from './pages/RootLayout'
|
||||||
import NewEventPage from './pages/NewEventPage'
|
import NewEventPage from './pages/NewEventPage'
|
||||||
import ExistingEventPage from './pages/ExistingEventPage'
|
import ExistingEventPage from './pages/ExistingEventPage'
|
||||||
import ThankYouPage from './pages/ThankYouPage'
|
import ThankYouPage from './pages/ThankYouPage'
|
||||||
|
import NotFoundPage from './pages/NotFoundPage'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -12,6 +13,7 @@ function App() {
|
||||||
<Route path="/" element={<NewEventPage />} />
|
<Route path="/" element={<NewEventPage />} />
|
||||||
<Route path="/:eventId" element={<ExistingEventPage />} />
|
<Route path="/:eventId" element={<ExistingEventPage />} />
|
||||||
<Route path="/thank-you" element={<ThankYouPage />} />
|
<Route path="/thank-you" element={<ThankYouPage />} />
|
||||||
|
<Route path="/not-found" element={<NotFoundPage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</RootLayout>
|
</RootLayout>
|
||||||
)
|
)
|
||||||
|
|
17
frontend/src/pages/NotFoundPage.tsx
Normal file
17
frontend/src/pages/NotFoundPage.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Typography } from '@mui/material';
|
||||||
|
import Grid from '@mui/material/Unstable_Grid2';
|
||||||
|
|
||||||
|
const NotFoundPage = () => {
|
||||||
|
return (
|
||||||
|
<Grid container>
|
||||||
|
<Grid xs={12}>
|
||||||
|
<Typography variant={"h2"}>404 Not Found!</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid xs={12}>
|
||||||
|
<Typography>Not sure what you were looking for, but you won't find it here.</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFoundPage;
|
|
@ -12,6 +12,13 @@ const utils = {
|
||||||
performRequest: (url: string | URL | Request, options?: RequestInit | undefined): Promise<any> => {
|
performRequest: (url: string | URL | Request, options?: RequestInit | undefined): Promise<any> => {
|
||||||
return fetch(url, options).then(async resp => {
|
return fetch(url, options).then(async resp => {
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
|
|
||||||
|
if (resp.status === 404) {
|
||||||
|
window.location.replace(`${window.location.origin}/not-found`)
|
||||||
|
|
||||||
|
throw 'Not Found';
|
||||||
|
}
|
||||||
|
|
||||||
let errorTextResult = await resp.text();
|
let errorTextResult = await resp.text();
|
||||||
|
|
||||||
var errorMsg = errorTextResult;
|
var errorMsg = errorTextResult;
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
"/api": {
|
||||||
target: "http://localhost:8080",
|
target: "https://localhost:8443",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,7 +14,7 @@ use axum::{
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::{migrate::MigrateError, PgPool};
|
use sqlx::{migrate::MigrateError, PgPool};
|
||||||
|
|
||||||
use crate::endpoints;
|
use crate::{endpoints, config};
|
||||||
|
|
||||||
pub(crate) async fn routes() -> Result<Router, ApplicationError> {
|
pub(crate) async fn routes() -> Result<Router, ApplicationError> {
|
||||||
Ok(Router::new()
|
Ok(Router::new()
|
||||||
|
@ -107,9 +107,7 @@ impl AppState {
|
||||||
|
|
||||||
pool
|
pool
|
||||||
},
|
},
|
||||||
event_uid_size: env::var("EVENT_UID_SIZE")?
|
event_uid_size: config::get_or_default("EVENT_UID_SIZE", 20),
|
||||||
.parse()
|
|
||||||
.expect("EVENT_UID_SIZE is undefined. Must be a number."),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
32
src/config.rs
Normal file
32
src/config.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use std::{env, str::FromStr};
|
||||||
|
|
||||||
|
use axum::http::StatusCode;
|
||||||
|
|
||||||
|
use crate::api::ApplicationError;
|
||||||
|
|
||||||
|
pub const DEFAULT_HTTP_PORT: u16 = 8080;
|
||||||
|
pub const DEFAULT_SSL_PORT: u16 = 8443;
|
||||||
|
|
||||||
|
pub const DEFAULT_SSL_ENABLED: bool = false;
|
||||||
|
pub const DEFAULT_SSL_REDIRECT: bool = true;
|
||||||
|
|
||||||
|
pub fn get<T>(name: &str, fail_status_code: StatusCode) -> Result<T, ApplicationError> where T: FromStr {
|
||||||
|
match env::var(name).map(|r| r.parse()) {
|
||||||
|
Ok(Ok(a)) => Ok(a),
|
||||||
|
_ => Err(ApplicationError::new(format!("Unabled to get or parse environment variable '{}'.", name), fail_status_code)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_default<T>(name: &str, default: T) -> T where T: FromStr {
|
||||||
|
match env::var(name).map(|r| r.parse()) {
|
||||||
|
Ok(Ok(a)) => a,
|
||||||
|
_ => default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_panic<T>(name: &str) -> T where T: FromStr {
|
||||||
|
match get(name, StatusCode::INTERNAL_SERVER_ERROR) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => panic!("{}", e),
|
||||||
|
}
|
||||||
|
}
|
|
@ -156,7 +156,11 @@ pub async fn fetch_event(
|
||||||
let res = conn
|
let res = conn
|
||||||
.transaction(|txn| {
|
.transaction(|txn| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let event = db::fetch_event_by_snowflake_id(txn, event_snowflake_id).await?;
|
let event = match db::fetch_event_by_snowflake_id(txn, event_snowflake_id).await {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(sqlx::Error::RowNotFound) => return Err(ApplicationError::new("No such event found".to_string(), StatusCode::NOT_FOUND)),
|
||||||
|
Err(e) => return Err(e.into())
|
||||||
|
};
|
||||||
|
|
||||||
Ok(EventDto {
|
Ok(EventDto {
|
||||||
snowflake_id: event.snowflake_id,
|
snowflake_id: event.snowflake_id,
|
||||||
|
|
127
src/main.rs
127
src/main.rs
|
@ -1,22 +1,28 @@
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use axum::extract::Host;
|
||||||
use axum::Router;
|
use axum::handler::HandlerWithoutStateExt;
|
||||||
|
use axum::http::uri::Scheme;
|
||||||
|
use axum::response::Redirect;
|
||||||
|
use axum::{Router, BoxError};
|
||||||
|
use axum::http::{Uri, StatusCode};
|
||||||
|
use axum_server::tls_rustls::RustlsConfig;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use tokio::net::TcpListener;
|
|
||||||
use tower_http::services::ServeFile;
|
use tower_http::services::ServeFile;
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tower_http::{services::ServeDir, trace};
|
use tower_http::{services::ServeDir, trace};
|
||||||
use tracing::Level;
|
use tracing::{Level, instrument, info, warn};
|
||||||
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod db;
|
mod db;
|
||||||
mod endpoints;
|
mod endpoints;
|
||||||
mod entity;
|
mod entity;
|
||||||
|
mod config;
|
||||||
|
|
||||||
|
#[instrument(name = "findtheti-me")]
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
println!("Starting findtheti.me...");
|
info!("Starting findtheti.me...");
|
||||||
|
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
|
@ -25,37 +31,112 @@ async fn main() {
|
||||||
.with(EnvFilter::from_env("LOG_LEVEL"))
|
.with(EnvFilter::from_env("LOG_LEVEL"))
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
let router = init_router()
|
||||||
|
.await
|
||||||
|
.layer(
|
||||||
|
TraceLayer::new_for_http()
|
||||||
|
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
|
||||||
|
.on_response(trace::DefaultOnResponse::new().level(Level::INFO)),
|
||||||
|
)
|
||||||
|
.into_make_service_with_connect_info::<SocketAddr>();
|
||||||
|
|
||||||
|
let http_port = config::get_or_default("HTTP_PORT", config::DEFAULT_HTTP_PORT);
|
||||||
|
|
||||||
|
let ssl_enabled = config::get_or_default("SSL_ENABLED", config::DEFAULT_SSL_ENABLED);
|
||||||
|
|
||||||
|
if ssl_enabled {
|
||||||
|
info!("SSL marked as enabled, will create http to https redirect.");
|
||||||
|
|
||||||
|
let (ssl_port, ssl_redirect, ssl_cert_path, ssl_key_path): (u16, bool, String, String) = (
|
||||||
|
config::get_or_default("SSL_PORT", config::DEFAULT_SSL_PORT),
|
||||||
|
config::get_or_default("SSL_REDIRECT", config::DEFAULT_SSL_REDIRECT),
|
||||||
|
config::get_or_panic("SSL_CERT_PATH"),
|
||||||
|
config::get_or_panic("SSL_KEY_PATH")
|
||||||
|
);
|
||||||
|
|
||||||
|
let ssl_config = match RustlsConfig::from_pem_file(ssl_cert_path, ssl_key_path).await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
panic!("Unable to use files configured in 'SSL_CERT_PATH' or 'SSL_KEY_PATH': {:?}", e);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if ssl_redirect {
|
||||||
|
tokio::spawn(redirect_http_to_https(http_port, ssl_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
let addr = SocketAddr::from(([0, 0, 0, 0], ssl_port));
|
||||||
|
|
||||||
|
info!("Listening on {}", addr);
|
||||||
|
|
||||||
|
axum_server::bind_rustls(addr, ssl_config)
|
||||||
|
.serve(router)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
let addr = SocketAddr::from(([0, 0, 0, 0], http_port));
|
||||||
|
|
||||||
|
info!("Listening on {}", addr);
|
||||||
|
|
||||||
|
axum_server::bind(addr).serve(router).await.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async fn init_router() -> Router {
|
||||||
let api_routes = api::routes().await.expect("Unable to create api routes");
|
let api_routes = api::routes().await.expect("Unable to create api routes");
|
||||||
|
|
||||||
let mut routes = Router::new().nest("/api", api_routes);
|
let mut routes = Router::new().nest("/api", api_routes);
|
||||||
|
|
||||||
// If in release mod, serve static files
|
// If in release mode, serve static files
|
||||||
if !cfg!(debug_assertions) {
|
if !cfg!(debug_assertions) {
|
||||||
println!("Initializing frontend routes...");
|
info!("Initializing frontend routes...");
|
||||||
|
|
||||||
routes = routes
|
routes = routes
|
||||||
.nest_service("/assets", ServeDir::new("./frontend/dist/assets"))
|
.nest_service("/assets", ServeDir::new("./frontend/dist/assets"))
|
||||||
.fallback_service(ServeFile::new("./frontend/dist/index.html"));
|
.fallback_service(ServeFile::new("./frontend/dist/index.html"));
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Routes initialized...");
|
info!("Routes initialized...");
|
||||||
|
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
|
|
||||||
|
|
||||||
let listener = TcpListener::bind(addr).await.unwrap();
|
|
||||||
|
|
||||||
println!("Starting server...");
|
|
||||||
|
|
||||||
axum::serve(
|
|
||||||
listener,
|
|
||||||
routes
|
routes
|
||||||
.layer(
|
}
|
||||||
TraceLayer::new_for_http()
|
|
||||||
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
|
async fn redirect_http_to_https(http_port: u16, https_port: u16) {
|
||||||
.on_response(trace::DefaultOnResponse::new().level(Level::INFO)),
|
fn make_https(host: String, uri: Uri, http_port: u16, https_port: u16) -> Result<Uri, BoxError> {
|
||||||
)
|
let mut parts = uri.into_parts();
|
||||||
.into_make_service_with_connect_info::<SocketAddr>(),
|
|
||||||
)
|
parts.scheme = Some(Scheme::HTTPS);
|
||||||
|
|
||||||
|
if parts.path_and_query.is_none() {
|
||||||
|
parts.path_and_query = Some("/".parse().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let https_host = host.replace(&http_port.to_string(), &https_port.to_string());
|
||||||
|
parts.authority = Some(https_host.parse()?);
|
||||||
|
|
||||||
|
Ok(Uri::from_parts(parts)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
let redirect = move |Host(host): Host, uri: Uri| async move {
|
||||||
|
match make_https(host, uri, http_port, https_port) {
|
||||||
|
Ok(uri) => Ok(Redirect::permanent(&uri.to_string())),
|
||||||
|
Err(error) => {
|
||||||
|
|
||||||
|
warn!(%error, "failed to convert URI to HTTPS");
|
||||||
|
|
||||||
|
Err(StatusCode::BAD_REQUEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let addr = SocketAddr::from(([0, 0, 0, 0], http_port));
|
||||||
|
|
||||||
|
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||||
|
|
||||||
|
info!("HTTPS redirect listening on {}", listener.local_addr().unwrap());
|
||||||
|
|
||||||
|
axum::serve(listener, redirect.into_make_service())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue