Add coexistence checks to all enqueue methods to prevent loading both React and Grid.js assets simultaneously. Changes: - ReactAdmin.php: Only enqueue React assets when ?react=1 - Init.php: Skip Grid.js when React active on admin pages - Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0 - Customer.php, Product.php, License.php: Add coexistence checks Now the toggle between Classic and React versions works correctly. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
140 lines
4.4 KiB
JavaScript
140 lines
4.4 KiB
JavaScript
const DOMAIN_IN_URL_REGEX = /:\/\/(\S*?)(:\d+)?(\/|$)/
|
|
const DOMAIN_CHARACTERS = /([a-z0-9.-]+\.[a-z0-9]+|localhost)/i
|
|
const IP_REGEX = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
|
|
const ROOT_DOMAIN_REGEX = /[^.]+\.([^.]+|(gov|com|co|ne)\.\w{2})$/i
|
|
|
|
function getDomainFromOriginOrURL(originOrURL) {
|
|
if (typeof originOrURL !== 'string') return null
|
|
if (originOrURL.length > 10000 || originOrURL.startsWith('data:')) return null
|
|
if (DOMAIN_IN_URL_REGEX.test(originOrURL)) return originOrURL.match(DOMAIN_IN_URL_REGEX)[1]
|
|
if (DOMAIN_CHARACTERS.test(originOrURL)) return originOrURL.match(DOMAIN_CHARACTERS)[0]
|
|
return null
|
|
}
|
|
|
|
function getRootDomain(originOrURL) {
|
|
const domain = getDomainFromOriginOrURL(originOrURL)
|
|
if (!domain) return null
|
|
if (IP_REGEX.test(domain)) return domain
|
|
const match = domain.match(ROOT_DOMAIN_REGEX)
|
|
return (match && match[0]) || domain
|
|
}
|
|
|
|
function sliceSubdomainFromDomain(domain, rootDomain) {
|
|
if (domain.length <= rootDomain.length) return domain
|
|
return domain
|
|
.split('.')
|
|
.slice(1)
|
|
.join('.')
|
|
}
|
|
|
|
function getEntityInDataset(entityByDomain, entityBySubDomain, entityByRootDomain, originOrURL) {
|
|
const domain = getDomainFromOriginOrURL(originOrURL)
|
|
const rootDomain = getRootDomain(domain)
|
|
if (!domain || !rootDomain) return undefined
|
|
if (entityByDomain.has(domain)) return entityByDomain.get(domain)
|
|
|
|
for (
|
|
let subdomain = domain;
|
|
subdomain.length > rootDomain.length;
|
|
subdomain = sliceSubdomainFromDomain(subdomain, rootDomain)
|
|
) {
|
|
if (entityBySubDomain.has(subdomain)) return entityBySubDomain.get(subdomain)
|
|
}
|
|
|
|
if (entityByRootDomain.has(rootDomain)) return entityByRootDomain.get(rootDomain)
|
|
return undefined
|
|
}
|
|
|
|
function getProductInDataset(entityByDomain, entityBySubDomain, entityByRootDomain, originOrURL) {
|
|
const entity = getEntityInDataset(
|
|
entityByDomain,
|
|
entityBySubDomain,
|
|
entityByRootDomain,
|
|
originOrURL
|
|
)
|
|
const products = entity && entity.products
|
|
if (!products) return undefined
|
|
if (typeof originOrURL !== 'string') return undefined
|
|
|
|
for (const product of products) {
|
|
for (const pattern of product.urlPatterns) {
|
|
if (pattern instanceof RegExp && pattern.test(originOrURL)) return product
|
|
if (typeof pattern === 'string' && originOrURL.includes(pattern)) return product
|
|
}
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
function cloneEntities(entities) {
|
|
return entities.map(entity_ => {
|
|
const entity = {
|
|
company: entity_.name,
|
|
categories: [entity_.category],
|
|
...entity_,
|
|
}
|
|
|
|
const products = (entity_.products || []).map(product => ({
|
|
company: entity.company,
|
|
category: entity.category,
|
|
categories: [entity.category],
|
|
facades: [],
|
|
...product,
|
|
urlPatterns: (product.urlPatterns || []).map(s =>
|
|
s.startsWith('REGEXP:') ? new RegExp(s.slice('REGEXP:'.length)) : s
|
|
),
|
|
}))
|
|
|
|
entity.products = products
|
|
return entity
|
|
})
|
|
}
|
|
|
|
function createAPIFromDataset(entities_) {
|
|
const entities = cloneEntities(entities_)
|
|
const entityByDomain = new Map()
|
|
const entityByRootDomain = new Map()
|
|
const entityBySubDomain = new Map()
|
|
|
|
for (const entity of entities) {
|
|
entity.totalExecutionTime = Number(entity.totalExecutionTime) || 0
|
|
entity.totalOccurrences = Number(entity.totalOccurrences) || 0
|
|
entity.averageExecutionTime = entity.totalExecutionTime / entity.totalOccurrences
|
|
|
|
for (const domain of entity.domains) {
|
|
if (entityByDomain.has(domain)) {
|
|
const duplicate = entityByDomain.get(domain)
|
|
throw new Error(`Duplicate domain ${domain} (${entity.name} and ${duplicate.name})`)
|
|
}
|
|
|
|
entityByDomain.set(domain, entity)
|
|
|
|
const rootDomain = getRootDomain(domain)
|
|
if (domain.startsWith('*.')) {
|
|
const wildcardDomain = domain.slice(2)
|
|
if (wildcardDomain === rootDomain) entityByRootDomain.set(rootDomain, entity)
|
|
else entityBySubDomain.set(wildcardDomain, entity)
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const [rootDomain, entity] of entityByRootDomain.entries()) {
|
|
if (!entity) entityByRootDomain.delete(rootDomain)
|
|
}
|
|
|
|
const getEntity = getEntityInDataset.bind(
|
|
null,
|
|
entityByDomain,
|
|
entityBySubDomain,
|
|
entityByRootDomain
|
|
)
|
|
const getProduct = getProductInDataset.bind(
|
|
null,
|
|
entityByDomain,
|
|
entityBySubDomain,
|
|
entityByRootDomain
|
|
)
|
|
return {getEntity, getProduct, getRootDomain, entities}
|
|
}
|
|
|
|
module.exports = {createAPIFromDataset}
|