fix: prevent asset conflicts between React and Grid.js versions

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>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

82
node_modules/find-process/lib/find.js generated vendored Normal file
View File

@@ -0,0 +1,82 @@
/*
* @Author: zoujie.wzj
* @Date: 2016-01-23 18:18:28
* @Last Modified by: Ayon Lee
* @Last Modified on: 2018-10-19
*/
'use strict'
const findPid = require('./find_pid')
const findProcess = require('./find_process')
const log = require('./logger')
const findBy = {
port (port, config) {
return findPid(port)
.then(pid => {
return findBy.pid(pid, config)
}, () => {
// return empty array when pid not found
return []
})
},
pid (pid, config) {
return findProcess({
pid,
config
})
},
name (name, config) {
return findProcess({
name,
config,
skipSelf: true
})
}
}
/**
* find process by condition
*
* return Promise: [{
* pid: <process id>,
* ppid: <process parent id>,
* uid: <user id (*nix)>,
* gid: <user group id (*nix)>,
* name: <command name>,
* cmd: <process run args>
* }, ...]
*
* If no process found, resolve process with empty array (only reject when error occured)
*
* @param {String} by condition: port/pid/name ...
* @param {Mixed} condition value
* @param {Boolean|Option}
* @return {Promise}
*/
function find (by, value, options) {
const config = Object.assign({
logLevel: 'warn',
strict: typeof options === 'boolean' ? options : false
}, options)
log.setLevel(config.logLevel)
return new Promise((resolve, reject) => {
if (!(by in findBy)) {
reject(new Error(`do not support find by "${by}"`))
} else {
const isNumber = /^\d+$/.test(value)
if (by === 'pid' && !isNumber) {
reject(new Error('pid must be a number'))
} else if (by === 'port' && !isNumber) {
reject(new Error('port must be a number'))
} else {
findBy[by](value, config).then(resolve, reject)
}
}
})
}
module.exports = find

211
node_modules/find-process/lib/find_pid.js generated vendored Normal file
View File

