@websolutespa/bom-mixer-store 3.1.0 → 3.1.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 +11 -0
- package/dist/exports/proxy.d.ts +1 -1
- package/dist/exports/proxy.js.map +1 -1
- package/dist/exports/server.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests/session.test.ts +60 -60
- package/src/proxy/proxy.service.ts +1 -1
- package/src/session/session.service.ts +48 -48
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @websolutespa/bom-mixer-store
|
|
2
2
|
|
|
3
|
+
## 3.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Server packages load in plain Node, and failed builds fail the cli:
|
|
8
|
+
|
|
9
|
+
- mixer server code imports `next/server.js` (next has no `exports` map, Node ESM could not resolve `next/server`). `bom-mixer-image` in `serverExternalPackages` no longer answers 500 with `ERR_MODULE_NOT_FOUND`.
|
|
10
|
+
- `bom <task>` (e.g. `bom build:web`) waits for turbo and exits with its code, so a failed build stops docker and ci at the build step.
|
|
11
|
+
|
|
12
|
+
## 3.1.1
|
|
13
|
+
|
|
3
14
|
## 3.1.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/dist/exports/proxy.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","names":[],"sources":["../../src/proxy/proxy.service.ts"],"sourcesContent":["import mime from 'mime';\nimport { NextRequest } from 'next/server';\n\nconst API_BASE = process.env.NEXT_PUBLIC_API || '/api';\n\nexport function isStaticRequest(request: NextRequest) {\n const { pathname } = request.nextUrl;\n const isStatic = mime.getType(pathname) !== null;\n return isStatic;\n}\n\nexport function isNextRequest(request: NextRequest) {\n const { pathname } = request.nextUrl;\n const isNext = pathname.startsWith('/_next');\n return isNext;\n}\n\nexport function isApiRequest(request: NextRequest) {\n const { pathname } = request.nextUrl;\n const isApi = pathname.startsWith(API_BASE);\n return isApi;\n}\n"],"mappings":";;AAGA,MAAM,WAAW,QAAQ,IAAI,mBAAmB;AAEhD,SAAgB,gBAAgB,SAAsB;CACpD,MAAM,EAAE,aAAa,QAAQ;AAE7B,QADiB,KAAK,QAAQ,SAAS,KAAK;;AAI9C,SAAgB,cAAc,SAAsB;CAClD,MAAM,EAAE,aAAa,QAAQ;AAE7B,QADe,SAAS,WAAW,SAAS;;AAI9C,SAAgB,aAAa,SAAsB;CACjD,MAAM,EAAE,aAAa,QAAQ;AAE7B,QADc,SAAS,WAAW,SAAS"}
|
|
1
|
+
{"version":3,"file":"proxy.js","names":[],"sources":["../../src/proxy/proxy.service.ts"],"sourcesContent":["import mime from 'mime';\nimport { NextRequest } from 'next/server.js';\n\nconst API_BASE = process.env.NEXT_PUBLIC_API || '/api';\n\nexport function isStaticRequest(request: NextRequest) {\n const { pathname } = request.nextUrl;\n const isStatic = mime.getType(pathname) !== null;\n return isStatic;\n}\n\nexport function isNextRequest(request: NextRequest) {\n const { pathname } = request.nextUrl;\n const isNext = pathname.startsWith('/_next');\n return isNext;\n}\n\nexport function isApiRequest(request: NextRequest) {\n const { pathname } = request.nextUrl;\n const isApi = pathname.startsWith(API_BASE);\n return isApi;\n}\n"],"mappings":";;AAGA,MAAM,WAAW,QAAQ,IAAI,mBAAmB;AAEhD,SAAgB,gBAAgB,SAAsB;CACpD,MAAM,EAAE,aAAa,QAAQ;AAE7B,QADiB,KAAK,QAAQ,SAAS,KAAK;;AAI9C,SAAgB,cAAc,SAAsB;CAClD,MAAM,EAAE,aAAa,QAAQ;AAE7B,QADe,SAAS,WAAW,SAAS;;AAI9C,SAAgB,aAAa,SAAsB;CACjD,MAAM,EAAE,aAAa,QAAQ;AAE7B,QADc,SAAS,WAAW,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":["PAGES"],"sources":["../../src/api/api.handler.ts","../../src/mock/mock.service.ts","../../src/mock/mock-menu.service.ts","../../src/mock/mock-page.service.ts","../../src/session/session.service.ts","../../src/fs/fs.service.ts","../../src/static/static.service.ts","../../src/mock/mock.store.ts","../../src/cached/cached.service.ts","../../src/remote/remote.service.ts","../../src/remote/remote-menu.service.ts","../../src/remote/remote-page.service.ts","../../src/remote/remote.store.ts","../../src/store/store.service.ts","../../src/store/store.handler.ts","../../src/mock/mock.handler.ts","../../src/preview/preview.handler.ts","../../src/vars/vars.handler.ts"],"sourcesContent":["// Next.js API route support: https://nextjs.org/docs/api-routes/introduction\r\nimport type { NextApiRequest, NextApiResponse } from 'next';\r\n\r\nexport type ICrudMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | string;\r\n\r\nexport type IHandler<T = any> = (request: NextApiRequest, response: NextApiResponse<T>) => Promise<void | Response>;\r\n\r\nexport type IApiHandler = Record<ICrudMethod, IHandler>;\r\n\r\nexport function apiHandler<T = any>(handler: IApiHandler): IHandler<T> {\r\n return async (request: NextApiRequest, response: NextApiResponse<T>): Promise<void | Response> => {\r\n const method: ICrudMethod = (request.method || 'GET').toLowerCase() as ICrudMethod;\r\n // check handler supports HTTP method\r\n if (!handler[method]) {\r\n response.status(405).end(`Method ${method} Not Allowed`);\r\n return;\r\n }\r\n try {\r\n // global middleware\r\n // await jwtMiddleware(request, response);\r\n // route handler\r\n await handler[method](request, response);\r\n } catch (error) {\r\n // console.log('error', error);\r\n // global error handler\r\n return errorHandler(error, response);\r\n }\r\n };\r\n}\r\n\r\nexport function errorHandler(error: any, response: NextApiResponse): void | Response {\r\n if (typeof (error) === 'string') {\r\n // custom application error\r\n const is404 = error.toLowerCase().endsWith('not found');\r\n const statusCode = is404 ? 404 : 400;\r\n response.status(statusCode).json({ message: error });\r\n return;\r\n }\r\n if (error.name === 'UnauthorizedError') {\r\n // jwt authentication error\r\n response.status(401).json({ message: 'Invalid Token' });\r\n return;\r\n }\r\n // default to 500 server error\r\n console.error(error);\r\n response.status(500).json({ message: error.message });\r\n return;\r\n}\r\n","import { IEntity, IStore, JsonService, QueryParams } from '@websolutespa/bom-core';\r\nimport { DEFAULT_DEPTH, decorateLocalizable_, decorateLocalizables_, decorateRelation_, decorateRelations_ } from '../decorators';\r\n\r\nexport class MockService<T extends IEntity = IEntity> extends JsonService<T> {\r\n store: IStore;\r\n\r\n constructor(items: T[], store: IStore) {\r\n super(items);\r\n this.store = store;\r\n }\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n decoratedItem = await decorateRelation_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth != null ? params.depth : DEFAULT_DEPTH,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n decoratedItems = await decorateRelations_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth != null ? params.depth : DEFAULT_DEPTH,\r\n });\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { IEntity, IUndecoratedMenu, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateLocalizable_, decorateLocalizables_, decorateMenu_, decorateMenus_ } from '../decorators';\r\nimport { MockService } from './mock.service';\r\n\r\nexport class MockMenuService<T extends IEntity = IEntity> extends MockService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n if (params.market && params.locale) {\r\n decoratedItem = await decorateMenu_(store, decoratedItem as any as IUndecoratedMenu, {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any;\r\n }\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n if (params.market && params.locale) {\r\n decoratedItems = await decorateMenus_(store, decoratedItems as any as IUndecoratedMenu[], {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any[];\r\n }\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { ICategorized, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateCategories_, decorateCategory_, decorateHref_, decorateHrefs_, decorateLocalizable_, decorateLocalizables_, decorateNav_, decorateRelation_, decorateRelations_ } from '../decorators';\r\nimport { MockService } from './mock.service';\r\n\r\nexport class MockPageService<T extends ICategorized = ICategorized> extends MockService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateCategory_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n decoratedItem = await decorateLocalizable_<B>(decoratedItem, params);\r\n decoratedItem = await decorateHref_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateNav_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateRelation_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n // decoratedItems = await optimizeMany_(decoratedItems);\r\n decoratedItems = await decorateCategories_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n decoratedItems = await decorateLocalizables_<B>(decoratedItems, params);\r\n decoratedItems = await decorateHrefs_<B>(store, decoratedItems, params);\r\n decoratedItems = await decorateRelations_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n // !!! optimization for index listing removing components\r\n const invalidKeys = ['components'];\r\n decoratedItems = decoratedItems.map(x => Object.fromEntries(\r\n Object.entries(x).filter(\r\n ([k, v]) => !invalidKeys.includes(k)\r\n )\r\n )) as B[];\r\n /*\r\n decoratedItems = await decorateNavs_<B>(store, decoratedItems, params);\r\n */\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { isDevelopment } from '@websolutespa/bom-core';\r\nimport { IncomingMessage, ServerResponse } from 'http';\r\nimport { getIronSession, IronSession, SessionOptions } from 'iron-session';\r\nimport { NextApiRequest, NextApiResponse } from 'next';\r\nimport { NextRequest, NextResponse } from 'next/server';\r\n\r\nexport async function getSession<T extends object = Record<string, any>>(\r\n request: NextRequest | NextApiRequest | IncomingMessage & {\r\n cookies: Partial<{ [key: string]: string }>;\r\n },\r\n response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>\r\n): Promise<IronSession<T>> {\r\n const COOKIE_NAME = 'websolutespa-next-js';\r\n const COOKIE_PASSWORD = process.env.MIXER_SECRET as string || 'MIXER_SECRET_32_CHARS_LONG_12345';\r\n const sessionOptions: SessionOptions = {\r\n cookieName: COOKIE_NAME,\r\n // secure: true should be used in production (HTTPS) but can't be used in development (HTTP)\r\n cookieOptions: {\r\n secure: !isDevelopment,\r\n },\r\n password: COOKIE_PASSWORD,\r\n };\r\n const session = await getIronSession<T>(request, response, sessionOptions);\r\n return session;\r\n}\r\n\r\nexport type SessionWithToken<T extends object = Record<string, any>> = T & Partial<{\r\n token: string;\r\n exp: number;\r\n}>;\r\n\r\nexport async function getSessionToken<T extends SessionWithToken>(\r\n request: NextRequest | NextApiRequest | IncomingMessage & {\r\n cookies: Partial<{ [key: string]: string }>;\r\n },\r\n response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>\r\n): Promise<string | undefined> {\r\n const session = await getSession<T>(request, response);\r\n if (session.token && session.exp && session.exp > Math.floor(new Date().getTime() / 1000)) {\r\n return session.token;\r\n } else {\r\n session.token = undefined;\r\n session.exp = undefined;\r\n await session.save();\r\n session.destroy();\r\n return;\r\n }\r\n}\r\n","import { Json } from '@websolutespa/bom-core';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\n\r\nexport type fsReadOptions = {\r\n encoding?: null | undefined;\r\n flag?: number | string | undefined;\r\n signal?: AbortSignal | undefined;\r\n};\r\n\r\nexport type fsWatchOptions = (fs.WatchOptions & { encoding: 'buffer' }) | 'buffer';\r\nexport type fsWatchFileOptions = (fs.WatchFileOptions & { bigint?: false });\r\nexport type fsWriteFileOptions = (fs.ObjectEncodingOptions & { mode?: fs.Mode; flag?: fs.OpenMode }) | BufferEncoding | null;\r\n\r\nexport async function fsExists(pathname: string): Promise<boolean> {\r\n try {\r\n await fs.promises.access(pathname);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nexport async function fsRead(pathname: string, option?: fsReadOptions): Promise<string | undefined> {\r\n try {\r\n const data = await fs.promises.readFile(pathname, option?.encoding || 'utf8');\r\n return data || undefined;\r\n } catch (error) {\r\n console.log('fsRead.error', error, pathname);\r\n return;\r\n }\r\n}\r\n\r\nexport async function fsReadJson<T = Json>(pathname: string): Promise<T | undefined> {\r\n try {\r\n const data = await fsRead(pathname);\r\n // console.log('fsReadJson', 'pathname', pathname, 'data', data);\r\n if (data) {\r\n return JSON.parse(data) as T;\r\n } else {\r\n return;\r\n }\r\n } catch (error) {\r\n console.log('fsReadJson.error', error, pathname);\r\n // throw (error);\r\n return;\r\n }\r\n}\r\n\r\nexport async function fsWrite(pathname: string, data: string, encoding: fsWriteFileOptions = 'utf8'): Promise<void> {\r\n try {\r\n await fs.promises.mkdir(path.dirname(pathname), { recursive: true });\r\n await fs.promises.writeFile(pathname, data, encoding);\r\n } catch (error) {\r\n console.log('fsWrite', error, pathname);\r\n }\r\n}\r\n\r\nexport async function fsWriteJson(pathname: string, data: Json): Promise<void> {\r\n try {\r\n await fsWrite(pathname, JSON.stringify(data, null, 2));\r\n } catch (error) {\r\n console.log('fsWriteJson', error, pathname);\r\n }\r\n}\r\n\r\nexport async function fsExistOrCreateFolder(pathname: string): Promise<void> {\r\n try {\r\n const exists = fs.existsSync(pathname);\r\n if (!exists) {\r\n await fs.promises.mkdir(pathname);\r\n }\r\n } catch (error) {\r\n console.log('fsExistOrCreateFolder', error, pathname);\r\n }\r\n}\r\n\r\nexport async function fsReadDirectory(pathname: string, extension?: string): Promise<{ name: string, data: string }[]> {\r\n const files = await fs.promises.readdir(pathname);\r\n const items = files.flatMap(async (file: string) => {\r\n const filePath = path.join(pathname, file);\r\n const stat = await fs.promises.stat(filePath);\r\n if (stat.isDirectory()) {\r\n return;\r\n }\r\n if (extension && path.parse(file).ext !== extension) {\r\n return;\r\n }\r\n const data = await fs.promises.readFile(filePath);\r\n if (!data) {\r\n return;\r\n }\r\n return {\r\n name: path.parse(file).name,\r\n data: data.toString(),\r\n };\r\n }) as Promise<{ name: string, data: string }>[];\r\n const items2 = (await Promise.all(items)).filter(Boolean);\r\n return items2;\r\n}\r\n\r\nexport async function fsReadJsonDirectory(pathname: string): Promise<{ name: string, data: any }[]> {\r\n try {\r\n const datas = await fsReadDirectory(pathname, '.json');\r\n return datas.filter(Boolean).map(x => {\r\n x.data = JSON.parse(x.data);\r\n return x;\r\n });\r\n } catch (error) {\r\n console.log('fsReadJsonDirectory', error, pathname);\r\n // throw (error);\r\n return [];\r\n }\r\n}\r\n\r\nexport function fsWatch(pathname: string, callback: (eventType: fs.WatchEventType, fileName: string) => void, options: fsWatchOptions) {\r\n const debounced = debounce(callback);\r\n return fs.watch(pathname, options, debounced);\r\n}\r\n\r\nexport function fsWatchFile(pathname: string, callback: (eventType: fs.WatchEventType, fileName: string) => void, options?: fsWatchFileOptions) {\r\n const debounced = debounce(callback);\r\n return fs.watchFile(pathname, options, debounced);\r\n}\r\n\r\nexport function pathJoin(...paths: string[]): string {\r\n return path.join(process.cwd(), ...paths);\r\n}\r\n\r\nexport function debounce(callback: (...args: any[]) => void, timeout: number = 300) {\r\n let timer: NodeJS.Timeout;\r\n return (...args: any[]) => {\r\n clearTimeout(timer);\r\n timer = setTimeout(() => {\r\n callback(...args);\r\n }, timeout);\r\n };\r\n}\r\n","import { IEntity, IEquatable, IStore, JsonService, QueryParams, httpGet } from '@websolutespa/bom-core';\r\nimport { DEFAULT_DEPTH, decorateLocalizable_, decorateLocalizables_ } from '../decorators';\r\nimport { IField, MapTo, StaticServiceConfig } from './static';\r\n\r\nexport class StaticService<T extends IEntity = IEntity> extends JsonService<T> {\r\n loaded: boolean = false;\r\n config: StaticServiceConfig<T>;\r\n store: IStore;\r\n\r\n constructor(config: StaticServiceConfig<T>, store: IStore) {\r\n super([]);\r\n this.config = config;\r\n this.store = store;\r\n }\r\n\r\n protected mapItems_<B extends T = T>(items: B[], map?: MapTo): B[] {\r\n if (typeof map === 'object') {\r\n items = items.map(x => {\r\n const item: Partial<B> = {};\r\n Object.entries(x).forEach(([k, v]) => {\r\n const key = map[k] || k;\r\n (item as any)[key] = v;\r\n });\r\n return item as B;\r\n });\r\n }\r\n return items;\r\n }\r\n\r\n async getItems<B extends T = T>(): Promise<B[]> {\r\n let items = this.items;\r\n if (this.loaded && items) {\r\n return items as B[];\r\n } else {\r\n const config = this.config;\r\n const src = config.src;\r\n if (typeof src === 'string') {\r\n const response = await httpGet(src, {});\r\n items = response.items;\r\n } else if (typeof src === 'function') {\r\n if (src instanceof Promise) {\r\n items = await src(config);\r\n } else {\r\n items = src(config) as T[];\r\n }\r\n } else {\r\n items = src;\r\n }\r\n items = this.mapItems_(items, config.map);\r\n this.items = items;\r\n this.loaded = true;\r\n return items as B[];\r\n }\r\n }\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n decoratedItem = await decorateStaticRelation_<B>(store, this.config.fields, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n decoratedItems = await decorateStaticRelations_<B>(store, this.config.fields, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n\r\nexport async function decorateStaticRelation_<T>(store: IStore, fields: IField[], item: T, params: QueryParams = {}): Promise<T> {\r\n const depth = params.depth != null ? params.depth : DEFAULT_DEPTH;\r\n if (depth === 0) {\r\n return item;\r\n }\r\n const decoratedItem = { ...item };\r\n for (const field of fields) {\r\n if (\r\n field.type === 'relationship' &&\r\n typeof field.relationTo === 'string'\r\n ) {\r\n const name = field.name as keyof typeof item;\r\n const value = item[name];\r\n const ids = field.hasMany ? value as IEquatable[] : [value];\r\n const items = await store[field.relationTo]!.findMany({\r\n where: {\r\n id: { in: ids },\r\n },\r\n depth: depth - 1,\r\n locale: params.locale,\r\n });\r\n if (field.hasMany) {\r\n decoratedItem[name] = items as any; // !!!\r\n } else {\r\n decoratedItem[name] = (items.length ? items[0] : undefined) as any; // !!!\r\n }\r\n }\r\n }\r\n return decoratedItem;\r\n}\r\n\r\nexport async function decorateStaticRelations_<T>(store: IStore, fields: IField[], items: T[], params: QueryParams = {}): Promise<T[]> {\r\n const decoratedItems: T[] = [];\r\n for (const item of items) {\r\n const decoratedItem = await decorateStaticRelation_<T>(store, fields, item, params) as T;\r\n decoratedItems.push(decoratedItem);\r\n }\r\n return decoratedItems;\r\n}\r\n","import { ICategorized, IEntity, IMemoryStore, IQuerable, isDevelopment, IStore } from '@websolutespa/bom-core';\r\nimport { fromCache } from '../cache/cache.service';\r\nimport { fsReadJson } from '../fs/fs.service';\r\nimport { staticCollections } from '../static/static';\r\nimport { StaticService } from '../static/static.service';\r\nimport { MockMenuService } from './mock-menu.service';\r\nimport { MockPageService } from './mock-page.service';\r\nimport { MockService } from './mock.service';\r\n\r\nconst PAGES: string[] = process.env.PAGES ? process.env.PAGES.split(',') : [];\r\n\r\nexport function getMockStoreFromData_<T extends IStore>(data?: IMemoryStore): T {\r\n const store: IStore = {};\r\n if (data) {\r\n for (const key in data) {\r\n if (!PAGES.includes(key)) {\r\n // new collection\r\n const service = new MockService(data[key]!, store);\r\n store[key] = service;\r\n }\r\n }\r\n const category = store.category;\r\n if (!category) {\r\n console.warn('mock category not found');\r\n }\r\n const route = store.route;\r\n if (!route) {\r\n console.warn('mock route not found');\r\n }\r\n if (category && route) {\r\n const menu = data.menu;\r\n if (menu) {\r\n store.menu = new MockMenuService(menu, store);\r\n }\r\n for (const key in data) {\r\n if (PAGES.includes(key)) {\r\n const service = new MockPageService(data[key] as ICategorized[], store) as IQuerable<IEntity>;\r\n store[key] = service;\r\n }\r\n }\r\n }\r\n } else {\r\n console.warn('mock store not found');\r\n }\r\n staticCollections.forEach(config => {\r\n store[config.slug] = new StaticService(config, store);\r\n });\r\n return store as T;\r\n}\r\n\r\nasync function getStoreData_(): Promise<IMemoryStore> {\r\n if (isDevelopment) {\r\n\r\n const path = require('path');\r\n const directory = 'cwd' in process && typeof process.cwd === 'function' ? process.cwd() : process.env.CWD;\r\n // console.log('directory', directory);\r\n const pathname = path.join(directory, '.mixer', 'store.json');\r\n return (await fsReadJson<IMemoryStore>(pathname) || {});\r\n } else {\r\n const store = process.env.STORE_ || {};\r\n return (typeof store === 'string' ? JSON.parse(store) : store) as IMemoryStore;\r\n }\r\n}\r\n\r\nasync function getMockStore_<T extends IStore = IStore>(): Promise<T> {\r\n const data = await getStoreData_();\r\n const store: IStore = getMockStoreFromData_(data);\r\n return store as T;\r\n}\r\n\r\nexport async function getMockStore<T extends IStore = IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n if (!isDevelopment) {\r\n console.warn('getMockStore');\r\n }\r\n const store = await getMockStore_<T>();\r\n return store;\r\n }, isDevelopment ? undefined : 'STORE_MOCK_');\r\n}\r\n","import { GlobalQueryParams, IEntity, IEquatable, IPagedResult, IQuerable, JsonService, QueryParams, qsSerialize } from '@websolutespa/bom-core';\r\nimport { StoreStrategy, storeCacheDuration, storeStrategy } from '../store/store';\r\n\r\nexport type ICacheResult<T extends IEntity = IEntity> = T | T[] | IPagedResult<T> | undefined;\r\n\r\nexport type ICache<T extends IEntity = IEntity> = {\r\n timestamp: number;\r\n data: ICacheResult<T>;\r\n};\r\n\r\nexport type InMemoryCacheOptions = {\r\n duration?: number | false;\r\n};\r\n\r\nexport class CachedService<T extends IEntity = IEntity> extends JsonService<T> {\r\n\r\n private service_: IQuerable<T>;\r\n private cache_: Map<string, ICache<T>> = new Map<string, ICache<T>>();\r\n private duration_: number | false = storeCacheDuration;\r\n\r\n constructor(service: IQuerable<T>, options: InMemoryCacheOptions = {}) {\r\n super([]);\r\n if (!service) {\r\n throw new Error('CachedService: service is required');\r\n }\r\n this.service_ = service;\r\n this.duration_ = typeof options.duration !== 'undefined' ? options.duration : this.duration_;\r\n }\r\n\r\n private async fromCache_(callback: () => Promise<ICacheResult<T>>, key?: string): Promise<ICacheResult<T>> {\r\n if (key && this.hasKey_(key)) {\r\n return this.getKey_(key);\r\n } else {\r\n const t = await callback();\r\n if (key && t) {\r\n this.setKey_(key, t);\r\n }\r\n return t;\r\n }\r\n }\r\n\r\n private isExpired_(item: ICache<T>): boolean {\r\n return this.duration_ === false ? true : item.timestamp < (new Date().getTime() - this.duration_);\r\n }\r\n\r\n private hasKey_(key: string): boolean {\r\n const item = this.cache_.get(key);\r\n return item ? (!this.isExpired_(item)) : false;\r\n }\r\n\r\n private getKey_(key: string): ICacheResult<T> {\r\n return this.cache_.get(key)!.data;\r\n }\r\n\r\n private setKey_(key: string, data: ICacheResult<T>) {\r\n this.cache_.set(key, {\r\n timestamp: new Date().getTime(),\r\n data,\r\n });\r\n }\r\n\r\n async findGlobal<B extends T = T>(params: GlobalQueryParams = {}): Promise<B | undefined> {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findGlobal?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findGlobal(params);\r\n }, key) as B | undefined;\r\n }\r\n\r\n async findOne<B extends T = T>(id: IEquatable, params: QueryParams = {}): Promise<B | undefined> {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findOne[${id}]?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findOne(id, params);\r\n }, key) as B | undefined;\r\n }\r\n\r\n async findMany<B extends T = T>(params: QueryParams = {}): Promise<B[]> {\r\n if (storeStrategy === StoreStrategy.DecoratedApi) {\r\n const { market, locale, ...otherParams } = params;\r\n const serializedParams = qsSerialize({ market, locale });\r\n const key = `findMany?${serializedParams}`;\r\n let items = await this.fromCache_(async () => {\r\n return await this.service_.findMany({ market, locale });\r\n }, key) as B[];\r\n if (otherParams.where) {\r\n items = this.whereItems_(items, otherParams.where);\r\n }\r\n if (otherParams.sort) {\r\n items = this.sortCollection_(items, otherParams.sort);\r\n }\r\n return items;\r\n } else {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findMany?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findMany(params);\r\n }, key) as B[];\r\n }\r\n }\r\n\r\n async findPaged<B extends T = T>(params: QueryParams = {}): Promise<IPagedResult<B>> {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findPaged?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findPaged(params);\r\n }, key) as IPagedResult<B>;\r\n }\r\n\r\n async create<B extends T = T>(payload: Omit<B, 'id'>): Promise<B> {\r\n return await this.service_.create(payload);\r\n }\r\n\r\n async updateOne<B extends T = T>(id: IEquatable, payload: Partial<B>, params: Omit<QueryParams, 'where'> = {}): Promise<B> {\r\n return await this.service_.updateOne(id, payload, params);\r\n }\r\n\r\n async updateMany<B extends T = T>(payload: Partial<B>, params: QueryParams = {}): Promise<B[]> {\r\n return await this.service_.updateMany(payload, params);\r\n }\r\n\r\n async deleteOne<B extends T = T>(id: IEquatable, params: Omit<QueryParams, 'where'> = {}): Promise<B | undefined> {\r\n return await this.service_.deleteOne(id, params);\r\n }\r\n\r\n async deleteMany<B extends T = T>(params: QueryParams = {}): Promise<B[]> {\r\n return await this.service_.deleteMany(params);\r\n }\r\n\r\n // !!! old\r\n\r\n async update<B extends T = T>(payload: Partial<B>): Promise<B> {\r\n return await this.service_.update(payload);\r\n }\r\n\r\n async delete<B extends T = T>(id: IEquatable): Promise<B | undefined> {\r\n return await this.service_.delete(id);\r\n }\r\n\r\n}\r\n","import { GlobalQueryParams, IEntity, IEquatable, IPagedResult, IQuerable, IStore, qsSerialize, QueryParams } from '@websolutespa/bom-core';\r\nimport { getRemoteApiRequestOptions } from '../api/api';\r\nimport { decorateSchema_, decorateSchemas_ } from '../decorators';\r\nimport { remoteApiDelete, remoteApiGet, remoteApiPatch, remoteApiPost } from './remote.api';\r\n\r\n/**\r\n * This service handle call to remote api (bowl or over)\r\n */\r\n\r\nexport class RemoteService<T extends IEntity = IEntity> implements IQuerable<T> {\r\n key: string;\r\n store: IStore;\r\n\r\n constructor(key: string, store: IStore) {\r\n if (!key) {\r\n throw new Error('RemoteService: key is required');\r\n }\r\n if (!store) {\r\n throw new Error('RemoteService: store is required');\r\n }\r\n this.key = key;\r\n this.store = store;\r\n }\r\n\r\n async findGlobal<B extends T = T>(params: GlobalQueryParams = {}, token?: string): Promise<B | undefined> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/globals/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'findGlobal', url);\r\n try {\r\n const item = await remoteApiGet<B>(url, getRemoteApiRequestOptions(token));\r\n if (item) {\r\n return await this.decorateOne_(item, params);\r\n }\r\n } catch (error) {\r\n console.log('RemoteService', this.key, 'findGlobal.error');\r\n }\r\n return;\r\n }\r\n\r\n async findOne<B extends T = T>(id: IEquatable, params: Omit<QueryParams, 'where'> = {}, token?: string): Promise<B | undefined> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}/${encodeURIComponent(id)}${search}`;\r\n // console.log('RemoteService', this.key, 'findOne', url);\r\n try {\r\n console.log('remoteApiGet', url);\r\n const item = await remoteApiGet<B>(url, getRemoteApiRequestOptions(token));\r\n if (item) {\r\n return await this.decorateOne_(item, params);\r\n }\r\n } catch (error) {\r\n console.log('RemoteService', this.key, 'findOne.error');\r\n }\r\n return;\r\n }\r\n\r\n async findMany<B extends T = T>(params: QueryParams = {}, token?: string): Promise<B[]> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'findMany', url);\r\n const response = await remoteApiGet<B[] | { docs: B[] }>(url, getRemoteApiRequestOptions(token));\r\n const items: B[] = Array.isArray(response) ? response : response.docs;\r\n return await this.decorateMany_(items, params);\r\n }\r\n\r\n async findPaged<B extends T = T>(params: QueryParams = {}, token?: string): Promise<IPagedResult<B>> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = true;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'findPaged', url);\r\n const response = await remoteApiGet<IPagedResult<B>>(url, getRemoteApiRequestOptions(token));\r\n let items: B[] = response.docs;\r\n items = await this.decorateMany_(items, params);\r\n return { ...response, docs: items };\r\n }\r\n\r\n async create<B extends T = T>(payload: Omit<B, 'id'>, params: QueryParams = {}, token?: string): Promise<B> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'create', url);\r\n const result = await remoteApiPost<B | { doc: B }>(url, payload, getRemoteApiRequestOptions(token));\r\n const item = (result.doc ? result.doc : result) as B;\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async updateOne<B extends T = T>(id: IEquatable, payload: Partial<B>, params: Omit<QueryParams, 'where'> = {}, token?: string): Promise<B> {\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}/${encodeURIComponent(id)}${search}`;\r\n // console.log('RemoteService', this.key, 'updateOne', url);\r\n const result = await remoteApiPatch<B | { doc: B }>(url, payload, getRemoteApiRequestOptions(token));\r\n const item = (result.doc ? result.doc : result) as B;\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async updateMany<B extends T = T>(payload: Partial<B>, params: QueryParams = {}, token?: string): Promise<B[]> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'updateMany', url);\r\n const response = await remoteApiPatch<B[] | { docs: B[] }>(url, payload, getRemoteApiRequestOptions(token));\r\n const items: B[] = Array.isArray(response) ? response : response.docs;\r\n return await this.decorateMany_(items, params);\r\n }\r\n\r\n async deleteOne<B extends T = T>(id: IEquatable, params: Omit<QueryParams, 'where'> = {}, token?: string): Promise<B | undefined> {\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}/${encodeURIComponent(id)}${search}`;\r\n // console.log('RemoteService', this.key, 'deleteOne', url);\r\n const item = await remoteApiDelete<B>(url, getRemoteApiRequestOptions(token));\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async deleteMany<B extends T = T>(params: QueryParams = {}, token?: string): Promise<B[]> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'deleteMany', url);\r\n const response = await remoteApiDelete<B[] | { docs: B[] }>(url, getRemoteApiRequestOptions(token));\r\n const items: B[] = Array.isArray(response) ? response : response.docs;\r\n return await this.decorateMany_(items, params);\r\n }\r\n\r\n // !!! old\r\n\r\n async update<B extends T = T>(payload: Partial<B>, params: QueryParams = {}, token?: string): Promise<B> {\r\n const url = `/${this.key}`;\r\n // console.log('RemoteService', this.key, 'update', url);\r\n const result = await remoteApiPatch<B | { doc: B }>(url, payload, getRemoteApiRequestOptions(token));\r\n const item = (result.doc ? result.doc : result) as B;\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async delete<B extends T = T>(id: IEquatable, token?: string): Promise<B | undefined> {\r\n const params = {\r\n where: {\r\n id: {\r\n equals: id,\r\n },\r\n },\r\n };\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'delete', url);\r\n const item = await remoteApiDelete<B>(url, getRemoteApiRequestOptions(token));\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n protected async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n let decoratedItem = item;\r\n decoratedItem = await decorateSchema_<B>(this.store, decoratedItem, this.key);\r\n return decoratedItem;\r\n }\r\n\r\n protected async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n let decoratedItems = items;\r\n decoratedItems = await decorateSchemas_<B>(this.store, decoratedItems, this.key);\r\n return decoratedItems;\r\n }\r\n}\r\n","import { IEntity, IUndecoratedMenu, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateLocalizable_, decorateLocalizables_, decorateMenu_, decorateMenus_, decorateSchema_, decorateSchemas_ } from '../decorators';\r\nimport { RemoteService } from './remote.service';\r\n\r\nexport class RemoteMenuService<T extends IEntity = IEntity> extends RemoteService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateSchema_<B>(store, decoratedItem, this.key);\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n if (params.market && params.locale) {\r\n decoratedItem = await decorateMenu_(store, decoratedItem as IUndecoratedMenu, {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any;\r\n }\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateSchemas_<B>(store, decoratedItems, this.key);\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n if (params.market && params.locale) {\r\n decoratedItems = await decorateMenus_(store, decoratedItems as IUndecoratedMenu[], {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any[];\r\n }\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { ICategorized, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateCategories_, decorateCategory_, decorateHref_, decorateHrefs_, decorateNav_, decorateRelation_, decorateSchema_, decorateSchemas_ } from '../decorators';\r\nimport { RemoteService } from './remote.service';\r\n\r\nexport class RemotePageService<T extends ICategorized = ICategorized> extends RemoteService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateSchema_<B>(store, decoratedItem, this.key);\r\n decoratedItem = await decorateCategory_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateHref_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateNav_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateRelation_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n // decoratedItems = await optimizeMany_<B>(decoratedItems);\r\n decoratedItems = await decorateSchemas_<B>(store, decoratedItems, this.key);\r\n decoratedItems = await decorateCategories_<B>(store, decoratedItems, params);\r\n decoratedItems = await decorateHrefs_<B>(store, decoratedItems, params);\r\n /*\r\n // !!! optimization for index listing\r\n decoratedItems = await decorateNavs_<B>(store, decoratedItems, params);\r\n decoratedItems = await decorateRelations_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n */\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { IMemoryStore, IQuerable, IStore } from '@websolutespa/bom-core';\r\nimport { fromCache } from '../cache/cache.service';\r\nimport { CachedService } from '../cached/cached.service';\r\nimport { getMockStore, getMockStoreFromData_ } from '../mock/mock.store';\r\nimport { COLLECTIONS, KNOWN_COLLECTIONS, MOCK_COLLECTIONS, NextPhase, StoreStrategy, buildStrategy, nextPhase, storeStrategy } from '../store/store';\r\nimport { RemoteMenuService } from './remote-menu.service';\r\nimport { RemotePageService } from './remote-page.service';\r\nimport { remoteApiGet } from './remote.api';\r\nimport { RemoteService } from './remote.service';\r\n\r\nconst PAGES: string[] = process.env.PAGES ? process.env.PAGES.split(',') : [];\r\n\r\nasync function getRemoteStore_<T extends IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n // console.log('getRemoteStore_');\r\n if (COLLECTIONS.length === 0) {\r\n console.error('RemoteStore.getRemoteStore_ no collections');\r\n throw ('RemoteStore.getRemoteStore_ no collections');\r\n }\r\n let mockStore: IStore = {};\r\n if (MOCK_COLLECTIONS.length > 0) {\r\n mockStore = await getMockStore<T>();\r\n }\r\n const store: IStore = {};\r\n COLLECTIONS.sort();\r\n COLLECTIONS.forEach(key => {\r\n if (MOCK_COLLECTIONS.includes(key)) {\r\n store[key] = mockStore[key]!;\r\n } else if (PAGES.includes(key) && storeStrategy === StoreStrategy.DecoratedApi) {\r\n store[key] = new RemotePageService(key, store) as IQuerable;\r\n } else if (key === 'menu' && storeStrategy === StoreStrategy.DecoratedApi) {\r\n store[key] = new CachedService(\r\n new RemoteMenuService(key, store)\r\n );\r\n } else if (KNOWN_COLLECTIONS.includes(key)) {\r\n store[key] = new CachedService(\r\n new RemoteService(key, store)\r\n );\r\n } else {\r\n store[key] = new RemoteService(key, store);\r\n }\r\n });\r\n return store as T;\r\n }, 'STORE_RUNTIME_');\r\n // }, isDevelopment ? undefined : 'STORE_RUNTIME_');\r\n}\r\n\r\nasync function getRemoteStoreStaticBuild_<T extends IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n console.log('getRemoteStoreStaticBuild_');\r\n let store: IStore = {};\r\n const url = `/store?strategy=${buildStrategy}`;\r\n const json = await remoteApiGet<IMemoryStore>(url);\r\n if (json != null) {\r\n const data = Object.fromEntries(\r\n COLLECTIONS.map(key => ([key, json[key] || []]))\r\n );\r\n store = getMockStoreFromData_(data);\r\n }\r\n return store as T;\r\n }, 'STORE_BUILD_');\r\n}\r\n\r\nasync function getRemoteStoreRuntimeBuild_<T extends IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n console.log('getRemoteStoreRuntimeBuild_');\r\n const data: IMemoryStore = {};\r\n COLLECTIONS.forEach(key => {\r\n data[key] = [];\r\n });\r\n const store = getMockStoreFromData_(data);\r\n return store as T;\r\n }, 'STORE_RUNTIME_BUILD_');\r\n}\r\n\r\nexport async function getRemoteStore<T extends IStore>(): Promise<T> {\r\n if (nextPhase === NextPhase.ProductionBuild) {\r\n /*\r\n if (buildStrategy === BuildStrategy.Runtime) {\r\n return await getRemoteStoreRuntimeBuild_();\r\n } else {\r\n return await getRemoteStoreStaticBuild_();\r\n }\r\n */\r\n return await getRemoteStoreStaticBuild_();\r\n } else {\r\n return await getRemoteStore_();\r\n }\r\n}\r\n","import { IStore } from '@websolutespa/bom-core';\r\n// import { getLocalStore } from '../local_/local.store';\r\nimport { getMockStore } from '../mock/mock.store';\r\nimport { getRemoteStore } from '../remote/remote.store';\r\nimport { StoreStrategy, storeStrategy } from './store';\r\n\r\nexport async function getStore<T extends IStore>(): Promise<T> {\r\n // console.trace('getStore', storeStrategy);\r\n switch (storeStrategy) {\r\n case StoreStrategy.Mock:\r\n return await getMockStore<T>();\r\n /*\r\n case StoreStrategy.Local:\r\n return await getLocalStore<T>();\r\n */\r\n default:\r\n return await getRemoteStore<T>();\r\n }\r\n}\r\n","import { IEntity, IRedirect, IRoute, IStore, asEquatable, defaultLocale, defaultMarket, qsDeserialize } from '@websolutespa/bom-core';\r\nimport { NextApiRequest, NextApiResponse } from 'next';\r\nimport { apiHandler } from '../api/api.handler';\r\nimport { getSessionToken } from '../session/session.service';\r\nimport { COLLECTIONS } from './store';\r\nimport { getStore } from './store.service';\r\n\r\n/*\r\n* storeHandler contains generic local api CRUD handlers of next app\r\n* it could serve both api or mock data\r\n*/\r\n\r\nexport function storeHandler<T extends IStore>(storePromise: () => Promise<T> = async () => await getStore<T>()) {\r\n return apiHandler({\r\n get: collectionGet(storePromise),\r\n post: collectionPost(storePromise),\r\n patch: collectionPatch(storePromise),\r\n delete: collectionDelete(storePromise),\r\n });\r\n}\r\n\r\n// find a specific document by id or\r\n// find paginated documents\r\nexport function collectionGet<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n // console.log('query', query);\r\n if (!Array.isArray(collections) || collections?.length === 0) {\r\n console.error('storeHandler.collectionGet', 'method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n // console.log('params', params);\r\n if (!COLLECTIONS.includes(collection)) {\r\n console.error('storeHandler.collectionGet', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('storeHandler.collectionGet', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const token = await getSessionToken(request, response);\r\n // find a specific document by id\r\n if (id) {\r\n // console.log('storeHandler.collectionGet', collection, id, params);\r\n const item = await service.findOne(asEquatable(id), params, token);\r\n if (item) {\r\n return response.status(200).json(item);\r\n } else {\r\n return response.status(404).send('not found');\r\n }\r\n // find paginated documents\r\n } else if (String(params.pagination) === 'true') {\r\n // console.log('storeHandler.collectionGet', collection, params);\r\n const pagination = await service.findPaged(params, token);\r\n return response.status(200).json(pagination);\r\n } else {\r\n // console.log('storeHandler.collectionGet', collection, params);\r\n const items = await service.findMany(params, token);\r\n return response.status(200).json(items);\r\n }\r\n } catch (error: any) {\r\n console.error('storeHandler.collectionGet.error', '500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// create a new document + post route\r\nexport function collectionPost<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n if (!Array.isArray(collections) || collections.length !== 1) {\r\n console.error('method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n if (id) {\r\n console.error('method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n // api post route pathname\r\n if (collection === 'route') {\r\n return routePost(store, request, response);\r\n }\r\n const token = await getSessionToken(request, response);\r\n const payload: Omit<IEntity, 'id'> = request.body;\r\n const item = await service.create(payload, params, token);\r\n return response.status(200).json(item);\r\n } catch (error: any) {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// update a document by id\r\nexport function collectionPatch<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n // console.log('query', query);\r\n if (!Array.isArray(collections) || collections?.length === 0) {\r\n console.error('storeHandler.collectionUpdate', 'method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n if (!Array.isArray(collections) || collections.length !== 2) {\r\n console.error('method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n // console.log('params', params);\r\n if (!COLLECTIONS.includes(collection)) {\r\n console.error('storeHandler.collectionUpdate', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('storeHandler.collectionUpdate', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const token = await getSessionToken(request, response);\r\n const payload: Partial<IEntity> = request.body;\r\n // update a specific document by id\r\n if (id) {\r\n // console.log('storeHandler.collectionUpdate', collection, id, params);\r\n const item = await service.updateOne(asEquatable(id), payload, params, token);\r\n if (item) {\r\n return response.status(200).json(item);\r\n } else {\r\n return response.status(404).send('not found');\r\n }\r\n } else {\r\n // console.log('storeHandler.collectionUpdate', collection, params);\r\n const items = await service.updateMany(payload, params, token);\r\n return response.status(200).json(items);\r\n }\r\n } catch (error: any) {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// delete an existing document by id\r\nexport function collectionDelete<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n // console.log('query', query);\r\n if (!Array.isArray(collections) || collections?.length === 0) {\r\n console.error('storeHandler.collectionDelete', 'method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n if (!Array.isArray(collections) || collections.length !== 2) {\r\n console.error('method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n // console.log('params', params);\r\n if (!COLLECTIONS.includes(collection)) {\r\n console.error('storeHandler.collectionDelete', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('storeHandler.collectionDelete', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const token = await getSessionToken(request, response);\r\n // delete a specific document by id\r\n if (id) {\r\n // console.log('storeHandler.collectionDelete', collection, id, params);\r\n const item = await service.deleteOne(asEquatable(id), params, token);\r\n if (item) {\r\n return response.status(200).json(item);\r\n } else {\r\n return response.status(404).send('not found');\r\n }\r\n } else {\r\n // console.log('storeHandler.collectionDelete', collection, params);\r\n const items = await service.deleteMany(params, token);\r\n return response.status(200).json(items);\r\n }\r\n } catch (error: any) {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// route post resolver\r\n// !!! todo, should be moved to models -> route service\r\nexport async function routePost(store: IStore, request: NextApiRequest, response: NextApiResponse) {\r\n try {\r\n const { pathname, href, hrefBeforeRedirect } = request.body;\r\n console.log('routePost', pathname, href, hrefBeforeRedirect);\r\n if (pathname && href) {\r\n const route = await findRoute(store, pathname, href, hrefBeforeRedirect);\r\n return response.status(200).json(route);\r\n } else {\r\n return response.status(400).send('bad request');\r\n }\r\n } catch (error: any) {\r\n if (error.status < 500) {\r\n if (error.status < 400) {\r\n return response.status(error.status).send({\r\n redirectUrl: error.redirectUrl,\r\n });\r\n }\r\n if (error.status === 404) {\r\n return response.status(404).send('not found');\r\n } else {\r\n return response.status(error.status).send('gone');\r\n }\r\n } else {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n }\r\n}\r\n\r\n/*\r\n* storeIndexHandler contains a list of all the generic local api CRUD handlers of next app\r\n*/\r\nexport function storeIndexHandler() {\r\n return apiHandler({\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const market = defaultMarket;\r\n const locale = defaultLocale;\r\n if (!Array.isArray(COLLECTIONS) || COLLECTIONS?.length === 0) {\r\n console.error('apiHome.collectionGet', 'collections not found', COLLECTIONS);\r\n return response.status(405).send('method not allowed');\r\n }\r\n COLLECTIONS.sort((a, b) => a.localeCompare(b));\r\n response.setHeader('Content-Type', 'text/html; charset=UTF-8');\r\n response.setHeader('X-Frame-Options', 'SAMEORIGIN');\r\n response.setHeader('X-Robots-Tag', 'noindex, follow');\r\n return response.status(200).send(`\r\n <html>\r\n <head>\r\n <title>REST Api</title>\r\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n <style type=\"text/css\">\r\n html {\r\n padding: 0;\r\n background: #09090A;\r\n color: #fefefe;\r\n }\r\n body {\r\n padding: 15px;\r\n font-family: Inter,-apple-system,'Helvetica Neue','Segoe UI',Roboto,Arial,'Noto Sans',sans-serif;\r\n font-size: 13px;\r\n background: #09090A;\r\n color: #fefefe;\r\n }\r\n .container {\r\n margin: 0 auto;\r\n max-width: 1280px;\r\n }\r\n a {\r\n color: #fefefe;\r\n text-decoration: none;\r\n }\r\n a:visited {\r\n color: #9e9e9e;\r\n }\r\n a:hover {\r\n text-decoration: underline;\r\n }\r\n table {\r\n width: 100%;\r\n margin: 10px 0;\r\n border: none;\r\n border-collapse: collapse;\r\n }\r\n td,\r\n th {\r\n text-align: left;\r\n font-size: 12px;\r\n padding: 3px;\r\n }\r\n th:not(:last-child),\r\n td:not(:last-child) {\r\n padding-right: 30px;\r\n }\r\n thead th {\r\n border-bottom: 1px solid #0e0e0f;\r\n }\r\n #table tr:nth-child(odd) td {\r\n background-color: #0e0e0f;\r\n }\r\n #table tbody tr:hover td {\r\n background-color: #1A1A1B;\r\n }\r\n #table tbody tr:hover td,\r\n #table tbody tr:hover td a {\r\n color: #fefefe;\r\n }\r\n .wysiwyg {\r\n margin: 0;\r\n padding: 0 0 10px 0;\r\n line-height: 1.2em;\r\n }\r\n .wysiwyg a {\r\n color: #00ea4e;\r\n font-weight: 600;\r\n }\r\n .wysiwyg a:visited {\r\n color: #00ea4e;\r\n }\r\n </style>\r\n </head>\r\n <body>\r\n <div class=\"container\">\r\n <h1>REST API</h1>\r\n <p class=\"wysiwyg\">\r\n This is the REST Api generated by <a href=\"https://github.com/websolutespa/bom\" target=\"_blank\" rel=\"noopener\">Mixer</a>.\r\n </p>\r\n <p class=\"wysiwyg\">\r\n It contains a list of all the generic local api CRUD handlers of Mixer app. <br />\r\n All methods should be called with market and locale parameters.\r\n </p>\r\n <table id=\"table\" cellpadding=\"3\">\r\n <thead>\r\n <tr>\r\n <th width=\"150px\">Method</th>\r\n <th width=\"150px\">Collection</th>\r\n <th>Endpoint</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n ${COLLECTIONS.map(c => (`\r\n <tr>\r\n <td>GET, POST, PATCH</td>\r\n <td>${c}</td>\r\n <td><a href=\"/api/${c}?market=${market}&locale=${locale}\">/api/${c}</a></td>\r\n </tr>\r\n <tr>\r\n <td>GET, DELETE</td>\r\n <td>${c}</td>\r\n <td><a href=\"/api/${c}/{id}?market=${market}&locale=${locale}\">/api/${c}/{id}</a></td>\r\n </tr>\r\n `)).join('\\n')}\r\n\t\t\t\t\t </tbody>\r\n </table>\r\n </div>\r\n </body>\r\n `);\r\n } catch (error: any) {\r\n console.error('storeHandler.collectionGet.error', '500', error);\r\n throw error;\r\n }\r\n },\r\n });\r\n}\r\n\r\nconst trimTerminalSlash = (path: string) => {\r\n return path.replace(/\\/+$/, '');\r\n};\r\n\r\nconst sanitizeHref = (url: URL, urlBeforeRedirect: URL | null) => {\r\n if (urlBeforeRedirect) {\r\n const sanitizedUrl = new URL(urlBeforeRedirect);\r\n sanitizedUrl.protocol = url.protocol;\r\n return trimTerminalSlash(sanitizedUrl.href);\r\n } else {\r\n return trimTerminalSlash(url.href);\r\n }\r\n};\r\n\r\nconst sanitizeRedirectFrom = (path: string, url: URL, urlBeforeRedirect: URL | null) => {\r\n path = trimTerminalSlash(path);\r\n if (urlBeforeRedirect) {\r\n return path.replace(/^(\\^?)((https?:\\/\\/)+)/, `$1${url.protocol}//`);\r\n } else {\r\n return path.replace(/^(\\^?)((https?:\\/\\/)+(\\w|\\.|:)+)/, `$1${url.origin}`);\r\n }\r\n};\r\n\r\nconst sanitizeRedirectTo = (path?: string) => {\r\n return path ? trimTerminalSlash(path) : undefined;\r\n};\r\n\r\nconst sanitizeRedirectUrl = (path: string | undefined, url: URL) => {\r\n if (path != null) {\r\n path = trimTerminalSlash(path);\r\n return path.match(/^((https?:\\/\\/)+)/) ? path : `${url.origin}${path}`;\r\n } else {\r\n return path;\r\n }\r\n};\r\n\r\nexport async function findRoute(store: IStore, pathname: string, href: string, hrefBeforeRedirect: string | null): Promise<IRoute | undefined> {\r\n // here we use findMany because route id would break api url\r\n const routes = await store.route!.findMany<IRoute>({\r\n where: {\r\n id: {\r\n equals: pathname,\r\n },\r\n },\r\n });\r\n // console.log('routes', routes);\r\n // console.log('pathname', pathname);\r\n if (routes.length > 0) {\r\n return routes[0];\r\n } else {\r\n // exact route match not found\r\n // let's try if match splat routes\r\n const splatRoutes = await store.route!.findMany<IRoute>({\r\n where: {\r\n useSplat: {\r\n equals: true,\r\n },\r\n },\r\n });\r\n // find best match splat route\r\n const splatRoute = splatRoutes.reduce((p: (IRoute & { splat: string }) | undefined, c: IRoute) => {\r\n const match = pathname.indexOf(c.id) === 0;\r\n if (match && (!p || p.id.length < c.id.length)) {\r\n return { ...c, splat: pathname.substring(c.id.length, pathname.length) };\r\n } else {\r\n return p;\r\n }\r\n }, undefined);\r\n if (splatRoute) {\r\n return splatRoute;\r\n }\r\n const url = new URL(href);\r\n const urlBeforeRedirect = hrefBeforeRedirect ? new URL(hrefBeforeRedirect) : null;\r\n const sanitizedHref = sanitizeHref(url, urlBeforeRedirect);\r\n /*\r\n console.log('href', url.href);\r\n console.log('hrefBeforeRedirect', urlBeforeRedirect?.href);\r\n console.log('sanitizedHref', sanitizedHref);\r\n */\r\n // collect redirects\r\n let redirects = await store.redirect!.findMany<IRedirect>({\r\n where: {\r\n isActive: {\r\n equals: true,\r\n },\r\n },\r\n });\r\n redirects = redirects.map(x => ({\r\n ...x,\r\n from: sanitizeRedirectFrom(x.from, url, urlBeforeRedirect),\r\n to: sanitizeRedirectTo(x.to),\r\n }));\r\n // redirects.sort((a, b) => a.order - b.order);\r\n // console.log('findRoute redirects', redirects);\r\n // exact match redirect resolver\r\n const exactMatchResolver = (redirect: IRedirect) => {\r\n const status: number = parseInt(redirect.status) || 307;\r\n if (status < 400) {\r\n const redirectUrl = sanitizeRedirectUrl(redirect.to, url);\r\n // console.log('>>>>>>>>>> findRoute exactMatchResolver', status, redirectUrl, redirect.to);\r\n throw { status, redirectUrl };\r\n } else {\r\n throw { status };\r\n }\r\n };\r\n // check exact match redirects\r\n const exactMatchRedirect = redirects.find(x => x.from === sanitizedHref);\r\n if (exactMatchRedirect !== undefined) {\r\n return exactMatchResolver(exactMatchRedirect);\r\n }\r\n // pattern match redirect resolver\r\n const patternMatchResolver = (redirect: IRedirect) => {\r\n const status: number = parseInt(redirect.status) || 307;\r\n if (status < 400) {\r\n const regexp = new RegExp(redirect.from);\r\n let i = 0;\r\n let redirectUrl = (redirect.to as string).replace(/\\*/g, (...rest) => {\r\n return `$${++i}`;\r\n });\r\n redirectUrl = sanitizedHref.replace(regexp, redirectUrl);\r\n redirectUrl = sanitizeRedirectUrl(redirectUrl, url) as string;\r\n // console.log('>>>>>>>>>> findRoute patternMatchResolver', status, redirectUrl, redirect.to);\r\n throw { status, redirectUrl };\r\n } else {\r\n throw { status };\r\n }\r\n };\r\n // check redirects by pattern\r\n redirects.forEach(x => {\r\n let expression = x.from;\r\n // wrapping catchall in (.*)\r\n expression = expression.replace(/\\*(?!\\))/g, '(.*)');\r\n // wrapping expression in ^...$\r\n expression = `^${expression.replace(/(^\\^)|(\\$$)/g, '')}$`;\r\n x.from = expression;\r\n });\r\n // check if a regex pattern redirect does match\r\n const firstMatch = redirects.find(x => sanitizedHref.match(x.from));\r\n // console.log('>>>>>>>>>> findRoute firstMatch', firstMatch);\r\n if (firstMatch) {\r\n return patternMatchResolver(firstMatch);\r\n }\r\n throw { status: 404 };\r\n }\r\n}\r\n","import { storeHandler } from '../store/store.handler';\r\nimport { getMockStore } from './mock.store';\r\n\r\nexport function mockHandler() {\r\n return storeHandler(async () => await getMockStore());\r\n}\r\n","import { NextApiRequest, NextApiResponse } from 'next';\r\nimport { apiHandler } from '../api/api.handler';\r\n\r\nexport function previewHandler() {\r\n return apiHandler({\r\n options: async (request: NextApiRequest, response: NextApiResponse) => {\r\n response.status(200).end();\r\n },\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n const query = request.query as {\r\n marketId: string;\r\n localeId: string;\r\n templateId: string;\r\n documentId: string;\r\n _status: 'published' | 'draft';\r\n _live: string;\r\n _token: string;\r\n };\r\n const { marketId, localeId, templateId, documentId, _status } = query;\r\n const destination = `/${marketId}/${localeId}/${templateId}/${documentId}${query._live ? '?_live=true' : ''}`;\r\n response.setPreviewData({ _status });\r\n response.setDraftMode({ enable: true });\r\n response.redirect(307, destination);\r\n },\r\n });\r\n}\r\n","import { getHost, getOrigin, isDevelopment, isProduction } from '@websolutespa/bom-core';\r\nimport { getLayout } from '@websolutespa/bom-mixer-models/server';\r\nimport { NextApiRequest, NextApiResponse } from 'next';\r\nimport { apiHandler } from '../api/api.handler';\r\n\r\nconst DISALLOWED_KEYS = [\r\n '__NEXT_PRIVATE_STANDALONE_CONFIG',\r\n];\r\n\r\nconst getFilteredEnv = (env: Record<string, unknown>): Record<string, unknown> => {\r\n const entries = Object.entries(env).filter(([k, v]) => (\r\n !DISALLOWED_KEYS.includes(k) && typeof v === 'string'\r\n ));\r\n entries.sort();\r\n return Object.fromEntries(entries);\r\n};\r\n\r\nconst sortKeys = (object?: Record<string, unknown>): Record<string, unknown> => {\r\n const entries = Object.entries(object || {});\r\n entries.sort();\r\n return Object.fromEntries(entries);\r\n};\r\n\r\nexport function varsHandler() {\r\n return apiHandler({\r\n options: async (request: NextApiRequest, response: NextApiResponse) => {\r\n response.status(200).end();\r\n },\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const defaultLocale = process.env.DEFAULT_LOCALE || 'it';\r\n const defaultMaket = process.env.DEFAULT_MARKET || 'ww';\r\n const { locale = defaultLocale, market = defaultMaket } = (request.query || {});\r\n const layout =\r\n typeof locale === 'string' && typeof market === 'string' ?\r\n await getLayout(market, locale) :\r\n undefined;\r\n const info = {\r\n production: isProduction,\r\n development: isDevelopment,\r\n origin: getOrigin(request.headers),\r\n host: getHost(request.headers),\r\n headers: { ...sortKeys(request.headers) },\r\n env: { ...getFilteredEnv(process.env) },\r\n layout: { ...sortKeys(layout) },\r\n };\r\n if (!(request.headers.accept?.includes('text/html'))) {\r\n return response.status(200).json(info);\r\n }\r\n response.status(200).send(`<!DOCTYPE html>\r\n<html>\r\n <head>\r\n <meta charSet=\"utf-8\"/>\r\n <style>\r\n html, body {\r\n background: #282828;\r\n color: #7cacf8;\r\n }\r\n body {\r\n padding: 0;\r\n margin: 0;\r\n font-family: monospace;\r\n font-size: 14px;\r\n line-height: 1.16;\r\n font-weight: 100;\r\n }\r\n json-viewer {\r\n padding: 20px;\r\n min-height: 100vh;\r\n box-sizing: border-box;\r\n line-height: 1.2;\r\n --background-color: #282828;\r\n --color: #e3e3e3;\r\n --property-color: #7cacf8;\r\n --string-color: #53d5fb;\r\n --number-color: #7b80ff;\r\n --boolean-color: #9980ff;\r\n --null-color: #6f6f6f;\r\n --preview-color: #8f8f8f;\r\n --highlight-color: #7b0000;\r\n --font-family: monospace;\r\n --font-size: 14px;\r\n --indent-size: 1em;\r\n --indentguide-size: 1px;\r\n --indentguide-style: solid;\r\n --indentguide-color: #333;\r\n --indentguide-color-active: #666;\r\n --indentguide: var(--indentguide-size) var(--indentguide-style) var(--indentguide-color);\r\n --indentguide-active: var(--indentguide-size) var(--indentguide-style) var(--indentguide-color-active);\r\n }\r\n\r\n :root {\r\n --scrollbar-size: 8px;\r\n --scrollbar-border-radius: 2px;\r\n --scrollbar-track-color: transparent;\r\n --scrollbar-track-hover-color: transparent;\r\n --scrollbar-thumb-color: #888888;\r\n --scrollbar-thumb-hover-color: #aaaaaa;\r\n }\r\n\r\n ::-webkit-scrollbar {\r\n width: var(--scrollbar-size);\r\n height: var(--scrollbar-size);\r\n border-radius: 0;\r\n }\r\n\r\n ::-webkit-scrollbar-button {\r\n display: none;\r\n }\r\n\r\n ::-webkit-scrollbar-button:hover {\r\n display: none;\r\n }\r\n\r\n ::-webkit-scrollbar-thumb {\r\n background-color: var(--scrollbar-thumb-color);\r\n box-shadow: none;\r\n border-radius: calc(var(--scrollbar-size) * 0.5);\r\n }\r\n\r\n ::-webkit-scrollbar-thumb:hover {\r\n background-color: var(--scrollbar-thumb-hover-color);\r\n }\r\n\r\n ::-webkit-scrollbar-track {\r\n background-color: var(--scrollbar-track-color);\r\n border-radius: 0;\r\n }\r\n\r\n ::-webkit-scrollbar-track:hover {\r\n background-color: var(--scrollbar-track-hover-color);\r\n }\r\n\r\n </style>\r\n <script src=\"https://unpkg.com/@alenaksu/json-viewer@2.0.0/dist/json-viewer.bundle.js\"></script>\r\n <script>\r\n document.addEventListener('DOMContentLoaded', () => {\r\n const viewer = document.querySelector('#json-viewer');\r\n viewer.data = ${JSON.stringify(info)};\r\n viewer.expand(/headers|env/);\r\n });\r\n </script>\r\n </head>\r\n <body>\r\n <json-viewer id=\"json-viewer\"></json-viewer>\r\n </body>\r\n</html>`);\r\n } catch (error) {\r\n response.status(400).json(error);\r\n }\r\n },\r\n });\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","names":["PAGES"],"sources":["../../src/api/api.handler.ts","../../src/mock/mock.service.ts","../../src/mock/mock-menu.service.ts","../../src/mock/mock-page.service.ts","../../src/session/session.service.ts","../../src/fs/fs.service.ts","../../src/static/static.service.ts","../../src/mock/mock.store.ts","../../src/cached/cached.service.ts","../../src/remote/remote.service.ts","../../src/remote/remote-menu.service.ts","../../src/remote/remote-page.service.ts","../../src/remote/remote.store.ts","../../src/store/store.service.ts","../../src/store/store.handler.ts","../../src/mock/mock.handler.ts","../../src/preview/preview.handler.ts","../../src/vars/vars.handler.ts"],"sourcesContent":["// Next.js API route support: https://nextjs.org/docs/api-routes/introduction\r\nimport type { NextApiRequest, NextApiResponse } from 'next';\r\n\r\nexport type ICrudMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | string;\r\n\r\nexport type IHandler<T = any> = (request: NextApiRequest, response: NextApiResponse<T>) => Promise<void | Response>;\r\n\r\nexport type IApiHandler = Record<ICrudMethod, IHandler>;\r\n\r\nexport function apiHandler<T = any>(handler: IApiHandler): IHandler<T> {\r\n return async (request: NextApiRequest, response: NextApiResponse<T>): Promise<void | Response> => {\r\n const method: ICrudMethod = (request.method || 'GET').toLowerCase() as ICrudMethod;\r\n // check handler supports HTTP method\r\n if (!handler[method]) {\r\n response.status(405).end(`Method ${method} Not Allowed`);\r\n return;\r\n }\r\n try {\r\n // global middleware\r\n // await jwtMiddleware(request, response);\r\n // route handler\r\n await handler[method](request, response);\r\n } catch (error) {\r\n // console.log('error', error);\r\n // global error handler\r\n return errorHandler(error, response);\r\n }\r\n };\r\n}\r\n\r\nexport function errorHandler(error: any, response: NextApiResponse): void | Response {\r\n if (typeof (error) === 'string') {\r\n // custom application error\r\n const is404 = error.toLowerCase().endsWith('not found');\r\n const statusCode = is404 ? 404 : 400;\r\n response.status(statusCode).json({ message: error });\r\n return;\r\n }\r\n if (error.name === 'UnauthorizedError') {\r\n // jwt authentication error\r\n response.status(401).json({ message: 'Invalid Token' });\r\n return;\r\n }\r\n // default to 500 server error\r\n console.error(error);\r\n response.status(500).json({ message: error.message });\r\n return;\r\n}\r\n","import { IEntity, IStore, JsonService, QueryParams } from '@websolutespa/bom-core';\r\nimport { DEFAULT_DEPTH, decorateLocalizable_, decorateLocalizables_, decorateRelation_, decorateRelations_ } from '../decorators';\r\n\r\nexport class MockService<T extends IEntity = IEntity> extends JsonService<T> {\r\n store: IStore;\r\n\r\n constructor(items: T[], store: IStore) {\r\n super(items);\r\n this.store = store;\r\n }\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n decoratedItem = await decorateRelation_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth != null ? params.depth : DEFAULT_DEPTH,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n decoratedItems = await decorateRelations_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth != null ? params.depth : DEFAULT_DEPTH,\r\n });\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { IEntity, IUndecoratedMenu, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateLocalizable_, decorateLocalizables_, decorateMenu_, decorateMenus_ } from '../decorators';\r\nimport { MockService } from './mock.service';\r\n\r\nexport class MockMenuService<T extends IEntity = IEntity> extends MockService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n if (params.market && params.locale) {\r\n decoratedItem = await decorateMenu_(store, decoratedItem as any as IUndecoratedMenu, {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any;\r\n }\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n if (params.market && params.locale) {\r\n decoratedItems = await decorateMenus_(store, decoratedItems as any as IUndecoratedMenu[], {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any[];\r\n }\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { ICategorized, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateCategories_, decorateCategory_, decorateHref_, decorateHrefs_, decorateLocalizable_, decorateLocalizables_, decorateNav_, decorateRelation_, decorateRelations_ } from '../decorators';\r\nimport { MockService } from './mock.service';\r\n\r\nexport class MockPageService<T extends ICategorized = ICategorized> extends MockService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateCategory_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n decoratedItem = await decorateLocalizable_<B>(decoratedItem, params);\r\n decoratedItem = await decorateHref_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateNav_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateRelation_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n // decoratedItems = await optimizeMany_(decoratedItems);\r\n decoratedItems = await decorateCategories_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n decoratedItems = await decorateLocalizables_<B>(decoratedItems, params);\r\n decoratedItems = await decorateHrefs_<B>(store, decoratedItems, params);\r\n decoratedItems = await decorateRelations_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n // !!! optimization for index listing removing components\r\n const invalidKeys = ['components'];\r\n decoratedItems = decoratedItems.map(x => Object.fromEntries(\r\n Object.entries(x).filter(\r\n ([k, v]) => !invalidKeys.includes(k)\r\n )\r\n )) as B[];\r\n /*\r\n decoratedItems = await decorateNavs_<B>(store, decoratedItems, params);\r\n */\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { isDevelopment } from '@websolutespa/bom-core';\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { getIronSession, IronSession, SessionOptions } from 'iron-session';\nimport { NextApiRequest, NextApiResponse } from 'next';\nimport { NextRequest, NextResponse } from 'next/server.js';\n\nexport async function getSession<T extends object = Record<string, any>>(\n request: NextRequest | NextApiRequest | IncomingMessage & {\n cookies: Partial<{ [key: string]: string }>;\n },\n response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>\n): Promise<IronSession<T>> {\n const COOKIE_NAME = 'websolutespa-next-js';\n const COOKIE_PASSWORD = process.env.MIXER_SECRET as string || 'MIXER_SECRET_32_CHARS_LONG_12345';\n const sessionOptions: SessionOptions = {\n cookieName: COOKIE_NAME,\n // secure: true should be used in production (HTTPS) but can't be used in development (HTTP)\n cookieOptions: {\n secure: !isDevelopment,\n },\n password: COOKIE_PASSWORD,\n };\n const session = await getIronSession<T>(request, response, sessionOptions);\n return session;\n}\n\nexport type SessionWithToken<T extends object = Record<string, any>> = T & Partial<{\n token: string;\n exp: number;\n}>;\n\nexport async function getSessionToken<T extends SessionWithToken>(\n request: NextRequest | NextApiRequest | IncomingMessage & {\n cookies: Partial<{ [key: string]: string }>;\n },\n response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>\n): Promise<string | undefined> {\n const session = await getSession<T>(request, response);\n if (session.token && session.exp && session.exp > Math.floor(new Date().getTime() / 1000)) {\n return session.token;\n } else {\n session.token = undefined;\n session.exp = undefined;\n await session.save();\n session.destroy();\n return;\n }\n}\n","import { Json } from '@websolutespa/bom-core';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\n\r\nexport type fsReadOptions = {\r\n encoding?: null | undefined;\r\n flag?: number | string | undefined;\r\n signal?: AbortSignal | undefined;\r\n};\r\n\r\nexport type fsWatchOptions = (fs.WatchOptions & { encoding: 'buffer' }) | 'buffer';\r\nexport type fsWatchFileOptions = (fs.WatchFileOptions & { bigint?: false });\r\nexport type fsWriteFileOptions = (fs.ObjectEncodingOptions & { mode?: fs.Mode; flag?: fs.OpenMode }) | BufferEncoding | null;\r\n\r\nexport async function fsExists(pathname: string): Promise<boolean> {\r\n try {\r\n await fs.promises.access(pathname);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\nexport async function fsRead(pathname: string, option?: fsReadOptions): Promise<string | undefined> {\r\n try {\r\n const data = await fs.promises.readFile(pathname, option?.encoding || 'utf8');\r\n return data || undefined;\r\n } catch (error) {\r\n console.log('fsRead.error', error, pathname);\r\n return;\r\n }\r\n}\r\n\r\nexport async function fsReadJson<T = Json>(pathname: string): Promise<T | undefined> {\r\n try {\r\n const data = await fsRead(pathname);\r\n // console.log('fsReadJson', 'pathname', pathname, 'data', data);\r\n if (data) {\r\n return JSON.parse(data) as T;\r\n } else {\r\n return;\r\n }\r\n } catch (error) {\r\n console.log('fsReadJson.error', error, pathname);\r\n // throw (error);\r\n return;\r\n }\r\n}\r\n\r\nexport async function fsWrite(pathname: string, data: string, encoding: fsWriteFileOptions = 'utf8'): Promise<void> {\r\n try {\r\n await fs.promises.mkdir(path.dirname(pathname), { recursive: true });\r\n await fs.promises.writeFile(pathname, data, encoding);\r\n } catch (error) {\r\n console.log('fsWrite', error, pathname);\r\n }\r\n}\r\n\r\nexport async function fsWriteJson(pathname: string, data: Json): Promise<void> {\r\n try {\r\n await fsWrite(pathname, JSON.stringify(data, null, 2));\r\n } catch (error) {\r\n console.log('fsWriteJson', error, pathname);\r\n }\r\n}\r\n\r\nexport async function fsExistOrCreateFolder(pathname: string): Promise<void> {\r\n try {\r\n const exists = fs.existsSync(pathname);\r\n if (!exists) {\r\n await fs.promises.mkdir(pathname);\r\n }\r\n } catch (error) {\r\n console.log('fsExistOrCreateFolder', error, pathname);\r\n }\r\n}\r\n\r\nexport async function fsReadDirectory(pathname: string, extension?: string): Promise<{ name: string, data: string }[]> {\r\n const files = await fs.promises.readdir(pathname);\r\n const items = files.flatMap(async (file: string) => {\r\n const filePath = path.join(pathname, file);\r\n const stat = await fs.promises.stat(filePath);\r\n if (stat.isDirectory()) {\r\n return;\r\n }\r\n if (extension && path.parse(file).ext !== extension) {\r\n return;\r\n }\r\n const data = await fs.promises.readFile(filePath);\r\n if (!data) {\r\n return;\r\n }\r\n return {\r\n name: path.parse(file).name,\r\n data: data.toString(),\r\n };\r\n }) as Promise<{ name: string, data: string }>[];\r\n const items2 = (await Promise.all(items)).filter(Boolean);\r\n return items2;\r\n}\r\n\r\nexport async function fsReadJsonDirectory(pathname: string): Promise<{ name: string, data: any }[]> {\r\n try {\r\n const datas = await fsReadDirectory(pathname, '.json');\r\n return datas.filter(Boolean).map(x => {\r\n x.data = JSON.parse(x.data);\r\n return x;\r\n });\r\n } catch (error) {\r\n console.log('fsReadJsonDirectory', error, pathname);\r\n // throw (error);\r\n return [];\r\n }\r\n}\r\n\r\nexport function fsWatch(pathname: string, callback: (eventType: fs.WatchEventType, fileName: string) => void, options: fsWatchOptions) {\r\n const debounced = debounce(callback);\r\n return fs.watch(pathname, options, debounced);\r\n}\r\n\r\nexport function fsWatchFile(pathname: string, callback: (eventType: fs.WatchEventType, fileName: string) => void, options?: fsWatchFileOptions) {\r\n const debounced = debounce(callback);\r\n return fs.watchFile(pathname, options, debounced);\r\n}\r\n\r\nexport function pathJoin(...paths: string[]): string {\r\n return path.join(process.cwd(), ...paths);\r\n}\r\n\r\nexport function debounce(callback: (...args: any[]) => void, timeout: number = 300) {\r\n let timer: NodeJS.Timeout;\r\n return (...args: any[]) => {\r\n clearTimeout(timer);\r\n timer = setTimeout(() => {\r\n callback(...args);\r\n }, timeout);\r\n };\r\n}\r\n","import { IEntity, IEquatable, IStore, JsonService, QueryParams, httpGet } from '@websolutespa/bom-core';\r\nimport { DEFAULT_DEPTH, decorateLocalizable_, decorateLocalizables_ } from '../decorators';\r\nimport { IField, MapTo, StaticServiceConfig } from './static';\r\n\r\nexport class StaticService<T extends IEntity = IEntity> extends JsonService<T> {\r\n loaded: boolean = false;\r\n config: StaticServiceConfig<T>;\r\n store: IStore;\r\n\r\n constructor(config: StaticServiceConfig<T>, store: IStore) {\r\n super([]);\r\n this.config = config;\r\n this.store = store;\r\n }\r\n\r\n protected mapItems_<B extends T = T>(items: B[], map?: MapTo): B[] {\r\n if (typeof map === 'object') {\r\n items = items.map(x => {\r\n const item: Partial<B> = {};\r\n Object.entries(x).forEach(([k, v]) => {\r\n const key = map[k] || k;\r\n (item as any)[key] = v;\r\n });\r\n return item as B;\r\n });\r\n }\r\n return items;\r\n }\r\n\r\n async getItems<B extends T = T>(): Promise<B[]> {\r\n let items = this.items;\r\n if (this.loaded && items) {\r\n return items as B[];\r\n } else {\r\n const config = this.config;\r\n const src = config.src;\r\n if (typeof src === 'string') {\r\n const response = await httpGet(src, {});\r\n items = response.items;\r\n } else if (typeof src === 'function') {\r\n if (src instanceof Promise) {\r\n items = await src(config);\r\n } else {\r\n items = src(config) as T[];\r\n }\r\n } else {\r\n items = src;\r\n }\r\n items = this.mapItems_(items, config.map);\r\n this.items = items;\r\n this.loaded = true;\r\n return items as B[];\r\n }\r\n }\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n decoratedItem = await decorateStaticRelation_<B>(store, this.config.fields, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n decoratedItems = await decorateStaticRelations_<B>(store, this.config.fields, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n\r\nexport async function decorateStaticRelation_<T>(store: IStore, fields: IField[], item: T, params: QueryParams = {}): Promise<T> {\r\n const depth = params.depth != null ? params.depth : DEFAULT_DEPTH;\r\n if (depth === 0) {\r\n return item;\r\n }\r\n const decoratedItem = { ...item };\r\n for (const field of fields) {\r\n if (\r\n field.type === 'relationship' &&\r\n typeof field.relationTo === 'string'\r\n ) {\r\n const name = field.name as keyof typeof item;\r\n const value = item[name];\r\n const ids = field.hasMany ? value as IEquatable[] : [value];\r\n const items = await store[field.relationTo]!.findMany({\r\n where: {\r\n id: { in: ids },\r\n },\r\n depth: depth - 1,\r\n locale: params.locale,\r\n });\r\n if (field.hasMany) {\r\n decoratedItem[name] = items as any; // !!!\r\n } else {\r\n decoratedItem[name] = (items.length ? items[0] : undefined) as any; // !!!\r\n }\r\n }\r\n }\r\n return decoratedItem;\r\n}\r\n\r\nexport async function decorateStaticRelations_<T>(store: IStore, fields: IField[], items: T[], params: QueryParams = {}): Promise<T[]> {\r\n const decoratedItems: T[] = [];\r\n for (const item of items) {\r\n const decoratedItem = await decorateStaticRelation_<T>(store, fields, item, params) as T;\r\n decoratedItems.push(decoratedItem);\r\n }\r\n return decoratedItems;\r\n}\r\n","import { ICategorized, IEntity, IMemoryStore, IQuerable, isDevelopment, IStore } from '@websolutespa/bom-core';\r\nimport { fromCache } from '../cache/cache.service';\r\nimport { fsReadJson } from '../fs/fs.service';\r\nimport { staticCollections } from '../static/static';\r\nimport { StaticService } from '../static/static.service';\r\nimport { MockMenuService } from './mock-menu.service';\r\nimport { MockPageService } from './mock-page.service';\r\nimport { MockService } from './mock.service';\r\n\r\nconst PAGES: string[] = process.env.PAGES ? process.env.PAGES.split(',') : [];\r\n\r\nexport function getMockStoreFromData_<T extends IStore>(data?: IMemoryStore): T {\r\n const store: IStore = {};\r\n if (data) {\r\n for (const key in data) {\r\n if (!PAGES.includes(key)) {\r\n // new collection\r\n const service = new MockService(data[key]!, store);\r\n store[key] = service;\r\n }\r\n }\r\n const category = store.category;\r\n if (!category) {\r\n console.warn('mock category not found');\r\n }\r\n const route = store.route;\r\n if (!route) {\r\n console.warn('mock route not found');\r\n }\r\n if (category && route) {\r\n const menu = data.menu;\r\n if (menu) {\r\n store.menu = new MockMenuService(menu, store);\r\n }\r\n for (const key in data) {\r\n if (PAGES.includes(key)) {\r\n const service = new MockPageService(data[key] as ICategorized[], store) as IQuerable<IEntity>;\r\n store[key] = service;\r\n }\r\n }\r\n }\r\n } else {\r\n console.warn('mock store not found');\r\n }\r\n staticCollections.forEach(config => {\r\n store[config.slug] = new StaticService(config, store);\r\n });\r\n return store as T;\r\n}\r\n\r\nasync function getStoreData_(): Promise<IMemoryStore> {\r\n if (isDevelopment) {\r\n\r\n const path = require('path');\r\n const directory = 'cwd' in process && typeof process.cwd === 'function' ? process.cwd() : process.env.CWD;\r\n // console.log('directory', directory);\r\n const pathname = path.join(directory, '.mixer', 'store.json');\r\n return (await fsReadJson<IMemoryStore>(pathname) || {});\r\n } else {\r\n const store = process.env.STORE_ || {};\r\n return (typeof store === 'string' ? JSON.parse(store) : store) as IMemoryStore;\r\n }\r\n}\r\n\r\nasync function getMockStore_<T extends IStore = IStore>(): Promise<T> {\r\n const data = await getStoreData_();\r\n const store: IStore = getMockStoreFromData_(data);\r\n return store as T;\r\n}\r\n\r\nexport async function getMockStore<T extends IStore = IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n if (!isDevelopment) {\r\n console.warn('getMockStore');\r\n }\r\n const store = await getMockStore_<T>();\r\n return store;\r\n }, isDevelopment ? undefined : 'STORE_MOCK_');\r\n}\r\n","import { GlobalQueryParams, IEntity, IEquatable, IPagedResult, IQuerable, JsonService, QueryParams, qsSerialize } from '@websolutespa/bom-core';\r\nimport { StoreStrategy, storeCacheDuration, storeStrategy } from '../store/store';\r\n\r\nexport type ICacheResult<T extends IEntity = IEntity> = T | T[] | IPagedResult<T> | undefined;\r\n\r\nexport type ICache<T extends IEntity = IEntity> = {\r\n timestamp: number;\r\n data: ICacheResult<T>;\r\n};\r\n\r\nexport type InMemoryCacheOptions = {\r\n duration?: number | false;\r\n};\r\n\r\nexport class CachedService<T extends IEntity = IEntity> extends JsonService<T> {\r\n\r\n private service_: IQuerable<T>;\r\n private cache_: Map<string, ICache<T>> = new Map<string, ICache<T>>();\r\n private duration_: number | false = storeCacheDuration;\r\n\r\n constructor(service: IQuerable<T>, options: InMemoryCacheOptions = {}) {\r\n super([]);\r\n if (!service) {\r\n throw new Error('CachedService: service is required');\r\n }\r\n this.service_ = service;\r\n this.duration_ = typeof options.duration !== 'undefined' ? options.duration : this.duration_;\r\n }\r\n\r\n private async fromCache_(callback: () => Promise<ICacheResult<T>>, key?: string): Promise<ICacheResult<T>> {\r\n if (key && this.hasKey_(key)) {\r\n return this.getKey_(key);\r\n } else {\r\n const t = await callback();\r\n if (key && t) {\r\n this.setKey_(key, t);\r\n }\r\n return t;\r\n }\r\n }\r\n\r\n private isExpired_(item: ICache<T>): boolean {\r\n return this.duration_ === false ? true : item.timestamp < (new Date().getTime() - this.duration_);\r\n }\r\n\r\n private hasKey_(key: string): boolean {\r\n const item = this.cache_.get(key);\r\n return item ? (!this.isExpired_(item)) : false;\r\n }\r\n\r\n private getKey_(key: string): ICacheResult<T> {\r\n return this.cache_.get(key)!.data;\r\n }\r\n\r\n private setKey_(key: string, data: ICacheResult<T>) {\r\n this.cache_.set(key, {\r\n timestamp: new Date().getTime(),\r\n data,\r\n });\r\n }\r\n\r\n async findGlobal<B extends T = T>(params: GlobalQueryParams = {}): Promise<B | undefined> {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findGlobal?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findGlobal(params);\r\n }, key) as B | undefined;\r\n }\r\n\r\n async findOne<B extends T = T>(id: IEquatable, params: QueryParams = {}): Promise<B | undefined> {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findOne[${id}]?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findOne(id, params);\r\n }, key) as B | undefined;\r\n }\r\n\r\n async findMany<B extends T = T>(params: QueryParams = {}): Promise<B[]> {\r\n if (storeStrategy === StoreStrategy.DecoratedApi) {\r\n const { market, locale, ...otherParams } = params;\r\n const serializedParams = qsSerialize({ market, locale });\r\n const key = `findMany?${serializedParams}`;\r\n let items = await this.fromCache_(async () => {\r\n return await this.service_.findMany({ market, locale });\r\n }, key) as B[];\r\n if (otherParams.where) {\r\n items = this.whereItems_(items, otherParams.where);\r\n }\r\n if (otherParams.sort) {\r\n items = this.sortCollection_(items, otherParams.sort);\r\n }\r\n return items;\r\n } else {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findMany?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findMany(params);\r\n }, key) as B[];\r\n }\r\n }\r\n\r\n async findPaged<B extends T = T>(params: QueryParams = {}): Promise<IPagedResult<B>> {\r\n const serializedParams = qsSerialize(params);\r\n const key = `findPaged?${serializedParams}`;\r\n return await this.fromCache_(async () => {\r\n return await this.service_.findPaged(params);\r\n }, key) as IPagedResult<B>;\r\n }\r\n\r\n async create<B extends T = T>(payload: Omit<B, 'id'>): Promise<B> {\r\n return await this.service_.create(payload);\r\n }\r\n\r\n async updateOne<B extends T = T>(id: IEquatable, payload: Partial<B>, params: Omit<QueryParams, 'where'> = {}): Promise<B> {\r\n return await this.service_.updateOne(id, payload, params);\r\n }\r\n\r\n async updateMany<B extends T = T>(payload: Partial<B>, params: QueryParams = {}): Promise<B[]> {\r\n return await this.service_.updateMany(payload, params);\r\n }\r\n\r\n async deleteOne<B extends T = T>(id: IEquatable, params: Omit<QueryParams, 'where'> = {}): Promise<B | undefined> {\r\n return await this.service_.deleteOne(id, params);\r\n }\r\n\r\n async deleteMany<B extends T = T>(params: QueryParams = {}): Promise<B[]> {\r\n return await this.service_.deleteMany(params);\r\n }\r\n\r\n // !!! old\r\n\r\n async update<B extends T = T>(payload: Partial<B>): Promise<B> {\r\n return await this.service_.update(payload);\r\n }\r\n\r\n async delete<B extends T = T>(id: IEquatable): Promise<B | undefined> {\r\n return await this.service_.delete(id);\r\n }\r\n\r\n}\r\n","import { GlobalQueryParams, IEntity, IEquatable, IPagedResult, IQuerable, IStore, qsSerialize, QueryParams } from '@websolutespa/bom-core';\r\nimport { getRemoteApiRequestOptions } from '../api/api';\r\nimport { decorateSchema_, decorateSchemas_ } from '../decorators';\r\nimport { remoteApiDelete, remoteApiGet, remoteApiPatch, remoteApiPost } from './remote.api';\r\n\r\n/**\r\n * This service handle call to remote api (bowl or over)\r\n */\r\n\r\nexport class RemoteService<T extends IEntity = IEntity> implements IQuerable<T> {\r\n key: string;\r\n store: IStore;\r\n\r\n constructor(key: string, store: IStore) {\r\n if (!key) {\r\n throw new Error('RemoteService: key is required');\r\n }\r\n if (!store) {\r\n throw new Error('RemoteService: store is required');\r\n }\r\n this.key = key;\r\n this.store = store;\r\n }\r\n\r\n async findGlobal<B extends T = T>(params: GlobalQueryParams = {}, token?: string): Promise<B | undefined> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/globals/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'findGlobal', url);\r\n try {\r\n const item = await remoteApiGet<B>(url, getRemoteApiRequestOptions(token));\r\n if (item) {\r\n return await this.decorateOne_(item, params);\r\n }\r\n } catch (error) {\r\n console.log('RemoteService', this.key, 'findGlobal.error');\r\n }\r\n return;\r\n }\r\n\r\n async findOne<B extends T = T>(id: IEquatable, params: Omit<QueryParams, 'where'> = {}, token?: string): Promise<B | undefined> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}/${encodeURIComponent(id)}${search}`;\r\n // console.log('RemoteService', this.key, 'findOne', url);\r\n try {\r\n console.log('remoteApiGet', url);\r\n const item = await remoteApiGet<B>(url, getRemoteApiRequestOptions(token));\r\n if (item) {\r\n return await this.decorateOne_(item, params);\r\n }\r\n } catch (error) {\r\n console.log('RemoteService', this.key, 'findOne.error');\r\n }\r\n return;\r\n }\r\n\r\n async findMany<B extends T = T>(params: QueryParams = {}, token?: string): Promise<B[]> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'findMany', url);\r\n const response = await remoteApiGet<B[] | { docs: B[] }>(url, getRemoteApiRequestOptions(token));\r\n const items: B[] = Array.isArray(response) ? response : response.docs;\r\n return await this.decorateMany_(items, params);\r\n }\r\n\r\n async findPaged<B extends T = T>(params: QueryParams = {}, token?: string): Promise<IPagedResult<B>> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = true;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'findPaged', url);\r\n const response = await remoteApiGet<IPagedResult<B>>(url, getRemoteApiRequestOptions(token));\r\n let items: B[] = response.docs;\r\n items = await this.decorateMany_(items, params);\r\n return { ...response, docs: items };\r\n }\r\n\r\n async create<B extends T = T>(payload: Omit<B, 'id'>, params: QueryParams = {}, token?: string): Promise<B> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'create', url);\r\n const result = await remoteApiPost<B | { doc: B }>(url, payload, getRemoteApiRequestOptions(token));\r\n const item = (result.doc ? result.doc : result) as B;\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async updateOne<B extends T = T>(id: IEquatable, payload: Partial<B>, params: Omit<QueryParams, 'where'> = {}, token?: string): Promise<B> {\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}/${encodeURIComponent(id)}${search}`;\r\n // console.log('RemoteService', this.key, 'updateOne', url);\r\n const result = await remoteApiPatch<B | { doc: B }>(url, payload, getRemoteApiRequestOptions(token));\r\n const item = (result.doc ? result.doc : result) as B;\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async updateMany<B extends T = T>(payload: Partial<B>, params: QueryParams = {}, token?: string): Promise<B[]> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'updateMany', url);\r\n const response = await remoteApiPatch<B[] | { docs: B[] }>(url, payload, getRemoteApiRequestOptions(token));\r\n const items: B[] = Array.isArray(response) ? response : response.docs;\r\n return await this.decorateMany_(items, params);\r\n }\r\n\r\n async deleteOne<B extends T = T>(id: IEquatable, params: Omit<QueryParams, 'where'> = {}, token?: string): Promise<B | undefined> {\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}/${encodeURIComponent(id)}${search}`;\r\n // console.log('RemoteService', this.key, 'deleteOne', url);\r\n const item = await remoteApiDelete<B>(url, getRemoteApiRequestOptions(token));\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async deleteMany<B extends T = T>(params: QueryParams = {}, token?: string): Promise<B[]> {\r\n params.richText = String(params.richText) === 'true' ? true : false;\r\n params.pagination = false;\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'deleteMany', url);\r\n const response = await remoteApiDelete<B[] | { docs: B[] }>(url, getRemoteApiRequestOptions(token));\r\n const items: B[] = Array.isArray(response) ? response : response.docs;\r\n return await this.decorateMany_(items, params);\r\n }\r\n\r\n // !!! old\r\n\r\n async update<B extends T = T>(payload: Partial<B>, params: QueryParams = {}, token?: string): Promise<B> {\r\n const url = `/${this.key}`;\r\n // console.log('RemoteService', this.key, 'update', url);\r\n const result = await remoteApiPatch<B | { doc: B }>(url, payload, getRemoteApiRequestOptions(token));\r\n const item = (result.doc ? result.doc : result) as B;\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n async delete<B extends T = T>(id: IEquatable, token?: string): Promise<B | undefined> {\r\n const params = {\r\n where: {\r\n id: {\r\n equals: id,\r\n },\r\n },\r\n };\r\n const query = qsSerialize(params);\r\n const search = query ? `?${query}` : '';\r\n const url = `/${this.key}${search}`;\r\n // console.log('RemoteService', this.key, 'delete', url);\r\n const item = await remoteApiDelete<B>(url, getRemoteApiRequestOptions(token));\r\n return await this.decorateOne_(item, {});\r\n }\r\n\r\n protected async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n let decoratedItem = item;\r\n decoratedItem = await decorateSchema_<B>(this.store, decoratedItem, this.key);\r\n return decoratedItem;\r\n }\r\n\r\n protected async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n let decoratedItems = items;\r\n decoratedItems = await decorateSchemas_<B>(this.store, decoratedItems, this.key);\r\n return decoratedItems;\r\n }\r\n}\r\n","import { IEntity, IUndecoratedMenu, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateLocalizable_, decorateLocalizables_, decorateMenu_, decorateMenus_, decorateSchema_, decorateSchemas_ } from '../decorators';\r\nimport { RemoteService } from './remote.service';\r\n\r\nexport class RemoteMenuService<T extends IEntity = IEntity> extends RemoteService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateSchema_<B>(store, decoratedItem, this.key);\r\n decoratedItem = await decorateLocalizable_<B>(item, params);\r\n if (params.market && params.locale) {\r\n decoratedItem = await decorateMenu_(store, decoratedItem as IUndecoratedMenu, {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any;\r\n }\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n decoratedItems = await decorateSchemas_<B>(store, decoratedItems, this.key);\r\n decoratedItems = await decorateLocalizables_<B>(items, params);\r\n if (params.market && params.locale) {\r\n decoratedItems = await decorateMenus_(store, decoratedItems as IUndecoratedMenu[], {\r\n market: params.market,\r\n locale: params.locale,\r\n }) as any[];\r\n }\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { ICategorized, QueryParams } from '@websolutespa/bom-core';\r\nimport { decorateCategories_, decorateCategory_, decorateHref_, decorateHrefs_, decorateNav_, decorateRelation_, decorateSchema_, decorateSchemas_ } from '../decorators';\r\nimport { RemoteService } from './remote.service';\r\n\r\nexport class RemotePageService<T extends ICategorized = ICategorized> extends RemoteService<T> {\r\n\r\n protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {\r\n const store = this.store;\r\n let decoratedItem = item;\r\n decoratedItem = await decorateSchema_<B>(store, decoratedItem, this.key);\r\n decoratedItem = await decorateCategory_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateHref_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateNav_<B>(store, decoratedItem, params);\r\n decoratedItem = await decorateRelation_<B>(store, decoratedItem, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n return decoratedItem;\r\n }\r\n\r\n protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {\r\n const store = this.store;\r\n let decoratedItems = items;\r\n // decoratedItems = await optimizeMany_<B>(decoratedItems);\r\n decoratedItems = await decorateSchemas_<B>(store, decoratedItems, this.key);\r\n decoratedItems = await decorateCategories_<B>(store, decoratedItems, params);\r\n decoratedItems = await decorateHrefs_<B>(store, decoratedItems, params);\r\n /*\r\n // !!! optimization for index listing\r\n decoratedItems = await decorateNavs_<B>(store, decoratedItems, params);\r\n decoratedItems = await decorateRelations_<B>(store, decoratedItems, {\r\n market: params.market,\r\n locale: params.locale,\r\n depth: params.depth,\r\n });\r\n */\r\n return decoratedItems;\r\n }\r\n\r\n}\r\n","import { IMemoryStore, IQuerable, IStore } from '@websolutespa/bom-core';\r\nimport { fromCache } from '../cache/cache.service';\r\nimport { CachedService } from '../cached/cached.service';\r\nimport { getMockStore, getMockStoreFromData_ } from '../mock/mock.store';\r\nimport { COLLECTIONS, KNOWN_COLLECTIONS, MOCK_COLLECTIONS, NextPhase, StoreStrategy, buildStrategy, nextPhase, storeStrategy } from '../store/store';\r\nimport { RemoteMenuService } from './remote-menu.service';\r\nimport { RemotePageService } from './remote-page.service';\r\nimport { remoteApiGet } from './remote.api';\r\nimport { RemoteService } from './remote.service';\r\n\r\nconst PAGES: string[] = process.env.PAGES ? process.env.PAGES.split(',') : [];\r\n\r\nasync function getRemoteStore_<T extends IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n // console.log('getRemoteStore_');\r\n if (COLLECTIONS.length === 0) {\r\n console.error('RemoteStore.getRemoteStore_ no collections');\r\n throw ('RemoteStore.getRemoteStore_ no collections');\r\n }\r\n let mockStore: IStore = {};\r\n if (MOCK_COLLECTIONS.length > 0) {\r\n mockStore = await getMockStore<T>();\r\n }\r\n const store: IStore = {};\r\n COLLECTIONS.sort();\r\n COLLECTIONS.forEach(key => {\r\n if (MOCK_COLLECTIONS.includes(key)) {\r\n store[key] = mockStore[key]!;\r\n } else if (PAGES.includes(key) && storeStrategy === StoreStrategy.DecoratedApi) {\r\n store[key] = new RemotePageService(key, store) as IQuerable;\r\n } else if (key === 'menu' && storeStrategy === StoreStrategy.DecoratedApi) {\r\n store[key] = new CachedService(\r\n new RemoteMenuService(key, store)\r\n );\r\n } else if (KNOWN_COLLECTIONS.includes(key)) {\r\n store[key] = new CachedService(\r\n new RemoteService(key, store)\r\n );\r\n } else {\r\n store[key] = new RemoteService(key, store);\r\n }\r\n });\r\n return store as T;\r\n }, 'STORE_RUNTIME_');\r\n // }, isDevelopment ? undefined : 'STORE_RUNTIME_');\r\n}\r\n\r\nasync function getRemoteStoreStaticBuild_<T extends IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n console.log('getRemoteStoreStaticBuild_');\r\n let store: IStore = {};\r\n const url = `/store?strategy=${buildStrategy}`;\r\n const json = await remoteApiGet<IMemoryStore>(url);\r\n if (json != null) {\r\n const data = Object.fromEntries(\r\n COLLECTIONS.map(key => ([key, json[key] || []]))\r\n );\r\n store = getMockStoreFromData_(data);\r\n }\r\n return store as T;\r\n }, 'STORE_BUILD_');\r\n}\r\n\r\nasync function getRemoteStoreRuntimeBuild_<T extends IStore>(): Promise<T> {\r\n return fromCache<T>(async () => {\r\n console.log('getRemoteStoreRuntimeBuild_');\r\n const data: IMemoryStore = {};\r\n COLLECTIONS.forEach(key => {\r\n data[key] = [];\r\n });\r\n const store = getMockStoreFromData_(data);\r\n return store as T;\r\n }, 'STORE_RUNTIME_BUILD_');\r\n}\r\n\r\nexport async function getRemoteStore<T extends IStore>(): Promise<T> {\r\n if (nextPhase === NextPhase.ProductionBuild) {\r\n /*\r\n if (buildStrategy === BuildStrategy.Runtime) {\r\n return await getRemoteStoreRuntimeBuild_();\r\n } else {\r\n return await getRemoteStoreStaticBuild_();\r\n }\r\n */\r\n return await getRemoteStoreStaticBuild_();\r\n } else {\r\n return await getRemoteStore_();\r\n }\r\n}\r\n","import { IStore } from '@websolutespa/bom-core';\r\n// import { getLocalStore } from '../local_/local.store';\r\nimport { getMockStore } from '../mock/mock.store';\r\nimport { getRemoteStore } from '../remote/remote.store';\r\nimport { StoreStrategy, storeStrategy } from './store';\r\n\r\nexport async function getStore<T extends IStore>(): Promise<T> {\r\n // console.trace('getStore', storeStrategy);\r\n switch (storeStrategy) {\r\n case StoreStrategy.Mock:\r\n return await getMockStore<T>();\r\n /*\r\n case StoreStrategy.Local:\r\n return await getLocalStore<T>();\r\n */\r\n default:\r\n return await getRemoteStore<T>();\r\n }\r\n}\r\n","import { IEntity, IRedirect, IRoute, IStore, asEquatable, defaultLocale, defaultMarket, qsDeserialize } from '@websolutespa/bom-core';\r\nimport { NextApiRequest, NextApiResponse } from 'next';\r\nimport { apiHandler } from '../api/api.handler';\r\nimport { getSessionToken } from '../session/session.service';\r\nimport { COLLECTIONS } from './store';\r\nimport { getStore } from './store.service';\r\n\r\n/*\r\n* storeHandler contains generic local api CRUD handlers of next app\r\n* it could serve both api or mock data\r\n*/\r\n\r\nexport function storeHandler<T extends IStore>(storePromise: () => Promise<T> = async () => await getStore<T>()) {\r\n return apiHandler({\r\n get: collectionGet(storePromise),\r\n post: collectionPost(storePromise),\r\n patch: collectionPatch(storePromise),\r\n delete: collectionDelete(storePromise),\r\n });\r\n}\r\n\r\n// find a specific document by id or\r\n// find paginated documents\r\nexport function collectionGet<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n // console.log('query', query);\r\n if (!Array.isArray(collections) || collections?.length === 0) {\r\n console.error('storeHandler.collectionGet', 'method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n // console.log('params', params);\r\n if (!COLLECTIONS.includes(collection)) {\r\n console.error('storeHandler.collectionGet', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('storeHandler.collectionGet', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const token = await getSessionToken(request, response);\r\n // find a specific document by id\r\n if (id) {\r\n // console.log('storeHandler.collectionGet', collection, id, params);\r\n const item = await service.findOne(asEquatable(id), params, token);\r\n if (item) {\r\n return response.status(200).json(item);\r\n } else {\r\n return response.status(404).send('not found');\r\n }\r\n // find paginated documents\r\n } else if (String(params.pagination) === 'true') {\r\n // console.log('storeHandler.collectionGet', collection, params);\r\n const pagination = await service.findPaged(params, token);\r\n return response.status(200).json(pagination);\r\n } else {\r\n // console.log('storeHandler.collectionGet', collection, params);\r\n const items = await service.findMany(params, token);\r\n return response.status(200).json(items);\r\n }\r\n } catch (error: any) {\r\n console.error('storeHandler.collectionGet.error', '500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// create a new document + post route\r\nexport function collectionPost<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n if (!Array.isArray(collections) || collections.length !== 1) {\r\n console.error('method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n if (id) {\r\n console.error('method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n // api post route pathname\r\n if (collection === 'route') {\r\n return routePost(store, request, response);\r\n }\r\n const token = await getSessionToken(request, response);\r\n const payload: Omit<IEntity, 'id'> = request.body;\r\n const item = await service.create(payload, params, token);\r\n return response.status(200).json(item);\r\n } catch (error: any) {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// update a document by id\r\nexport function collectionPatch<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n // console.log('query', query);\r\n if (!Array.isArray(collections) || collections?.length === 0) {\r\n console.error('storeHandler.collectionUpdate', 'method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n if (!Array.isArray(collections) || collections.length !== 2) {\r\n console.error('method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n // console.log('params', params);\r\n if (!COLLECTIONS.includes(collection)) {\r\n console.error('storeHandler.collectionUpdate', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('storeHandler.collectionUpdate', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const token = await getSessionToken(request, response);\r\n const payload: Partial<IEntity> = request.body;\r\n // update a specific document by id\r\n if (id) {\r\n // console.log('storeHandler.collectionUpdate', collection, id, params);\r\n const item = await service.updateOne(asEquatable(id), payload, params, token);\r\n if (item) {\r\n return response.status(200).json(item);\r\n } else {\r\n return response.status(404).send('not found');\r\n }\r\n } else {\r\n // console.log('storeHandler.collectionUpdate', collection, params);\r\n const items = await service.updateMany(payload, params, token);\r\n return response.status(200).json(items);\r\n }\r\n } catch (error: any) {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// delete an existing document by id\r\nexport function collectionDelete<T extends IStore>(storePromise: () => Promise<T>) {\r\n return async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const { collections, ...query } = request.query;\r\n // console.log('query', query);\r\n if (!Array.isArray(collections) || collections?.length === 0) {\r\n console.error('storeHandler.collectionDelete', 'method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n if (!Array.isArray(collections) || collections.length !== 2) {\r\n console.error('method not allowed', collections);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const collection: string = collections[0]!;\r\n const id: string = collections[1]!;\r\n const qs = Object.entries(query).map(kv => kv.join('=')).join('&');\r\n const params = qsDeserialize(qs);\r\n // console.log('params', params);\r\n if (!COLLECTIONS.includes(collection)) {\r\n console.error('storeHandler.collectionDelete', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const store = await storePromise();\r\n const service = store[collection];\r\n if (!service) {\r\n console.error('storeHandler.collectionDelete', 'method not allowed', collection);\r\n return response.status(405).send('method not allowed');\r\n }\r\n const token = await getSessionToken(request, response);\r\n // delete a specific document by id\r\n if (id) {\r\n // console.log('storeHandler.collectionDelete', collection, id, params);\r\n const item = await service.deleteOne(asEquatable(id), params, token);\r\n if (item) {\r\n return response.status(200).json(item);\r\n } else {\r\n return response.status(404).send('not found');\r\n }\r\n } else {\r\n // console.log('storeHandler.collectionDelete', collection, params);\r\n const items = await service.deleteMany(params, token);\r\n return response.status(200).json(items);\r\n }\r\n } catch (error: any) {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n// route post resolver\r\n// !!! todo, should be moved to models -> route service\r\nexport async function routePost(store: IStore, request: NextApiRequest, response: NextApiResponse) {\r\n try {\r\n const { pathname, href, hrefBeforeRedirect } = request.body;\r\n console.log('routePost', pathname, href, hrefBeforeRedirect);\r\n if (pathname && href) {\r\n const route = await findRoute(store, pathname, href, hrefBeforeRedirect);\r\n return response.status(200).json(route);\r\n } else {\r\n return response.status(400).send('bad request');\r\n }\r\n } catch (error: any) {\r\n if (error.status < 500) {\r\n if (error.status < 400) {\r\n return response.status(error.status).send({\r\n redirectUrl: error.redirectUrl,\r\n });\r\n }\r\n if (error.status === 404) {\r\n return response.status(404).send('not found');\r\n } else {\r\n return response.status(error.status).send('gone');\r\n }\r\n } else {\r\n console.error('storeHandler.error.500', error);\r\n throw error;\r\n }\r\n }\r\n}\r\n\r\n/*\r\n* storeIndexHandler contains a list of all the generic local api CRUD handlers of next app\r\n*/\r\nexport function storeIndexHandler() {\r\n return apiHandler({\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const market = defaultMarket;\r\n const locale = defaultLocale;\r\n if (!Array.isArray(COLLECTIONS) || COLLECTIONS?.length === 0) {\r\n console.error('apiHome.collectionGet', 'collections not found', COLLECTIONS);\r\n return response.status(405).send('method not allowed');\r\n }\r\n COLLECTIONS.sort((a, b) => a.localeCompare(b));\r\n response.setHeader('Content-Type', 'text/html; charset=UTF-8');\r\n response.setHeader('X-Frame-Options', 'SAMEORIGIN');\r\n response.setHeader('X-Robots-Tag', 'noindex, follow');\r\n return response.status(200).send(`\r\n <html>\r\n <head>\r\n <title>REST Api</title>\r\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n <style type=\"text/css\">\r\n html {\r\n padding: 0;\r\n background: #09090A;\r\n color: #fefefe;\r\n }\r\n body {\r\n padding: 15px;\r\n font-family: Inter,-apple-system,'Helvetica Neue','Segoe UI',Roboto,Arial,'Noto Sans',sans-serif;\r\n font-size: 13px;\r\n background: #09090A;\r\n color: #fefefe;\r\n }\r\n .container {\r\n margin: 0 auto;\r\n max-width: 1280px;\r\n }\r\n a {\r\n color: #fefefe;\r\n text-decoration: none;\r\n }\r\n a:visited {\r\n color: #9e9e9e;\r\n }\r\n a:hover {\r\n text-decoration: underline;\r\n }\r\n table {\r\n width: 100%;\r\n margin: 10px 0;\r\n border: none;\r\n border-collapse: collapse;\r\n }\r\n td,\r\n th {\r\n text-align: left;\r\n font-size: 12px;\r\n padding: 3px;\r\n }\r\n th:not(:last-child),\r\n td:not(:last-child) {\r\n padding-right: 30px;\r\n }\r\n thead th {\r\n border-bottom: 1px solid #0e0e0f;\r\n }\r\n #table tr:nth-child(odd) td {\r\n background-color: #0e0e0f;\r\n }\r\n #table tbody tr:hover td {\r\n background-color: #1A1A1B;\r\n }\r\n #table tbody tr:hover td,\r\n #table tbody tr:hover td a {\r\n color: #fefefe;\r\n }\r\n .wysiwyg {\r\n margin: 0;\r\n padding: 0 0 10px 0;\r\n line-height: 1.2em;\r\n }\r\n .wysiwyg a {\r\n color: #00ea4e;\r\n font-weight: 600;\r\n }\r\n .wysiwyg a:visited {\r\n color: #00ea4e;\r\n }\r\n </style>\r\n </head>\r\n <body>\r\n <div class=\"container\">\r\n <h1>REST API</h1>\r\n <p class=\"wysiwyg\">\r\n This is the REST Api generated by <a href=\"https://github.com/websolutespa/bom\" target=\"_blank\" rel=\"noopener\">Mixer</a>.\r\n </p>\r\n <p class=\"wysiwyg\">\r\n It contains a list of all the generic local api CRUD handlers of Mixer app. <br />\r\n All methods should be called with market and locale parameters.\r\n </p>\r\n <table id=\"table\" cellpadding=\"3\">\r\n <thead>\r\n <tr>\r\n <th width=\"150px\">Method</th>\r\n <th width=\"150px\">Collection</th>\r\n <th>Endpoint</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n ${COLLECTIONS.map(c => (`\r\n <tr>\r\n <td>GET, POST, PATCH</td>\r\n <td>${c}</td>\r\n <td><a href=\"/api/${c}?market=${market}&locale=${locale}\">/api/${c}</a></td>\r\n </tr>\r\n <tr>\r\n <td>GET, DELETE</td>\r\n <td>${c}</td>\r\n <td><a href=\"/api/${c}/{id}?market=${market}&locale=${locale}\">/api/${c}/{id}</a></td>\r\n </tr>\r\n `)).join('\\n')}\r\n\t\t\t\t\t </tbody>\r\n </table>\r\n </div>\r\n </body>\r\n `);\r\n } catch (error: any) {\r\n console.error('storeHandler.collectionGet.error', '500', error);\r\n throw error;\r\n }\r\n },\r\n });\r\n}\r\n\r\nconst trimTerminalSlash = (path: string) => {\r\n return path.replace(/\\/+$/, '');\r\n};\r\n\r\nconst sanitizeHref = (url: URL, urlBeforeRedirect: URL | null) => {\r\n if (urlBeforeRedirect) {\r\n const sanitizedUrl = new URL(urlBeforeRedirect);\r\n sanitizedUrl.protocol = url.protocol;\r\n return trimTerminalSlash(sanitizedUrl.href);\r\n } else {\r\n return trimTerminalSlash(url.href);\r\n }\r\n};\r\n\r\nconst sanitizeRedirectFrom = (path: string, url: URL, urlBeforeRedirect: URL | null) => {\r\n path = trimTerminalSlash(path);\r\n if (urlBeforeRedirect) {\r\n return path.replace(/^(\\^?)((https?:\\/\\/)+)/, `$1${url.protocol}//`);\r\n } else {\r\n return path.replace(/^(\\^?)((https?:\\/\\/)+(\\w|\\.|:)+)/, `$1${url.origin}`);\r\n }\r\n};\r\n\r\nconst sanitizeRedirectTo = (path?: string) => {\r\n return path ? trimTerminalSlash(path) : undefined;\r\n};\r\n\r\nconst sanitizeRedirectUrl = (path: string | undefined, url: URL) => {\r\n if (path != null) {\r\n path = trimTerminalSlash(path);\r\n return path.match(/^((https?:\\/\\/)+)/) ? path : `${url.origin}${path}`;\r\n } else {\r\n return path;\r\n }\r\n};\r\n\r\nexport async function findRoute(store: IStore, pathname: string, href: string, hrefBeforeRedirect: string | null): Promise<IRoute | undefined> {\r\n // here we use findMany because route id would break api url\r\n const routes = await store.route!.findMany<IRoute>({\r\n where: {\r\n id: {\r\n equals: pathname,\r\n },\r\n },\r\n });\r\n // console.log('routes', routes);\r\n // console.log('pathname', pathname);\r\n if (routes.length > 0) {\r\n return routes[0];\r\n } else {\r\n // exact route match not found\r\n // let's try if match splat routes\r\n const splatRoutes = await store.route!.findMany<IRoute>({\r\n where: {\r\n useSplat: {\r\n equals: true,\r\n },\r\n },\r\n });\r\n // find best match splat route\r\n const splatRoute = splatRoutes.reduce((p: (IRoute & { splat: string }) | undefined, c: IRoute) => {\r\n const match = pathname.indexOf(c.id) === 0;\r\n if (match && (!p || p.id.length < c.id.length)) {\r\n return { ...c, splat: pathname.substring(c.id.length, pathname.length) };\r\n } else {\r\n return p;\r\n }\r\n }, undefined);\r\n if (splatRoute) {\r\n return splatRoute;\r\n }\r\n const url = new URL(href);\r\n const urlBeforeRedirect = hrefBeforeRedirect ? new URL(hrefBeforeRedirect) : null;\r\n const sanitizedHref = sanitizeHref(url, urlBeforeRedirect);\r\n /*\r\n console.log('href', url.href);\r\n console.log('hrefBeforeRedirect', urlBeforeRedirect?.href);\r\n console.log('sanitizedHref', sanitizedHref);\r\n */\r\n // collect redirects\r\n let redirects = await store.redirect!.findMany<IRedirect>({\r\n where: {\r\n isActive: {\r\n equals: true,\r\n },\r\n },\r\n });\r\n redirects = redirects.map(x => ({\r\n ...x,\r\n from: sanitizeRedirectFrom(x.from, url, urlBeforeRedirect),\r\n to: sanitizeRedirectTo(x.to),\r\n }));\r\n // redirects.sort((a, b) => a.order - b.order);\r\n // console.log('findRoute redirects', redirects);\r\n // exact match redirect resolver\r\n const exactMatchResolver = (redirect: IRedirect) => {\r\n const status: number = parseInt(redirect.status) || 307;\r\n if (status < 400) {\r\n const redirectUrl = sanitizeRedirectUrl(redirect.to, url);\r\n // console.log('>>>>>>>>>> findRoute exactMatchResolver', status, redirectUrl, redirect.to);\r\n throw { status, redirectUrl };\r\n } else {\r\n throw { status };\r\n }\r\n };\r\n // check exact match redirects\r\n const exactMatchRedirect = redirects.find(x => x.from === sanitizedHref);\r\n if (exactMatchRedirect !== undefined) {\r\n return exactMatchResolver(exactMatchRedirect);\r\n }\r\n // pattern match redirect resolver\r\n const patternMatchResolver = (redirect: IRedirect) => {\r\n const status: number = parseInt(redirect.status) || 307;\r\n if (status < 400) {\r\n const regexp = new RegExp(redirect.from);\r\n let i = 0;\r\n let redirectUrl = (redirect.to as string).replace(/\\*/g, (...rest) => {\r\n return `$${++i}`;\r\n });\r\n redirectUrl = sanitizedHref.replace(regexp, redirectUrl);\r\n redirectUrl = sanitizeRedirectUrl(redirectUrl, url) as string;\r\n // console.log('>>>>>>>>>> findRoute patternMatchResolver', status, redirectUrl, redirect.to);\r\n throw { status, redirectUrl };\r\n } else {\r\n throw { status };\r\n }\r\n };\r\n // check redirects by pattern\r\n redirects.forEach(x => {\r\n let expression = x.from;\r\n // wrapping catchall in (.*)\r\n expression = expression.replace(/\\*(?!\\))/g, '(.*)');\r\n // wrapping expression in ^...$\r\n expression = `^${expression.replace(/(^\\^)|(\\$$)/g, '')}$`;\r\n x.from = expression;\r\n });\r\n // check if a regex pattern redirect does match\r\n const firstMatch = redirects.find(x => sanitizedHref.match(x.from));\r\n // console.log('>>>>>>>>>> findRoute firstMatch', firstMatch);\r\n if (firstMatch) {\r\n return patternMatchResolver(firstMatch);\r\n }\r\n throw { status: 404 };\r\n }\r\n}\r\n","import { storeHandler } from '../store/store.handler';\r\nimport { getMockStore } from './mock.store';\r\n\r\nexport function mockHandler() {\r\n return storeHandler(async () => await getMockStore());\r\n}\r\n","import { NextApiRequest, NextApiResponse } from 'next';\r\nimport { apiHandler } from '../api/api.handler';\r\n\r\nexport function previewHandler() {\r\n return apiHandler({\r\n options: async (request: NextApiRequest, response: NextApiResponse) => {\r\n response.status(200).end();\r\n },\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n const query = request.query as {\r\n marketId: string;\r\n localeId: string;\r\n templateId: string;\r\n documentId: string;\r\n _status: 'published' | 'draft';\r\n _live: string;\r\n _token: string;\r\n };\r\n const { marketId, localeId, templateId, documentId, _status } = query;\r\n const destination = `/${marketId}/${localeId}/${templateId}/${documentId}${query._live ? '?_live=true' : ''}`;\r\n response.setPreviewData({ _status });\r\n response.setDraftMode({ enable: true });\r\n response.redirect(307, destination);\r\n },\r\n });\r\n}\r\n","import { getHost, getOrigin, isDevelopment, isProduction } from '@websolutespa/bom-core';\r\nimport { getLayout } from '@websolutespa/bom-mixer-models/server';\r\nimport { NextApiRequest, NextApiResponse } from 'next';\r\nimport { apiHandler } from '../api/api.handler';\r\n\r\nconst DISALLOWED_KEYS = [\r\n '__NEXT_PRIVATE_STANDALONE_CONFIG',\r\n];\r\n\r\nconst getFilteredEnv = (env: Record<string, unknown>): Record<string, unknown> => {\r\n const entries = Object.entries(env).filter(([k, v]) => (\r\n !DISALLOWED_KEYS.includes(k) && typeof v === 'string'\r\n ));\r\n entries.sort();\r\n return Object.fromEntries(entries);\r\n};\r\n\r\nconst sortKeys = (object?: Record<string, unknown>): Record<string, unknown> => {\r\n const entries = Object.entries(object || {});\r\n entries.sort();\r\n return Object.fromEntries(entries);\r\n};\r\n\r\nexport function varsHandler() {\r\n return apiHandler({\r\n options: async (request: NextApiRequest, response: NextApiResponse) => {\r\n response.status(200).end();\r\n },\r\n get: async (request: NextApiRequest, response: NextApiResponse) => {\r\n try {\r\n const defaultLocale = process.env.DEFAULT_LOCALE || 'it';\r\n const defaultMaket = process.env.DEFAULT_MARKET || 'ww';\r\n const { locale = defaultLocale, market = defaultMaket } = (request.query || {});\r\n const layout =\r\n typeof locale === 'string' && typeof market === 'string' ?\r\n await getLayout(market, locale) :\r\n undefined;\r\n const info = {\r\n production: isProduction,\r\n development: isDevelopment,\r\n origin: getOrigin(request.headers),\r\n host: getHost(request.headers),\r\n headers: { ...sortKeys(request.headers) },\r\n env: { ...getFilteredEnv(process.env) },\r\n layout: { ...sortKeys(layout) },\r\n };\r\n if (!(request.headers.accept?.includes('text/html'))) {\r\n return response.status(200).json(info);\r\n }\r\n response.status(200).send(`<!DOCTYPE html>\r\n<html>\r\n <head>\r\n <meta charSet=\"utf-8\"/>\r\n <style>\r\n html, body {\r\n background: #282828;\r\n color: #7cacf8;\r\n }\r\n body {\r\n padding: 0;\r\n margin: 0;\r\n font-family: monospace;\r\n font-size: 14px;\r\n line-height: 1.16;\r\n font-weight: 100;\r\n }\r\n json-viewer {\r\n padding: 20px;\r\n min-height: 100vh;\r\n box-sizing: border-box;\r\n line-height: 1.2;\r\n --background-color: #282828;\r\n --color: #e3e3e3;\r\n --property-color: #7cacf8;\r\n --string-color: #53d5fb;\r\n --number-color: #7b80ff;\r\n --boolean-color: #9980ff;\r\n --null-color: #6f6f6f;\r\n --preview-color: #8f8f8f;\r\n --highlight-color: #7b0000;\r\n --font-family: monospace;\r\n --font-size: 14px;\r\n --indent-size: 1em;\r\n --indentguide-size: 1px;\r\n --indentguide-style: solid;\r\n --indentguide-color: #333;\r\n --indentguide-color-active: #666;\r\n --indentguide: var(--indentguide-size) var(--indentguide-style) var(--indentguide-color);\r\n --indentguide-active: var(--indentguide-size) var(--indentguide-style) var(--indentguide-color-active);\r\n }\r\n\r\n :root {\r\n --scrollbar-size: 8px;\r\n --scrollbar-border-radius: 2px;\r\n --scrollbar-track-color: transparent;\r\n --scrollbar-track-hover-color: transparent;\r\n --scrollbar-thumb-color: #888888;\r\n --scrollbar-thumb-hover-color: #aaaaaa;\r\n }\r\n\r\n ::-webkit-scrollbar {\r\n width: var(--scrollbar-size);\r\n height: var(--scrollbar-size);\r\n border-radius: 0;\r\n }\r\n\r\n ::-webkit-scrollbar-button {\r\n display: none;\r\n }\r\n\r\n ::-webkit-scrollbar-button:hover {\r\n display: none;\r\n }\r\n\r\n ::-webkit-scrollbar-thumb {\r\n background-color: var(--scrollbar-thumb-color);\r\n box-shadow: none;\r\n border-radius: calc(var(--scrollbar-size) * 0.5);\r\n }\r\n\r\n ::-webkit-scrollbar-thumb:hover {\r\n background-color: var(--scrollbar-thumb-hover-color);\r\n }\r\n\r\n ::-webkit-scrollbar-track {\r\n background-color: var(--scrollbar-track-color);\r\n border-radius: 0;\r\n }\r\n\r\n ::-webkit-scrollbar-track:hover {\r\n background-color: var(--scrollbar-track-hover-color);\r\n }\r\n\r\n </style>\r\n <script src=\"https://unpkg.com/@alenaksu/json-viewer@2.0.0/dist/json-viewer.bundle.js\"></script>\r\n <script>\r\n document.addEventListener('DOMContentLoaded', () => {\r\n const viewer = document.querySelector('#json-viewer');\r\n viewer.data = ${JSON.stringify(info)};\r\n viewer.expand(/headers|env/);\r\n });\r\n </script>\r\n </head>\r\n <body>\r\n <json-viewer id=\"json-viewer\"></json-viewer>\r\n </body>\r\n</html>`);\r\n } catch (error) {\r\n response.status(400).json(error);\r\n }\r\n },\r\n });\r\n}\r\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@websolutespa/bom-mixer-store",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "Mixer Store module of the BOM Repository",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bom",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@vitest/coverage-istanbul": "5.0.5",
|
|
38
|
-
"@websolutespa/bom-cli": "^3.1.
|
|
39
|
-
"@websolutespa/bom-mixer-models": "^3.1.
|
|
40
|
-
"@websolutespa/eslint-config": "^3.1.
|
|
41
|
-
"@websolutespa/test": "^3.1.
|
|
42
|
-
"@websolutespa/tsconfig": "^3.1.
|
|
38
|
+
"@websolutespa/bom-cli": "^3.1.2",
|
|
39
|
+
"@websolutespa/bom-mixer-models": "^3.1.2",
|
|
40
|
+
"@websolutespa/eslint-config": "^3.1.2",
|
|
41
|
+
"@websolutespa/test": "^3.1.2",
|
|
42
|
+
"@websolutespa/tsconfig": "^3.1.2",
|
|
43
43
|
"eslint": "10.13.0",
|
|
44
44
|
"npm-run-all": "4.1.5",
|
|
45
45
|
"payload": "3.91.0",
|
|
@@ -97,7 +97,6 @@
|
|
|
97
97
|
},
|
|
98
98
|
"types": "./dist/index.d.ts",
|
|
99
99
|
"main": "./dist/index.js",
|
|
100
|
-
"source": "./src/index.ts",
|
|
101
100
|
"exports": {
|
|
102
101
|
".": {
|
|
103
102
|
"import": "./dist/index.js",
|
|
@@ -114,5 +113,6 @@
|
|
|
114
113
|
"types": "./dist/exports/server.d.ts",
|
|
115
114
|
"default": "./dist/exports/server.js"
|
|
116
115
|
}
|
|
117
|
-
}
|
|
116
|
+
},
|
|
117
|
+
"source": "./src/index.ts"
|
|
118
118
|
}
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
-
import { Socket } from 'net';
|
|
3
|
-
import type { NextRequest } from 'next/server';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import { isStaticRequest } from '../proxy/proxy.service';
|
|
6
|
-
import { getSession, getSessionToken } from '../session/session.service';
|
|
7
|
-
|
|
8
|
-
function createRequest(cookie?: string) {
|
|
9
|
-
const request = new IncomingMessage(new Socket()) as IncomingMessage & { cookies: Record<string, string> };
|
|
10
|
-
if (cookie) {
|
|
11
|
-
request.headers.cookie = cookie;
|
|
12
|
-
}
|
|
13
|
-
request.cookies = {};
|
|
14
|
-
return request;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function getCookie(response: ServerResponse) {
|
|
18
|
-
const header = response.getHeader('set-cookie');
|
|
19
|
-
const value = Array.isArray(header) ? header[0] : String(header);
|
|
20
|
-
return value.split(';')[0];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe('session (iron-session)', () => {
|
|
24
|
-
it('seals the session in a cookie and reads it back on the next request', async () => {
|
|
25
|
-
const response = new ServerResponse(createRequest());
|
|
26
|
-
const session = await getSession<{ token?: string; exp?: number }>(createRequest(), response);
|
|
27
|
-
session.token = 'token';
|
|
28
|
-
session.exp = Math.floor(Date.now() / 1000) + 60;
|
|
29
|
-
await session.save();
|
|
30
|
-
|
|
31
|
-
const cookie = getCookie(response);
|
|
32
|
-
expect(cookie.startsWith('websolutespa-next-js=')).toBe(true);
|
|
33
|
-
const token = await getSessionToken(createRequest(cookie), new ServerResponse(createRequest()));
|
|
34
|
-
expect(token).toBe('token');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('clears an expired token', async () => {
|
|
38
|
-
const response = new ServerResponse(createRequest());
|
|
39
|
-
const session = await getSession<{ token?: string; exp?: number }>(createRequest(), response);
|
|
40
|
-
session.token = 'token';
|
|
41
|
-
session.exp = Math.floor(Date.now() / 1000) - 60;
|
|
42
|
-
await session.save();
|
|
43
|
-
|
|
44
|
-
const token = await getSessionToken(createRequest(getCookie(response)), new ServerResponse(createRequest()));
|
|
45
|
-
expect(token).toBeUndefined();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('isStaticRequest (mime)', () => {
|
|
50
|
-
const request = (pathname: string) => ({ nextUrl: { pathname } }) as NextRequest;
|
|
51
|
-
|
|
52
|
-
it('detects files by extension', () => {
|
|
53
|
-
expect(isStaticRequest(request('/images/logo.png'))).toBe(true);
|
|
54
|
-
expect(isStaticRequest(request('/fonts/font.woff2'))).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('ignores routes without a known extension', () => {
|
|
58
|
-
expect(isStaticRequest(request('/en/products'))).toBe(false);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
2
|
+
import { Socket } from 'net';
|
|
3
|
+
import type { NextRequest } from 'next/server';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { isStaticRequest } from '../proxy/proxy.service';
|
|
6
|
+
import { getSession, getSessionToken } from '../session/session.service';
|
|
7
|
+
|
|
8
|
+
function createRequest(cookie?: string) {
|
|
9
|
+
const request = new IncomingMessage(new Socket()) as IncomingMessage & { cookies: Record<string, string> };
|
|
10
|
+
if (cookie) {
|
|
11
|
+
request.headers.cookie = cookie;
|
|
12
|
+
}
|
|
13
|
+
request.cookies = {};
|
|
14
|
+
return request;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getCookie(response: ServerResponse) {
|
|
18
|
+
const header = response.getHeader('set-cookie');
|
|
19
|
+
const value = Array.isArray(header) ? header[0] : String(header);
|
|
20
|
+
return value.split(';')[0];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('session (iron-session)', () => {
|
|
24
|
+
it('seals the session in a cookie and reads it back on the next request', async () => {
|
|
25
|
+
const response = new ServerResponse(createRequest());
|
|
26
|
+
const session = await getSession<{ token?: string; exp?: number }>(createRequest(), response);
|
|
27
|
+
session.token = 'token';
|
|
28
|
+
session.exp = Math.floor(Date.now() / 1000) + 60;
|
|
29
|
+
await session.save();
|
|
30
|
+
|
|
31
|
+
const cookie = getCookie(response);
|
|
32
|
+
expect(cookie.startsWith('websolutespa-next-js=')).toBe(true);
|
|
33
|
+
const token = await getSessionToken(createRequest(cookie), new ServerResponse(createRequest()));
|
|
34
|
+
expect(token).toBe('token');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('clears an expired token', async () => {
|
|
38
|
+
const response = new ServerResponse(createRequest());
|
|
39
|
+
const session = await getSession<{ token?: string; exp?: number }>(createRequest(), response);
|
|
40
|
+
session.token = 'token';
|
|
41
|
+
session.exp = Math.floor(Date.now() / 1000) - 60;
|
|
42
|
+
await session.save();
|
|
43
|
+
|
|
44
|
+
const token = await getSessionToken(createRequest(getCookie(response)), new ServerResponse(createRequest()));
|
|
45
|
+
expect(token).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('isStaticRequest (mime)', () => {
|
|
50
|
+
const request = (pathname: string) => ({ nextUrl: { pathname } }) as NextRequest;
|
|
51
|
+
|
|
52
|
+
it('detects files by extension', () => {
|
|
53
|
+
expect(isStaticRequest(request('/images/logo.png'))).toBe(true);
|
|
54
|
+
expect(isStaticRequest(request('/fonts/font.woff2'))).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('ignores routes without a known extension', () => {
|
|
58
|
+
expect(isStaticRequest(request('/en/products'))).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import { isDevelopment } from '@websolutespa/bom-core';
|
|
2
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
-
import { getIronSession, IronSession, SessionOptions } from 'iron-session';
|
|
4
|
-
import { NextApiRequest, NextApiResponse } from 'next';
|
|
5
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
6
|
-
|
|
7
|
-
export async function getSession<T extends object = Record<string, any>>(
|
|
8
|
-
request: NextRequest | NextApiRequest | IncomingMessage & {
|
|
9
|
-
cookies: Partial<{ [key: string]: string }>;
|
|
10
|
-
},
|
|
11
|
-
response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>
|
|
12
|
-
): Promise<IronSession<T>> {
|
|
13
|
-
const COOKIE_NAME = 'websolutespa-next-js';
|
|
14
|
-
const COOKIE_PASSWORD = process.env.MIXER_SECRET as string || 'MIXER_SECRET_32_CHARS_LONG_12345';
|
|
15
|
-
const sessionOptions: SessionOptions = {
|
|
16
|
-
cookieName: COOKIE_NAME,
|
|
17
|
-
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
18
|
-
cookieOptions: {
|
|
19
|
-
secure: !isDevelopment,
|
|
20
|
-
},
|
|
21
|
-
password: COOKIE_PASSWORD,
|
|
22
|
-
};
|
|
23
|
-
const session = await getIronSession<T>(request, response, sessionOptions);
|
|
24
|
-
return session;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type SessionWithToken<T extends object = Record<string, any>> = T & Partial<{
|
|
28
|
-
token: string;
|
|
29
|
-
exp: number;
|
|
30
|
-
}>;
|
|
31
|
-
|
|
32
|
-
export async function getSessionToken<T extends SessionWithToken>(
|
|
33
|
-
request: NextRequest | NextApiRequest | IncomingMessage & {
|
|
34
|
-
cookies: Partial<{ [key: string]: string }>;
|
|
35
|
-
},
|
|
36
|
-
response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>
|
|
37
|
-
): Promise<string | undefined> {
|
|
38
|
-
const session = await getSession<T>(request, response);
|
|
39
|
-
if (session.token && session.exp && session.exp > Math.floor(new Date().getTime() / 1000)) {
|
|
40
|
-
return session.token;
|
|
41
|
-
} else {
|
|
42
|
-
session.token = undefined;
|
|
43
|
-
session.exp = undefined;
|
|
44
|
-
await session.save();
|
|
45
|
-
session.destroy();
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
import { isDevelopment } from '@websolutespa/bom-core';
|
|
2
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
+
import { getIronSession, IronSession, SessionOptions } from 'iron-session';
|
|
4
|
+
import { NextApiRequest, NextApiResponse } from 'next';
|
|
5
|
+
import { NextRequest, NextResponse } from 'next/server.js';
|
|
6
|
+
|
|
7
|
+
export async function getSession<T extends object = Record<string, any>>(
|
|
8
|
+
request: NextRequest | NextApiRequest | IncomingMessage & {
|
|
9
|
+
cookies: Partial<{ [key: string]: string }>;
|
|
10
|
+
},
|
|
11
|
+
response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>
|
|
12
|
+
): Promise<IronSession<T>> {
|
|
13
|
+
const COOKIE_NAME = 'websolutespa-next-js';
|
|
14
|
+
const COOKIE_PASSWORD = process.env.MIXER_SECRET as string || 'MIXER_SECRET_32_CHARS_LONG_12345';
|
|
15
|
+
const sessionOptions: SessionOptions = {
|
|
16
|
+
cookieName: COOKIE_NAME,
|
|
17
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
18
|
+
cookieOptions: {
|
|
19
|
+
secure: !isDevelopment,
|
|
20
|
+
},
|
|
21
|
+
password: COOKIE_PASSWORD,
|
|
22
|
+
};
|
|
23
|
+
const session = await getIronSession<T>(request, response, sessionOptions);
|
|
24
|
+
return session;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type SessionWithToken<T extends object = Record<string, any>> = T & Partial<{
|
|
28
|
+
token: string;
|
|
29
|
+
exp: number;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
export async function getSessionToken<T extends SessionWithToken>(
|
|
33
|
+
request: NextRequest | NextApiRequest | IncomingMessage & {
|
|
34
|
+
cookies: Partial<{ [key: string]: string }>;
|
|
35
|
+
},
|
|
36
|
+
response: NextResponse | NextApiResponse | ServerResponse<IncomingMessage>
|
|
37
|
+
): Promise<string | undefined> {
|
|
38
|
+
const session = await getSession<T>(request, response);
|
|
39
|
+
if (session.token && session.exp && session.exp > Math.floor(new Date().getTime() / 1000)) {
|
|
40
|
+
return session.token;
|
|
41
|
+
} else {
|
|
42
|
+
session.token = undefined;
|
|
43
|
+
session.exp = undefined;
|
|
44
|
+
await session.save();
|
|
45
|
+
session.destroy();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|