@fastify-core/base 1.0.7 → 1.0.8

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/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import e from"argon2";import{format as t}from"date-fns-tz";import*as n from"decimal.js";import r from"fs";import i from"path";import{format as a,isValid as o,parseISO as s}from"date-fns";import{pipeline as c}from"stream/promises";import l from"jsonwebtoken";import u from"node:crypto";var d=class e{md;constructor(e){this.md=e}errors=[];async runValidate(e,t={}){if(Object.entries(t).length===0)return;let n=[];for(let r in t){if(!Object.prototype.hasOwnProperty.call(t,r))continue;let i=t[r],a=e?.id||0,o=e?.[r]??``,s=this?.md?.fieldName(r)??r;n.push({field:r,value:o,rules:i,fieldName:s,id:a})}if(n.length&&(await this.validate(n),this.getErrors().length)){let e=this.getErrors().join(`, `);throw Error(e)}}async validate(e,t=!1){let n=!0;for await(let r of e){let e=r.filed,i=r.id,a=r.fieldName,o=r.value,s=r.rules;s=s.split(`|`);for await(let r of s){let s=r,c=[];if(this.isruleHasArgs(r)?(s=this.getRuleName(r),c=this.getRuleArgs(r)):c=e,!(s in this))throw Error(`Method doesnt exists: `+s);if(!await this[s](o,c,i)&&(this.addError(s,a,o,c),n=!1,t))return!1}}return n}isruleHasArgs(e){return e.split(`(`)[1]!==void 0}getRuleName(e){return e.split(`(`)[0]}getRuleArgs(e){e=e.trim();let t=e.split(`(`)[1];return t=t.endsWith(`)`)?t.slice(0,-1):t,t.split(`,`)}addError(t,n,r,i=[]){if(t){let a=e.defaultMessages(t);a?a.trim()!==``&&(r=typeof r==`string`?r:``,a=a.replace(`{placeholder}`,n),a=a.replace(`{value}`,r),i=typeof i==`string`?[i]:i,i.forEach((e,t)=>{a=a.replace(`{${t}}`,e)}),this.errors.push(a)):this.errors.push(`The value you entered for `+n+` is invalid`)}}passes(){return this.errors.length===0}getErrors(){return this.errors}clearErrors(){this.errors=[]}required(e){return!(e==null||typeof e==`string`&&e.trim()===``)}requiredId(e){if(e==null)return!1;if(typeof e==`string`){if(e.trim()===``)return!1}else if(typeof e==`number`&&e===0)return!1;return!0}minLen(e,t){return e.length>=parseInt(t[0])}equalLen(e,t){return e.length===parseInt(t[0])}maxLen(e,t){return e.length<=parseInt(t[0])}rangeNum(e,t){return e>=parseInt(t[0])&&e<=parseInt(t[1])}max(e,t){return e<=Number(t[0])}min(e,t){return e>=Number(t[0])}integer(e){return!Number.isNaN(parseInt(e))}inArray(e,t){if(typeof e==`object`){for(let n in e)if(!(n in t))return!1;return!0}return e in t}inArrayNumber(e,t){let n=t.map(Number);return typeof e==`object`?e.every(e=>n.includes(e)):n.includes(e)}alphaNum(e){return!e||/^[a-z0-9]+$/i.test(e)}number(e){if(typeof e==`number`&&(e>2**53-1||e<-(2**53-1)))throw Error(`INVALID_SAFE_NUMBER`);return typeof e==`number`}alphaNumWithSpaces(e){return!e||/^[a-z0-9 ]+$/i.test(e)}password(e){return!e||/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#!%^&*])[0-9a-zA-Z@#!%^&*.]{8,32}/.test(e)}phoneVn(e){return!e||/^[03|05|07|08|09]{2}[0-9]{8}/.test(e)}equals(e,t){return e===t[0]}notEqual(e,t){return e!==t[0]}email(e){return!e||/^([A-Za-z0-9_.-])+@([A-Za-z0-9_.-]+)\.([A-Za-z]{2,4})$/.test(e)}async unique(e,t,n){try{if(!this.md)throw Error(`invalid_md`);let r=`SELECT id FROM \`${t[0].trim()}\` WHERE \`${t[1].trim()}\` = ?`,i=[e];this.md.isDeleted&&(r+=` AND isDeleted = 0`),n&&(r+=` AND id != ?`,i.push(n));let[a]=await this.md.query(r,i);return a.length===0}catch(e){return console.log(e),!1}}static defaultMessages(e){return{required:`{placeholder} không thể trống`,requiredId:`{placeholder} không hợp lệ`,minLen:`{placeholder} không thể nhỏ hơn {0} ký tự`,equalLen:`{placeholder} phải có {0} ký tự`,phoneVn:`{placeholder} không phải thuê bao Việt Nam`,findOne:`{placeholder} không tồn tại`,maxLen:`{placeholder} không thể lớn hơn {0} ký tự`,min:`{placeholder} không thể nhỏ hơn {0}`,max:`{placeholder} không thể lớn hơn {0}`,rangeNum:`{placeholder} phải nằm trong khoản từ {0} đến {1}`,integer:`{placeholder} phải là số`,number:`{placeholder} không đúng định dạng`,inArray:`{placeholder} không hợp lệ`,inArrayNumber:`{placeholder} không hợp lệ`,alphaNum:`Chỉ cho phép chữ cái và số cho {placeholder}`,alphaNumWithSpaces:`Chỉ cho phép chữ cái, số và khoảng trắng cho {placeholder}`,password:`Vui lòng nhập mật khẩu dài 8-32 ký tự, có ký tự chữ số, chữ hoa và chữ thường, ký tự @#$!%^&*`,equals:`{placeholder} không đúng`,notEqual:`{placeholder} không thể bằng {0}`,email:`Email không đúng định dạng`,unique:`{placeholder} đã được sử dụng`}[e]??null}},f=class{pool;isDeleted=!1;errors=[];vdObject;modifieds;constructor(e){this.pool=e}fieldName(e){return e}getErrors(){return this.errors}async getConnection(){return await this.pool.getConnection()}async validate(e,t={}){await new d(this).runValidate(e,t)}async query(e,t,n){return n?n.query(e,t):this.pool.query(e,t)}async modifiedSync(e){if(!this.modifieds)return e;for(let t in this.modifieds){let{smodel:n,tkey:r,skey:i,fmap:a}=this.modifieds[t];if(!e[r])continue;let o=await n.findOne({[i]:e[r]});o&&(e[t]=o[a]||``)}return e}buildWhere(e,t){let n={...e};this.isDeleted&&n.isDeleted===void 0?n.isDeleted=0:this.isDeleted||delete n.isDeleted;let r=[],i=e=>e.split(`.`).map(e=>`\`${e}\``).join(`.`),a=e=>typeof e==`boolean`?+!!e:e,o=e=>{let n=[];for(let s in e){let c=e[s];if(s===`$or`&&Array.isArray(c)){let e=c.map(e=>o(e)).filter(Boolean).map(e=>`(${e})`);e.length&&n.push(`(${e.join(` OR `)})`);continue}if(s===`$and`&&Array.isArray(c)){let e=c.map(e=>o(e)).filter(Boolean).map(e=>`(${e})`);e.length&&n.push(`(${e.join(` AND `)})`);continue}let l=t?`${i(t)}.${i(s)}`:i(s);if(c===null){n.push(`${l} IS NULL`);continue}if(typeof c==`object`&&c&&!Array.isArray(c)){let e=[];for(let t in c){let n=c[t];switch(t){case`$in`:n.length?(e.push(`${l} IN (${n.map(()=>`?`).join(`,`)})`),r.push(...n.map(a))):e.push(`1=0`);break;case`$nin`:n.length?(e.push(`${l} NOT IN (${n.map(()=>`?`).join(`,`)})`),r.push(...n.map(a))):e.push(`1=1`);break;case`$ne`:n===null?e.push(`${l} IS NOT NULL`):(e.push(`${l} <> ?`),r.push(a(n)));break;case`$gt`:e.push(`${l} > ?`),r.push(a(n));break;case`$gte`:e.push(`${l} >= ?`),r.push(a(n));break;case`$lt`:e.push(`${l} < ?`),r.push(a(n));break;case`$lte`:e.push(`${l} <= ?`),r.push(a(n));break;case`$between`:if(!Array.isArray(n)||n.length!==2)throw Error(`$between requires [min, max]`);e.push(`${l} BETWEEN ? AND ?`),r.push(a(n[0]),a(n[1]));break;case`$like`:e.push(`${l} LIKE ?`),r.push(`%${n}%`);break;case`$likeIn`:if(!n.length)e.push(`1=0`);else{let t=n.map(()=>`${l} LIKE ?`).join(` OR `);e.push(`(${t})`),r.push(...n.map(e=>`%${e}%`))}break;default:throw Error(`Unsupported operator: ${t}`)}}e.length&&n.push(`(${e.join(` AND `)})`)}else n.push(`${l} = ?`),r.push(a(c))}return n.join(` AND `)};return{sql:o(n)||`1=1`,params:r}}async count(e={}){let t=`SELECT COUNT(*) AS total FROM \`${this.table}\``,n=[],r=this.buildWhere(e);r.sql&&(t+=` WHERE ${r.sql}`,n=r.params);let[i]=await this.query(t,n);return i?.[0]?.total??0}async insert(e,t){let n=Object.keys(e),r=Object.values(e),i=Array(n.length).fill(`?`).join(`, `),a=`INSERT INTO \`${this.table}\` (\`${n.join("`, `")}\`) VALUES (${i})`,[o]=await this.query(a,r,t);return o.insertId}async update(e,t,n){let r=Object.keys(e),i=Object.keys(t);if(r.length===0)return 0;if(i.length===0)throw Error(`Điều kiện lọc (filter) không được rỗng.`);let a=Object.values(e),o=r.map(e=>`\`${e}\`=?`).join(`, `),s=Object.values(t),c=i.map(e=>`\`${e}\`=?`).join(` AND `),l=[...a,...s],u=`UPDATE \`${this.table}\` SET ${o} WHERE ${c}`,[d]=await this.query(u,l,n);return d.affectedRows}async updateAdv(e={},t={},n){try{if(!Object.keys(e).length)return;let r=this.buildWhere(t),i=[],a=[];for(let[t,n]of Object.entries(e))i.push(`\`${t}\` = ?`),a.push(n);let o=`UPDATE \`${this.table}\` SET ${i.join(`, `)}`;r.sql&&(o+=` WHERE ${r.sql}`,a.push(...r.params));let[s]=await this.query(o,a,n);return s.affectedRows}catch(e){throw console.log(e.message),Error(`invalid_update_is_deleted`)}}async save(e,t){try{if(e.id){let n={...e};if(delete n.id,Object.keys(n).length===0)return e;let r={id:e.id};return await this.update(n,r,t),e}{let n=e;return e.id=await this.insert(n,t),e}}catch(e){return console.log(e.message),this.errors.push(e.message),!1}}async vdSave(e,t=!1,n){e=await this.modifiedSync(e),t&&await this.validate(e,t);let r=await this.save(e,n);if(!r){let e=this.getErrors().join(`, `);throw Error(e)}return r}async updateMany(e,t,n){if(e.length)try{let r=Object.keys(e[0]).filter(e=>e!==t),i=e.map(e=>e[t]),a=r.map(n=>`\`${n}\` = CASE \`${t}\` ${e.map(e=>`WHEN ? THEN ?`).join(` `)} ELSE \`${n}\` END`).join(`, `),o=[];r.forEach(n=>{e.forEach(e=>{o.push(e[t]),o.push(e[n])})}),o.push(...i);let s=`UPDATE \`${this.table}\` SET ${a} WHERE \`${t}\` IN (${i.map(()=>`?`).join(`,`)});`;await this.query(s,o,n)}catch(e){throw console.log(e.message),Error(`invalid_update_many`)}}async updateAndCreateMany(e,t,n){if(e.length)try{let r=Object.keys(e[0]),i=r.map(e=>`\`${e}\``),a=e.map(()=>`(${r.map(()=>`?`).join(`,`)})`).join(`,`),o=e.flatMap(e=>r.map(t=>e[t])),s=r.filter(e=>e!==t).map(e=>`\`${e}\` = VALUES(\`${e}\`)`).join(`, `),c=`INSERT INTO \`${this.table}\` (${i.join(`,`)}) VALUES ${a}`;s&&(c+=` ON DUPLICATE KEY UPDATE ${s}`),c+=`;`,await this.query(c,o,n)}catch(e){throw console.log(e.message),Error(`invalid_update_and_create`)}}async find(e={},t=!1,n=!1,r=!1,i=0){let a=`SELECT * FROM \`${this.table}\``,o=[];if(n){let e=Array.isArray(n)?n:[n],t=[];for(let n of e){let e=n.type?n.type.toUpperCase():`LEFT JOIN`;a+=` ${e} \`${n.table}\` ON ${n.on}`,n.fields&&n.fields.length&&t.push(n.fields.map(e=>`\`${n.table}\`.${e} AS ${n.table}_${e}`).join(`, `))}t.length&&(a=a.replace(`SELECT *`,`SELECT \`${this.table}\`.*, ${t.join(`, `)}`))}let s=this.buildWhere(e);if(s.sql&&(a+=` WHERE ${s.sql}`,o=o.concat(s.params)),r&&(Array.isArray(r)&&(r=r.join(`, `)),a=a.replace(`SELECT *`,`SELECT ${r}`)),t){let e=Object.entries(t).map(([e,t])=>`${e} ${t.toUpperCase()}`);e.length&&(a+=` ORDER BY ${e.join(`, `)}`)}i&&(a+=` LIMIT ?`,o.push(i));let[c]=await this.query(a,o);return c}async findOne(e={},t=!1,n=!1,r=!1){let i=await this.find(e,t,n,r,1);return i&&i.length?i[0]:null}async findById(e,t={}){return t.id=e,await this.findOne(t)}async deleteOne(e,t){return await this.deleteMany(e,1,t)}async deleteById(e,t){let n={};return n.id=e,await this.deleteOne(n,t)}async deleteMany(e,t=0,n){let r=this.buildWhere(e);if(!r.sql)throw Error(`deleteMany requires a filter condition`);let i=`DELETE FROM \`${this.table}\` WHERE ${r.sql}`;t&&(i+=` LIMIT `+t);let[a]=await this.query(i,r.params,n);return!!a.affectedRows&&a.affectedRows>0}async isField(e,t=this.table){return(await this.query(`SHOW COLUMNS FROM \`${t}\` LIKE '${e}'`)).length>0}async isFields(e=[]){if(!e.length)return[];let t=(await this.query(`SHOW COLUMNS FROM \`${this.table}\``)).map(e=>e.Field);if(e.filter(e=>!t.includes(e)).length>0)throw Error(`invalid_fields`);return e}async getMapName(e,t,n,r,i=`name`){if(t&&t!==r?.[e]){let r=await n.findOne({[e]:t});return r&&r[i]?r[i]:``}return``}async findWithPagination(e={},t=!1,n=!1,r=!1,i=1,a=10){i=Math.max(1,i);let o=(i-1)*a,s=`FROM \`${this.table}\``,c=[],l=`SELECT \`${this.table}\`.*`;if(n){let e=Array.isArray(n)?n:[n],t=[];for(let n of e){let e=n.type?n.type.toUpperCase():`LEFT JOIN`;s+=` ${e} \`${n.table}\` ON ${n.on}`,n.fields?.length&&t.push(n.fields.map(e=>`\`${n.table}\`.${e} AS ${n.table}_${e}`).join(`, `))}t.length&&(l=`SELECT \`${this.table}\`.*, ${t.join(`, `)}`)}let u=this.buildWhere(e);u.sql&&(s+=` WHERE ${u.sql}`,c=c.concat(u.params)),r&&(Array.isArray(r)&&(r=r.join(`, `)),l=`SELECT ${r}`);let d=`SELECT COUNT(*) AS total ${s}`,[f]=await this.query(d,c),p=f?.[0]?.total||0,m=`${l} ${s}`;if(t){let e=Object.entries(t).map(([e,t])=>`${e} ${t.toUpperCase()}`);e.length&&(m+=` ORDER BY ${e.join(`, `)}`)}m+=` LIMIT ? OFFSET ?`;let h=c.concat([a,o]),[g]=await this.query(m,h);return{page:i,length:a,pageTotal:Math.ceil(p/a),recordTotal:p,items:g||[]}}};async function p(t){return await e.hash(t)}async function m(t,n){return await e.verify(n,t)}function h(e,t=!1){let n=``,r=`0123456789`;r=t?`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`+r:r;for(let t=0;t<e;t++)n+=r.charAt(Math.floor(Math.random()*r.length));return n}function g(e){return e?String(e).normalize(`NFD`).replace(/[\u0300-\u036f]/g,``).replace(/đ/g,`d`).replace(/Đ/g,`D`).toLowerCase():``}function _(e){return e.toString().toLowerCase().normalize(`NFD`).replace(/[\u0300-\u036f]/g,``).replace(/[^a-z0-9\s-]/g,``).trim().replace(/\s+/g,`-`).replace(/-+/g,`-`)}async function v(e,t={},n=`POST`,r=`application/json`,i={}){let a={method:n,headers:{"Content-Type":r,...i}};return n===`POST`&&(a.body=JSON.stringify(t)),await(await fetch(e,a)).json()}function y(e=``){return e?e.toLowerCase().replace(/[^a-z0-9\s]/g,``).split(/\s+/).filter((e,t,n)=>e.length>2&&n.indexOf(e)===t).join(`, `):``}function b(e){let t=e instanceof Date?e:new Date(e);if(isNaN(t.getTime()))throw Error(`Ngày không hợp lệ: `+e);return t}function x(e,t=`YYYY-MM-DD HH:mm:ss`){if(!e)return``;let n=typeof e==`string`?new Date(e):e;if(isNaN(n.getTime()))return``;let r=e=>e.toString().padStart(2,`0`),i={YYYY:n.getFullYear().toString(),MM:r(n.getMonth()+1),DD:r(n.getDate()),HH:r(n.getHours()),mm:r(n.getMinutes()),ss:r(n.getSeconds())};return t.replace(/YYYY|MM|DD|HH|mm|ss/g,e=>i[e])}function S(e,t){return{start:new Date(e,t-1,1),end:new Date(e,t,0,23,59,59,999)}}function C(e,t){let n=new Date(e,t,0).getDate();return Array.from({length:n},(e,t)=>t+1)}function w(){return t(new Date,`yyyy-MM-dd HH:mm:ss`,{timeZone:`Asia/Ho_Chi_Minh`})}function T(e,t,n=``){return e?.[t]===void 0?n:e[t]}function E(e,t){return e?.reduce((e,n)=>e+(n[t]??0),0)||0}function D(e){let t=[{value:1e3,numeral:`M`},{value:900,numeral:`CM`},{value:500,numeral:`D`},{value:400,numeral:`CD`},{value:100,numeral:`C`},{value:90,numeral:`XC`},{value:50,numeral:`L`},{value:40,numeral:`XL`},{value:10,numeral:`X`},{value:9,numeral:`IX`},{value:5,numeral:`V`},{value:4,numeral:`IV`},{value:1,numeral:`I`}],n=``;for(let{value:r,numeral:i}of t)for(;e>=r;)n+=i,e-=r;return n}function ee(e,t=!1){let n=[{id:1,name:`Hoạt động`,color:`success`},{id:0,name:`Khóa`,color:`danger`}];if(e===!1)return n;let r=n.find(t=>t.id===e);return r?t?`<span class="badge badge-sm bg-gradient-${r.color}">${r.name}</span>`:r.name:``}function te(e,t=!1){let n=[{id:1,name:`Xuất bản`,color:`success`},{id:0,name:`Nháp`,color:`danger`}];if(e===!1)return n;let r=n.find(t=>t.id===e);return r?t?`<span class="badge badge-sm bg-gradient-${r.color}">${r.name}</span>`:r.name:``}function O(e,t=!1){let n=[{id:1,name:`Có`,color:`success`},{id:0,name:`Không`,color:`secondary`}];if(e===!1)return n;let r=n.find(t=>t.id===e);return r?t?`<span class="badge badge-sm bg-gradient-${r.color}">${r.name}</span>`:r.name:``}function k(e,t){let n=new Set(e),r=new Set(t);return[...e.filter(e=>!r.has(e)),...t.filter(e=>!n.has(e))]}function A(e,t){let n=new Set(t);return e.every(e=>n.has(e))}function j(e,t){if(!Array.isArray(e)||!Array.isArray(t))return[];let n=new Set(t);return e.filter(e=>!n.has(e))}function M(e,t){if(!Array.isArray(e)||e.length===0)return[];if(!t)return e;let n=Array(e.length);for(let r=0;r<e.length;r++){let i=e[r];n[r]=i&&typeof i==`object`?i[t]:void 0}return n}function N(e,t,n){return e.reduce((e,r)=>{let i=!0;if(n&&(i=Object.entries(n).every(([e,t])=>r[e]===t)),i){let n=r[t];return e+(typeof n==`number`?n:0)}return e},0)}function P(e){let t=String(e).replace(/[^\d.]/g,``);return t?new n.Decimal(t):new n.Decimal(0)}function F(e,t={}){if(Array.isArray(e)||typeof e==`object`&&e)return e;if(typeof e==`number`)return t;if(typeof e==`string`)try{let n=JSON.parse(e);return Array.isArray(n)||typeof n==`object`&&n?n:t}catch(e){return console.log(e),t}return t}function I(e=!1){let t=[{id:1,name:`Tiếng Việt`}];return e===!1?t:t.filter(t=>e.includes(t.id))}const L=new Set([`POST`,`PUT`,`PATCH`,`DELETE`]);function R(e){return L.has(String(e).toUpperCase())}function z(e){let t=e?.headers?.[`x-csrf-token`]||e?.headers?.[`X-CSRF-Token`],n=e?.body?.csrfToken||e?.body?.csrf_token;return t||n||``}function B(e,t,n={}){if(!R(e?.method||`GET`))return{allowed:!0,reason:null};let r=t?.csrfToken||n.expectedToken||``,i=z(e);return!r||!i||r!==i?{allowed:!1,reason:`missing_csrf_token`}:{allowed:!0,reason:null}}var V=class e{files;uploadType=`static`;constructor(e,t=`static`){this.files=e,this.uploadType=t}async copyFiles(t){if(t.length){let n=e.getUploadDir(this.uploadType);for await(let e of t){let t=i.join(n,e.src);if(r.existsSync(t)){let a=i.join(n,e.dist);await r.promises.copyFile(t,a)}}}}async uploadFile(e=`filedname`,t=``){try{let n=this.files[e];return!n||!n[0]?!1:await this.upload(n[0],t)}catch(e){throw Error(e)}}async uploadFiles(e=`filedname`,t=``){try{let n=[],r=this.files[e];if(!r||!r.length)return[];for await(let e of r){let r=await this.upload(e,t);r&&n.push(r)}return n}catch(e){throw Error(e)}}async upload(t,n=``){try{let a=Date.now()+`___`+t.filename,o=e.getUploadDir(this.uploadType),s=n?i.join(o,n):o;r.existsSync(s)||r.mkdirSync(s,{recursive:!0});let c=i.join(s,a);return r.existsSync(t.path)?(await r.promises.rename(t.path,c),n?i.posix.join(n,a):a):!1}catch(e){throw Error(e)}}async checkFile(t=`file`,n=`OfficeFile`){try{let r=this.files[t];if(!r)return;for await(let t of r){let r=`check`+n;if(!(r in e))throw Error(`Method does not exist: `+r);e[r](t.filename,t.mimetype);return}return}catch(e){throw Error(e)}}static removeFiles=(e,t=`static`)=>{if(e===!1||!Array.isArray(e))return!1;for(let n of e)this.removeFile(n,t);return!0};static removeFile=(t,n=`static`)=>{if(!t)return!1;let a=i.join(e.getUploadDir(n),t);return r.existsSync(a)&&r.unlinkSync(a),!0};static renameFile=e=>{let t=i.extname(e);return`${i.basename(e,t)}_${Date.now()}${t}`};static getUploadDir(e=`static`){return e==`static`?i.join(i.resolve(),`public`,`static`):e==`public`?i.join(i.resolve(),`public`):e==`uploads`?i.join(i.resolve(),`uploads`):``}static checkDefFile(e,t,n=!0){if(!/(.*)\.(png|jpeg|jpg|ico|pdf|xls|xlsx|doc|docx|ppt|pptx)$/i.test(e)){if(n)throw Error(`Định dạng tập tin ${e} không được phép`);return!1}if(![`image/x-icon`,`image/jpeg`,`image/png`,`image/jpg`,`application/pdf`,`application/msword`,`application/vnd.ms-excel`,`application/vnd.ms-powerpoint`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`].includes(t)){if(n)throw Error(`Định dạng tập tin ${e} không được phép`);return!1}return!0}static checkOfficeFile(e,t){try{if(!/(.*)\.(pdf|xls|xlsx|doc|docx|ppt|pptx)$/i.test(e)||![`application/pdf`,`application/msword`,`application/vnd.ms-excel`,`application/vnd.ms-powerpoint`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`].includes(t))throw Error(`Tập tin ${e} không đúng định dạng (.pdf,.xls,.xlsx,.doc,.docx,.ppt,.pptx)`)}catch(e){throw Error(e)}}static checkImgFile(e,t){try{if(!/(.*)\.(png|jpeg|jpg|ico)$/i.test(e)||![`image/jpeg`,`image/png`,`image/jpg`,`image/x-icon`].includes(t))throw Error(`Ảnh ${e} không đúng định dạng (.png,.jpeg,.jpg,.ico)`)}catch(e){throw Error(e)}}static checkVideoFile(e,t){try{if(!/(.*)\.(mp4|mkv|webm|mov|avi)$/i.test(e)||![`video/mp4`,`video/mkv`,`video/webm`,`video/mov`,`video/avi`].includes(t))throw Error(`Video ${e} không đúng định dạng (.mp4,.mkv,.webm,.mov,.avi)`)}catch(e){throw Error(e)}}},H=class e{req;body={};files={};constructor(e){this.req=e}start=async()=>{if(this.req.body)this.body=this.req.body;else if(this.req.isMultipart()){let e=i.join(i.resolve(),`uploads`,`tmb`);r.existsSync(e)||r.mkdirSync(e,{recursive:!0});for await(let t of this.req.parts())if(t.type===`field`)this.body[t.fieldname]===void 0?this.body[t.fieldname]=t.value:Array.isArray(this.body[t.fieldname])?this.body[t.fieldname].push(t.value):this.body[t.fieldname]=[this.body[t.fieldname],t.value];else if(V.checkDefFile(t.filename,t.mimetype,!1)){let n=i.extname(t.filename),a=_(i.basename(t.filename,n)),o=a+n,s=i.join(e,o);try{await c(t.file,r.createWriteStream(s))}catch(e){e.code===`ABORT_ERR`?console.error(`Người dùng hủy upload.`):e.code===`ERR_STREAM_PREMATURE_CLOSE`?console.error(`Stream đóng sớm bất thường.`):console.error(`Lỗi upload:`,e.message)}this.files[t.fieldname]===void 0&&(this.files[t.fieldname]=[]),this.files[t.fieldname].push({filename:o,basename:a,path:s,mimetype:t.mimetype,ext:n})}}};end=async()=>{let e=Object.values(this.files);for await(let t of e)for await(let e of t)r.existsSync(e.path)&&r.unlinkSync(e.path)};isPost(){return this.req.method===`POST`}isGet(){return this.req.method===`GET`}isAjax(){return this.req.headers[`x-requested-with`]===`XMLHttpRequest`}getPost(t=``,n=``,r=`stripTags`){if(t===``)return this.body;if(!this.hasPost(t))return n;let i=this.body[t];return i?.value!==void 0&&(i=i.value),e.filterSafeData(i,r,n)}getLangPost(t,n=``,r=`stripTags`){if(!t)return n;let i=t.match(/^([^{]+)\{([^}]+)\}$/);if(!i)return n;let a=i[1],o=i[2],s=this.body?.[a];typeof s==`string`&&(s=F(s));let c=s?.[o];return e.filterSafeData(c,r,n)}getParam(t,n=null,r=`stripTags`){let i=this.req.params;return!i||!i[t]?n:e.filterSafeData(i[t],r)}get(t=``,n=``,r=`stripTags`){let i=this.req.query;return t===``?i:this.has(t)?e.filterSafeData(i[t],r):n}has(e){let t=this.req.query;return Array.isArray(e)?e.every(e=>t[e]!==void 0):t[e]!==void 0}hasPost(e){return Array.isArray(e)?e.every(e=>this.body[e]!==void 0):this.body[e]!==void 0}getHeader(e){return this.req.headers[e.toLowerCase()]?.toString()||``}static filterSafeData(t,n=`stripTags`,r=``){if(typeof t==`number`)return t;if(n===`ids`)return Array.isArray(t)?e.filterIds(t):e.filterIds([t]);if(n===`stringIds`)return Array.isArray(t)?e.filterStringIds(t):e.filterStringIds([t]);if(n===`strings`)return Array.isArray(t)?e.filterStrings(t):e.filterStrings([t]);if(n===`dQuoteIds`)return Array.isArray(t)?e.filterDQuoteIds(t):e.filterDQuoteIds([t]);if(n===`int`||n===`number`){let e=Number(t);if(e>2**53-1)throw Error(`invalid_max_safe_integer`);if(e<-(2**53-1))throw Error(`invalid_min_safe_integer`);return Number.isNaN(e)?r:n===`int`?Math.trunc(e):e}if(n===`decimal`)return P(t);if(typeof t==`string`){if(n===`stripTags`)return t.replace(/<[^>]*>?/gm,``).trim();if(n===`html`)return e.filterHtml(t);if(n===`date`||n===`datetime`){let e=s(t);return o(e)?a(e,n===`date`?`yyyy-MM-dd`:`yyyy-MM-dd HH:mm:ss`):null}}return n===`raw`?e.parseRawInput(t):typeof t===n?t:``}static parseRawInput(e){if(typeof e!=`string`)return e;try{return JSON.parse(e)}catch{return e}}static filterHtml(e){return typeof e==`string`?e.replace(/<(script|style|meta)[^>]*>[\s\S]*?<\/\1>|<(script|style|meta)[^>]*\/?>/gi,``):``}static filterIds(e){if(typeof e==`string`){let t=F(e,void 0);if(Array.isArray(t))e=t;else if(!Number.isNaN(Number(e)))e=[Number(e)];else return[]}return Array.isArray(e)?[...new Set(e.map(e=>Number(e)).filter(e=>!Number.isNaN(e)))]:[]}static filterStringIds(e){return typeof e==`string`&&(e=F(e,[])),Array.isArray(e)?this.filterIds(e).map(e=>e.toString()):[]}static filterStrings(e){return[...new Set(e.map(e=>this.filterSafeData(e,`stripTags`)))]}static filterDQuoteIds(e){return this.filterIds(e).map(e=>`%"${e}"%`)}},U=class extends Error{statusCode;code;constructor(e,t=400,n){super(e),this.statusCode=t,this.code=n,this.name=`ClientError`,Object.setPrototypeOf(this,new.target.prototype)}},ne=class{static createToken(e,t){let{JWT_KEY:n,JWT_AUD:r,JWT_ISS:i}=process.env,a=Number(process.env.JWT_TIMEOUT||3600),o=t.ip,s=t.headers[`user-agent`],c=Math.floor(Date.now()/1e3),u={iss:i,aud:r,sub:e.id,name:e.fullname,ip:o,us:s,iat:c,exp:c+a,expired:(c+a)*1e3};return l.sign(u,n||``,{algorithm:`HS256`})}static verifyToken(e,t=!1){let{JWT_KEY:n}=process.env,r;if(t){if(r=e.cookies?.accessToken,!r)throw new U(`invalid_cookie_access_token`)}else{let t=e.headers.authorization;if(!t)throw Error(`missing_authorization`);let[n,i]=t.split(` `);if(n!==`Bearer`||!i)throw new U(`invalid_authorization`);r=i}let i=l.verify(r,n||``,{ignoreExpiration:!0});if(!i?.sub)throw new U(`invalid_userId_access_token`);let a=e.headers[`user-agent`]||`unknown`;if(i.us!==a)throw new U(`invalid_us_access_token`);if(i.ip&&i.ip!==e.ip)throw new U(`invalid_ip_access_token`);return i.accessToken=r,i}static checkSecretKey(e){if(!process.env.JWT_SECRET_KEY)throw Error(`missing_jwt_secret_key`);if(e!=process.env.JWT_SECRET_KEY)throw new U(`invalid_secret_key`)}};let W=null;function G(e,t){return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,new n(t)]))}function K(e,t){return W||=G(e,t),W}function q(){if(!W)throw Error(`Models chưa init`);return W}var J=class{pool;constructor(e){this.pool=e}async get(e,t){try{let[n]=await this.pool.query(`SELECT data FROM sessions WHERE sid = ?`,[e]);if(!n.length)return t(null,null);t(null,JSON.parse(n[0].data))}catch(e){t(e)}}async set(e,t,n){try{let r=JSON.stringify(t);await this.pool.query(`INSERT INTO sessions (sid, data, expires) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 14 DAY)) ON DUPLICATE KEY UPDATE data = VALUES(data), expires = VALUES(expires)`,[e,r]),n(null)}catch(e){n(e)}}async destroy(e,t){try{await this.pool.query(`DELETE FROM sessions WHERE sid = ?`,[e]),t(null)}catch(e){t(e)}}},Y=class{routes=[];static getMethod(e){return e.method?e.method:`get`}add(e,t=[]){let n=[];t.forEach(t=>{t.link=e+t.link,n.push(t)}),this.routes=this.routes.length?[...this.routes,...n]:n}addGS(e,t,n){let r=[{link:e+`/getList`,module:n,controller:t,action:`index`},{link:e+`/detail/:code([0-9]+)`,module:n,controller:t,action:`detail`},{link:e+`/create`,module:n,controller:t,action:`update`,method:`post`},{link:e+`/edit/:code([0-9]+)`,module:n,controller:t,action:`update`,method:`post`},{link:e+`/copy`,module:n,controller:t,action:`copy`,method:`post`},{link:e+`/import`,module:n,controller:t,action:`import`,method:`post`},{link:e+`/export`,module:n,controller:t,action:`export`},{link:e+`/delete/:code([0-9]+)`,module:n,controller:t,action:`deleteOne`},{link:e+`/delete`,module:n,controller:t,action:`delete`,method:`post`}];this.routes=this.routes.length?[...this.routes,...r]:r}getRouter(){return this.routes}};const X=new Set([`password`,`token`,`accessToken`,`refreshToken`,`secret`,`apiKey`,`authorization`]);function Z(e,t=``){return e==null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:Array.isArray(e)?e.map(e=>Z(e,t)):typeof e==`object`?Object.entries(e).reduce((e,[t,n])=>{let r=t.toLowerCase();return X.has(r)||r.includes(`password`)||r.includes(`token`)||r.includes(`secret`)||(e[t]=Z(n,t)),e},{}):e}function re(e,t,n=[]){let r=new Set([...n,`updatedAt`,`createdAt`,`__v`]),i={},a={};return Object.keys(e??{}).forEach(n=>{if(r.has(n))return;let o=t?.[n],s=e?.[n];o!==s&&(i[n]={old:o,new:s}),a[n]=s}),{difference:Z(i),item:Z(a)}}function ie(e,t){return e.length>0?e:!t}const Q={vi:{invalid_403:`Truy cập không được phép`,invalid_404:`Không tìm thấy thông tin`,invalid_500:`Thông tin không hợp lệ`,invalid_user:`Người dùng không hợp lệ`,invalid_permission_403:`Bạn không có quyền truy cập`,invalid_user_token:`Token người dùng không hợp lệ`,invalid_expired_refresh_token:`Refresh token đã quá hạn`,invalid_method_403:`Phương thức không được phép`,invalid_importfile:`Tập tin tải lên không hợp lệ`,invalid_isDeleted:`Trạng thái xóa không hợp lệ`,invalid_buffer:`Dữ liệu bộ nhớ không hợp lệ`,invalid_controller:`Controller không hợp lệ`,invalid_refresh_token:`Refresh token không hợp lệ`,invalid_save_user_token:`Không thể lưu token người dùng`,invalid_user_access_token:`Token truy cập người dùng không hợp lệ`},en:{invalid_403:`Access is not allowed`,invalid_404:`Information not found`,invalid_500:`Invalid information`,invalid_user:`Invalid user`,invalid_permission_403:`You do not have permission to access this resource`,invalid_user_token:`Invalid user token`,invalid_expired_refresh_token:`Refresh token has expired`,invalid_method_403:`Method is not allowed`,invalid_importfile:`Invalid uploaded file`,invalid_isDeleted:`Invalid deletion status`,invalid_buffer:`Invalid buffer data`,invalid_controller:`Invalid controller`,invalid_refresh_token:`Invalid refresh token`,invalid_save_user_token:`Unable to save user token`,invalid_user_access_token:`Invalid user access token`}};let $={...Q};function ae(e){$={};for(let[t,n]of Object.entries(Q))$[t]={...n,...e[t]??{}};for(let[t,n]of Object.entries(e))$[t]||($[t]={...n})}function oe(e,t=`vi`){return $[t]?.[e]??e}function se(e){let t=process.env.HMAC_SECRET,n=JSON.stringify(e);return u.createHmac(`sha256`,t).update(n).digest(`hex`)}async function ce(e,t){let n=new TextEncoder,r=await u.subtle.importKey(`raw`,n.encode(t),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]),i=await u.subtle.sign(`HMAC`,r,n.encode(JSON.stringify(e)));return Array.from(new Uint8Array(i)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}export{f as BaseModel,U as ClientError,H as FastRequest,V as FileUpload,ne as JWTApp,J as MySQLSessionStore,Y as Route,d as Validation,re as buildAuditLogData,v as callFetchApi,m as checkPassword,ce as createHmac,G as createModels,se as createSignature,P as decimalNumber,B as ensureCsrfProtection,x as formatDate,y as generateKeywords,ee as getActiveStatus,M as getArrColumn,j as getArrOnlyInFirst,ie as getCorsOriginPolicy,z as getCsrfTokenFromRequest,C as getDaysInMonth,k as getDiffArr,I as getLangs,q as getModels,S as getMonthRange,te as getPublishStatus,O as getTrueFlaseStatus,T as getValue,ae as initI18n,K as initModels,R as isStateChangingMethod,A as isSubArr,p as makePassword,b as parseDate,h as randomText,g as removeVietnameseTones,Z as sanitizeAuditPayload,_ as slugify,F as stringToJson,E as sumArrCol,N as sumArrColumn,oe as t,w as toMySQLDateNowVN,D as toRoman};
1
+ import e from"argon2";import t,{createHash as n,randomInt as r,timingSafeEqual as i}from"node:crypto";import{format as a}from"date-fns-tz";import*as o from"decimal.js";import s from"fs";import c from"path";import{format as l,isValid as u,parseISO as d}from"date-fns";import{pipeline as f}from"stream/promises";import p from"jsonwebtoken";const m={vi:{invalid_403:`Truy cập không được phép`,invalid_404:`Không tìm thấy thông tin`,invalid_500:`Thông tin không hợp lệ`,invalid_user:`Người dùng không hợp lệ`,invalid_permission_403:`Bạn không có quyền truy cập`,invalid_user_token:`Token người dùng không hợp lệ`,invalid_expired_refresh_token:`Refresh token đã quá hạn`,invalid_method_403:`Phương thức không được phép`,invalid_importfile:`Tập tin tải lên không hợp lệ`,invalid_isDeleted:`Trạng thái xóa không hợp lệ`,invalid_buffer:`Dữ liệu bộ nhớ không hợp lệ`,invalid_controller:`Controller không hợp lệ`,invalid_refresh_token:`Refresh token không hợp lệ`,invalid_save_user_token:`Không thể lưu token người dùng`,invalid_user_access_token:`Token truy cập người dùng không hợp lệ`,invalid_expired_access_token:`Token truy cập đã hết hạn`,invalid_notbefore_access_token:`Token truy cập chưa có hiệu lực`,invalid_models_init:`Danh sách model chưa được khởi tạo, hãy gọi initModels() trước`,validate_required:`{placeholder} không thể trống`,validate_requiredId:`{placeholder} không hợp lệ`,validate_minLen:`{placeholder} không thể nhỏ hơn {0} ký tự`,validate_equalLen:`{placeholder} phải có {0} ký tự`,validate_maxLen:`{placeholder} không thể lớn hơn {0} ký tự`,validate_min:`{placeholder} không thể nhỏ hơn {0}`,validate_max:`{placeholder} không thể lớn hơn {0}`,validate_rangeNum:`{placeholder} phải nằm trong khoản từ {0} đến {1}`,validate_integer:`{placeholder} phải là số`,validate_number:`{placeholder} không đúng định dạng`,validate_inArray:`{placeholder} không hợp lệ`,validate_inArrayNumber:`{placeholder} không hợp lệ`,validate_alphaNum:`Chỉ cho phép chữ cái và số cho {placeholder}`,validate_alphaNumWithSpaces:`Chỉ cho phép chữ cái, số và khoảng trắng cho {placeholder}`,validate_password:`Vui lòng nhập mật khẩu dài 8-32 ký tự, có ký tự chữ số, chữ hoa và chữ thường, ký tự @#$!%^&*`,validate_equals:`{placeholder} không đúng`,validate_notEqual:`{placeholder} không thể bằng {0}`,validate_email:`Email không đúng định dạng`,validate_regex:`{placeholder} không đúng định dạng yêu cầu`,validate_phoneVn:`{placeholder} không phải thuê bao Việt Nam`,validate_default:`Giá trị của {placeholder} không hợp lệ`},en:{invalid_403:`Access is not allowed`,invalid_404:`Information not found`,invalid_500:`Invalid information`,invalid_user:`Invalid user`,invalid_permission_403:`You do not have permission to access this resource`,invalid_user_token:`Invalid user token`,invalid_expired_refresh_token:`Refresh token has expired`,invalid_method_403:`Method is not allowed`,invalid_importfile:`Invalid uploaded file`,invalid_isDeleted:`Invalid deletion status`,invalid_buffer:`Invalid buffer data`,invalid_controller:`Invalid controller`,invalid_refresh_token:`Invalid refresh token`,invalid_save_user_token:`Unable to save user token`,invalid_user_access_token:`Invalid user access token`,invalid_expired_access_token:`Access token has expired`,invalid_notbefore_access_token:`Access token is not active yet`,invalid_models_init:`Models have not been initialized, call initModels() first`,validate_required:`{placeholder} cannot be empty`,validate_requiredId:`{placeholder} is invalid`,validate_minLen:`{placeholder} cannot be shorter than {0} characters`,validate_equalLen:`{placeholder} must be exactly {0} characters`,validate_maxLen:`{placeholder} cannot be longer than {0} characters`,validate_min:`{placeholder} cannot be less than {0}`,validate_max:`{placeholder} cannot be greater than {0}`,validate_rangeNum:`{placeholder} must be between {0} and {1}`,validate_integer:`{placeholder} must be a number`,validate_number:`{placeholder} is not in the correct format`,validate_inArray:`{placeholder} is invalid`,validate_inArrayNumber:`{placeholder} is invalid`,validate_alphaNum:`{placeholder} only allows letters and numbers`,validate_alphaNumWithSpaces:`{placeholder} only allows letters, numbers and spaces`,validate_password:`Please enter a password of 8-32 characters with digits, upper and lower case letters, and @#$!%^&*`,validate_equals:`{placeholder} is incorrect`,validate_notEqual:`{placeholder} cannot be equal to {0}`,validate_email:`Email is not in the correct format`,validate_regex:`{placeholder} does not match the required format`,validate_phoneVn:`{placeholder} is not a Vietnamese phone number`,validate_default:`The value of {placeholder} is invalid`}};let h={...m};function g(e){h={};for(let[t,n]of Object.entries(m))h[t]={...n,...e[t]??{}};for(let[t,n]of Object.entries(e))h[t]||(h[t]={...n})}function _(e,t=`vi`){return h[t]?.[e]??e}const v=new Set([`required`,`requiredId`,`minLen`,`equalLen`,`maxLen`,`min`,`max`,`rangeNum`,`integer`,`number`,`inArray`,`inArrayNumber`,`alphaNum`,`alphaNumWithSpaces`,`password`,`phoneVn`,`equals`,`notEqual`,`email`,`regex`]);var y=class e{errors=[];async runValidate(e,t={},n=`vi`){if(this.errors=[],Object.entries(t).length===0)return;let r=[];for(let n in t){if(!Object.prototype.hasOwnProperty.call(t,n))continue;let i=t[n],a=e?.[n]??``,o=n;r.push({field:n,value:a,rules:i,fieldName:o})}if(r.length&&(await this.validate(r,!1,n),this.getErrors().length)){let e=this.getErrors().join(`, `);throw Error(e)}}async validate(t,n=!1,r=`vi`){let i=!0;for await(let a of t){let t=a.filed,o=a.fieldName,s=a.value,c=e.splitRules(a.rules);for await(let e of c){let a=e,c=[];if(this.isruleHasArgs(e)?(a=this.getRuleName(e),c=a===`regex`?[this.getRegexPattern(e)]:this.getRuleArgs(e)):c=t??[],!v.has(a))throw Error(`Method doesnt exists: `+a);if(!await this[a](s,c)&&(this.addError(a,o,s,c,r),i=!1,n))return!1}}return i}isruleHasArgs(e){return e.split(`(`)[1]!==void 0}getRuleName(e){return e.split(`(`)[0]}getRuleArgs(e){e=e.trim();let t=e.split(`(`)[1];return t=t.endsWith(`)`)?t.slice(0,-1):t,t.split(`,`)}addError(t,n,r,i=[],a=`vi`){if(t){let o=e.defaultMessages(t,a);if(o){if(o.trim()!==``){r=typeof r==`string`?r:``;let e=o.replace(`{placeholder}`,n);i=typeof i==`string`?[i]:i,i.forEach((t,n)=>{e=e.replace(`{${n}}`,t)}),this.errors.push(e.replace(`{value}`,r))}}else this.errors.push(e.translate(`validate_default`,a).replace(`{placeholder}`,n))}}passes(){return this.errors.length===0}getErrors(){return this.errors}clearErrors(){this.errors=[]}required(e){return!(e==null||typeof e==`string`&&e.trim()===``)}requiredId(e){if(e==null)return!1;if(typeof e==`string`){if(e.trim()===``)return!1}else if(typeof e==`number`&&e===0)return!1;return!0}minLen(e,t){return e.length>=parseInt(t[0])}equalLen(e,t){return e.length===parseInt(t[0])}maxLen(e,t){return e.length<=parseInt(t[0])}rangeNum(e,t){return e>=parseInt(t[0])&&e<=parseInt(t[1])}max(e,t){return e<=Number(t[0])}min(e,t){return e>=Number(t[0])}integer(e){if(e==null)return!1;let t=String(e).trim();return t!==``&&Number.isInteger(Number(t))}inArray(e,t){return typeof e==`object`&&e?Object.values(e).every(e=>t.includes(String(e))):t.includes(String(e))}inArrayNumber(e,t){let n=t.map(Number);return e==null?!1:Array.isArray(e)?e.every(e=>n.includes(Number(e))):typeof e==`object`?Object.values(e).every(e=>n.includes(Number(e))):n.includes(e)}alphaNum(e){return!e||/^[a-z0-9]+$/i.test(e)}number(e){return typeof e==`number`&&e>=-(2**53-1)&&e<=2**53-1}alphaNumWithSpaces(e){return!e||/^[a-z0-9 ]+$/i.test(String(e))}password(e){return!e||/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#!%^&*])[0-9a-zA-Z@#!%^&*.]{8,32}$/.test(String(e))}phoneVn(e){if(!e)return!0;let t=String(e).replace(/[\s.\-()]/g,``);return t.startsWith(`00`)&&(t=t.slice(2)),t.startsWith(`+84`)?t=`0${t.slice(3)}`:t.startsWith(`84`)&&(t=`0${t.slice(2)}`),/^0[35789]\d{8}$/.test(t)}equals(e,t){return String(e)===t[0]}notEqual(e,t){return String(e)!==t[0]}static splitRules(e){let t=[],n=``;for(let r=0;r<e.length;r++){let i=e[r];if(i===`\\`&&e[r+1]===`|`){n+=`|`,r++;continue}if(i===`|`){t.push(n),n=``;continue}n+=i}return t.push(n),t}getRegexPattern(e){let t=e.indexOf(`(`),n=e.lastIndexOf(`)`);return t<0||n<=t?``:e.slice(t+1,n).trim()}regex(e,t){if(!e)return!0;let n=t[0]??``;if(!n||n.length>1e3)return!1;let r=n,i=``,a=/^\/([\s\S]*)\/([a-z]*)$/i.exec(n);a&&(r=a[1],i=a[2]);let o;try{o=new RegExp(r,i.replace(/[gy]/g,``))}catch{return!1}return o.test(String(e))}email(e){return!e||/^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}$/.test(e)}static defaultMessages(t,n=`vi`){let r=`validate_${t}`,i=e.translate(r,n);return i===r?null:i}static translate(e,t){let n=_(e,t);return n===e?t===`vi`?e:_(e,`vi`):n}};const b=/^[A-Za-z_][A-Za-z0-9_$]*$/;function x(e){if(typeof e!=`string`||e.trim().length===0||e.includes(`\0`)||e.length>64)throw Error(`invalid_identifier: ${JSON.stringify(e)}`);return`\`${e.replace(/`/g,"``")}\``}function S(e){if(typeof e!=`string`||e.trim().length===0)throw Error(`invalid_identifier: ${JSON.stringify(e)}`);return e.split(`.`).map(x).join(`.`)}function ee(e){let t=typeof e==`string`&&e.length>0&&e.trim()===e,n=t?e.split(`.`):[],r=n.length===1&&b.test(n[0]),i=n.length===2&&b.test(n[0])&&(n[1]===`*`||b.test(n[1]));if(!(t&&(e===`*`||r||i)))throw Error(`invalid_column_reference: ${JSON.stringify(e)}`)}function C(e){if(ee(e),e===`*`)return`*`;let t=e.split(`.`);return t[1]===`*`?`${x(t[0])}.*`:S(e)}function te(e){if(typeof e!=`string`||e.trim()===``)throw Error(`invalid_join_table: ${JSON.stringify(e)}`);let t=e.split(/\s+AS\s*/i);if(t.length>2)throw Error(`invalid_join_table: ${JSON.stringify(e)}`);let n=t[0].trim(),r=t[1]?.trim();if(!n||t.length===2&&!r)throw Error(`invalid_join_table: ${JSON.stringify(e)}`);return r?`${x(n)} ${x(r)}`:x(n)}function w(e){let t=String(e??``).trim().toUpperCase();if(t!==`ASC`&&t!==`DESC`)throw Error(`invalid_order_direction: ${JSON.stringify(String(e))}`);return t}const ne=new Set([`JOIN`,`INNER JOIN`,`LEFT JOIN`,`RIGHT JOIN`,`FULL JOIN`,`CROSS JOIN`,`LEFT OUTER JOIN`,`RIGHT OUTER JOIN`,`FULL OUTER JOIN`,`STRAIGHT_JOIN`]);function T(e){let t=String(e??``).trim().replace(/\s+/g,` `).toUpperCase()||`LEFT JOIN`;if(!ne.has(t))throw Error(`invalid_join_type: ${JSON.stringify(String(e??``))}`);return t}function E(e){let t=typeof e==`string`?e.split(`,`).map(e=>e.trim()):e;if(!Array.isArray(t)||t.length===0)throw Error(`invalid_select: expected at least one column`);return t.map(e=>{if(typeof e==`string`){if(e.length===0)throw Error(`invalid_select: empty column`);return C(e)}if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`invalid_select: ${JSON.stringify(e)}`);let t=C(e.column);return e.alias===void 0?t:`${t} AS ${x(e.alias)}`}).join(`, `)}const re={"=":`=`,"<>":`<>`,"!=":`<>`,"<=":`<=`,">=":`>=`,"<":`<`,">":`>`};function D(e,t,n){let r=re[t];if(!r)throw Error(`invalid_join_operator: ${JSON.stringify(t)}`);return`${C(e)} ${r} ${C(n)}`}function O(e){if(typeof e==`string`){let t=/^\s*([A-Za-z_][A-Za-z0-9_$]*(?:\.[A-Za-z_][A-Za-z0-9_$]*)?)\s*(<>|!=|<=|>=|=|<|>)\s*([A-Za-z_][A-Za-z0-9_$]*(?:\.[A-Za-z_][A-Za-z0-9_$]*)?)\s*$/.exec(e);if(!t)throw Error(`invalid_join_condition: ${JSON.stringify(e)}`);return D(t[1],t[2],t[3])}if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`invalid_join_condition: ${JSON.stringify(e)}`);return D(e.left,e.operator??`=`,e.right)}function k(e){return e instanceof Error?e:Error(String(e))}var ie=class{pool;isDeleted=!1;errors=[];validate;modifieds;schemaCache=new Map;constructor(e){this.pool=e}fieldName(e){return e}getErrors(){return this.errors}async getConnection(){return await this.pool.getConnection()}async runValidate(e,t={},n=`vi`){await new y().runValidate(e,t,n)}async query(e,t,n){return n?n.query(e,t):this.pool.query(e,t)}async modifiedSync(e){if(!this.modifieds)return e;let t=new Map;for(let n in this.modifieds){let{smodel:r,tkey:i,skey:a,fmap:o}=this.modifieds[n],s=e[i];if(s==null||s===``)continue;let c=t.get(r)??new Map,l=c.get(a);l||(l={smodel:r,skey:a,fmap:o,entries:[]},c.set(a,l),t.set(r,c)),l.entries.push({field:n,raw:s,key:String(s)})}for(let n of t.values())for(let{smodel:t,skey:r,fmap:i,entries:a}of n.values()){let n=[...new Map(a.map(e=>[e.key,e.raw])).values()],o=await t.find({[r]:{$in:n}}),s=new Map(o.map(e=>[String(e[r]),e]));for(let{field:t,key:n}of a){let r=s.get(n);r&&(e[t]=r[i]||``)}}return e}buildWhere(e,t){let n={...e};this.isDeleted&&n.isDeleted===void 0?n.isDeleted=0:this.isDeleted||delete n.isDeleted;let r=[],i=e=>typeof e==`boolean`?+!!e:e,a=e=>{let n=[];for(let o in e){let s=e[o];if(o===`$or`&&Array.isArray(s)){let e=s.map(e=>a(e)).filter(Boolean).map(e=>`(${e})`);e.length&&n.push(`(${e.join(` OR `)})`);continue}if(o===`$and`&&Array.isArray(s)){let e=s.map(e=>a(e)).filter(Boolean).map(e=>`(${e})`);e.length&&n.push(`(${e.join(` AND `)})`);continue}let c=t?`${x(t)}.${S(o)}`:S(o);if(s===null){n.push(`${c} IS NULL`);continue}if(typeof s==`object`&&s&&!Array.isArray(s)){let e=s,t=[];for(let n in e){let a=e[n];switch(n){case`$in`:{let e=a;e.length?(t.push(`${c} IN (${e.map(()=>`?`).join(`,`)})`),r.push(...e.map(i))):t.push(`1=0`);break}case`$nin`:{let e=a;e.length?(t.push(`${c} NOT IN (${e.map(()=>`?`).join(`,`)})`),r.push(...e.map(i))):t.push(`1=1`);break}case`$ne`:a===null?t.push(`${c} IS NOT NULL`):(t.push(`${c} <> ?`),r.push(i(a)));break;case`$gt`:t.push(`${c} > ?`),r.push(i(a));break;case`$gte`:t.push(`${c} >= ?`),r.push(i(a));break;case`$lt`:t.push(`${c} < ?`),r.push(i(a));break;case`$lte`:t.push(`${c} <= ?`),r.push(i(a));break;case`$between`:{let e=a;if(!Array.isArray(a)||e.length!==2)throw Error(`$between requires [min, max]`);t.push(`${c} BETWEEN ? AND ?`),r.push(i(e[0]),i(e[1]));break}case`$like`:t.push(`${c} LIKE ?`),r.push(`%${String(a)}%`);break;case`$likeIn`:{let e=a;if(!e.length)t.push(`1=0`);else{let n=e.map(()=>`${c} LIKE ?`).join(` OR `);t.push(`(${n})`),r.push(...e.map(e=>`%${e}%`))}break}default:throw Error(`Unsupported operator: ${n}`)}}t.length&&n.push(`(${t.join(` AND `)})`)}else n.push(`${c} = ?`),r.push(i(s))}return n.join(` AND `)};return{sql:a(n)||`1=1`,params:r}}async count(e={}){let t=`SELECT COUNT(*) AS total FROM ${x(this.table)}`,n=[],r=this.buildWhere(e);r.sql&&(t+=` WHERE ${r.sql}`,n=r.params);let[i]=await this.query(t,n);return i?.[0]?.total??0}async insert(e,t){let n=Object.keys(e),r=Object.values(e),i=Array(n.length).fill(`?`).join(`, `),a=`INSERT INTO ${x(this.table)} (${n.map(x).join(`, `)}) VALUES (${i})`,[o]=await this.query(a,r,t);return o.insertId}async update(e,t,n){if(!Object.keys(e).length)return 0;if(!t||Object.keys(t).length===0)throw Error(`invalid_update_filter`);let r=this.buildWhere(t),i=Object.values(e),a=Object.keys(e).map(e=>`${x(e)} = ?`).join(`, `),o=`UPDATE ${x(this.table)} SET ${a} WHERE ${r.sql}`,s=[...i,...r.params],[c]=await this.query(o,s,n);return c.affectedRows}async updateAdv(e={},t={},n){if(!Object.keys(e).length)return;let r=this.buildWhere(t),i=[],a=[];for(let[t,n]of Object.entries(e))i.push(`${x(t)} = ?`),a.push(n);let o=`UPDATE ${x(this.table)} SET ${i.join(`, `)}`;r.sql&&(o+=` WHERE ${r.sql}`,a.push(...r.params));let[s]=await this.query(o,a,n);return s.affectedRows}async saveResult(e,t){try{if(e.id){let n={...e};return delete n.id,Object.keys(n).length===0||await this.update(n,{id:e.id},t),{ok:!0,item:e}}return e.id=await this.insert(e,t),{ok:!0,item:e}}catch(e){return{ok:!1,errors:[k(e).message]}}}async save(e,t){let n=await this.saveResult(e,t);return this.errors=n.ok?[]:n.errors,n.ok?n.item:!1}async vdSave(e,t=!1,n,r=`vi`){e=await this.modifiedSync(e),t&&await this.runValidate(e,t,r);let i=await this.saveResult(e,n);if(!i.ok)throw Error(i.errors.join(`, `));return i.item}async updateMany(e,t,n){if(e.length)try{let r=Object.keys(e[0]).filter(e=>e!==t),i=e.map(e=>e[t]),a=r.map(n=>{let r=e.map(e=>`WHEN ? THEN ?`).join(` `);return`${x(n)} = CASE ${x(t)} ${r} ELSE ${x(n)} END`}).join(`, `),o=[];r.forEach(n=>{e.forEach(e=>{o.push(e[t]),o.push(e[n])})}),o.push(...i);let s=`UPDATE ${x(this.table)} SET ${a} WHERE ${x(t)} IN (${i.map(()=>`?`).join(`,`)});`;await this.query(s,o,n)}catch(e){throw console.log(k(e).message),Error(`invalid_update_many`)}}async updateAndCreateMany(e,t,n){if(e.length)try{let r=Object.keys(e[0]),i=r.map(x),a=e.map(()=>`(${r.map(()=>`?`).join(`,`)})`).join(`,`),o=e.flatMap(e=>r.map(t=>e[t])),s=r.filter(e=>e!==t).map(e=>`${x(e)} = VALUES(${x(e)})`).join(`, `),c=`INSERT INTO ${x(this.table)} (${i.join(`,`)}) VALUES ${a}`;s&&(c+=` ON DUPLICATE KEY UPDATE ${s}`),c+=`;`,await this.query(c,o,n)}catch(e){throw console.log(k(e).message),Error(`invalid_update_and_create`)}}async find(e={},t=!1,n=!1,r=!1,i=0){let a=`SELECT ${x(this.table)}.*`,o=`FROM ${x(this.table)}`,s=[];if(n){let e=Array.isArray(n)?n:[n],t=[];for(let n of e){let e=T(n.type);o+=` ${e} ${x(n.table)} ON ${O(n.on)}`,n.fields&&n.fields.length&&t.push(n.fields.map(e=>`${x(n.table)}.${x(e)} AS ${x(`${n.table}_${e}`)}`).join(`, `))}t.length&&(a=`SELECT ${x(this.table)}.*, ${t.join(`, `)}`)}let c=this.buildWhere(e);if(c.sql&&(o+=` WHERE ${c.sql}`,s=s.concat(c.params)),r&&(a=`SELECT ${E(r)}`),t){let e=Object.entries(t).map(([e,t])=>`${S(e)} ${w(t)}`);e.length&&(o+=` ORDER BY ${e.join(`, `)}`)}i&&(o+=` LIMIT ?`,s.push(Math.max(0,Math.trunc(Number(i)))));let[l]=await this.query(`${a} ${o}`,s);return l}async findOne(e={},t=!1,n=!1,r=!1){let i=await this.find(e,t,n,r,1);return i&&i.length?i[0]:null}async findById(e,t={}){return t.id=e,await this.findOne(t)}async deleteOne(e,t){return await this.deleteMany(e,1,t)}async deleteById(e,t){let n={};return n.id=e,await this.deleteOne(n,t)}async deleteMany(e,t=0,n){if(!e||Object.keys(e).length===0)throw Error(`deleteMany requires a filter condition`);let r=this.buildWhere(e);if(!r.sql)throw Error(`deleteMany requires a filter condition`);let i=`DELETE FROM ${x(this.table)} WHERE ${r.sql}`;t&&(i+=` LIMIT ?`);let a=t?[...r.params,Math.max(0,Math.trunc(Number(t)))]:r.params,[o]=await this.query(i,a,n);return!!o.affectedRows&&o.affectedRows>0}async isField(e,t=this.table){return(await this.getSchemaFields(t)).includes(e)}async isFields(e=[]){if(!e.length)return[];let t=await this.getSchemaFields(this.table);if(e.filter(e=>!t.includes(e)).length>0)throw Error(`invalid_fields`);return e}async getSchemaFields(e){let t=this.schemaCache.get(e);if(t)return t;let[n]=await this.query(`SHOW COLUMNS FROM ${x(e)}`),r=n.map(e=>String(e.Field));return this.schemaCache.set(e,r),r}clearSchemaCache(){this.schemaCache.clear()}async getMapName(e,t,n,r,i=`name`){let a=r?.[e];if(t&&t!==a){let r=await n?.findOne?.({[e]:t});return r&&r[i]?r[i]:``}return``}async findWithPagination(e={},t=!1,n=!1,r=!1,i=1,a=10){i=Math.max(1,Math.trunc(Number(i)||1)),a=Math.max(1,Math.trunc(Number(a)||1));let o=(i-1)*a,s=`FROM ${x(this.table)}`,c=[],l=`SELECT ${x(this.table)}.*`;if(n){let e=Array.isArray(n)?n:[n],t=[];for(let n of e){let e=T(n.type);s+=` ${e} ${x(n.table)} ON ${O(n.on)}`,n.fields?.length&&t.push(n.fields.map(e=>`${x(n.table)}.${x(e)} AS ${x(`${n.table}_${e}`)}`).join(`, `))}t.length&&(l=`SELECT ${x(this.table)}.*, ${t.join(`, `)}`)}let u=this.buildWhere(e);u.sql&&(s+=` WHERE ${u.sql}`,c=c.concat(u.params)),r&&(l=`SELECT ${E(r)}`);let d=`SELECT COUNT(*) AS total ${s}`,[f]=await this.query(d,c),p=f?.[0]?.total||0,m=`${l} ${s}`;if(t){let e=Object.entries(t).map(([e,t])=>`${S(e)} ${w(t)}`);e.length&&(m+=` ORDER BY ${e.join(`, `)}`)}m+=` LIMIT ? OFFSET ?`;let h=c.concat([a,o]),[g]=await this.query(m,h);return{page:i,length:a,pageTotal:Math.ceil(p/a),recordTotal:p,items:g||[]}}};async function ae(t){return await e.hash(t)}async function oe(t,n){return await e.verify(n,t)}function A(e){return typeof e==`string`?e.replace(/[&<>"'`]/g,e=>se[e]):``}const se={"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`,"`":`&#96;`};function ce(e,t=!1){let n=t?`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`:`0123456789`,i=``;for(let t=0;t<e;t++)i+=n.charAt(r(n.length));return i}function le(e){return e?String(e).normalize(`NFD`).replace(/[\u0300-\u036f]/g,``).replace(/đ/g,`d`).replace(/Đ/g,`D`).toLowerCase():``}function j(e){return e.toString().toLowerCase().normalize(`NFD`).replace(/[\u0300-\u036f]/g,``).replace(/[^a-z0-9\s-]/g,``).trim().replace(/\s+/g,`-`).replace(/-+/g,`-`)}async function ue(e,t={},n=`POST`,r=`application/json`,i={}){let a={method:n,headers:{"Content-Type":r,...i}};return n===`POST`&&(a.body=JSON.stringify(t)),await(await fetch(e,a)).json()}function de(e=``){return e?e.toLowerCase().replace(/[^a-z0-9\s]/g,``).split(/\s+/).filter((e,t,n)=>e.length>2&&n.indexOf(e)===t).join(`, `):``}function fe(e){let t=e instanceof Date?e:new Date(e);if(isNaN(t.getTime()))throw Error(`Ngày không hợp lệ: `+e);return t}function M(e,t=`YYYY-MM-DD HH:mm:ss`){if(!e)return``;let n=typeof e==`string`?new Date(e):e;if(isNaN(n.getTime()))return``;let r=e=>e.toString().padStart(2,`0`),i={YYYY:n.getFullYear().toString(),MM:r(n.getMonth()+1),DD:r(n.getDate()),HH:r(n.getHours()),mm:r(n.getMinutes()),ss:r(n.getSeconds())};return t.replace(/YYYY|MM|DD|HH|mm|ss/g,e=>i[e])}function N(e,t){return{start:new Date(e,t-1,1),end:new Date(e,t,0,23,59,59,999)}}function P(e,t){let n=new Date(e,t,0).getDate();return Array.from({length:n},(e,t)=>t+1)}function F(){return a(new Date,`yyyy-MM-dd HH:mm:ss`,{timeZone:`Asia/Ho_Chi_Minh`})}function I(e,t,n=``){let r=e;return r?.[t]===void 0?n:r[t]}function L(e,t){return e?.reduce((e,n)=>e+(n[t]??0),0)||0}function R(e){let t=[{value:1e3,numeral:`M`},{value:900,numeral:`CM`},{value:500,numeral:`D`},{value:400,numeral:`CD`},{value:100,numeral:`C`},{value:90,numeral:`XC`},{value:50,numeral:`L`},{value:40,numeral:`XL`},{value:10,numeral:`X`},{value:9,numeral:`IX`},{value:5,numeral:`V`},{value:4,numeral:`IV`},{value:1,numeral:`I`}],n=``;for(let{value:r,numeral:i}of t)for(;e>=r;)n+=i,e-=r;return n}function z(e,t=!1){let n=[{id:1,name:`Hoạt động`,color:`success`},{id:0,name:`Khóa`,color:`danger`}];if(e===!1)return n;let r=n.find(t=>t.id===e);return r?t?`<span class="badge badge-sm bg-gradient-${r.color}">${r.name}</span>`:r.name:``}function B(e,t=!1){let n=[{id:1,name:`Xuất bản`,color:`success`},{id:0,name:`Nháp`,color:`danger`}];if(e===!1)return n;let r=n.find(t=>t.id===e);return r?t?`<span class="badge badge-sm bg-gradient-${r.color}">${r.name}</span>`:r.name:``}function V(e,t=!1){let n=[{id:1,name:`Có`,color:`success`},{id:0,name:`Không`,color:`secondary`}];if(e===!1)return n;let r=n.find(t=>t.id===e);return r?t?`<span class="badge badge-sm bg-gradient-${r.color}">${r.name}</span>`:r.name:``}function pe(e,t){let n=new Set(e),r=new Set(t);return[...e.filter(e=>!r.has(e)),...t.filter(e=>!n.has(e))]}function me(e,t){let n=new Set(t);return e.every(e=>n.has(e))}function he(e,t){if(!Array.isArray(e)||!Array.isArray(t))return[];let n=new Set(t);return e.filter(e=>!n.has(e))}function ge(e,t){if(!Array.isArray(e)||e.length===0)return[];if(!t)return e;let n=Array(e.length);for(let r=0;r<e.length;r++){let i=e[r];n[r]=i&&typeof i==`object`?i[t]:void 0}return n}function _e(e,t,n){return e.reduce((e,r)=>{let i=!0;if(n&&(i=Object.entries(n).every(([e,t])=>r[e]===t)),i){let n=r[t];return e+(typeof n==`number`?n:0)}return e},0)}function H(e){let t=String(e).replace(/[^\d.]/g,``);return t?new o.Decimal(t):new o.Decimal(0)}function U(e,t={}){if(Array.isArray(e)||typeof e==`object`&&e)return e;if(typeof e==`number`)return t;if(typeof e==`string`)try{let n=JSON.parse(e);return Array.isArray(n)||typeof n==`object`&&n?n:t}catch{return t}return t}function ve(e=!1){let t=[{id:1,name:`Tiếng Việt`}];return e===!1?t:t.filter(t=>e.includes(t.id))}function W(e,t){let r=n(`sha256`).update(String(e),`utf8`).digest(),a=n(`sha256`).update(String(t),`utf8`).digest();return i(r,a)}const ye=new Set([`POST`,`PUT`,`PATCH`,`DELETE`]);function G(e){return ye.has(String(e).toUpperCase())}function K(e){let t=String(e?.headers?.[`x-csrf-token`]??e?.headers?.[`X-CSRF-Token`]??``),n=e?.body??{},r=String(n.csrfToken??n.csrf_token??``);return t||r||``}function be(e,t,n={}){if(!G(e?.method||`GET`))return{allowed:!0,reason:null};let r=t?.csrfToken||n.expectedToken||``,i=K(e);return!r||!i||!W(r,i)?{allowed:!1,reason:`missing_csrf_token`}:{allowed:!0,reason:null}}var q=class e{files;uploadType=`static`;constructor(e,t=`static`){this.files=e,this.uploadType=t}static resolveInside(e,t){let n=c.resolve(e);if(!t)return n;let r=c.resolve(n,t),i=c.relative(n,r);if(!i||i===`..`||i.startsWith(`..${c.sep}`)||c.isAbsolute(i))throw Error(`invalid_upload_folder: ${JSON.stringify(t)}`);return r}async copyFiles(t){if(t.length){let n=e.getUploadDir(this.uploadType);for await(let r of t){let t=e.resolveInside(n,r.src);if(s.existsSync(t)){let i=e.resolveInside(n,r.dist);await s.promises.copyFile(t,i)}}}}async uploadFile(e=`filedname`,t=``){try{let n=this.files[e];return!n||!n[0]?!1:await this.upload(n[0],t)}catch(e){throw Error(String(e))}}async uploadFiles(e=`filedname`,t=``){try{let n=[],r=this.files[e];if(!r||!r.length)return[];for await(let e of r){let r=await this.upload(e,t);r&&n.push(r)}return n}catch(e){throw Error(String(e))}}async upload(t,n=``){try{let r=Date.now()+`___`+c.basename(t.filename),i=e.getUploadDir(this.uploadType),a=e.resolveInside(i,n);s.existsSync(a)||s.mkdirSync(a,{recursive:!0});let o=c.join(a,r);return s.existsSync(t.path)?(await s.promises.rename(t.path,o),n?c.posix.join(n,r):r):!1}catch(e){throw Error(String(e))}}async checkFile(t=`file`,n=`OfficeFile`){try{let r=this.files[t];if(!r)return;for await(let t of r){let r=`check`+n;if(!(r in e))throw Error(`Method does not exist: `+r);e[r](t.filename,t.mimetype);return}return}catch(e){throw Error(String(e))}}static removeFiles=(e,t=`static`)=>{if(e===!1||!Array.isArray(e))return!1;for(let n of e)this.removeFile(n,t);return!0};static removeFile=(t,n=`static`)=>{if(!t)return!1;let r=e.resolveInside(e.getUploadDir(n),t);return s.existsSync(r)&&s.unlinkSync(r),!0};static renameFile=e=>{let t=c.extname(e);return`${c.basename(e,t)}_${Date.now()}${t}`};static getUploadDir(e=`static`){return e==`static`?c.join(c.resolve(),`public`,`static`):e==`public`?c.join(c.resolve(),`public`):e==`uploads`?c.join(c.resolve(),`uploads`):``}static checkDefFile(e,t,n=!0){if(!/(.*)\.(png|jpeg|jpg|ico|pdf|xls|xlsx|doc|docx|ppt|pptx)$/i.test(e)){if(n)throw Error(`Định dạng tập tin ${e} không được phép`);return!1}if(![`image/x-icon`,`image/jpeg`,`image/png`,`image/jpg`,`application/pdf`,`application/msword`,`application/vnd.ms-excel`,`application/vnd.ms-powerpoint`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`].includes(t)){if(n)throw Error(`Định dạng tập tin ${e} không được phép`);return!1}return!0}static checkOfficeFile(e,t){try{if(!/(.*)\.(pdf|xls|xlsx|doc|docx|ppt|pptx)$/i.test(e)||![`application/pdf`,`application/msword`,`application/vnd.ms-excel`,`application/vnd.ms-powerpoint`,`application/vnd.openxmlformats-officedocument.presentationml.presentation`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`].includes(t))throw Error(`Tập tin ${e} không đúng định dạng (.pdf,.xls,.xlsx,.doc,.docx,.ppt,.pptx)`)}catch(e){throw Error(String(e))}}static checkImgFile(e,t){try{if(!/(.*)\.(png|jpeg|jpg|ico)$/i.test(e)||![`image/jpeg`,`image/png`,`image/jpg`,`image/x-icon`].includes(t))throw Error(`Ảnh ${e} không đúng định dạng (.png,.jpeg,.jpg,.ico)`)}catch(e){throw Error(String(e))}}static checkVideoFile(e,t){try{if(!/(.*)\.(mp4|mkv|webm|mov|avi)$/i.test(e)||![`video/mp4`,`video/mkv`,`video/webm`,`video/mov`,`video/avi`].includes(t))throw Error(`Video ${e} không đúng định dạng (.mp4,.mkv,.webm,.mov,.avi)`)}catch(e){throw Error(String(e))}}},xe=class e{req;body={};files={};constructor(e){this.req=e}start=async()=>{if(this.req.body)this.body=this.req.body;else if(this.req.isMultipart()){let e=c.join(c.resolve(),`uploads`,`tmb`);s.existsSync(e)||s.mkdirSync(e,{recursive:!0});for await(let t of this.req.parts())if(t.type===`field`){let e=this.body[t.fieldname];e===void 0?this.body[t.fieldname]=t.value:Array.isArray(e)?e.push(t.value):this.body[t.fieldname]=[e,t.value]}else if(q.checkDefFile(t.filename,t.mimetype,!1)){let n=c.extname(t.filename),r=j(c.basename(t.filename,n)),i=r+n,a=c.join(e,i);try{await f(t.file,s.createWriteStream(a))}catch(e){let t=e;t.code===`ABORT_ERR`?console.error(`The user canceled the upload.`):t.code===`ERR_STREAM_PREMATURE_CLOSE`?console.error(`The stream closed prematurely.`):console.error(`Upload error:`,t.message)}this.files[t.fieldname]===void 0&&(this.files[t.fieldname]=[]),this.files[t.fieldname].push({filename:i,basename:r,path:a,mimetype:t.mimetype,ext:n})}else t.file.resume()}};end=async()=>{let e=Object.values(this.files);for await(let t of e)for await(let e of t)s.existsSync(e.path)&&s.unlinkSync(e.path)};isPost(){return this.req.method===`POST`}isGet(){return this.req.method===`GET`}isAjax(){return this.req.headers[`x-requested-with`]===`XMLHttpRequest`}getPost(t=``,n=``,r=`stripTags`){if(t===``)return this.body;if(!this.hasPost(t))return n;let i=this.body[t];return typeof i==`object`&&i&&`value`in i&&i.value!==void 0&&(i=i.value),e.filterSafeData(i,r,n)}getLangPost(t,n=``,r=`stripTags`){if(!t)return n;let i=t.match(/^([^{]+)\{([^}]+)\}$/);if(!i)return n;let a=i[1],o=i[2],s=this.body?.[a];typeof s==`string`&&(s=U(s));let c=typeof s==`object`&&s?s[o]:void 0;return e.filterSafeData(c,r,n)}getParam(t,n=null,r=`stripTags`){let i=this.req.params;return!i||!i[t]?n:e.filterSafeData(i[t],r)}get(t=``,n=``,r=`stripTags`){let i=this.req.query;return t===``?i:this.has(t)?e.filterSafeData(i[t],r):n}has(e){let t=this.req.query;return Array.isArray(e)?e.every(e=>t[e]!==void 0):t[e]!==void 0}hasPost(e){return Array.isArray(e)?e.every(e=>this.body[e]!==void 0):this.body[e]!==void 0}getHeader(e){return this.req.headers[e.toLowerCase()]?.toString()||``}static filterSafeData(t,n=`stripTags`,r=``){if(typeof t==`number`)return t;let i=e=>Array.isArray(e)||typeof e==`string`?e:[e];if(n===`ids`)return e.filterIds(i(t));if(n===`stringIds`)return e.filterStringIds(i(t));if(n===`strings`)return Array.isArray(t)?e.filterStrings(t):e.filterStrings([t]);if(n===`dQuoteIds`)return e.filterDQuoteIds(i(t));if(n===`int`||n===`number`){let e=Number(t);if(e>2**53-1)throw Error(`invalid_max_safe_integer`);if(e<-(2**53-1))throw Error(`invalid_min_safe_integer`);return Number.isNaN(e)?r:n===`int`?Math.trunc(e):e}if(n===`decimal`)return H(String(t));if(typeof t==`string`){if(n===`stripTags`)return t.replace(/<[^>]*>?/gm,``).trim();if(n===`html`)return e.filterHtml(t);if(n===`date`||n===`datetime`){let e=d(t);return u(e)?l(e,n===`date`?`yyyy-MM-dd`:`yyyy-MM-dd HH:mm:ss`):null}}return n===`raw`?e.parseRawInput(t):typeof t===n?t:``}static parseRawInput(e){if(typeof e!=`string`)return e;try{return JSON.parse(e)}catch{return e}}static filterHtml(e){return A(e)}static filterIds(e){if(typeof e==`string`){let t=U(e,void 0);if(Array.isArray(t))e=t;else if(!Number.isNaN(Number(e)))e=[Number(e)];else return[]}return Array.isArray(e)?[...new Set(e.map(e=>Number(e)).filter(e=>!Number.isNaN(e)))]:[]}static filterStringIds(e){return typeof e==`string`&&(e=U(e,[])),Array.isArray(e)?this.filterIds(e).map(e=>e.toString()):[]}static filterStrings(e){return[...new Set(e.map(e=>this.filterSafeData(e,`stripTags`)))]}static filterDQuoteIds(e){return this.filterIds(e).map(e=>`%"${e}"%`)}},J=class extends Error{statusCode;code;constructor(e,t=400,n){super(e),this.statusCode=t,this.code=n,this.name=`ClientError`,Object.setPrototypeOf(this,new.target.prototype)}};function Y(){let e=process.env.JWT_KEY;if(!e)throw Error(`missing_jwt_key`);return e}function Se(e){switch(e?.name){case`TokenExpiredError`:return new J(`invalid_expired_access_token`,401,`invalid_expired_access_token`);case`NotBeforeError`:return new J(`invalid_notbefore_access_token`,401,`invalid_notbefore_access_token`);case`JsonWebTokenError`:case`TokenSignatureMismatchError`:return new J(`invalid_user_access_token`,401,`invalid_user_access_token`);default:return e instanceof Error?e:Error(String(e))}}var Ce=class{static createToken(e,t){let{JWT_AUD:n,JWT_ISS:r}=process.env,i=Number(process.env.JWT_TIMEOUT||3600),a=t.ip,o=t.headers[`user-agent`],s=Math.floor(Date.now()/1e3),c={iss:r,aud:n,sub:e.id,name:e.fullname,ip:a,us:o,iat:s,exp:s+i,expired:(s+i)*1e3};return p.sign(c,Y(),{algorithm:`HS256`})}static verifyToken(e,t=!1){let n;if(t){if(n=e.cookies?.accessToken,!n)throw new J(`invalid_cookie_access_token`,401,`invalid_cookie_access_token`)}else{let t=e.headers.authorization;if(!t)throw Error(`missing_authorization`);let[r,i]=t.split(` `);if(r!==`Bearer`||!i)throw new J(`invalid_authorization`,401,`invalid_authorization`);n=i}let r=Y(),i;try{i=p.verify(n,r)}catch(e){throw Se(e)}if(!i?.sub)throw new J(`invalid_userId_access_token`,401,`invalid_userId_access_token`);if(typeof i.expired==`number`&&i.expired<=Date.now())throw new J(`invalid_expired_access_token`,401,`invalid_expired_access_token`);let a=e.headers[`user-agent`]||`unknown`;if(i.us!==a)throw new J(`invalid_us_access_token`,401,`invalid_us_access_token`);if(i.ip&&i.ip!==e.ip)throw new J(`invalid_ip_access_token`,401,`invalid_ip_access_token`);return i.accessToken=n,i}static checkSecretKey(e){if(!process.env.JWT_SECRET_KEY)throw Error(`missing_jwt_secret_key`);if(e!==process.env.JWT_SECRET_KEY&&!W(e,process.env.JWT_SECRET_KEY))throw new J(`invalid_secret_key`,403,`invalid_secret_key`)}};let X=null;function Z(e,t){return Object.fromEntries(Object.entries(e).map(([e,n])=>[e,new n(t)]))}function we(e,t){return X||=Z(e,t),X}function Te(){if(!X)throw Error(_(`invalid_models_init`));return X}function Q(e){return e instanceof Error?e:Error(String(e))}var Ee=class{pool;ttlDays;constructor(e,t){this.pool=e;let n=t??Number(process.env.SESSION_TTL_DAYS);this.ttlDays=Number.isFinite(n)&&n>0?Math.trunc(n):14}async get(e,t){try{let[n]=await this.pool.query(`SELECT data FROM sessions WHERE sid = ?`,[e]);if(!n.length)return t(null,null);t(null,JSON.parse(n[0].data))}catch(e){t(Q(e))}}async set(e,t,n){try{let r=JSON.stringify(t);await this.pool.query(`INSERT INTO sessions (sid, data, expires) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL ? DAY)) AS new ON DUPLICATE KEY UPDATE data = new.data, expires = new.expires`,[e,r,this.ttlDays]),n(null)}catch(e){n(Q(e))}}async destroy(e,t){try{await this.pool.query(`DELETE FROM sessions WHERE sid = ?`,[e]),t(null)}catch(e){t(Q(e))}}},De=class{routes=[];static getMethod(e){return e.method?e.method:`get`}add(e,t=[]){let n=t.map(t=>({...t,link:e+t.link}));this.routes=[...this.routes,...n]}addGS(e,t,n,r={}){let i=r.codeParam??`:code([0-9]+)`,a=[{link:e+`/getList`,module:n,controller:t,action:`index`},{link:e+`/detail/`+i,module:n,controller:t,action:`detail`},{link:e+`/create`,module:n,controller:t,action:`update`,method:`post`},{link:e+`/edit/`+i,module:n,controller:t,action:`update`,method:`post`},{link:e+`/copy`,module:n,controller:t,action:`copy`,method:`post`},{link:e+`/import`,module:n,controller:t,action:`import`,method:`post`},{link:e+`/export`,module:n,controller:t,action:`export`},{link:e+`/delete/`+i,module:n,controller:t,action:`deleteOne`,method:`post`},{link:e+`/delete`,module:n,controller:t,action:`delete`,method:`post`}];this.routes=[...this.routes,...a]}getRouter(){return this.routes}};const Oe=new Set([`password`,`token`,`accesstoken`,`refreshtoken`,`secret`,`apikey`,`authorization`,`privatekey`,`clientsecret`]);function $(e,t=``){return e==null||typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:Array.isArray(e)?e.map(e=>$(e,t)):typeof e==`object`?Object.entries(e).reduce((e,[t,n])=>{let r=t.toLowerCase().replace(/[_\-\s]/g,``);return Oe.has(r)||r.includes(`password`)||r.includes(`token`)||r.includes(`secret`)||(e[t]=$(n,t)),e},{}):e}function ke(e,t,n=[]){let r=new Set([...n,`updatedAt`,`createdAt`,`__v`]),i={},a={};return Object.keys(e??{}).forEach(n=>{if(r.has(n))return;let o=t?.[n],s=e?.[n];o!==s&&(i[n]={old:o,new:s}),a[n]=s}),{difference:$(i),item:$(a)}}function Ae(e,t){return e&&e.length>0?e:!t}function je(e){let n=process.env.HMAC_SECRET,r=JSON.stringify(e);return t.createHmac(`sha256`,n).update(r).digest(`hex`)}async function Me(e,n){let r=new TextEncoder,i=await t.subtle.importKey(`raw`,r.encode(n),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]),a=await t.subtle.sign(`HMAC`,i,r.encode(JSON.stringify(e)));return Array.from(new Uint8Array(a)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}export{ie as BaseModel,J as ClientError,xe as FastRequest,q as FileUpload,Ce as JWTApp,Ee as MySQLSessionStore,De as Route,y as Validation,T as assertJoinType,w as assertOrderDirection,ke as buildAuditLogData,O as buildJoinCondition,te as buildJoinTable,E as buildSelectList,ue as callFetchApi,oe as checkPassword,Me as createHmac,Z as createModels,je as createSignature,H as decimalNumber,be as ensureCsrfProtection,C as escapeColumnReference,A as escapeHtml,x as escapeIdentifier,S as escapeQualifiedName,M as formatDate,de as generateKeywords,z as getActiveStatus,ge as getArrColumn,he as getArrOnlyInFirst,Ae as getCorsOriginPolicy,K as getCsrfTokenFromRequest,P as getDaysInMonth,pe as getDiffArr,ve as getLangs,Te as getModels,N as getMonthRange,B as getPublishStatus,V as getTrueFlaseStatus,I as getValue,g as initI18n,we as initModels,G as isStateChangingMethod,me as isSubArr,ae as makePassword,fe as parseDate,ce as randomText,le as removeVietnameseTones,W as safeEqual,$ as sanitizeAuditPayload,j as slugify,U as stringToJson,L as sumArrCol,_e as sumArrColumn,_ as t,k as toError,F as toMySQLDateNowVN,R as toRoman};
package/package.json CHANGED
@@ -1,57 +1,71 @@
1
- {
2
- "name": "@fastify-core/base",
3
- "version": "1.0.7",
4
- "type": "module",
5
- "description": "",
6
- "types": "./dist/index.d.ts",
7
- "exports": {
8
- ".": {
9
- "types": "./dist/index.d.ts",
10
- "import": "./dist/index.mjs"
11
- }
12
- },
13
- "files": [
14
- "dist",
15
- "README.md"
16
- ],
17
- "scripts": {
18
- "test": "tsc --noEmit && oxlint .",
19
- "build": "tsdown",
20
- "pack": "npm run build && npm pack",
21
- "prepublishOnly": "npm run build"
22
- },
23
- "keywords": [],
24
- "author": "MIT",
25
- "license": "ISC",
26
- "peerDependencies": {
27
- "@fastify/session": "^11.1.2",
28
- "argon2": "^0.45.0",
29
- "date-fns": "^4.4.0",
30
- "date-fns-tz": "^3.2.0",
31
- "decimal.js": "^10.6.0",
32
- "dotenv": "^17.4.2",
33
- "fastify": "^5.10.0",
34
- "jsonwebtoken": "^9.0.3",
35
- "mysql2": "^3.23.3"
36
- },
37
- "devDependencies": {
38
- "@fastify/session": "^11.1.2",
39
- "argon2": "^0.45.0",
40
- "date-fns": "^4.4.0",
41
- "date-fns-tz": "^3.2.0",
42
- "decimal.js": "^10.6.0",
43
- "dotenv": "^17.4.2",
44
- "fastify": "^5.10.0",
45
- "jsonwebtoken": "^9.0.3",
46
- "mysql2": "^3.23.3",
47
- "@types/jsonwebtoken": "^9.0.10",
48
- "@types/node": "^26.2.0",
49
- "oxlint": "^1.74.0",
50
- "ts-node": "^10.9.2",
51
- "tsdown": "^0.22.14",
52
- "typescript": "^6.0.3"
53
- },
54
- "engines": {
55
- "node": ">=24"
56
- }
57
- }
1
+ {
2
+ "name": "@fastify-core/base",
3
+ "version": "1.0.8",
4
+ "type": "module",
5
+ "description": "",
6
+ "types": "./dist/index.d.mts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.mts",
10
+ "import": "./dist/index.mjs"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "oxlint": "tsc --noEmit && oxlint .",
19
+ "typecheck": "tsc --noEmit",
20
+ "lint": "oxlint .",
21
+ "test": "npm run build && npm run typecheck && npm run lint && npm run test:sql && npm run test:node",
22
+ "test:sql": "node --test scripts/sql-regression.test.mjs",
23
+ "test:unit": "npm run build && node --test \"test/unit/*.test.mjs\"",
24
+ "test:e2e": "npm run build && node --test \"test/e2e/*.test.mjs\"",
25
+ "test:node": "node --test \"test/**/*.test.mjs\"",
26
+ "build": "tsdown",
27
+ "pack": "npm run build && npm pack",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "keywords": [],
31
+ "author": "MIT",
32
+ "license": "ISC",
33
+ "peerDependencies": {
34
+ "@fastify/multipart": "^9.4.0",
35
+ "@fastify/session": "^11.1.3",
36
+ "argon2": "^0.45.0",
37
+ "date-fns": "^4.4.0",
38
+ "date-fns-tz": "^3.2.0",
39
+ "decimal.js": "^10.6.0",
40
+ "dotenv": "^18.0.3",
41
+ "fastify": "^5.12.5",
42
+ "jsonwebtoken": "^9.0.3",
43
+ "mysql2": "^3.23.3"
44
+ },
45
+ "peerDependenciesMeta": {
46
+ "@fastify/multipart": {
47
+ "optional": true
48
+ }
49
+ },
50
+ "devDependencies": {
51
+ "@fastify/multipart": "^9.4.0",
52
+ "@fastify/session": "^11.1.3",
53
+ "@types/jsonwebtoken": "^9.0.10",
54
+ "@types/node": "^26.6.2",
55
+ "argon2": "^0.45.0",
56
+ "date-fns": "^4.4.0",
57
+ "date-fns-tz": "^3.2.0",
58
+ "decimal.js": "^10.6.0",
59
+ "dotenv": "^18.0.3",
60
+ "fastify": "^5.12.5",
61
+ "jsonwebtoken": "^9.0.3",
62
+ "mysql2": "^3.23.3",
63
+ "oxlint": "^1.74.0",
64
+ "ts-node": "^10.9.2",
65
+ "tsdown": "^0.22.14",
66
+ "typescript": "^6.0.3"
67
+ },
68
+ "engines": {
69
+ "node": ">=24"
70
+ }
71
+ }