@@ -0,0 +1,211 @@
/*
* @Author: zoujie.wzj
* @Date: 2016-01-22 19:27:17
* @Last Modified by: Ayon Lee
* @Last Modified on: 2018-10-19
*/
'use strict'
// find pid by port
const os = require('os')
const fs = require('fs')
const utils = require('./utils')
const log = require('./logger')
const ensureDir = (path) => new Promise((resolve, reject) => {
if (fs.existsSync(path)) {
resolve()
} else {
fs.mkdir(path, err => {
err ? reject(err) : resolve()
})
}
})
const finders = {
darwin (port) {
return new Promise((resolve, reject) => {
utils.exec('netstat -anv -p TCP && netstat -anv -p UDP', function (err, stdout, stderr) {
if (err) {
reject(err)
} else {
err = stderr.toString().trim()
if (err) {
reject(err)
return
}
// Drop group header, e.g. "Active Internet connections"
const table = utils.stripLine(stdout.toString(), 1)
// Get the next line with the column headers
const headers = table.slice(0, table.indexOf('\n'))
// Drop the header line to get the table body
const body = utils.stripLine(table, 1)
// In macOS >=Sequoia, columns include `rxbytes` and `txbytes`, which
// shifts the PID column to index 10. Detect this with a search
// for rxbytes. (Parsing the headers more robustly isn't possible
// because some colmn names contain spaces, and others are only separated
// by a single space.)
const pidColumn = headers.indexOf('rxbytes') >= 0 ? 10 : 8
const found = utils.extractColumns(body, [0, 3, pidColumn], 10)
.filter(row => {
return !!String(row[0]).match(/^(udp|tcp)/)
})
.find(row => {
const matches = String(row[1]).match(/\.(\d+)$/)
if (matches && matches[1] === String(port)) {
return true
}
return false
})
if (found && found[2].length) {
resolve(parseInt(found[2], 10))
} else {
reject(new Error(`pid of port (${port}) not found`))
}
}
})
})
},
freebsd: 'darwin',
sunos: 'darwin',
linux (port) {
return new Promise((resolve, reject) => {
const cmd = 'netstat -tunlp'
utils.exec(cmd, function (err, stdout, stderr) {
if (err) {
reject(err)
} else {
const warn = stderr.toString().trim()
if (warn) {
// netstat -p ouputs warning if user is no-root
log.warn(warn)
}
// replace header
const data = utils.stripLine(stdout.toString(), 2)
const columns = utils.extractColumns(data, [3, 6], 7).find(column => {
const matches = String(column[0]).match(/:(\d+)$/)
if (matches && matches[1] === String(port)) {
return true
}
return false
})
if (columns && columns[1]) {
const pid = columns[1].split('/', 1)[0]
if (pid.length) {
resolve(parseInt(pid, 10))
} else {
reject(new Error(`pid of port (${port}) not found`))
}
} else {
reject(new Error(`pid of port (${port}) not found`))
}
}
})
})
},
win32 (port) {
return new Promise((resolve, reject) => {
utils.exec('netstat -ano', function (err, stdout, stderr) {
if (err) {
reject(err)
} else {
err = stderr.toString().trim()
if (err) {
reject(err)
return
}
// replace header
const data = utils.stripLine(stdout.toString(), 4)
const columns = utils.extractColumns(data, [1, 4], 5).find(column => {
const matches = String(column[0]).match(/:(\d+)$/)
if (matches && matches[1] === String(port)) {
return true
}
return false
})
if (columns && columns[1].length && parseInt(columns[1], 10) > 0) {
resolve(parseInt(columns[1], 10))
} else {
reject(new Error(`pid of port (${port}) not found`))
}
}
})
})
},
android (port) {
return new Promise((resolve, reject) => {
// on Android Termux, an warning will be emitted when executing `netstat`
// with option `-p` says 'showing only processes with your user ID', but
// it can still fetch the information we need. However, NodeJS treat this
// warning as an error, `util.exec()` will get nothing but the error. To
// get the true output of the command, we need to save it to a tmpfile and
// read that file instead.
const dir = os.tmpdir() + '/.find-process'
const file = dir + '/' + process.pid
const cmd = 'netstat -tunp >> "' + file + '"'
ensureDir(dir).then(() => {
utils.exec(cmd, () => {
fs.readFile(file, 'utf8', (err, data) => {
fs.unlink(file, () => { })
if (err) {
reject(err)
} else {
data = utils.stripLine(data, 2)
const columns = utils.extractColumns(data, [3, 6], 7).find(column => {
const matches = String(column[0]).match(/:(\d+)$/)
if (matches && matches[1] === String(port)) {
return true
}
return false
})
if (columns && columns[1]) {
const pid = columns[1].split('/', 1)[0]
if (pid.length) {
resolve(parseInt(pid, 10))
} else {
reject(new Error(`pid of port (${port}) not found`))
}
} else {
reject(new Error(`pid of port (${port}) not found`))
}
}
})
})
})
})
}
}
function findPidByPort (port) {
const platform = process.platform
return new Promise((resolve, reject) => {
if (!(platform in finders)) {
return reject(new Error(`platform ${platform} is unsupported`))
}
let findPid = finders[platform]
if (typeof findPid === 'string') {
findPid = finders[findPid]
}
findPid(port).then(resolve, reject)
})
}
module.exports = findPidByPort

249
node_modules/find-process/lib/find_process.js generated vendored Normal file
View File

