mirror of
https://github.com/Feuerhamster/mailform.git
synced 2025-04-29 02:07:42 +02:00
Add more logging and make origin optional
This commit is contained in:
parent
76808371eb
commit
21c410d3e0
6 changed files with 80 additions and 77 deletions
|
@ -73,7 +73,7 @@ They are JSON files placed in the `/targets` directory.
|
|||
|
||||
**Available fields:**
|
||||
- `smtp` *required* | A valid SMTP(S) url.
|
||||
- `origin` *required* | A HTTP origin that is used for CORS and to restrict access.
|
||||
- `origin` *optional* | A HTTP origin that is used for CORS and to restrict access. Default is * if not set.
|
||||
- `recipients` *required* | An array of email addresses which should receive the email.
|
||||
- `from` *optional* | The "from" field of an email. This is used as fallback if no "from" is provided in the request.
|
||||
- `key` *optional* | A string used as API key if you want to restrict access to this target.
|
||||
|
|
134
package-lock.json
generated
134
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "mailform",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"express": "^4.17.1",
|
||||
"nodemailer": "^6.6.0",
|
||||
|
@ -39,9 +39,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
|
||||
"integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==",
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz",
|
||||
"integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
|
@ -51,9 +51,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz",
|
||||
"integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz",
|
||||
"integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
|
@ -68,15 +68,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "15.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz",
|
||||
"integrity": "sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A==",
|
||||
"version": "15.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz",
|
||||
"integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/nodemailer": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.1.tgz",
|
||||
"integrity": "sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ==",
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.2.tgz",
|
||||
"integrity": "sha512-yhsqg5Xbr8aWdwjFS3QjkniW5/tLpWXtOYQcJdo9qE3DolBxsKzgRCQrteaMY0hos8MklJNSEsMqDpZynGzMNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
|
@ -286,9 +286,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
@ -373,19 +373,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.47.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
|
||||
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==",
|
||||
"version": "1.48.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
|
||||
"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.30",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
|
||||
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
|
||||
"version": "2.1.31",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
|
||||
"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.47.0"
|
||||
"mime-db": "1.48.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
|
@ -405,9 +405,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.0.tgz",
|
||||
"integrity": "sha512-ikSMDU1nZqpo2WUPE0wTTw/NGGImTkwpJKDIFPZT+YvvR9Sj+ze5wzu95JHkBMglQLoG2ITxU21WukCC/XsFkg==",
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.2.tgz",
|
||||
"integrity": "sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
|
@ -437,11 +437,11 @@
|
|||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dependencies": {
|
||||
"forwarded": "~0.1.2",
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -569,9 +569,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz",
|
||||
"integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz",
|
||||
"integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
@ -632,9 +632,9 @@
|
|||
}
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz",
|
||||
"integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==",
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.12.tgz",
|
||||
"integrity": "sha512-pTYas6FrP15B1Oa0bkN5tQMNqOcVXa9j4FTFtO8DWI9kppKib+6NJtfTOOLcwxuuYvcX2+dVG6et1SxW/Kc17Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
|
@ -644,9 +644,9 @@
|
|||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz",
|
||||
"integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz",
|
||||
"integrity": "sha512-gwCiEZqW6f7EoR8TTEfalyEhb1zA5jQJnRngr97+3pzMaO1RKoI1w2bw07TK72renMUVWcWS5mLI6rk1NqN0nA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
|
@ -661,15 +661,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "15.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz",
|
||||
"integrity": "sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A==",
|
||||
"version": "15.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz",
|
||||
"integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/nodemailer": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.1.tgz",
|
||||
"integrity": "sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ==",
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.2.tgz",
|
||||
"integrity": "sha512-yhsqg5Xbr8aWdwjFS3QjkniW5/tLpWXtOYQcJdo9qE3DolBxsKzgRCQrteaMY0hos8MklJNSEsMqDpZynGzMNg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
|
@ -846,9 +846,9 @@
|
|||
}
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
|
||||
},
|
||||
"fresh": {
|
||||
"version": "0.5.2",
|
||||
|
@ -906,16 +906,16 @@
|
|||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.47.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
|
||||
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw=="
|
||||
"version": "1.48.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
|
||||
"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.30",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
|
||||
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
|
||||
"version": "2.1.31",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
|
||||
"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
|
||||
"requires": {
|
||||
"mime-db": "1.47.0"
|
||||
"mime-db": "1.48.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
|
@ -929,9 +929,9 @@
|
|||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||
},
|
||||
"nodemailer": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.0.tgz",
|
||||
"integrity": "sha512-ikSMDU1nZqpo2WUPE0wTTw/NGGImTkwpJKDIFPZT+YvvR9Sj+ze5wzu95JHkBMglQLoG2ITxU21WukCC/XsFkg=="
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.2.tgz",
|
||||
"integrity": "sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
|
@ -952,11 +952,11 @@
|
|||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"requires": {
|
||||
"forwarded": "~0.1.2",
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
}
|
||||
},
|
||||
|
@ -1059,9 +1059,9 @@
|
|||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz",
|
||||
"integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz",
|
||||
"integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==",
|
||||
"dev": true
|
||||
},
|
||||
"unpipe": {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "mailform",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"description": "The minimalistic email relay for contact forms and more!",
|
||||
"main": "dist/main.js",
|
||||
"scripts": {
|
||||
|
|
|
@ -9,7 +9,7 @@ export const targetModel = {
|
|||
},
|
||||
origin: {
|
||||
type: "string",
|
||||
presence: { allowEmpty: false }
|
||||
presence: false
|
||||
},
|
||||
recipients: {
|
||||
type: "array",
|
||||
|
|
|
@ -21,12 +21,12 @@ router.use("/:target", async (req: Request, res: Response, next: NextFunction) =
|
|||
let target: Target = TargetManager.targets.get(req.params.target);
|
||||
|
||||
// CORS
|
||||
res.setHeader("Access-Control-Allow-Origin", target.origin);
|
||||
res.setHeader("Access-Control-Allow-Origin", target.origin ? target.origin : "*");
|
||||
res.setHeader("Access-Control-Allow-Method", "POST");
|
||||
res.setHeader("Access-Control-Allow-Headers", "*");
|
||||
|
||||
// Check origin
|
||||
if(target.origin !== req.header("origin")) {
|
||||
if(target.origin && target.origin !== req.header("origin")) {
|
||||
return res.status(403).end();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@ import nodemailer, {Transporter} from "nodemailer";
|
|||
import {TargetManager} from "./targetManager";
|
||||
import {Target} from "../@types/target";
|
||||
|
||||
let enableDebugLog = process.env["DEBUG_LOG"] || false;
|
||||
|
||||
export class EmailService {
|
||||
|
||||
private static targetTransports: Map<string, Transporter> = new Map<string, Transporter>();
|
||||
|
@ -59,19 +57,24 @@ export class EmailService {
|
|||
let transporter: Transporter = this.targetTransports.get(targetName);
|
||||
|
||||
try {
|
||||
let res = await transporter.sendMail({
|
||||
await transporter.sendMail({
|
||||
from,
|
||||
replyTo: from,
|
||||
to: target.recipients,
|
||||
subject,
|
||||
html: body
|
||||
});
|
||||
if(enableDebugLog) console.log(res);
|
||||
} catch (e) {
|
||||
if(enableDebugLog) console.error(e);
|
||||
console.error("[!] An error occurred while sending an email");
|
||||
console.error("* target: " + targetName);
|
||||
console.error("* " + e.message);
|
||||
return e;
|
||||
}
|
||||
|
||||
console.log(`Email successful sent`);
|
||||
console.log("* target: " + targetName);
|
||||
console.log("* time: " + new Date().toString());
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue