fix forgot password and add runscripts
This commit is contained in:
@@ -167,6 +167,21 @@ docker compose -f traefik.yaml up -d
|
|||||||
npm run docker:prod:up
|
npm run docker:prod:up
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or use the new helper scripts if you want to start one production container at a time:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docker:prod:start:traefik
|
||||||
|
npm run docker:prod:start:postgres
|
||||||
|
npm run docker:prod:start:redis
|
||||||
|
npm run docker:prod:start:api
|
||||||
|
npm run docker:prod:start:marketplace
|
||||||
|
npm run docker:prod:start:dashboard
|
||||||
|
npm run docker:prod:start:admin
|
||||||
|
npm run docker:prod:start:frontends
|
||||||
|
npm run docker:prod:start:pgmanage
|
||||||
|
npm run docker:prod:start:all
|
||||||
|
```
|
||||||
|
|
||||||
Docker will:
|
Docker will:
|
||||||
1. Build the monorepo image
|
1. Build the monorepo image
|
||||||
2. Start all app services (`api`, `marketplace`, `dashboard`, `admin`, `pgmanage`)
|
2. Start all app services (`api`, `marketplace`, `dashboard`, `admin`, `pgmanage`)
|
||||||
|
|||||||
@@ -316,6 +316,22 @@ router.post('/auth/2fa/verify', requireAdminAuth, async (req, res, next) => {
|
|||||||
|
|
||||||
const ADMIN_RESET_TOKEN_TTL_MINUTES = 60
|
const ADMIN_RESET_TOKEN_TTL_MINUTES = 60
|
||||||
|
|
||||||
|
function ensureAppBasePath(baseUrl: string, basePath: string) {
|
||||||
|
try {
|
||||||
|
const url = new URL(baseUrl)
|
||||||
|
const normalizedPathname = url.pathname.replace(/\/$/, '')
|
||||||
|
if (normalizedPathname === basePath || normalizedPathname.endsWith(basePath)) {
|
||||||
|
return url.toString().replace(/\/$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
url.pathname = `${normalizedPathname}${basePath}` || basePath
|
||||||
|
return url.toString().replace(/\/$/, '')
|
||||||
|
} catch {
|
||||||
|
const trimmed = baseUrl.replace(/\/$/, '')
|
||||||
|
return trimmed.endsWith(basePath) ? trimmed : `${trimmed}${basePath}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
router.post('/auth/forgot-password', async (req, res, next) => {
|
router.post('/auth/forgot-password', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { email } = z.object({ email: z.string().email().max(255).trim().toLowerCase() }).parse(req.body)
|
const { email } = z.object({ email: z.string().email().max(255).trim().toLowerCase() }).parse(req.body)
|
||||||
@@ -330,7 +346,10 @@ router.post('/auth/forgot-password', async (req, res, next) => {
|
|||||||
data: { passwordResetToken: rawToken, passwordResetExpiresAt: expiresAt },
|
data: { passwordResetToken: rawToken, passwordResetExpiresAt: expiresAt },
|
||||||
})
|
})
|
||||||
|
|
||||||
const adminUrl = process.env.ADMIN_URL ?? 'http://localhost:3002'
|
const adminUrl = ensureAppBasePath(
|
||||||
|
process.env.ADMIN_URL ?? process.env.NEXT_PUBLIC_ADMIN_URL ?? 'http://localhost:3002/admin',
|
||||||
|
'/admin',
|
||||||
|
)
|
||||||
const resetUrl = `${adminUrl}/reset-password?token=${rawToken}`
|
const resetUrl = `${adminUrl}/reset-password?token=${rawToken}`
|
||||||
|
|
||||||
await sendTransactionalEmail({
|
await sendTransactionalEmail({
|
||||||
|
|||||||
@@ -10,6 +10,22 @@ const router = Router()
|
|||||||
|
|
||||||
const RESET_TOKEN_TTL_MINUTES = 60
|
const RESET_TOKEN_TTL_MINUTES = 60
|
||||||
|
|
||||||
|
function ensureAppBasePath(baseUrl: string, basePath: string) {
|
||||||
|
try {
|
||||||
|
const url = new URL(baseUrl)
|
||||||
|
const normalizedPathname = url.pathname.replace(/\/$/, '')
|
||||||
|
if (normalizedPathname === basePath || normalizedPathname.endsWith(basePath)) {
|
||||||
|
return url.toString().replace(/\/$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
url.pathname = `${normalizedPathname}${basePath}` || basePath
|
||||||
|
return url.toString().replace(/\/$/, '')
|
||||||
|
} catch {
|
||||||
|
const trimmed = baseUrl.replace(/\/$/, '')
|
||||||
|
return trimmed.endsWith(basePath) ? trimmed : `${trimmed}${basePath}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function signEmployeeToken(employeeId: string) {
|
function signEmployeeToken(employeeId: string) {
|
||||||
return jwt.sign(
|
return jwt.sign(
|
||||||
{ sub: employeeId, type: 'employee' },
|
{ sub: employeeId, type: 'employee' },
|
||||||
@@ -150,7 +166,10 @@ router.post('/forgot-password', async (req, res, next) => {
|
|||||||
data: { passwordResetToken: rawToken, passwordResetExpiresAt: expiresAt },
|
data: { passwordResetToken: rawToken, passwordResetExpiresAt: expiresAt },
|
||||||
})
|
})
|
||||||
|
|
||||||
const dashboardUrl = process.env.DASHBOARD_URL ?? 'http://localhost:3001'
|
const dashboardUrl = ensureAppBasePath(
|
||||||
|
process.env.DASHBOARD_URL ?? process.env.NEXT_PUBLIC_DASHBOARD_URL ?? 'http://localhost:3001/dashboard',
|
||||||
|
'/dashboard',
|
||||||
|
)
|
||||||
const resetUrl = `${dashboardUrl}/reset-password?token=${rawToken}`
|
const resetUrl = `${dashboardUrl}/reset-password?token=${rawToken}`
|
||||||
|
|
||||||
await sendTransactionalEmail({
|
await sendTransactionalEmail({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const API_BASE =
|
export const API_BASE =
|
||||||
typeof window === 'undefined'
|
typeof window === 'undefined'
|
||||||
? (process.env.API_INTERNAL_URL || 'http://localhost:4000/api/v1')
|
? (process.env.API_INTERNAL_URL || 'http://localhost:4000/api/v1')
|
||||||
: (process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000/api/v1')
|
: (process.env.NEXT_PUBLIC_API_URL || '/dashboard/api/v1')
|
||||||
|
|
||||||
export const EMPLOYEE_TOKEN_KEY = 'employee_token'
|
export const EMPLOYEE_TOKEN_KEY = 'employee_token'
|
||||||
export const EMPLOYEE_PROFILE_KEY = 'employee_profile'
|
export const EMPLOYEE_PROFILE_KEY = 'employee_profile'
|
||||||
|
|||||||
@@ -11,6 +11,16 @@
|
|||||||
"dev": "turbo dev",
|
"dev": "turbo dev",
|
||||||
"lint": "turbo lint",
|
"lint": "turbo lint",
|
||||||
"type-check": "turbo type-check",
|
"type-check": "turbo type-check",
|
||||||
|
"docker:prod:start:all": "bash scripts/docker-prod-up-all.sh",
|
||||||
|
"docker:prod:start:traefik": "bash scripts/docker-prod-up-traefik.sh",
|
||||||
|
"docker:prod:start:postgres": "bash scripts/docker-prod-up-postgres.sh",
|
||||||
|
"docker:prod:start:redis": "bash scripts/docker-prod-up-redis.sh",
|
||||||
|
"docker:prod:start:api": "bash scripts/docker-prod-up-api.sh",
|
||||||
|
"docker:prod:start:marketplace": "bash scripts/docker-prod-up-marketplace.sh",
|
||||||
|
"docker:prod:start:dashboard": "bash scripts/docker-prod-up-dashboard.sh",
|
||||||
|
"docker:prod:start:admin": "bash scripts/docker-prod-up-admin.sh",
|
||||||
|
"docker:prod:start:frontends": "bash scripts/docker-prod-up-frontends.sh",
|
||||||
|
"docker:prod:start:pgmanage": "bash scripts/docker-prod-up-pgmanage.sh",
|
||||||
"docker:prod:up": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d",
|
"docker:prod:up": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d",
|
||||||
"docker:prod:down": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml down",
|
"docker:prod:down": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml down",
|
||||||
"docker:prod:logs": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml logs -f",
|
"docker:prod:logs": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml logs -f",
|
||||||
|
|||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
PROD_COMPOSE_FILE="${ROOT_DIR}/docker-compose.production.yml"
|
||||||
|
TRAEFIK_COMPOSE_FILE="${ROOT_DIR}/traefik.yaml"
|
||||||
|
ENV_FILE="${ROOT_DIR}/.env.docker.production"
|
||||||
|
PROJECT_NAME="${DOCKER_PROD_PROJECT_NAME:-rentaldrivego-prod}"
|
||||||
|
TRAEFIK_NETWORK="${DOCKER_PROD_TRAEFIK_NETWORK:-traefik-proxy}"
|
||||||
|
|
||||||
|
ensure_env_file() {
|
||||||
|
if [[ ! -f "${ENV_FILE}" ]]; then
|
||||||
|
echo "Missing ${ENV_FILE}" >&2
|
||||||
|
echo "Create it from .env.docker.production.example before starting production containers." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_traefik_network() {
|
||||||
|
if ! docker network inspect "${TRAEFIK_NETWORK}" >/dev/null 2>&1; then
|
||||||
|
echo "Creating Docker network: ${TRAEFIK_NETWORK}"
|
||||||
|
docker network create "${TRAEFIK_NETWORK}" >/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prod_compose() {
|
||||||
|
docker compose -p "${PROJECT_NAME}" --env-file "${ENV_FILE}" -f "${PROD_COMPOSE_FILE}" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
traefik_compose() {
|
||||||
|
docker compose -f "${TRAEFIK_COMPOSE_FILE}" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_prod_services() {
|
||||||
|
ensure_env_file
|
||||||
|
prod_compose up --build -d "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_traefik() {
|
||||||
|
ensure_env_file
|
||||||
|
ensure_traefik_network
|
||||||
|
traefik_compose up -d
|
||||||
|
}
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services admin
|
||||||
|
|
||||||
|
echo "Admin is starting."
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_traefik
|
||||||
|
start_prod_services
|
||||||
|
|
||||||
|
echo "Production stack is starting: traefik, postgres, redis, api, marketplace, dashboard, admin, pgmanage"
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services api
|
||||||
|
|
||||||
|
echo "API is starting."
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services dashboard
|
||||||
|
|
||||||
|
echo "Dashboard is starting."
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services marketplace dashboard admin
|
||||||
|
|
||||||
|
echo "Frontend production services are starting: marketplace, dashboard, admin"
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services marketplace
|
||||||
|
|
||||||
|
echo "Marketplace is starting."
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services pgmanage
|
||||||
|
|
||||||
|
echo "pgManage is starting."
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services postgres
|
||||||
|
|
||||||
|
echo "Postgres is starting."
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_prod_services redis
|
||||||
|
|
||||||
|
echo "Redis is starting."
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||||
|
|
||||||
|
start_traefik
|
||||||
|
|
||||||
|
echo "Traefik is starting."
|
||||||
Reference in New Issue
Block a user