Merge pull request 'docs: add appendix for permanent path redirect fixes in cPanel deployment guide' (#39) from fix/hide-delete-for-inactive-users into dev
Reviewed-on: #39
This commit is contained in:
commit
fc1914fe7a
@ -1150,3 +1150,169 @@ If you encounter issues:
|
||||
|
||||
**Congratulations!** You've successfully deployed PunimTag to cPanel! 🎉
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Permanent Fix for Path Redirect Issues (Avoid Per-Feature `.htaccess` Rules)
|
||||
|
||||
This appendix documents the long-term fix for deployments where PunimTag runs under
|
||||
path prefixes such as:
|
||||
|
||||
- `/punim-admin/`
|
||||
- `/punim-viewer/`
|
||||
- `/punim-api/`
|
||||
|
||||
### Why this matters
|
||||
|
||||
If the viewer app is only partially path-aware, some actions may still navigate to
|
||||
root URLs like `/`, `/search`, or `/api/auth/session`. On WordPress + cPanel hosting,
|
||||
those root paths are handled by WordPress instead of PunimTag, causing redirects,
|
||||
404/400/405 errors, and broken flows.
|
||||
|
||||
Temporary `.htaccess` redirect rules can patch specific cases, but they are not
|
||||
scalable. New features can introduce new root paths that require additional rules.
|
||||
|
||||
### Goal (Target State)
|
||||
|
||||
Use one consistent strategy so the app works without adding route-by-route redirects:
|
||||
|
||||
1. Viewer app is fully aware it runs at `/punim-viewer`
|
||||
2. Auth routes resolve consistently under the same strategy
|
||||
3. Reverse proxy behavior matches app expectations
|
||||
4. `.htaccess` contains only minimal compatibility rules (or none)
|
||||
|
||||
---
|
||||
|
||||
### Step A - Configure Viewer Base Path (Code-Level, One-Time)
|
||||
|
||||
Edit `viewer-frontend/next.config.ts` and ensure these settings are present:
|
||||
|
||||
```ts
|
||||
const nextConfig: NextConfig = {
|
||||
basePath: '/punim-viewer',
|
||||
assetPrefix: '/punim-viewer',
|
||||
// ...existing config
|
||||
};
|
||||
```
|
||||
|
||||
Why:
|
||||
- `basePath` makes Next.js routes and app navigation prefix-aware
|
||||
- `assetPrefix` ensures static assets load from `/punim-viewer/_next/...`
|
||||
|
||||
---
|
||||
|
||||
### Step B - Align Viewer Environment Variables
|
||||
|
||||
In `viewer-frontend/.env` (production), keep auth/app URLs internally consistent.
|
||||
Use the same canonical public origin and prefix strategy.
|
||||
|
||||
Recommended baseline:
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_APP_URL=https://your-domain.com/punim-viewer
|
||||
NEXTAUTH_URL=https://your-domain.com/punim-viewer
|
||||
AUTH_URL=https://your-domain.com/punim-viewer
|
||||
AUTH_TRUST_HOST=true
|
||||
NEXT_PUBLIC_API_URL=/punim-api
|
||||
BACKEND_BASE_URL=/punim-api
|
||||
```
|
||||
|
||||
Important:
|
||||
- If your proxy forwards auth requests to Next.js as `/api/auth/*` (without prefix),
|
||||
adjust `AUTH_URL`/`NEXTAUTH_URL` to match that behavior.
|
||||
- Do not mix multiple base URL strategies at the same time.
|
||||
|
||||
---
|
||||
|
||||
### Step C - Enforce a Single URL Construction Pattern in Viewer
|
||||
|
||||
In viewer frontend code, avoid hardcoding root-absolute app routes for internal
|
||||
navigation (`'/'`, `'/search'`, `'/upload'`, `'/api/auth/...'`) unless you are sure
|
||||
Next base path handling applies.
|
||||
|
||||
Use shared helpers/utilities for:
|
||||
- app route construction
|
||||
- API route construction
|
||||
- auth endpoints
|
||||
|
||||
This prevents new features from accidentally introducing root paths that bypass
|
||||
`/punim-viewer`.
|
||||
|
||||
---
|
||||
|
||||
### Step D - Proxy Contract with Hosting Provider
|
||||
|
||||
Confirm the reverse proxy contract and keep it stable across deploys:
|
||||
|
||||
- `/punim-admin/*` -> React admin service
|
||||
- `/punim-viewer/*` -> Next viewer service
|
||||
- `/punim-api/*` -> FastAPI service root
|
||||
|
||||
For API mapping to FastAPI root:
|
||||
- Browser endpoints remain `/punim-api/api/v1/...` for versioned routes
|
||||
- Root routes like `/docs`, `/health`, and `/openapi.json` are under `/punim-api/...`
|
||||
|
||||
For viewer mapping:
|
||||
- Ensure proxy behavior (path preserved vs stripped) is explicitly documented
|
||||
- Match viewer env/auth settings to that behavior
|
||||
|
||||
---
|
||||
|
||||
### Step E - Rebuild and Restart Services
|
||||
|
||||
After changing base path/env/proxy assumptions:
|
||||
|
||||
```bash
|
||||
cd ~/punimtag/viewer-frontend
|
||||
rm -rf .next
|
||||
npm run build
|
||||
pm2 restart punimtag-viewer
|
||||
```
|
||||
|
||||
If admin env/settings changed, rebuild/restart admin too.
|
||||
|
||||
---
|
||||
|
||||
### Step F - Validation Checklist (Must Pass)
|
||||
|
||||
Run these checks in browser network tab and direct URL tests:
|
||||
|
||||
1. Viewer entry:
|
||||
- `https://your-domain.com/punim-viewer/` loads correctly
|
||||
2. Viewer navigation:
|
||||
- Actions (filters, clear all, login flows) remain under `/punim-viewer/...`
|
||||
3. Auth session:
|
||||
- `/punim-viewer/api/auth/session` succeeds (no 400/405)
|
||||
4. Viewer API:
|
||||
- Requests resolve under `/punim-viewer/api/...` (or your chosen consistent path)
|
||||
5. Backend docs:
|
||||
- `https://your-domain.com/punim-api/docs` loads
|
||||
- `https://your-domain.com/punim-api/openapi.json` loads
|
||||
6. Backend API:
|
||||
- Versioned endpoints reachable at `/punim-api/api/v1/...`
|
||||
|
||||
---
|
||||
|
||||
### Step G - Decommission Temporary `.htaccess` Workarounds
|
||||
|
||||
Once target state is validated:
|
||||
|
||||
1. Remove route-by-route workaround redirects one by one
|
||||
2. Retest after each removal
|
||||
3. Keep only minimal generic rules required by hosting constraints
|
||||
|
||||
This prevents maintenance debt where each new viewer feature needs a new rewrite rule.
|
||||
|
||||
---
|
||||
|
||||
### Recommended Deployment Policy
|
||||
|
||||
Before every production deployment:
|
||||
|
||||
1. Confirm `next.config.ts` still has `basePath` + `assetPrefix`
|
||||
2. Confirm viewer `.env` values are consistent with proxy contract
|
||||
3. Build viewer from clean `.next`
|
||||
4. Perform the validation checklist above
|
||||
5. Only use `.htaccess` rewrites as temporary emergency mitigation
|
||||
|
||||
Following this policy gives a stable, permanent setup and avoids per-feature redirect
|
||||
maintenance.
|
||||
|
||||
@ -21,6 +21,11 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
import { isValidEmail } from '@/lib/utils';
|
||||
|
||||
interface User {
|
||||
@ -387,17 +392,26 @@ export function ManageUsersContent() {
|
||||
>
|
||||
<Edit2 className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setUserToDelete(user);
|
||||
setDeleteConfirmOpen(true);
|
||||
}}
|
||||
className="text-red-600 hover:text-red-700"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
{user.isActive !== false && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setUserToDelete(user);
|
||||
setDeleteConfirmOpen(true);
|
||||
}}
|
||||
className="text-red-600 hover:text-red-700"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Deactivate User</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user