@websolutespa/bom-mixer-store 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +75 -32
- package/dist/index.mjs +74 -32
- package/package.json +1 -1
- package/src/middleware/middleware.service.ts +6 -7
- package/src/remote/remote.api.ts +1 -1
- package/src/store/store.handler.ts +82 -27
package/CHANGELOG.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -152,6 +152,7 @@ declare class RemotePageService<T extends ICategorized = ICategorized> extends R
|
|
|
152
152
|
protected decorateMany_<B extends T = T>(items: B[], params?: QueryParams): Promise<B[]>;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
declare function getRemoteApiUrl(pathname: string): string;
|
|
155
156
|
declare function remoteApiFetch<T = unknown>(pathname: string, options?: ApiRequestOptions): Promise<T>;
|
|
156
157
|
declare function remoteApiGet<T = unknown>(url: string, options?: ApiRequestOptions): Promise<T>;
|
|
157
158
|
declare function remoteApiPost<T = unknown>(url: string, payload: unknown, options?: ApiRequestOptions): Promise<T>;
|
|
@@ -231,8 +232,8 @@ declare function collectionPatch<T extends IStore>(storePromise: () => Promise<T
|
|
|
231
232
|
declare function collectionDelete<T extends IStore>(storePromise: () => Promise<T>): (request: NextApiRequest, response: NextApiResponse) => Promise<void>;
|
|
232
233
|
declare function routePost(store: IStore, request: NextApiRequest, response: NextApiResponse): Promise<void>;
|
|
233
234
|
declare function storeIndexHandler(): any;
|
|
234
|
-
declare function findRoute(store: IStore, pathname: string, href: string): Promise<IRoute | undefined>;
|
|
235
|
+
declare function findRoute(store: IStore, pathname: string, href: string, hrefBeforeRedirect: string | null): Promise<IRoute | undefined>;
|
|
235
236
|
|
|
236
237
|
declare function getStore<T extends IStore>(): Promise<T>;
|
|
237
238
|
|
|
238
|
-
export { ApiRequestOptions, BuildStrategy, COLLECTIONS, IApiHandler, ICrudMethod, IField, IHandler, IPageResolvedRelation, KNOWN_COLLECTIONS, MAX_INT, MOCK_COLLECTIONS, MapTo, MockMenuService, MockPageService, MockService, NextPhase, RemoteMenuService, RemotePageService, RemoteService, RemoteServiceStrategy, SERVICE_DEFAULT_DEPTH, STATIC_COLLECTIONS, StaticService, StaticServiceConfig, StaticServiceSrc, StoreStrategy, apiHandler, buildStrategy, collectionDelete, collectionGet, collectionPatch, collectionPost, decorateCategories_, decorateCategory_, decorateHref_, decorateHrefs_, decorateLocalizable_, decorateLocalizables_, decorateMenu_, decorateMenus_, decorateNav_, decorateNavs_, decorateRelation_, decorateRelations_, decorateSchema_, decorateSchemas_, decorateStaticRelation_, decorateStaticRelations_, defaultApiRequestOptions, errorHandler, findRoute, fromCache, getDefaultApiRequestOptions, getLocalApiUrl, getMockStore, getMockStoreFromData_, getRemoteStore, getStore, isApiRequest, isNextRequest, isStaticRequest, localApiDelete, localApiFetch, localApiGet, localApiPatch, localApiPost, localApiPut, mockApiDelete, mockApiFetch, mockApiGet, mockApiPatch, mockApiPost, mockApiPut, mockHandler, nextPhase, optimizeMany_, optimizeOne_, remoteApiDelete, remoteApiFetch, remoteApiGet, remoteApiPatch, remoteApiPost, remoteApiPut, routePost, staticCollections, storeApiDelete, storeApiGet, storeApiPatch, storeApiPost, storeApiPut, storeCacheDuration, storeHandler, storeIndexHandler, storeStrategy };
|
|
239
|
+
export { ApiRequestOptions, BuildStrategy, COLLECTIONS, IApiHandler, ICrudMethod, IField, IHandler, IPageResolvedRelation, KNOWN_COLLECTIONS, MAX_INT, MOCK_COLLECTIONS, MapTo, MockMenuService, MockPageService, MockService, NextPhase, RemoteMenuService, RemotePageService, RemoteService, RemoteServiceStrategy, SERVICE_DEFAULT_DEPTH, STATIC_COLLECTIONS, StaticService, StaticServiceConfig, StaticServiceSrc, StoreStrategy, apiHandler, buildStrategy, collectionDelete, collectionGet, collectionPatch, collectionPost, decorateCategories_, decorateCategory_, decorateHref_, decorateHrefs_, decorateLocalizable_, decorateLocalizables_, decorateMenu_, decorateMenus_, decorateNav_, decorateNavs_, decorateRelation_, decorateRelations_, decorateSchema_, decorateSchemas_, decorateStaticRelation_, decorateStaticRelations_, defaultApiRequestOptions, errorHandler, findRoute, fromCache, getDefaultApiRequestOptions, getLocalApiUrl, getMockStore, getMockStoreFromData_, getRemoteApiUrl, getRemoteStore, getStore, isApiRequest, isNextRequest, isStaticRequest, localApiDelete, localApiFetch, localApiGet, localApiPatch, localApiPost, localApiPut, mockApiDelete, mockApiFetch, mockApiGet, mockApiPatch, mockApiPost, mockApiPut, mockHandler, nextPhase, optimizeMany_, optimizeOne_, remoteApiDelete, remoteApiFetch, remoteApiGet, remoteApiPatch, remoteApiPost, remoteApiPut, routePost, staticCollections, storeApiDelete, storeApiGet, storeApiPatch, storeApiPost, storeApiPut, storeCacheDuration, storeHandler, storeIndexHandler, storeStrategy };
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,7 @@ __export(src_exports, {
|
|
|
77
77
|
getLocalApiUrl: () => getLocalApiUrl,
|
|
78
78
|
getMockStore: () => getMockStore,
|
|
79
79
|
getMockStoreFromData_: () => getMockStoreFromData_,
|
|
80
|
+
getRemoteApiUrl: () => getRemoteApiUrl,
|
|
80
81
|
getRemoteStore: () => getRemoteStore,
|
|
81
82
|
getStore: () => getStore,
|
|
82
83
|
isApiRequest: () => isApiRequest,
|
|
@@ -649,18 +650,18 @@ async function localApiDelete(url, options = {}, serviceOptions) {
|
|
|
649
650
|
var mime = __toESM(require("mime"));
|
|
650
651
|
var API_BASE2 = process.env.NEXT_PUBLIC_API || "/api";
|
|
651
652
|
function isStaticRequest(request) {
|
|
652
|
-
const
|
|
653
|
-
const isStatic = mime.getType(
|
|
653
|
+
const { pathname } = request.nextUrl;
|
|
654
|
+
const isStatic = mime.getType(pathname) !== null;
|
|
654
655
|
return isStatic;
|
|
655
656
|
}
|
|
656
657
|
function isNextRequest(request) {
|
|
657
|
-
const
|
|
658
|
-
const isNext =
|
|
658
|
+
const { pathname } = request.nextUrl;
|
|
659
|
+
const isNext = pathname.startsWith("/_next");
|
|
659
660
|
return isNext;
|
|
660
661
|
}
|
|
661
662
|
function isApiRequest(request) {
|
|
662
|
-
const
|
|
663
|
-
const isApi =
|
|
663
|
+
const { pathname } = request.nextUrl;
|
|
664
|
+
const isApi = pathname.startsWith(API_BASE2);
|
|
664
665
|
return isApi;
|
|
665
666
|
}
|
|
666
667
|
|
|
@@ -1730,30 +1731,31 @@ function collectionDelete(storePromise) {
|
|
|
1730
1731
|
};
|
|
1731
1732
|
}
|
|
1732
1733
|
async function routePost(store, request, response) {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1734
|
+
try {
|
|
1735
|
+
const { pathname, href, hrefBeforeRedirect } = request.body;
|
|
1736
|
+
console.log("routePost", pathname, href, hrefBeforeRedirect);
|
|
1737
|
+
if (pathname && href) {
|
|
1738
|
+
const route = await findRoute(store, pathname, href, hrefBeforeRedirect);
|
|
1737
1739
|
return response.status(200).json(route);
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1740
|
+
} else {
|
|
1741
|
+
return response.status(400).send("bad request");
|
|
1742
|
+
}
|
|
1743
|
+
} catch (error) {
|
|
1744
|
+
if (error.status < 500) {
|
|
1745
|
+
if (error.status < 400) {
|
|
1746
|
+
return response.status(error.status).send({
|
|
1747
|
+
redirectUrl: error.redirectUrl
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
if (error.status === 404) {
|
|
1751
|
+
return response.status(404).send("not found");
|
|
1750
1752
|
} else {
|
|
1751
|
-
|
|
1752
|
-
throw error;
|
|
1753
|
+
return response.status(error.status).send("gone");
|
|
1753
1754
|
}
|
|
1755
|
+
} else {
|
|
1756
|
+
console.error("storeHandler.error.500", error);
|
|
1757
|
+
throw error;
|
|
1754
1758
|
}
|
|
1755
|
-
} else {
|
|
1756
|
-
return response.status(400).send("bad request");
|
|
1757
1759
|
}
|
|
1758
1760
|
}
|
|
1759
1761
|
function storeIndexHandler() {
|
|
@@ -1887,7 +1889,38 @@ function storeIndexHandler() {
|
|
|
1887
1889
|
}
|
|
1888
1890
|
});
|
|
1889
1891
|
}
|
|
1890
|
-
|
|
1892
|
+
var trimTerminalSlash = (path) => {
|
|
1893
|
+
return path.replace(/\/+$/, "");
|
|
1894
|
+
};
|
|
1895
|
+
var sanitizeHref = (url, urlBeforeRedirect) => {
|
|
1896
|
+
if (urlBeforeRedirect) {
|
|
1897
|
+
const sanitizedUrl = new URL(urlBeforeRedirect);
|
|
1898
|
+
sanitizedUrl.protocol = url.protocol;
|
|
1899
|
+
return trimTerminalSlash(sanitizedUrl.href);
|
|
1900
|
+
} else {
|
|
1901
|
+
return trimTerminalSlash(url.href);
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
var sanitizeRedirectFrom = (path, url, urlBeforeRedirect) => {
|
|
1905
|
+
path = trimTerminalSlash(path);
|
|
1906
|
+
if (urlBeforeRedirect) {
|
|
1907
|
+
return path.replace(/^(\^?)((https?:\/\/)+)/, `$1${url.protocol}//`);
|
|
1908
|
+
} else {
|
|
1909
|
+
return path.replace(/^(\^?)((https?:\/\/)+(\w|\.|:)+)/, `$1${url.origin}`);
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
var sanitizeRedirectTo = (path) => {
|
|
1913
|
+
return path ? trimTerminalSlash(path) : void 0;
|
|
1914
|
+
};
|
|
1915
|
+
var sanitizeRedirectUrl = (path, url) => {
|
|
1916
|
+
if (path != null) {
|
|
1917
|
+
path = trimTerminalSlash(path);
|
|
1918
|
+
return path.match(/^((https?:\/\/)+)/) ? path : `${url.origin}${path}`;
|
|
1919
|
+
} else {
|
|
1920
|
+
return path;
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
async function findRoute(store, pathname, href, hrefBeforeRedirect) {
|
|
1891
1924
|
const routes = await store.route.findMany({
|
|
1892
1925
|
where: {
|
|
1893
1926
|
id: {
|
|
@@ -1916,23 +1949,31 @@ async function findRoute(store, pathname, href) {
|
|
|
1916
1949
|
if (splatRoute) {
|
|
1917
1950
|
return splatRoute;
|
|
1918
1951
|
}
|
|
1919
|
-
const
|
|
1952
|
+
const url = new URL(href);
|
|
1953
|
+
const urlBeforeRedirect = hrefBeforeRedirect ? new URL(hrefBeforeRedirect) : null;
|
|
1954
|
+
const sanitizedHref = sanitizeHref(url, urlBeforeRedirect);
|
|
1955
|
+
let redirects = await store.redirect.findMany({
|
|
1920
1956
|
where: {
|
|
1921
1957
|
isActive: {
|
|
1922
1958
|
equals: true
|
|
1923
1959
|
}
|
|
1924
1960
|
}
|
|
1925
1961
|
});
|
|
1962
|
+
redirects = redirects.map((x) => ({
|
|
1963
|
+
...x,
|
|
1964
|
+
from: sanitizeRedirectFrom(x.from, url, urlBeforeRedirect),
|
|
1965
|
+
to: sanitizeRedirectTo(x.to)
|
|
1966
|
+
}));
|
|
1926
1967
|
const exactMatchResolver = (redirect) => {
|
|
1927
1968
|
const status = parseInt(redirect.status) || 307;
|
|
1928
1969
|
if (status < 400) {
|
|
1929
|
-
const redirectUrl = redirect.to;
|
|
1970
|
+
const redirectUrl = sanitizeRedirectUrl(redirect.to, url);
|
|
1930
1971
|
throw { status, redirectUrl };
|
|
1931
1972
|
} else {
|
|
1932
1973
|
throw { status };
|
|
1933
1974
|
}
|
|
1934
1975
|
};
|
|
1935
|
-
const exactMatchRedirect = redirects.find((x) => x.from ===
|
|
1976
|
+
const exactMatchRedirect = redirects.find((x) => x.from === sanitizedHref);
|
|
1936
1977
|
if (exactMatchRedirect !== void 0) {
|
|
1937
1978
|
return exactMatchResolver(exactMatchRedirect);
|
|
1938
1979
|
}
|
|
@@ -1944,7 +1985,8 @@ async function findRoute(store, pathname, href) {
|
|
|
1944
1985
|
let redirectUrl = redirect.to.replace(/\*/g, (...rest) => {
|
|
1945
1986
|
return `$${++i}`;
|
|
1946
1987
|
});
|
|
1947
|
-
redirectUrl =
|
|
1988
|
+
redirectUrl = sanitizedHref.replace(regexp, redirectUrl);
|
|
1989
|
+
redirectUrl = sanitizeRedirectUrl(redirectUrl, url);
|
|
1948
1990
|
throw { status, redirectUrl };
|
|
1949
1991
|
} else {
|
|
1950
1992
|
throw { status };
|
|
@@ -1956,7 +1998,7 @@ async function findRoute(store, pathname, href) {
|
|
|
1956
1998
|
expression = `^${expression.replace(/(^\^)|(\$$)/g, "")}$`;
|
|
1957
1999
|
x.from = expression;
|
|
1958
2000
|
});
|
|
1959
|
-
const firstMatch = redirects.find((x) =>
|
|
2001
|
+
const firstMatch = redirects.find((x) => sanitizedHref.match(x.from));
|
|
1960
2002
|
if (firstMatch) {
|
|
1961
2003
|
return patternMatchResolver(firstMatch);
|
|
1962
2004
|
}
|
|
@@ -2035,6 +2077,7 @@ async function storeApiDelete(url, options = {}) {
|
|
|
2035
2077
|
getLocalApiUrl,
|
|
2036
2078
|
getMockStore,
|
|
2037
2079
|
getMockStoreFromData_,
|
|
2080
|
+
getRemoteApiUrl,
|
|
2038
2081
|
getRemoteStore,
|
|
2039
2082
|
getStore,
|
|
2040
2083
|
isApiRequest,
|
package/dist/index.mjs
CHANGED
|
@@ -541,18 +541,18 @@ async function localApiDelete(url, options = {}, serviceOptions) {
|
|
|
541
541
|
import * as mime from "mime";
|
|
542
542
|
var API_BASE2 = process.env.NEXT_PUBLIC_API || "/api";
|
|
543
543
|
function isStaticRequest(request) {
|
|
544
|
-
const
|
|
545
|
-
const isStatic = mime.getType(
|
|
544
|
+
const { pathname } = request.nextUrl;
|
|
545
|
+
const isStatic = mime.getType(pathname) !== null;
|
|
546
546
|
return isStatic;
|
|
547
547
|
}
|
|
548
548
|
function isNextRequest(request) {
|
|
549
|
-
const
|
|
550
|
-
const isNext =
|
|
549
|
+
const { pathname } = request.nextUrl;
|
|
550
|
+
const isNext = pathname.startsWith("/_next");
|
|
551
551
|
return isNext;
|
|
552
552
|
}
|
|
553
553
|
function isApiRequest(request) {
|
|
554
|
-
const
|
|
555
|
-
const isApi =
|
|
554
|
+
const { pathname } = request.nextUrl;
|
|
555
|
+
const isApi = pathname.startsWith(API_BASE2);
|
|
556
556
|
return isApi;
|
|
557
557
|
}
|
|
558
558
|
|
|
@@ -1622,30 +1622,31 @@ function collectionDelete(storePromise) {
|
|
|
1622
1622
|
};
|
|
1623
1623
|
}
|
|
1624
1624
|
async function routePost(store, request, response) {
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1625
|
+
try {
|
|
1626
|
+
const { pathname, href, hrefBeforeRedirect } = request.body;
|
|
1627
|
+
console.log("routePost", pathname, href, hrefBeforeRedirect);
|
|
1628
|
+
if (pathname && href) {
|
|
1629
|
+
const route = await findRoute(store, pathname, href, hrefBeforeRedirect);
|
|
1629
1630
|
return response.status(200).json(route);
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1631
|
+
} else {
|
|
1632
|
+
return response.status(400).send("bad request");
|
|
1633
|
+
}
|
|
1634
|
+
} catch (error) {
|
|
1635
|
+
if (error.status < 500) {
|
|
1636
|
+
if (error.status < 400) {
|
|
1637
|
+
return response.status(error.status).send({
|
|
1638
|
+
redirectUrl: error.redirectUrl
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
if (error.status === 404) {
|
|
1642
|
+
return response.status(404).send("not found");
|
|
1642
1643
|
} else {
|
|
1643
|
-
|
|
1644
|
-
throw error;
|
|
1644
|
+
return response.status(error.status).send("gone");
|
|
1645
1645
|
}
|
|
1646
|
+
} else {
|
|
1647
|
+
console.error("storeHandler.error.500", error);
|
|
1648
|
+
throw error;
|
|
1646
1649
|
}
|
|
1647
|
-
} else {
|
|
1648
|
-
return response.status(400).send("bad request");
|
|
1649
1650
|
}
|
|
1650
1651
|
}
|
|
1651
1652
|
function storeIndexHandler() {
|
|
@@ -1779,7 +1780,38 @@ function storeIndexHandler() {
|
|
|
1779
1780
|
}
|
|
1780
1781
|
});
|
|
1781
1782
|
}
|
|
1782
|
-
|
|
1783
|
+
var trimTerminalSlash = (path) => {
|
|
1784
|
+
return path.replace(/\/+$/, "");
|
|
1785
|
+
};
|
|
1786
|
+
var sanitizeHref = (url, urlBeforeRedirect) => {
|
|
1787
|
+
if (urlBeforeRedirect) {
|
|
1788
|
+
const sanitizedUrl = new URL(urlBeforeRedirect);
|
|
1789
|
+
sanitizedUrl.protocol = url.protocol;
|
|
1790
|
+
return trimTerminalSlash(sanitizedUrl.href);
|
|
1791
|
+
} else {
|
|
1792
|
+
return trimTerminalSlash(url.href);
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
var sanitizeRedirectFrom = (path, url, urlBeforeRedirect) => {
|
|
1796
|
+
path = trimTerminalSlash(path);
|
|
1797
|
+
if (urlBeforeRedirect) {
|
|
1798
|
+
return path.replace(/^(\^?)((https?:\/\/)+)/, `$1${url.protocol}//`);
|
|
1799
|
+
} else {
|
|
1800
|
+
return path.replace(/^(\^?)((https?:\/\/)+(\w|\.|:)+)/, `$1${url.origin}`);
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
var sanitizeRedirectTo = (path) => {
|
|
1804
|
+
return path ? trimTerminalSlash(path) : void 0;
|
|
1805
|
+
};
|
|
1806
|
+
var sanitizeRedirectUrl = (path, url) => {
|
|
1807
|
+
if (path != null) {
|
|
1808
|
+
path = trimTerminalSlash(path);
|
|
1809
|
+
return path.match(/^((https?:\/\/)+)/) ? path : `${url.origin}${path}`;
|
|
1810
|
+
} else {
|
|
1811
|
+
return path;
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
async function findRoute(store, pathname, href, hrefBeforeRedirect) {
|
|
1783
1815
|
const routes = await store.route.findMany({
|
|
1784
1816
|
where: {
|
|
1785
1817
|
id: {
|
|
@@ -1808,23 +1840,31 @@ async function findRoute(store, pathname, href) {
|
|
|
1808
1840
|
if (splatRoute) {
|
|
1809
1841
|
return splatRoute;
|
|
1810
1842
|
}
|
|
1811
|
-
const
|
|
1843
|
+
const url = new URL(href);
|
|
1844
|
+
const urlBeforeRedirect = hrefBeforeRedirect ? new URL(hrefBeforeRedirect) : null;
|
|
1845
|
+
const sanitizedHref = sanitizeHref(url, urlBeforeRedirect);
|
|
1846
|
+
let redirects = await store.redirect.findMany({
|
|
1812
1847
|
where: {
|
|
1813
1848
|
isActive: {
|
|
1814
1849
|
equals: true
|
|
1815
1850
|
}
|
|
1816
1851
|
}
|
|
1817
1852
|
});
|
|
1853
|
+
redirects = redirects.map((x) => ({
|
|
1854
|
+
...x,
|
|
1855
|
+
from: sanitizeRedirectFrom(x.from, url, urlBeforeRedirect),
|
|
1856
|
+
to: sanitizeRedirectTo(x.to)
|
|
1857
|
+
}));
|
|
1818
1858
|
const exactMatchResolver = (redirect) => {
|
|
1819
1859
|
const status = parseInt(redirect.status) || 307;
|
|
1820
1860
|
if (status < 400) {
|
|
1821
|
-
const redirectUrl = redirect.to;
|
|
1861
|
+
const redirectUrl = sanitizeRedirectUrl(redirect.to, url);
|
|
1822
1862
|
throw { status, redirectUrl };
|
|
1823
1863
|
} else {
|
|
1824
1864
|
throw { status };
|
|
1825
1865
|
}
|
|
1826
1866
|
};
|
|
1827
|
-
const exactMatchRedirect = redirects.find((x) => x.from ===
|
|
1867
|
+
const exactMatchRedirect = redirects.find((x) => x.from === sanitizedHref);
|
|
1828
1868
|
if (exactMatchRedirect !== void 0) {
|
|
1829
1869
|
return exactMatchResolver(exactMatchRedirect);
|
|
1830
1870
|
}
|
|
@@ -1836,7 +1876,8 @@ async function findRoute(store, pathname, href) {
|
|
|
1836
1876
|
let redirectUrl = redirect.to.replace(/\*/g, (...rest) => {
|
|
1837
1877
|
return `$${++i}`;
|
|
1838
1878
|
});
|
|
1839
|
-
redirectUrl =
|
|
1879
|
+
redirectUrl = sanitizedHref.replace(regexp, redirectUrl);
|
|
1880
|
+
redirectUrl = sanitizeRedirectUrl(redirectUrl, url);
|
|
1840
1881
|
throw { status, redirectUrl };
|
|
1841
1882
|
} else {
|
|
1842
1883
|
throw { status };
|
|
@@ -1848,7 +1889,7 @@ async function findRoute(store, pathname, href) {
|
|
|
1848
1889
|
expression = `^${expression.replace(/(^\^)|(\$$)/g, "")}$`;
|
|
1849
1890
|
x.from = expression;
|
|
1850
1891
|
});
|
|
1851
|
-
const firstMatch = redirects.find((x) =>
|
|
1892
|
+
const firstMatch = redirects.find((x) => sanitizedHref.match(x.from));
|
|
1852
1893
|
if (firstMatch) {
|
|
1853
1894
|
return patternMatchResolver(firstMatch);
|
|
1854
1895
|
}
|
|
@@ -1926,6 +1967,7 @@ export {
|
|
|
1926
1967
|
getLocalApiUrl,
|
|
1927
1968
|
getMockStore,
|
|
1928
1969
|
getMockStoreFromData_,
|
|
1970
|
+
getRemoteApiUrl,
|
|
1929
1971
|
getRemoteStore,
|
|
1930
1972
|
getStore,
|
|
1931
1973
|
isApiRequest,
|
package/package.json
CHANGED
|
@@ -4,20 +4,19 @@ import { NextRequest } from 'next/server';
|
|
|
4
4
|
const API_BASE = process.env.NEXT_PUBLIC_API || '/api';
|
|
5
5
|
|
|
6
6
|
export function isStaticRequest(request: NextRequest) {
|
|
7
|
-
const
|
|
8
|
-
const isStatic = mime.getType(
|
|
9
|
-
// const isStatic = url.pathname.indexOf('.') !== -1;
|
|
7
|
+
const { pathname } = request.nextUrl;
|
|
8
|
+
const isStatic = mime.getType(pathname) !== null;
|
|
10
9
|
return isStatic;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
export function isNextRequest(request: NextRequest) {
|
|
14
|
-
const
|
|
15
|
-
const isNext =
|
|
13
|
+
const { pathname } = request.nextUrl;
|
|
14
|
+
const isNext = pathname.startsWith('/_next');
|
|
16
15
|
return isNext;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
export function isApiRequest(request: NextRequest) {
|
|
20
|
-
const
|
|
21
|
-
const isApi =
|
|
19
|
+
const { pathname } = request.nextUrl;
|
|
20
|
+
const isApi = pathname.startsWith(API_BASE);
|
|
22
21
|
return isApi;
|
|
23
22
|
}
|
package/src/remote/remote.api.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { ApiRequestOptions, getDefaultApiRequestOptions } from '../api/api';
|
|
|
8
8
|
const API_URL = process.env.STORE_URL;
|
|
9
9
|
const API_BASE = process.env.STORE_API || '/api';
|
|
10
10
|
|
|
11
|
-
function getRemoteApiUrl(pathname: string): string {
|
|
11
|
+
export function getRemoteApiUrl(pathname: string): string {
|
|
12
12
|
let url = `${API_URL}${API_BASE}${pathname}`;
|
|
13
13
|
// !!! node 18 issue -> require('dns').setDefaultResultOrder('ipv4first');
|
|
14
14
|
url = isBrowser ? url : url.replace('://localhost', '://127.0.0.1');
|
|
@@ -152,30 +152,31 @@ export function collectionDelete<T extends IStore>(storePromise: () => Promise<T
|
|
|
152
152
|
// route post resolver
|
|
153
153
|
// !!! todo, should be moved to models -> route service
|
|
154
154
|
export async function routePost(store: IStore, request: NextApiRequest, response: NextApiResponse) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
try {
|
|
156
|
+
const { pathname, href, hrefBeforeRedirect } = request.body;
|
|
157
|
+
console.log('routePost', pathname, href, hrefBeforeRedirect);
|
|
158
|
+
if (pathname && href) {
|
|
159
|
+
const route = await findRoute(store, pathname, href, hrefBeforeRedirect);
|
|
159
160
|
return response.status(200).json(route);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
161
|
+
} else {
|
|
162
|
+
return response.status(400).send('bad request');
|
|
163
|
+
}
|
|
164
|
+
} catch (error: any) {
|
|
165
|
+
if (error.status < 500) {
|
|
166
|
+
if (error.status < 400) {
|
|
167
|
+
return response.status(error.status).send({
|
|
168
|
+
redirectUrl: error.redirectUrl,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (error.status === 404) {
|
|
172
|
+
return response.status(404).send('not found');
|
|
172
173
|
} else {
|
|
173
|
-
|
|
174
|
-
throw error;
|
|
174
|
+
return response.status(error.status).send('gone');
|
|
175
175
|
}
|
|
176
|
+
} else {
|
|
177
|
+
console.error('storeHandler.error.500', error);
|
|
178
|
+
throw error;
|
|
176
179
|
}
|
|
177
|
-
} else {
|
|
178
|
-
return response.status(400).send('bad request');
|
|
179
180
|
}
|
|
180
181
|
}
|
|
181
182
|
|
|
@@ -314,7 +315,43 @@ export function storeIndexHandler() {
|
|
|
314
315
|
});
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
|
|
318
|
+
const trimTerminalSlash = (path: string) => {
|
|
319
|
+
return path.replace(/\/+$/, '');
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const sanitizeHref = (url: URL, urlBeforeRedirect: URL | null) => {
|
|
323
|
+
if (urlBeforeRedirect) {
|
|
324
|
+
const sanitizedUrl = new URL(urlBeforeRedirect);
|
|
325
|
+
sanitizedUrl.protocol = url.protocol;
|
|
326
|
+
return trimTerminalSlash(sanitizedUrl.href);
|
|
327
|
+
} else {
|
|
328
|
+
return trimTerminalSlash(url.href);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const sanitizeRedirectFrom = (path: string, url: URL, urlBeforeRedirect: URL | null) => {
|
|
333
|
+
path = trimTerminalSlash(path);
|
|
334
|
+
if (urlBeforeRedirect) {
|
|
335
|
+
return path.replace(/^(\^?)((https?:\/\/)+)/, `$1${url.protocol}//`);
|
|
336
|
+
} else {
|
|
337
|
+
return path.replace(/^(\^?)((https?:\/\/)+(\w|\.|:)+)/, `$1${url.origin}`);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const sanitizeRedirectTo = (path?: string) => {
|
|
342
|
+
return path ? trimTerminalSlash(path) : undefined;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const sanitizeRedirectUrl = (path: string | undefined, url: URL) => {
|
|
346
|
+
if (path != null) {
|
|
347
|
+
path = trimTerminalSlash(path);
|
|
348
|
+
return path.match(/^((https?:\/\/)+)/) ? path : `${url.origin}${path}`;
|
|
349
|
+
} else {
|
|
350
|
+
return path;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
export async function findRoute(store: IStore, pathname: string, href: string, hrefBeforeRedirect: string | null): Promise<IRoute | undefined> {
|
|
318
355
|
// here we use findMany because route id would break api url
|
|
319
356
|
const routes = await store.route.findMany<IRoute>({
|
|
320
357
|
where: {
|
|
@@ -323,6 +360,7 @@ export async function findRoute(store: IStore, pathname: string, href: string):
|
|
|
323
360
|
},
|
|
324
361
|
},
|
|
325
362
|
});
|
|
363
|
+
// console.log('routes', routes);
|
|
326
364
|
// console.log('pathname', pathname);
|
|
327
365
|
if (routes.length > 0) {
|
|
328
366
|
return routes[0];
|
|
@@ -336,7 +374,6 @@ export async function findRoute(store: IStore, pathname: string, href: string):
|
|
|
336
374
|
},
|
|
337
375
|
},
|
|
338
376
|
});
|
|
339
|
-
// console.log('splatRoutes', splatRoutes);
|
|
340
377
|
// find best match splat route
|
|
341
378
|
const splatRoute = splatRoutes.reduce((p: (IRoute & { splat: string }) | undefined, c: IRoute) => {
|
|
342
379
|
const match = pathname.indexOf(c.id) === 0;
|
|
@@ -349,27 +386,42 @@ export async function findRoute(store: IStore, pathname: string, href: string):
|
|
|
349
386
|
if (splatRoute) {
|
|
350
387
|
return splatRoute;
|
|
351
388
|
}
|
|
389
|
+
const url = new URL(href);
|
|
390
|
+
const urlBeforeRedirect = hrefBeforeRedirect ? new URL(hrefBeforeRedirect) : null;
|
|
391
|
+
const sanitizedHref = sanitizeHref(url, urlBeforeRedirect);
|
|
392
|
+
/*
|
|
393
|
+
console.log('href', url.href);
|
|
394
|
+
console.log('hrefBeforeRedirect', urlBeforeRedirect?.href);
|
|
395
|
+
console.log('sanitizedHref', sanitizedHref);
|
|
396
|
+
*/
|
|
352
397
|
// collect redirects
|
|
353
|
-
|
|
398
|
+
let redirects = await store.redirect.findMany<IRedirect>({
|
|
354
399
|
where: {
|
|
355
400
|
isActive: {
|
|
356
401
|
equals: true,
|
|
357
402
|
},
|
|
358
403
|
},
|
|
359
404
|
});
|
|
405
|
+
redirects = redirects.map(x => ({
|
|
406
|
+
...x,
|
|
407
|
+
from: sanitizeRedirectFrom(x.from, url, urlBeforeRedirect),
|
|
408
|
+
to: sanitizeRedirectTo(x.to),
|
|
409
|
+
}));
|
|
360
410
|
// redirects.sort((a, b) => a.order - b.order);
|
|
411
|
+
// console.log('findRoute redirects', redirects);
|
|
361
412
|
// exact match redirect resolver
|
|
362
413
|
const exactMatchResolver = (redirect: IRedirect) => {
|
|
363
414
|
const status: number = parseInt(redirect.status) || 307;
|
|
364
415
|
if (status < 400) {
|
|
365
|
-
const redirectUrl = redirect.to;
|
|
416
|
+
const redirectUrl = sanitizeRedirectUrl(redirect.to, url);
|
|
417
|
+
// console.log('>>>>>>>>>> findRoute exactMatchResolver', status, redirectUrl, redirect.to);
|
|
366
418
|
throw { status, redirectUrl };
|
|
367
419
|
} else {
|
|
368
420
|
throw { status };
|
|
369
421
|
}
|
|
370
422
|
};
|
|
371
423
|
// check exact match redirects
|
|
372
|
-
const exactMatchRedirect = redirects.find(x => x.from ===
|
|
424
|
+
const exactMatchRedirect = redirects.find(x => x.from === sanitizedHref);
|
|
373
425
|
if (exactMatchRedirect !== undefined) {
|
|
374
426
|
return exactMatchResolver(exactMatchRedirect);
|
|
375
427
|
}
|
|
@@ -382,7 +434,9 @@ export async function findRoute(store: IStore, pathname: string, href: string):
|
|
|
382
434
|
let redirectUrl = (redirect.to as string).replace(/\*/g, (...rest) => {
|
|
383
435
|
return `$${++i}`;
|
|
384
436
|
});
|
|
385
|
-
redirectUrl =
|
|
437
|
+
redirectUrl = sanitizedHref.replace(regexp, redirectUrl);
|
|
438
|
+
redirectUrl = sanitizeRedirectUrl(redirectUrl, url) as string;
|
|
439
|
+
// console.log('>>>>>>>>>> findRoute patternMatchResolver', status, redirectUrl, redirect.to);
|
|
386
440
|
throw { status, redirectUrl };
|
|
387
441
|
} else {
|
|
388
442
|
throw { status };
|
|
@@ -398,7 +452,8 @@ export async function findRoute(store: IStore, pathname: string, href: string):
|
|
|
398
452
|
x.from = expression;
|
|
399
453
|
});
|
|
400
454
|
// check if a regex pattern redirect does match
|
|
401
|
-
const firstMatch = redirects.find(x =>
|
|
455
|
+
const firstMatch = redirects.find(x => sanitizedHref.match(x.from));
|
|
456
|
+
// console.log('>>>>>>>>>> findRoute firstMatch', firstMatch);
|
|
402
457
|
if (firstMatch) {
|
|
403
458
|
return patternMatchResolver(firstMatch);
|
|
404
459
|
}
|