@@ -0,0 +1,249 @@
/*
* @Author: zoujie.wzj
* @Date: 2016-01-23 18:25:37
* @Last Modified by: Sahel LUCAS--SAOUDI
* @Last Modified on: 2021-11-12
*/
'use strict'
const path = require('path')
const utils = require('./utils')
function matchName (text, name) {
if (!name) {
return true
}
// make sure text.match is valid, fix #30
if (text && text.match) {
return text.match(name)
}
return false
}
function fetchBin (cmd) {
const pieces = cmd.split(path.sep)
const last = pieces[pieces.length - 1]
if (last) {
pieces[pieces.length - 1] = last.split(' ')[0]
}
const fixed = []
for (const part of pieces) {
const optIdx = part.indexOf(' -')
if (optIdx >= 0) {
// case: /aaa/bbb/ccc -c
fixed.push(part.substring(0, optIdx).trim())
break
} else if (part.endsWith(' ')) {
// case: node /aaa/bbb/ccc.js
fixed.push(part.trim())
break
}
fixed.push(part)
}
return fixed.join(path.sep)
}
function fetchName (fullpath) {
if (process.platform === 'darwin') {
const idx = fullpath.indexOf('.app/')
if (idx >= 0) {
return path.basename(fullpath.substring(0, idx))
}
}
return path.basename(fullpath)
}
const finders = {
darwin (cond) {
return new Promise((resolve, reject) => {
let cmd
if ('pid' in cond) {
cmd = `ps -p ${cond.pid} -ww -o pid,ppid,uid,gid,args`
} else {
cmd = 'ps ax -ww -o pid,ppid,uid,gid,args'
}
utils.exec(cmd, function (err, stdout, stderr) {
if (err) {
if ('pid' in cond) {
// when pid not exists, call `ps -p ...` will cause error, we have to
// ignore the error and resolve with empty array
resolve([])
} else {
reject(err)
}
} else {
err = stderr.toString().trim()
if (err) {
reject(err)
return
}
const data = utils.stripLine(stdout.toString(), 1)
const columns = utils.extractColumns(data, [0, 1, 2, 3, 4], 5).filter(column => {
if (column[0] && cond.pid) {
return column[0] === String(cond.pid)
} else if (column[4] && cond.name) {
return matchName(column[4], cond.name)
} else {
return !!column[0]
}
})
let list = columns.map(column => {
const cmd = String(column[4])
const bin = fetchBin(cmd)
return {
pid: parseInt(column[0], 10),
ppid: parseInt(column[1], 10),
uid: parseInt(column[2], 10),
gid: parseInt(column[3], 10),
name: fetchName(bin),
bin,
cmd: column[4]
}
})
if (cond.config.strict && cond.name) {
list = list.filter(item => item.name === cond.name)
}
resolve(list)
}
})
})
},
linux: 'darwin',
sunos: 'darwin',
freebsd: 'darwin',
win32 (cond) {
return new Promise((resolve, reject) => {
const cmd = '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-CimInstance -className win32_process | select Name,ProcessId,ParentProcessId,CommandLine,ExecutablePath'
const lines = []
const proc = utils.spawn('powershell.exe', ['/c', cmd], { detached: false, windowsHide: true })
proc.stdout.on('data', data => {
lines.push(data.toString())
})
proc.on('error', err => {
reject(new Error('Command \'' + cmd + '\' failed with reason: ' + err.toString()))
})
proc.on('close', code => {
if (code !== 0) {
return reject(new Error('Command \'' + cmd + '\' terminated with code: ' + code))
}
const list = utils.parseTable(lines.join(''))
.filter(row => {
if ('pid' in cond) {
return row.ProcessId === String(cond.pid)
} else if (cond.name) {
const rowName = row.Name || '' // fix #40
if (cond.config.strict) {
return rowName === cond.name || (rowName.endsWith('.exe') && rowName.slice(0, -4) === cond.name)
} else {
// fix #9
return matchName(row.CommandLine || rowName, cond.name)
}
} else {
return true
}
})
.map(row => ({
pid: parseInt(row.ProcessId, 10),
ppid: parseInt(row.ParentProcessId, 10),
// uid: void 0,
// gid: void 0,
bin: row.ExecutablePath,
name: row.Name || '',
cmd: row.CommandLine
}))
resolve(list)
})
})
},
android (cond) {
return new Promise((resolve, reject) => {
const cmd = 'ps'
utils.exec(cmd, function (err, stdout, stderr) {
if (err) {
if ('pid' in cond) {
// when pid not exists, call `ps -p ...` will cause error, we have to
// ignore the error and resolve with empty array
resolve([])
} else {
reject(err)
}
} else {
err = stderr.toString().trim()
if (err) {
reject(err)
return
}
const data = utils.stripLine(stdout.toString(), 1)
const columns = utils.extractColumns(data, [0, 3], 4).filter(column => {
if (column[0] && cond.pid) {
return column[0] === String(cond.pid)
} else if (column[1] && cond.name) {
return matchName(column[1], cond.name)
} else {
return !!column[0]
}
})
let list = columns.map(column => {
const cmd = String(column[1])
const bin = fetchBin(cmd)
return {
pid: parseInt(column[0], 10),
// ppid: void 0,
// uid: void 0,
// gid: void 0,
name: fetchName(bin),
bin,
cmd
}
})
if (cond.config.strict && cond.name) {
list = list.filter(item => item.name === cond.name)
}
resolve(list)
}
})
})
}
}
function findProcess (cond) {
const platform = process.platform
return new Promise((resolve, reject) => {
if (!(platform in finders)) {
return reject(new Error(`platform ${platform} is unsupported`))
}
let find = finders[platform]
if (typeof find === 'string') {
find = finders[find]
}
find(cond).then((result) => {
if (cond.skipSelf) {
// skip find-process itself
const filteredResult = result.filter(item => item.pid !== process.pid)
resolve(filteredResult)
} else {
resolve(result)
}
}, reject)
})
}
module.exports = findProcess

