Merge pull request #20 from Haelnorr/development
General fixes and minor updates
This commit is contained in:
14
.github/workflows/deploy_production.yaml
vendored
14
.github/workflows/deploy_production.yaml
vendored
@@ -33,11 +33,15 @@ jobs:
|
|||||||
- name: Build the binary
|
- name: Build the binary
|
||||||
run: make build SUFFIX=-production-$GITHUB_SHA
|
run: make build SUFFIX=-production-$GITHUB_SHA
|
||||||
|
|
||||||
|
- name: Build the migration binary
|
||||||
|
run: make migrate SUFFIX=-production-$GITHUB_SHA
|
||||||
|
|
||||||
- name: Deploy to Server
|
- name: Deploy to Server
|
||||||
env:
|
env:
|
||||||
USER: deploy
|
USER: deploy
|
||||||
HOST: projectreshoot.com
|
HOST: projectreshoot.com
|
||||||
DIR: /home/deploy/releases/production
|
DIR: /home/deploy/releases/production
|
||||||
|
MIG_DIR: /home/deploy/migration-bin
|
||||||
DEPLOY_SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
|
DEPLOY_SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }}
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
@@ -49,7 +53,13 @@ jobs:
|
|||||||
echo " UserKnownHostsFile /dev/null" >> ~/.ssh/config
|
echo " UserKnownHostsFile /dev/null" >> ~/.ssh/config
|
||||||
|
|
||||||
ssh -i ~/.ssh/id_ed25519 $USER@$HOST mkdir -p $DIR
|
ssh -i ~/.ssh/id_ed25519 $USER@$HOST mkdir -p $DIR
|
||||||
|
|
||||||
scp -i ~/.ssh/id_ed25519 projectreshoot-production-${GITHUB_SHA} $USER@$HOST:$DIR
|
scp -i ~/.ssh/id_ed25519 projectreshoot-production-${GITHUB_SHA} $USER@$HOST:$DIR
|
||||||
|
|
||||||
ssh -i ~/.ssh/id_ed25519 $USER@$HOST 'bash -s' < ./deploy/deploy_production.sh $GITHUB_SHA
|
ssh -i ~/.ssh/id_ed25519 $USER@$HOST mkdir -p $MIG_DIR
|
||||||
|
scp -i ~/.ssh/id_ed25519 prmigrate-production-${GITHUB_SHA} $USER@$HOST:$MIG_DIR
|
||||||
|
|
||||||
|
scp -i ~/.ssh/id_ed25519 ./deploy/db/backup.sh $USER@$HOST:$MIG_DIR
|
||||||
|
scp -i ~/.ssh/id_ed25519 ./deploy/db/migrate.sh $USER@$HOST:$MIG_DIR
|
||||||
|
scp -i ~/.ssh/id_ed25519 ./deploy/db/migrationcleanup.sh $USER@$HOST:$MIG_DIR
|
||||||
|
|
||||||
|
ssh -i ~/.ssh/id_ed25519 $USER@$HOST 'bash -s' < ./deploy/deploy.sh $GITHUB_SHA production
|
||||||
|
|||||||
@@ -56,11 +56,12 @@ func fetchUserData(
|
|||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan the next row into the provided user pointer. Calls rows.Next() and
|
// Calls rows.Next() and scans the row into the provided user pointer.
|
||||||
// assumes only row in the result. Providing a rows object with more than 1
|
// Will error if no row available
|
||||||
// row may result in undefined behaviour.
|
|
||||||
func scanUserRow(user *User, rows *sql.Rows) error {
|
func scanUserRow(user *User, rows *sql.Rows) error {
|
||||||
for rows.Next() {
|
if !rows.Next() {
|
||||||
|
return errors.New("User not found")
|
||||||
|
}
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&user.ID,
|
&user.ID,
|
||||||
&user.Username,
|
&user.Username,
|
||||||
@@ -71,7 +72,6 @@ func scanUserRow(user *User, rows *sql.Rows) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "rows.Scan")
|
return errors.Wrap(err, "rows.Scan")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,58 @@
|
|||||||
projectreshoot.com {
|
projectreshoot.com {
|
||||||
|
rate_limit {
|
||||||
|
zone auth {
|
||||||
|
match {
|
||||||
|
method POST
|
||||||
|
path /login /register
|
||||||
|
}
|
||||||
|
key {remote_host}
|
||||||
|
events 4
|
||||||
|
window 1m
|
||||||
|
}
|
||||||
|
zone client {
|
||||||
|
key {remote_host}
|
||||||
|
events 100
|
||||||
|
window 1m
|
||||||
|
}
|
||||||
|
}
|
||||||
reverse_proxy localhost:3000 localhost:3001 localhost:3002 {
|
reverse_proxy localhost:3000 localhost:3001 localhost:3002 {
|
||||||
|
transport http {
|
||||||
|
max_conns_per_host 10
|
||||||
|
}
|
||||||
health_uri /healthz
|
health_uri /healthz
|
||||||
fail_duration 30s
|
fail_duration 30s
|
||||||
}
|
}
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/access.log
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
staging.projectreshoot.com {
|
staging.projectreshoot.com {
|
||||||
|
rate_limit {
|
||||||
|
zone auth {
|
||||||
|
match {
|
||||||
|
method POST
|
||||||
|
path /login /register
|
||||||
|
}
|
||||||
|
key {remote_host}
|
||||||
|
events 4
|
||||||
|
window 1m
|
||||||
|
}
|
||||||
|
zone client {
|
||||||
|
key {remote_host}
|
||||||
|
events 100
|
||||||
|
window 1m
|
||||||
|
}
|
||||||
|
}
|
||||||
reverse_proxy localhost:3005 localhost:3006 localhost:3007 {
|
reverse_proxy localhost:3005 localhost:3006 localhost:3007 {
|
||||||
|
transport http {
|
||||||
|
max_conns_per_host 10
|
||||||
|
}
|
||||||
health_uri /healthz
|
health_uri /healthz
|
||||||
fail_duration 30s
|
fail_duration 30s
|
||||||
}
|
}
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/access-staging.log
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Exit on error
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Check if commit hash is passed as an argument
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
echo "Usage: $0 <commit-hash>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMMIT_HASH=$1
|
|
||||||
RELEASES_DIR="/home/deploy/releases/production"
|
|
||||||
DEPLOY_BIN="/home/deploy/production/projectreshoot"
|
|
||||||
SERVICE_NAME="projectreshoot"
|
|
||||||
BINARY_NAME="projectreshoot-production-${COMMIT_HASH}"
|
|
||||||
declare -a PORTS=("3000" "3001" "3002")
|
|
||||||
|
|
||||||
# Check if the binary exists
|
|
||||||
if [ ! -f "${RELEASES_DIR}/${BINARY_NAME}" ]; then
|
|
||||||
echo "Binary ${BINARY_NAME} not found in ${RELEASES_DIR}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Keep a reference to the previous binary from the symlink
|
|
||||||
if [ -L "${DEPLOY_BIN}" ]; then
|
|
||||||
PREVIOUS=$(readlink -f $DEPLOY_BIN)
|
|
||||||
echo "Current binary is ${PREVIOUS}, saved for rollback."
|
|
||||||
else
|
|
||||||
echo "No symbolic link found, no previous binary to backup."
|
|
||||||
PREVIOUS=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
rollback_deployment() {
|
|
||||||
if [ -n "$PREVIOUS" ]; then
|
|
||||||
echo "Rolling back to previous binary: ${PREVIOUS}"
|
|
||||||
ln -sfn "${PREVIOUS}" "${DEPLOY_BIN}"
|
|
||||||
else
|
|
||||||
echo "No previous binary to roll back to."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# wait to restart the services
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# Restart all services with the previous binary
|
|
||||||
for port in "${PORTS[@]}"; do
|
|
||||||
SERVICE="${SERVICE_NAME}@${port}.service"
|
|
||||||
echo "Restarting $SERVICE..."
|
|
||||||
sudo systemctl restart $SERVICE
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Rollback completed."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Copy the binary to the deployment directory
|
|
||||||
echo "Promoting ${BINARY_NAME} to ${DEPLOY_BIN}..."
|
|
||||||
ln -sf "${RELEASES_DIR}/${BINARY_NAME}" "${DEPLOY_BIN}"
|
|
||||||
|
|
||||||
WAIT_TIME=5
|
|
||||||
restart_service() {
|
|
||||||
local port=$1
|
|
||||||
local SERVICE="${SERVICE_NAME}@${port}.service"
|
|
||||||
echo "Restarting ${SERVICE}..."
|
|
||||||
|
|
||||||
# Restart the service
|
|
||||||
if ! sudo systemctl restart "$SERVICE"; then
|
|
||||||
echo "Error: Failed to restart ${SERVICE}. Rolling back deployment."
|
|
||||||
|
|
||||||
# Call the rollback function
|
|
||||||
rollback_deployment
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Wait a few seconds to allow the service to fully start
|
|
||||||
echo "Waiting for ${SERVICE} to fully start..."
|
|
||||||
sleep $WAIT_TIME
|
|
||||||
|
|
||||||
# Check the status of the service
|
|
||||||
if ! systemctl is-active --quiet "${SERVICE}"; then
|
|
||||||
echo "Error: ${SERVICE} failed to start correctly. Rolling back deployment."
|
|
||||||
|
|
||||||
# Call the rollback function
|
|
||||||
rollback_deployment
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "${SERVICE}.service restarted successfully."
|
|
||||||
}
|
|
||||||
|
|
||||||
for port in "${PORTS[@]}"; do
|
|
||||||
restart_service $port
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Deployment completed successfully."
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Exit on error
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Check if commit hash is passed as an argument
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
echo "Usage: $0 <commit-hash>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
COMMIT_HASH=$1
|
|
||||||
RELEASES_DIR="/home/deploy/releases/staging"
|
|
||||||
DEPLOY_BIN="/home/deploy/staging/projectreshoot"
|
|
||||||
SERVICE_NAME="staging.projectreshoot"
|
|
||||||
BINARY_NAME="projectreshoot-staging-${COMMIT_HASH}"
|
|
||||||
declare -a PORTS=("3005" "3006" "3007")
|
|
||||||
|
|
||||||
# Check if the binary exists
|
|
||||||
if [ ! -f "${RELEASES_DIR}/${BINARY_NAME}" ]; then
|
|
||||||
echo "Binary ${BINARY_NAME} not found in ${RELEASES_DIR}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Keep a reference to the previous binary from the symlink
|
|
||||||
if [ -L "${DEPLOY_BIN}" ]; then
|
|
||||||
PREVIOUS=$(readlink -f $DEPLOY_BIN)
|
|
||||||
echo "Current binary is ${PREVIOUS}, saved for rollback."
|
|
||||||
else
|
|
||||||
echo "No symbolic link found, no previous binary to backup."
|
|
||||||
PREVIOUS=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
rollback_deployment() {
|
|
||||||
if [ -n "$PREVIOUS" ]; then
|
|
||||||
echo "Rolling back to previous binary: ${PREVIOUS}"
|
|
||||||
ln -sfn "${PREVIOUS}" "${DEPLOY_BIN}"
|
|
||||||
else
|
|
||||||
echo "No previous binary to roll back to."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# wait to restart the services
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# Restart all services with the previous binary
|
|
||||||
for port in "${PORTS[@]}"; do
|
|
||||||
SERVICE="${SERVICE_NAME}@${port}.service"
|
|
||||||
echo "Restarting $SERVICE..."
|
|
||||||
sudo systemctl restart $SERVICE
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Rollback completed."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Copy the binary to the deployment directory
|
|
||||||
echo "Promoting ${BINARY_NAME} to ${DEPLOY_BIN}..."
|
|
||||||
ln -sf "${RELEASES_DIR}/${BINARY_NAME}" "${DEPLOY_BIN}"
|
|
||||||
|
|
||||||
WAIT_TIME=5
|
|
||||||
restart_service() {
|
|
||||||
local port=$1
|
|
||||||
local SERVICE="${SERVICE_NAME}@${port}.service"
|
|
||||||
echo "Restarting ${SERVICE}..."
|
|
||||||
|
|
||||||
# Restart the service
|
|
||||||
if ! sudo systemctl restart "$SERVICE"; then
|
|
||||||
echo "Error: Failed to restart ${SERVICE}. Rolling back deployment."
|
|
||||||
|
|
||||||
# Call the rollback function
|
|
||||||
rollback_deployment
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Wait a few seconds to allow the service to fully start
|
|
||||||
echo "Waiting for ${SERVICE} to fully start..."
|
|
||||||
sleep $WAIT_TIME
|
|
||||||
|
|
||||||
# Check the status of the service
|
|
||||||
if ! systemctl is-active --quiet "${SERVICE}"; then
|
|
||||||
echo "Error: ${SERVICE} failed to start correctly. Rolling back deployment."
|
|
||||||
|
|
||||||
# Call the rollback function
|
|
||||||
rollback_deployment
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "${SERVICE}.service restarted successfully."
|
|
||||||
}
|
|
||||||
|
|
||||||
for port in "${PORTS[@]}"; do
|
|
||||||
restart_service $port
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Deployment completed successfully."
|
|
||||||
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module projectreshoot
|
module projectreshoot
|
||||||
|
|
||||||
go 1.23.5
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/a-h/templ v0.3.833
|
github.com/a-h/templ v0.3.833
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Renders the account page on the 'General' subpage
|
// Renders the account page on the 'General' subpage
|
||||||
func HandleAccountPage() http.Handler {
|
func AccountPage() http.Handler {
|
||||||
return http.HandlerFunc(
|
return http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
cookie, err := r.Cookie("subpage")
|
cookie, err := r.Cookie("subpage")
|
||||||
@@ -30,7 +30,7 @@ func HandleAccountPage() http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles a request to change the subpage for the Accou/accountnt page
|
// Handles a request to change the subpage for the Accou/accountnt page
|
||||||
func HandleAccountSubpage() http.Handler {
|
func AccountSubpage() http.Handler {
|
||||||
return http.HandlerFunc(
|
return http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
@@ -42,7 +42,7 @@ func HandleAccountSubpage() http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles a request to change the users username
|
// Handles a request to change the users username
|
||||||
func HandleChangeUsername(
|
func ChangeUsername(
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
) http.Handler {
|
) http.Handler {
|
||||||
@@ -88,7 +88,7 @@ func HandleChangeUsername(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles a request to change the users bio
|
// Handles a request to change the users bio
|
||||||
func HandleChangeBio(
|
func ChangeBio(
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
) http.Handler {
|
) http.Handler {
|
||||||
@@ -144,7 +144,7 @@ func validateChangePassword(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handles a request to change the users password
|
// Handles a request to change the users password
|
||||||
func HandleChangePassword(
|
func ChangePassword(
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
) http.Handler {
|
) http.Handler {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
// Handles responses to the / path. Also serves a 404 Page for paths that
|
// Handles responses to the / path. Also serves a 404 Page for paths that
|
||||||
// don't have explicit handlers
|
// don't have explicit handlers
|
||||||
func HandleRoot() http.Handler {
|
func Root() http.Handler {
|
||||||
return http.HandlerFunc(
|
return http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path != "/" {
|
if r.URL.Path != "/" {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -49,7 +49,7 @@ func checkRememberMe(r *http.Request) bool {
|
|||||||
// Handles an attempted login request. On success will return a HTMX redirect
|
// Handles an attempted login request. On success will return a HTMX redirect
|
||||||
// and on fail will return the login form again, passing the error to the
|
// and on fail will return the login form again, passing the error to the
|
||||||
// template for user feedback
|
// template for user feedback
|
||||||
func HandleLoginRequest(
|
func LoginRequest(
|
||||||
config *config.Config,
|
config *config.Config,
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
@@ -97,7 +97,7 @@ func HandleLoginRequest(
|
|||||||
|
|
||||||
// Handles a request to view the login page. Will attempt to set "pagefrom"
|
// Handles a request to view the login page. Will attempt to set "pagefrom"
|
||||||
// cookie so a successful login can redirect the user to the page they came
|
// cookie so a successful login can redirect the user to the page they came
|
||||||
func HandleLoginPage(trustedHost string) http.Handler {
|
func LoginPage(trustedHost string) http.Handler {
|
||||||
return http.HandlerFunc(
|
return http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
cookies.SetPageFrom(w, r, trustedHost)
|
cookies.SetPageFrom(w, r, trustedHost)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -80,7 +80,7 @@ func revokeTokens(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle a logout request
|
// Handle a logout request
|
||||||
func HandleLogout(
|
func Logout(
|
||||||
config *config.Config,
|
config *config.Config,
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"projectreshoot/view/page"
|
"projectreshoot/view/page"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HandleProfilePage() http.Handler {
|
func ProfilePage() http.Handler {
|
||||||
return http.HandlerFunc(
|
return http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
page.Profile().Render(r.Context(), w)
|
page.Profile().Render(r.Context(), w)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -99,7 +99,7 @@ func validatePassword(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle request to reauthenticate (i.e. make token fresh again)
|
// Handle request to reauthenticate (i.e. make token fresh again)
|
||||||
func HandleReauthenticate(
|
func Reauthenticate(
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
config *config.Config,
|
config *config.Config,
|
||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -44,7 +44,7 @@ func validateRegistration(
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleRegisterRequest(
|
func RegisterRequest(
|
||||||
config *config.Config,
|
config *config.Config,
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
@@ -93,7 +93,7 @@ func HandleRegisterRequest(
|
|||||||
|
|
||||||
// Handles a request to view the login page. Will attempt to set "pagefrom"
|
// Handles a request to view the login page. Will attempt to set "pagefrom"
|
||||||
// cookie so a successful login can redirect the user to the page they came
|
// cookie so a successful login can redirect the user to the page they came
|
||||||
func HandleRegisterPage(trustedHost string) http.Handler {
|
func RegisterPage(trustedHost string) http.Handler {
|
||||||
return http.HandlerFunc(
|
return http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
cookies.SetPageFrom(w, r, trustedHost)
|
cookies.SetPageFrom(w, r, trustedHost)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -42,7 +42,7 @@ func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
|||||||
|
|
||||||
// Handles requests for static files, without allowing access to the
|
// Handles requests for static files, without allowing access to the
|
||||||
// directory viewer and returning 404 if an exact file is not found
|
// directory viewer and returning 404 if an exact file is not found
|
||||||
func HandleStatic(staticFS *http.FileSystem) http.Handler {
|
func StaticFS(staticFS *http.FileSystem) http.Handler {
|
||||||
return http.HandlerFunc(
|
return http.HandlerFunc(
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
nfs := justFilesFilesystem{*staticFS}
|
nfs := justFilesFilesystem{*staticFS}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package handlers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"projectreshoot/contexts"
|
"projectreshoot/contexts"
|
||||||
"projectreshoot/cookies"
|
"projectreshoot/cookies"
|
||||||
"projectreshoot/db"
|
"projectreshoot/db"
|
||||||
"projectreshoot/handlers"
|
"projectreshoot/handler"
|
||||||
"projectreshoot/jwt"
|
"projectreshoot/jwt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -119,7 +119,7 @@ func Authentication(
|
|||||||
// Failed to start transaction, skip auth
|
// Failed to start transaction, skip auth
|
||||||
logger.Warn().Err(err).
|
logger.Warn().Err(err).
|
||||||
Msg("Skipping Auth - unable to start a transaction")
|
Msg("Skipping Auth - unable to start a transaction")
|
||||||
handlers.ErrorPage(http.StatusServiceUnavailable, w, r)
|
handler.ErrorPage(http.StatusServiceUnavailable, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user, err := getAuthenticatedUser(config, ctx, tx, w, r)
|
user, err := getAuthenticatedUser(config, ctx, tx, w, r)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"projectreshoot/contexts"
|
"projectreshoot/contexts"
|
||||||
"projectreshoot/handlers"
|
"projectreshoot/handler"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
@@ -31,7 +31,7 @@ func Logging(logger *zerolog.Logger, next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
start, err := contexts.GetStartTime(r.Context())
|
start, err := contexts.GetStartTime(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handlers.ErrorPage(http.StatusInternalServerError, w, r)
|
handler.ErrorPage(http.StatusInternalServerError, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wrapped := &wrappedWriter{
|
wrapped := &wrappedWriter{
|
||||||
@@ -44,7 +44,7 @@ func Logging(logger *zerolog.Logger, next http.Handler) http.Handler {
|
|||||||
Str("method", r.Method).
|
Str("method", r.Method).
|
||||||
Str("resource", r.URL.Path).
|
Str("resource", r.URL.Path).
|
||||||
Dur("time_elapsed", time.Since(start)).
|
Dur("time_elapsed", time.Since(start)).
|
||||||
Str("remote_addr", r.RemoteAddr).
|
Str("remote_addr", r.Header.Get("X-Forwarded-For")).
|
||||||
Msg("Served")
|
Msg("Served")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"projectreshoot/contexts"
|
"projectreshoot/contexts"
|
||||||
"projectreshoot/handlers"
|
"projectreshoot/handler"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Checks if the user is set in the context and shows 401 page if not logged in
|
// Checks if the user is set in the context and shows 401 page if not logged in
|
||||||
func RequiresLogin(next http.Handler) http.Handler {
|
func LoginReq(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
user := contexts.GetUser(r.Context())
|
user := contexts.GetUser(r.Context())
|
||||||
if user == nil {
|
if user == nil {
|
||||||
handlers.ErrorPage(http.StatusUnauthorized, w, r)
|
handler.ErrorPage(http.StatusUnauthorized, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
@@ -20,7 +20,7 @@ func RequiresLogin(next http.Handler) http.Handler {
|
|||||||
|
|
||||||
// Checks if the user is set in the context and redirects them to profile if
|
// Checks if the user is set in the context and redirects them to profile if
|
||||||
// they are logged in
|
// they are logged in
|
||||||
func RequiresLogout(next http.Handler) http.Handler {
|
func LogoutReq(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
user := contexts.GetUser(r.Context())
|
user := contexts.GetUser(r.Context())
|
||||||
if user != nil {
|
if user != nil {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func TestPageLoginRequired(t *testing.T) {
|
|||||||
var maint uint32
|
var maint uint32
|
||||||
atomic.StoreUint32(&maint, 0)
|
atomic.StoreUint32(&maint, 0)
|
||||||
// Add the middleware and create the server
|
// Add the middleware and create the server
|
||||||
loginRequiredHandler := RequiresLogin(testHandler)
|
loginRequiredHandler := LoginReq(testHandler)
|
||||||
authHandler := Authentication(logger, cfg, sconn, loginRequiredHandler, &maint)
|
authHandler := Authentication(logger, cfg, sconn, loginRequiredHandler, &maint)
|
||||||
server := httptest.NewServer(authHandler)
|
server := httptest.NewServer(authHandler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RequiresFresh(
|
func FreshReq(
|
||||||
next http.Handler,
|
next http.Handler,
|
||||||
) http.Handler {
|
) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ func TestReauthRequired(t *testing.T) {
|
|||||||
var maint uint32
|
var maint uint32
|
||||||
atomic.StoreUint32(&maint, 0)
|
atomic.StoreUint32(&maint, 0)
|
||||||
// Add the middleware and create the server
|
// Add the middleware and create the server
|
||||||
reauthRequiredHandler := RequiresFresh(testHandler)
|
reauthRequiredHandler := FreshReq(testHandler)
|
||||||
loginRequiredHandler := RequiresLogin(reauthRequiredHandler)
|
loginRequiredHandler := LoginReq(reauthRequiredHandler)
|
||||||
authHandler := Authentication(logger, cfg, sconn, loginRequiredHandler, &maint)
|
authHandler := Authentication(logger, cfg, sconn, loginRequiredHandler, &maint)
|
||||||
server := httptest.NewServer(authHandler)
|
server := httptest.NewServer(authHandler)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"projectreshoot/config"
|
"projectreshoot/config"
|
||||||
"projectreshoot/db"
|
"projectreshoot/db"
|
||||||
"projectreshoot/handlers"
|
"projectreshoot/handler"
|
||||||
"projectreshoot/middleware"
|
"projectreshoot/middleware"
|
||||||
"projectreshoot/view/page"
|
"projectreshoot/view/page"
|
||||||
|
|
||||||
@@ -20,82 +20,44 @@ func addRoutes(
|
|||||||
conn *db.SafeConn,
|
conn *db.SafeConn,
|
||||||
staticFS *http.FileSystem,
|
staticFS *http.FileSystem,
|
||||||
) {
|
) {
|
||||||
|
route := mux.Handle
|
||||||
|
loggedIn := middleware.LoginReq
|
||||||
|
loggedOut := middleware.LogoutReq
|
||||||
|
fresh := middleware.FreshReq
|
||||||
|
|
||||||
// Health check
|
// Health check
|
||||||
mux.HandleFunc("GET /healthz", func(http.ResponseWriter, *http.Request) {})
|
mux.HandleFunc("GET /healthz", func(http.ResponseWriter, *http.Request) {})
|
||||||
|
|
||||||
// Static files
|
// Static files
|
||||||
mux.Handle("GET /static/", http.StripPrefix("/static/", handlers.HandleStatic(staticFS)))
|
route("GET /static/", http.StripPrefix("/static/", handler.StaticFS(staticFS)))
|
||||||
|
|
||||||
// Index page and unhandled catchall (404)
|
// Index page and unhandled catchall (404)
|
||||||
mux.Handle("GET /", handlers.HandleRoot())
|
route("GET /", handler.Root())
|
||||||
|
|
||||||
// Static content, unprotected pages
|
// Static content, unprotected pages
|
||||||
mux.Handle("GET /about", handlers.HandlePage(page.About()))
|
route("GET /about", handler.HandlePage(page.About()))
|
||||||
|
|
||||||
// Login page and handlers
|
// Login page and handlers
|
||||||
mux.Handle("GET /login",
|
route("GET /login", loggedOut(handler.LoginPage(config.TrustedHost)))
|
||||||
middleware.RequiresLogout(
|
route("POST /login", loggedOut(handler.LoginRequest(config, logger, conn)))
|
||||||
handlers.HandleLoginPage(config.TrustedHost),
|
|
||||||
))
|
|
||||||
mux.Handle("POST /login",
|
|
||||||
middleware.RequiresLogout(
|
|
||||||
handlers.HandleLoginRequest(
|
|
||||||
config,
|
|
||||||
logger,
|
|
||||||
conn,
|
|
||||||
)))
|
|
||||||
|
|
||||||
// Register page and handlers
|
// Register page and handlers
|
||||||
mux.Handle("GET /register",
|
route("GET /register", loggedOut(handler.RegisterPage(config.TrustedHost)))
|
||||||
middleware.RequiresLogout(
|
route("POST /register", loggedOut(handler.RegisterRequest(config, logger, conn)))
|
||||||
handlers.HandleRegisterPage(config.TrustedHost),
|
|
||||||
))
|
|
||||||
mux.Handle("POST /register",
|
|
||||||
middleware.RequiresLogout(
|
|
||||||
handlers.HandleRegisterRequest(
|
|
||||||
config,
|
|
||||||
logger,
|
|
||||||
conn,
|
|
||||||
)))
|
|
||||||
|
|
||||||
// Logout
|
// Logout
|
||||||
mux.Handle("POST /logout", handlers.HandleLogout(config, logger, conn))
|
route("POST /logout", handler.Logout(config, logger, conn))
|
||||||
|
|
||||||
// Reauthentication request
|
// Reauthentication request
|
||||||
mux.Handle("POST /reauthenticate",
|
route("POST /reauthenticate", loggedIn(handler.Reauthenticate(logger, config, conn)))
|
||||||
middleware.RequiresLogin(
|
|
||||||
handlers.HandleReauthenticate(logger, config, conn),
|
|
||||||
))
|
|
||||||
|
|
||||||
// Profile page
|
// Profile page
|
||||||
mux.Handle("GET /profile",
|
route("GET /profile", loggedIn(handler.ProfilePage()))
|
||||||
middleware.RequiresLogin(
|
|
||||||
handlers.HandleProfilePage(),
|
|
||||||
))
|
|
||||||
|
|
||||||
// Account page
|
// Account page
|
||||||
mux.Handle("GET /account",
|
route("GET /account", loggedIn(handler.AccountPage()))
|
||||||
middleware.RequiresLogin(
|
route("POST /account-select-page", loggedIn(handler.AccountSubpage()))
|
||||||
handlers.HandleAccountPage(),
|
route("POST /change-username", loggedIn(fresh(handler.ChangeUsername(logger, conn))))
|
||||||
))
|
route("POST /change-bio", loggedIn(handler.ChangeBio(logger, conn)))
|
||||||
mux.Handle("POST /account-select-page",
|
route("POST /change-password", loggedIn(fresh(handler.ChangePassword(logger, conn))))
|
||||||
middleware.RequiresLogin(
|
|
||||||
handlers.HandleAccountSubpage(),
|
|
||||||
))
|
|
||||||
mux.Handle("POST /change-username",
|
|
||||||
middleware.RequiresLogin(
|
|
||||||
middleware.RequiresFresh(
|
|
||||||
handlers.HandleChangeUsername(logger, conn),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
mux.Handle("POST /change-bio",
|
|
||||||
middleware.RequiresLogin(
|
|
||||||
handlers.HandleChangeBio(logger, conn),
|
|
||||||
))
|
|
||||||
mux.Handle("POST /change-password",
|
|
||||||
middleware.RequiresLogin(
|
|
||||||
middleware.RequiresFresh(
|
|
||||||
handlers.HandleChangePassword(logger, conn),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ templ LoginForm(loginError string) {
|
|||||||
<!-- Form Group -->
|
<!-- Form Group -->
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
for="email"
|
for="username"
|
||||||
class="block text-sm mb-2"
|
class="block text-sm mb-2"
|
||||||
>Username</label>
|
>Username</label>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ templ RegisterForm(registerError string) {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
for="email"
|
for="username"
|
||||||
class="block text-sm mb-2"
|
class="block text-sm mb-2"
|
||||||
>Username</label>
|
>Username</label>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
|
|||||||
Reference in New Issue
Block a user