1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
// SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import {
isLocale,
localeCookieName,
localeCookieOptions,
requestHost,
} from "@/lib/localization";
const accountHosts = new Set(["account.progmasoft.com", "account.localhost"]);
const vigetHosts = new Set(["viget.progmasoft.com", "viget.localhost"]);
export function proxy(request: NextRequest) {
const host = requestHost(
request.headers.get("x-forwarded-host"),
request.headers.get("host"),
);
const pathname = request.nextUrl.pathname;
// Language selection is a user preference, not a second canonical URL.
// Consume the query parameter once, persist it across Progmasoft hosts, and
// redirect to the clean URL so crawlers never index preference variants.
// The header control switches in place through a server action; this
// parameter remains for links and for browsers that run no script.
const requestedLocale = request.nextUrl.searchParams.get("lang") ?? undefined;
if (isLocale(requestedLocale)) {
const destination = request.nextUrl.clone();
destination.searchParams.delete("lang");
const forwardedProtocol = request.headers.get("x-forwarded-proto");
const isProductionHost =
host === "progmasoft.com" || host.endsWith(".progmasoft.com");
destination.protocol = isProductionHost
? "https:"
: request.nextUrl.protocol;
const response = NextResponse.redirect(destination);
response.cookies.set(
localeCookieName,
requestedLocale,
localeCookieOptions(
host,
forwardedProtocol === "https" || request.nextUrl.protocol === "https:",
),
);
return response;
}
// The account root has its own landing page, but authentication and dashboard URLs remain stable and readable.
if (accountHosts.has(host) && pathname === "/") {
const destination = new URL("/account", request.url);
// TLS terminates at Nginx. Next derives the public scheme from X-Forwarded-Proto,
// so an unmodified absolute rewrite would attempt TLS against its HTTP-only
// loopback listener and fail before the account page can render.
destination.protocol = "http:";
return NextResponse.rewrite(destination);
}
// ViGet is deployed by Progmaweb but keeps short public catalog URLs. The
// internal /viget tree prevents those routes from colliding with the apex
// organization page and the account surface.
if (vigetHosts.has(host)) {
if (pathname === "/") {
return NextResponse.rewrite(internalRoute(request, "/viget"));
}
if (pathname === "/dslplugins" || pathname === "/dslplugins/") {
return NextResponse.rewrite(internalRoute(request, "/viget/dslplugins"));
}
if (pathname === "/login" || pathname === "/login/") {
return NextResponse.redirect(
new URL("https://account.progmasoft.com/login"),
);
}
if (pathname === "/register" || pathname === "/register/") {
return NextResponse.redirect(
new URL("https://account.progmasoft.com/register"),
);
}
if (pathname === "/recover" || pathname === "/recover/") {
return NextResponse.redirect(
new URL("https://account.progmasoft.com/recover"),
);
}
}
if (
!vigetHosts.has(host) &&
(pathname === "/viget" || pathname.startsWith("/viget/"))
) {
const publicPath = pathname.replace(/^\/viget/, "") || "/";
return NextResponse.redirect(
new URL(publicPath, "https://viget.progmasoft.com"),
);
}
// Corporate hosts must not accidentally expose the account-only landing page as a public navigation destination.
if (!accountHosts.has(host) && pathname === "/account") {
return NextResponse.redirect(new URL("https://account.progmasoft.com/"));
}
return NextResponse.next();
}
function internalRoute(request: NextRequest, pathname: string): URL {
const destination = new URL(pathname, request.url);
destination.protocol = "http:";
return destination;
}
export const config = {
matcher: [
"/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml).*)",
],
};
|