5
node_modules/find-process/lib/logger.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
'use strict'
const log = require('loglevel')
module.exports = log

167
node_modules/find-process/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,167 @@
/*
* @Author: zoujie.wzj
* @Date: 2016-01-23 18:17:55
* @Last Modified by: Sahel LUCAS--SAOUDI
* @Last Modified on: 2021-11-12
*/
'use strict'
const cp = require('child_process')
const UNIT_MB = 1024 * 1024
const utils = {
/**
* exec command with maxBuffer size
*/
exec (cmd, callback) {
cp.exec(cmd, {
maxBuffer: 2 * UNIT_MB,
windowsHide: true
}, callback)
},
/**
* spawn command
*/
spawn (cmd, args, options) {
return cp.spawn(cmd, args, options)
},
/**
* Strip top lines of text
*
* @param {String} text
* @param {Number} num
* @return {String}
*/
stripLine (text, num) {
let idx = 0
while (num-- > 0) {
const nIdx = text.indexOf('\n', idx)
if (nIdx >= 0) {
idx = nIdx + 1
}
}
return idx > 0 ? text.substring(idx) : text
},
/**
* Split string and stop at max parts
*
* @param {Number} line
* @param {Number} max
* @return {Array}
*/
split (line, max) {
const cols = line.trim().split(/\s+/)
if (cols.length > max) {
cols[max - 1] = cols.slice(max - 1).join(' ')
}
return cols
},
/**
* Extract columns from table text
*
* Example:
*
* ```
* extractColumns(text, [0, 2], 3)
* ```
*
* From:
* ```
* foo bar bar2
* valx valy valz
* ```
*
* To:
* ```
* [ ['foo', 'bar2'], ['valx', 'valz'] ]
* ```
*
* @param {String} text raw table text
* @param {Array} idxes the column index list to extract
* @param {Number} max max column number of table
* @return {Array}
*/
extractColumns (text, idxes, max) {
const lines = text.split(/(\r\n|\n|\r)/)
const columns = []
if (!max) {
max = Math.max.apply(null, idxes) + 1
}
lines.forEach(line => {
const cols = utils.split(line, max)
const column = []
idxes.forEach(idx => {
column.push(cols[idx] || '')
})
columns.push(column)
})
return columns
},
/**
* parse table text to array
*
* From:
* ```
* Header1 : foo
* Header2 : bar
* Header3 : val
*
* Header1 : foo2
* Header2 : bar2
* Header3 : val2
* ```
*
* To:
* ```
* [{ Header1: 'foo', Header2: 'bar', Header3: 'val' }, ...]
* ```
*
* @param {String} data raw table data
* @return {Array}
*/
parseTable (data) {
const lines = data.split(/(\r\n\r\n|\r\n\n|\n\r\n|\n\n)/).filter(line => {
return line && line.trim().length > 0
}).map((e) => e.split(/(\r\n|\n|\r)/).filter(line => line.trim().length > 0))
// Join multi-ligne value
lines.forEach((line) => {
for (let index = 0; line[index];) {
const entry = line[index]
if (entry.startsWith(' ')) {
line[index - 1] += entry.trimLeft()
line.splice(index, 1)
} else {
index += 1
}
}
})
return lines.map(line => {
const row = {}
line.forEach((string) => {
const splitterIndex = string.indexOf(':')
const key = string.slice(0, splitterIndex).trim()
row[key] = string.slice(splitterIndex + 1).trim()
})
return row
})
}
}
module.exports = utils