mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-09 07:47:58 -05:00
Merge branch 'docker_immutable_feature' into pre-release
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import os
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from datetime import datetime
|
||||
@@ -7,6 +6,8 @@ import activities.activity_laps.schema as activity_laps_schema
|
||||
|
||||
import activities.activity.schema as activities_schema
|
||||
|
||||
import core.config as core_config
|
||||
|
||||
def serialize_activity_lap(activity: activities_schema.Activity, activity_lap: activity_laps_schema.ActivityLaps):
|
||||
def make_aware_and_format(dt, timezone):
|
||||
if isinstance(dt, str):
|
||||
@@ -18,7 +19,7 @@ def serialize_activity_lap(activity: activities_schema.Activity, activity_lap: a
|
||||
timezone = (
|
||||
ZoneInfo(activity.timezone)
|
||||
if activity.timezone
|
||||
else ZoneInfo(os.environ.get("TZ"))
|
||||
else ZoneInfo(core_config.TZ)
|
||||
)
|
||||
|
||||
activity_lap.start_time = make_aware_and_format(activity_lap.start_time, timezone)
|
||||
|
||||
@@ -7,6 +7,8 @@ import activities.activity_sets.schema as activity_sets_schema
|
||||
|
||||
import activities.activity.schema as activities_schema
|
||||
|
||||
import core.config as core_config
|
||||
|
||||
def serialize_activity_set(activity: activities_schema.Activity, activity_set: activity_sets_schema.ActivitySets):
|
||||
def make_aware_and_format(dt, timezone):
|
||||
if isinstance(dt, str):
|
||||
@@ -18,7 +20,7 @@ def serialize_activity_set(activity: activities_schema.Activity, activity_set: a
|
||||
timezone = (
|
||||
ZoneInfo(activity.timezone)
|
||||
if activity.timezone
|
||||
else ZoneInfo(os.environ.get("TZ"))
|
||||
else ZoneInfo(core_config.TZ)
|
||||
)
|
||||
|
||||
activity_set.start_time = make_aware_and_format(activity_set.start_time, timezone)
|
||||
|
||||
@@ -9,30 +9,17 @@ LICENSE_IDENTIFIER = "AGPL-3.0-or-later"
|
||||
LICENSE_URL = "https://spdx.org/licenses/AGPL-3.0-or-later.html"
|
||||
ROOT_PATH = "/api/v1"
|
||||
FRONTEND_DIR = "/app/frontend/dist"
|
||||
ENVIRONMENT = os.getenv("ENVIRONMENT")
|
||||
ENVIRONMENT = os.getenv("ENVIRONMENT", "production")
|
||||
TZ = os.getenv("TZ", "UTC")
|
||||
|
||||
|
||||
def check_required_env_vars():
|
||||
required_env_vars = [
|
||||
"TZ",
|
||||
"DB_TYPE",
|
||||
"DB_HOST",
|
||||
"DB_PORT",
|
||||
"DB_USER",
|
||||
"DB_PASSWORD",
|
||||
"DB_DATABASE",
|
||||
"SECRET_KEY",
|
||||
"FERNET_KEY",
|
||||
"ALGORITHM",
|
||||
"ACCESS_TOKEN_EXPIRE_MINUTES",
|
||||
"REFRESH_TOKEN_EXPIRE_DAYS",
|
||||
"JAEGER_ENABLED",
|
||||
"JAEGER_PROTOCOL",
|
||||
"JAEGER_HOST",
|
||||
"JAGGER_PORT",
|
||||
"ENDURAIN_HOST",
|
||||
"GEOCODES_MAPS_API",
|
||||
"ENVIRONMENT",
|
||||
]
|
||||
|
||||
for var in required_env_vars:
|
||||
|
||||
@@ -5,34 +5,25 @@ from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.engine.url import URL
|
||||
|
||||
|
||||
# Helper to fetch environment variables
|
||||
def get_env_variable(var_name: str) -> str:
|
||||
value = os.environ.get(var_name)
|
||||
if value is None:
|
||||
raise EnvironmentError(f"Environment variable {var_name} is not set")
|
||||
return value
|
||||
|
||||
|
||||
# Fetch the database type (e.g., mariadb or postgresql)
|
||||
db_type = get_env_variable("DB_TYPE").lower()
|
||||
db_type = os.environ.get("DB_TYPE", "postgres").lower()
|
||||
|
||||
# Define supported database drivers
|
||||
supported_drivers = {
|
||||
"mariadb": "mysql+mysqldb",
|
||||
"postgres": "postgresql+psycopg"
|
||||
}
|
||||
supported_drivers = {"mariadb": "mysql+mysqldb", "postgres": "postgresql+psycopg"}
|
||||
|
||||
if db_type not in supported_drivers:
|
||||
raise ValueError(f"Unsupported DB_TYPE: {db_type}. Supported types are {list(supported_drivers.keys())}")
|
||||
raise ValueError(
|
||||
f"Unsupported DB_TYPE: {db_type}. Supported types are {list(supported_drivers.keys())}"
|
||||
)
|
||||
|
||||
# Define the database connection URL using environment variables
|
||||
db_url = URL.create(
|
||||
drivername=supported_drivers[db_type],
|
||||
username=get_env_variable("DB_USER"),
|
||||
password=get_env_variable("DB_PASSWORD"),
|
||||
host=get_env_variable("DB_HOST"),
|
||||
port=get_env_variable("DB_PORT"),
|
||||
database=get_env_variable("DB_DATABASE"),
|
||||
username=os.environ.get("DB_USER", "endurain"),
|
||||
password=os.environ.get("DB_PASSWORD"),
|
||||
host=os.environ.get("DB_HOST", "postgres"),
|
||||
port=os.environ.get("DB_PORT", "5432"),
|
||||
database=os.environ.get("DB_DATABASE", "endurain"),
|
||||
)
|
||||
|
||||
# Create the SQLAlchemy engine
|
||||
|
||||
@@ -9,7 +9,7 @@ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
||||
|
||||
def setup_tracing(app):
|
||||
# Check if Jaeger tracing is enabled using the 'JAEGER_ENABLED' environment variable
|
||||
if os.environ.get("JAEGER_ENABLED") == "true":
|
||||
if os.environ.get("JAEGER_ENABLED", "true") == "true":
|
||||
# Configure OpenTelemetry with a specified service name
|
||||
trace.set_tracer_provider(
|
||||
TracerProvider(resource=Resource.create({"service.name": "backend_api"}))
|
||||
@@ -17,11 +17,11 @@ def setup_tracing(app):
|
||||
trace.get_tracer_provider().add_span_processor(
|
||||
BatchSpanProcessor(
|
||||
OTLPSpanExporter(
|
||||
endpoint=os.environ.get("JAEGER_PROTOCOL")
|
||||
endpoint=os.environ.get("JAEGER_PROTOCOL", "http")
|
||||
+ "://"
|
||||
+ os.environ.get("JAEGER_HOST")
|
||||
+ os.environ.get("JAEGER_HOST", "jaeger")
|
||||
+ ":"
|
||||
+ os.environ.get("JAGGER_PORT")
|
||||
+ os.environ.get("JAEGER_PORT", "4317")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -26,6 +26,8 @@ import users.user_privacy_settings.schema as users_privacy_settings_schema
|
||||
|
||||
import core.logger as core_logger
|
||||
|
||||
import core.config as core_config
|
||||
|
||||
|
||||
def create_activity_objects(
|
||||
sessions_records: dict,
|
||||
@@ -38,7 +40,7 @@ def create_activity_objects(
|
||||
try:
|
||||
# Create an instance of TimezoneFinder
|
||||
tf = TimezoneFinder()
|
||||
timezone = os.environ.get("TZ")
|
||||
timezone = core_config.TZ
|
||||
|
||||
# Define variables
|
||||
gear_id = None
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import gpxpy
|
||||
import gpxpy.gpx
|
||||
import os
|
||||
from geopy.distance import geodesic
|
||||
from timezonefinder import TimezoneFinder
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -16,6 +14,7 @@ import users.user_default_gear.utils as user_default_gear_utils
|
||||
import users.user_privacy_settings.schema as users_privacy_settings_schema
|
||||
|
||||
import core.logger as core_logger
|
||||
import core.config as core_config
|
||||
|
||||
|
||||
def parse_gpx_file(
|
||||
@@ -27,7 +26,7 @@ def parse_gpx_file(
|
||||
try:
|
||||
# Create an instance of TimezoneFinder
|
||||
tf = TimezoneFinder()
|
||||
timezone = os.environ.get("TZ")
|
||||
timezone = core_config.TZ
|
||||
|
||||
# Initialize default values for various variables
|
||||
activity_type = "Workout"
|
||||
|
||||
@@ -11,6 +11,7 @@ import migrations.crud as migrations_crud
|
||||
import health_data.crud as health_data_crud
|
||||
|
||||
import core.logger as core_logger
|
||||
import core.config as core_config
|
||||
|
||||
|
||||
def process_migration_2(db: Session):
|
||||
@@ -47,7 +48,7 @@ def process_migration_2(db: Session):
|
||||
)
|
||||
continue
|
||||
|
||||
timezone = os.environ.get("TZ")
|
||||
timezone = core_config.TZ
|
||||
|
||||
# Get activity stream
|
||||
try:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import os
|
||||
|
||||
# JWT Token constants
|
||||
JWT_ALGORITHM = os.environ.get("ALGORITHM")
|
||||
JWT_ACCESS_TOKEN_EXPIRE_MINUTES = int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES"))
|
||||
JWT_REFRESH_TOKEN_EXPIRE_DAYS = int(os.environ.get("REFRESH_TOKEN_EXPIRE_DAYS"))
|
||||
JWT_ALGORITHM = os.environ.get("ALGORITHM", "HS256")
|
||||
JWT_ACCESS_TOKEN_EXPIRE_MINUTES = int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES", "15"))
|
||||
JWT_REFRESH_TOKEN_EXPIRE_DAYS = int(os.environ.get("REFRESH_TOKEN_EXPIRE_DAYS", "7"))
|
||||
JWT_SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||
|
||||
# Scopes definition
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import os
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -8,6 +6,7 @@ from stravalib.exc import AccessUnauthorized
|
||||
from timezonefinder import TimezoneFinder
|
||||
|
||||
import core.logger as core_logger
|
||||
import core.config as core_config
|
||||
|
||||
import activities.activity.schema as activities_schema
|
||||
import activities.activity.crud as activities_crud
|
||||
@@ -131,7 +130,7 @@ def parse_activity(
|
||||
) -> dict:
|
||||
# Create an instance of TimezoneFinder
|
||||
tf = TimezoneFinder()
|
||||
timezone = os.environ.get("TZ")
|
||||
timezone = core_config.TZ
|
||||
|
||||
# Get the detailed activity
|
||||
try:
|
||||
|
||||
@@ -62,21 +62,7 @@ RUN set -eux; \
|
||||
# Define environment variables
|
||||
ENV UID=1000 \
|
||||
GID=1000 \
|
||||
TZ="UTC" \
|
||||
DB_TYPE="postgres" \
|
||||
DB_HOST="postgres" \
|
||||
DB_PORT=5432 \
|
||||
DB_USER="endurain" \
|
||||
DB_DATABASE="endurain" \
|
||||
ALGORITHM="HS256" \
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=15 \
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7 \
|
||||
JAEGER_ENABLED="false" \
|
||||
JAEGER_HOST="jaeger" \
|
||||
JAEGER_PROTOCOL="http" \
|
||||
JAGGER_PORT=4317 \
|
||||
BEHIND_PROXY=false \
|
||||
ENVIRONMENT="production"
|
||||
BEHIND_PROXY=false
|
||||
|
||||
# Set the working directory to /app/frontend
|
||||
WORKDIR /app/frontend
|
||||
|
||||
@@ -19,30 +19,14 @@ validate_id() {
|
||||
esac
|
||||
}
|
||||
|
||||
adjust_folder_ownership() {
|
||||
if [ -d "$1" ]; then
|
||||
chown -R "$UID:$GID" "$1"
|
||||
echo_info_log "Ownership adjusted for $1"
|
||||
else
|
||||
echo_info_log "Directory $1 does not exist, skipping."
|
||||
fi
|
||||
}
|
||||
|
||||
validate_id "$UID"
|
||||
validate_id "$GID"
|
||||
|
||||
echo_info_log "UID=$UID, GID=$GID"
|
||||
|
||||
# List of directories (space-separated for POSIX shell)
|
||||
directories="/app/backend/logs /app/backend/user_images /app/backend/files /app/backend/server_images"
|
||||
|
||||
for dir in $directories; do
|
||||
adjust_folder_ownership "$dir"
|
||||
done
|
||||
|
||||
if [ -n "$ENDURAIN_HOST" ]; then
|
||||
echo_info_log "Substituting MY_APP_ENDURAIN_HOST with $ENDURAIN_HOST"
|
||||
find /app/frontend/dist -type f \( -name '*.js' -o -name '*.css' \) -exec sed -i "s|MY_APP_ENDURAIN_HOST|$ENDURAIN_HOST|g" {} +
|
||||
echo "window.env = { ENDURAIN_HOST: \"$ENDURAIN_HOST\" };" > /app/frontend/dist/env.js
|
||||
echo_info_log "Runtime env.js written with ENDURAIN_HOST=$ENDURAIN_HOST"
|
||||
fi
|
||||
|
||||
echo_info_log "Starting FastAPI with BEHIND_PROXY=$BEHIND_PROXY"
|
||||
|
||||
@@ -35,7 +35,7 @@ Environment variable | Default value | Optional | Notes |
|
||||
| JAEGER_ENABLED | false | Yes | N/A |
|
||||
| JAEGER_PROTOCOL | http | Yes | N/A |
|
||||
| JAEGER_HOST | jaeger | Yes | N/A |
|
||||
| JAGGER_PORT | 4317 | Yes | N/A |
|
||||
| JAEGER_PORT | 4317 | Yes | N/A |
|
||||
| BEHIND_PROXY | false | Yes | Change to true if behind reverse proxy |
|
||||
| ENVIRONMENT | production | Yes | "production" and "development" allowed. "development" allows connections from localhost:8080 and localhost:5173 at the CORS level |
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
VITE_ENDURAIN_HOST=MY_APP_ENDURAIN_HOST
|
||||
@@ -15,6 +15,7 @@
|
||||
<link rel="icon" href="/logo/favicon-16x16.png" type="image/png" sizes="16x16">
|
||||
<link rel="apple-touch-icon" href="/logo/apple-touch-icon.png">
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
<script src="/env.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
3
frontend/app/public/env.js
Normal file
3
frontend/app/public/env.js
Normal file
@@ -0,0 +1,3 @@
|
||||
window.env = {
|
||||
ENDURAIN_HOST: "http://localhost:8080"
|
||||
};
|
||||
@@ -31,7 +31,7 @@ export default {
|
||||
emits: ['userDeleted'],
|
||||
setup(props) {
|
||||
const altText = ref('User Avatar');
|
||||
const userPhotoUrl = ref(props.user?.photo_path ? `${import.meta.env.VITE_ENDURAIN_HOST}/${props.user.photo_path}` : null);
|
||||
const userPhotoUrl = ref(props.user?.photo_path ? `${window.env.ENDURAIN_HOST}/${props.user.photo_path}` : null);
|
||||
const alignTopValue = ref(props.alignTop);
|
||||
|
||||
return {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const strava = {
|
||||
return fetchPutRequest('strava/client', data);
|
||||
},
|
||||
linkStrava(state, stravaClientId) {
|
||||
let redirectUri = `${import.meta.env.VITE_ENDURAIN_HOST}`;
|
||||
let redirectUri = `${window.env.ENDURAIN_HOST}`;
|
||||
redirectUri = encodeURIComponent(redirectUri);
|
||||
const scope = 'read,read_all,profile:read_all,activity:read,activity:read_all';
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useAuthStore } from "@/stores/authStore";
|
||||
|
||||
let refreshTokenPromise = null;
|
||||
|
||||
export const API_URL = `${import.meta.env.VITE_ENDURAIN_HOST}/api/v1/`;
|
||||
export const FRONTEND_URL = `${import.meta.env.VITE_ENDURAIN_HOST}/`;
|
||||
export const API_URL = `${window.env.ENDURAIN_HOST}/api/v1/`
|
||||
export const FRONTEND_URL = `${window.env.ENDURAIN_HOST}/`
|
||||
|
||||
async function fetchWithRetry(url, options) {
|
||||
// Add CSRF token to headers for state-changing requests
|
||||
|
||||
@@ -65,7 +65,7 @@ export default {
|
||||
const serverSettingsStore = useServerSettingsStore();
|
||||
const showPassword = ref(false);
|
||||
const loginPhotoUrl = serverSettingsStore.serverSettings.login_photo_set
|
||||
? `${import.meta.env.VITE_ENDURAIN_HOST}/server_images/login.png`
|
||||
? `${window.env.ENDURAIN_HOST}/server_images/login.png`
|
||||
: null;
|
||||
|
||||
// Toggle password visibility
|
||||
|
||||
Reference in New Issue
Block a user