@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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @websolutespa/bom-mixer-store
2
2
 
3
+ ## 1.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 7898849: Modified: exporting getRemoteApiUrl
8
+
9
+ ## 1.7.1
10
+
11
+ ### Patch Changes
12
+
13
+ - Fixing: findRoute redirects
14
+
3
15
  ## 1.7.0
4
16
 
5
17
  ### Minor Changes
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 url = request.nextUrl;
653
- const isStatic = mime.getType(url.pathname) !== null;
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 url = request.nextUrl;
658
- const isNext = url.pathname.indexOf("/_next/") === 0;
658
+ const { pathname } = request.nextUrl;
659
+ const isNext = pathname.startsWith("/_next");
659
660
  return isNext;
660
661
  }
661
662
  function isApiRequest(request) {
662
- const url = request.nextUrl;
663
- const isApi = url.pathname.indexOf(API_BASE2) === 0;
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
- if (request.body.pathname && request.body.href) {
1734
- try {
1735
- const { pathname, href } = request.body;
1736
- const route = await findRoute(store, pathname, href);
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
- } catch (error) {
1739
- if (error.status < 500) {
1740
- if (error.status < 400) {
1741
- return response.status(error.status).send({
1742
- redirectUrl: error.redirectUrl
1743
- });
1744
- }
1745
- if (error.status === 404) {
1746
- return response.status(404).send("not found");
1747
- } else {
1748
- return response.status(error.status).send("gone");
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
- console.error("storeHandler.error.500", error);
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
- async function findRoute(store, pathname, href) {
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 redirects = await store.redirect.findMany({
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 === href);
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 = href.replace(regexp, 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) => href.match(x.from));
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 url = request.nextUrl;
545
- const isStatic = mime.getType(url.pathname) !== null;
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 url = request.nextUrl;
550
- const isNext = url.pathname.indexOf("/_next/") === 0;
549
+ const { pathname } = request.nextUrl;
550
+ const isNext = pathname.startsWith("/_next");
551
551
  return isNext;
552
552
  }
553
553
  function isApiRequest(request) {
554
- const url = request.nextUrl;
555
- const isApi = url.pathname.indexOf(API_BASE2) === 0;
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
- if (request.body.pathname && request.body.href) {
1626
- try {
1627
- const { pathname, href } = request.body;
1628
- const route = await findRoute(store, pathname, href);
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
- } catch (error) {
1631
- if (error.status < 500) {
1632
- if (error.status < 400) {
1633
- return response.status(error.status).send({
1634
- redirectUrl: error.redirectUrl
1635
- });
1636
- }
1637
- if (error.status === 404) {
1638
- return response.status(404).send("not found");
1639
- } else {
1640
- return response.status(error.status).send("gone");
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
- console.error("storeHandler.error.500", error);
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
- async function findRoute(store, pathname, href) {
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 redirects = await store.redirect.findMany({
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 === href);
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 = href.replace(regexp, 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) => href.match(x.from));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-mixer-store",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Mixer Store module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -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 url = request.nextUrl;
8
- const isStatic = mime.getType(url.pathname) !== null;
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 url = request.nextUrl;
15
- const isNext = url.pathname.indexOf('/_next/') === 0;
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 url = request.nextUrl;
21
- const isApi = url.pathname.indexOf(API_BASE) === 0;
19
+ const { pathname } = request.nextUrl;
20
+ const isApi = pathname.startsWith(API_BASE);
22
21
  return isApi;
23
22
  }
@@ -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
- if (request.body.pathname && request.body.href) {
156
- try {
157
- const { pathname, href } = request.body;
158
- const route = await findRoute(store, pathname, href);
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
- } catch (error: any) {
161
- if (error.status < 500) {
162
- if (error.status < 400) {
163
- return response.status(error.status).send({
164
- redirectUrl: error.redirectUrl,
165
- });
166
- }
167
- if (error.status === 404) {
168
- return response.status(404).send('not found');
169
- } else {
170
- return response.status(error.status).send('gone');
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
- console.error('storeHandler.error.500', error);
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
- export async function findRoute(store: IStore, pathname: string, href: string): Promise<IRoute | undefined> {
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
- const redirects = await store.redirect.findMany<IRedirect>({
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 === href);
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 = href.replace(regexp, 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 => href.match(x.from));
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
  }