feat: add alioss or other s3 protocol external storage support #104

pull/128/head
moonrailgun 1 year ago
parent cb927ae079
commit 584997826b

@ -13,7 +13,7 @@
"dependencies": {
"express": "^4.18.2",
"fs-extra": "^11.1.0",
"tailchat-server-sdk": "workspace:^0.0.14"
"tailchat-server-sdk": "workspace:^"
},
"devDependencies": {
"@types/express": "^4.17.15",

@ -29,4 +29,4 @@ registerBBCodeTag('at', MentionTag);
registerBBCodeTag('emoji', EmojiTag);
registerBBCodeTag('markdown', MarkdownTag);
registerBBCodeTag('md', MarkdownTag); // alias
registerBBCodeTag('card', CardTag); // alias
registerBBCodeTag('card', CardTag);

@ -77,7 +77,6 @@
"mongoose-findorcreate": "3.0.0"
},
"patchedDependencies": {
"moleculer-minio@2.0.0": "patches/moleculer-minio@2.0.0.patch",
"moleculer@0.14.23": "patches/moleculer@0.14.23.patch",
"vite-express@0.8.0": "patches/vite-express@0.8.0.patch"
}

@ -1,22 +0,0 @@
diff --git a/src/errors/MinioInitializationError.js b/src/errors/MinioInitializationError.js
index 2022b251c5bc9c036c7456d6937c270f8f920d3a..ae9d3808d87f8fe0ee5e82076802da09a1a7f676 100644
--- a/src/errors/MinioInitializationError.js
+++ b/src/errors/MinioInitializationError.js
@@ -1,4 +1,5 @@
-const {MoleculerError} = require("moleculer/src/errors");
+const {Errors} = require('moleculer');
+const MoleculerError = Errors.MoleculerError;
/**
* Error that should be thrown when the Minio Service can not be Initialized
diff --git a/src/errors/MinioPingError.js b/src/errors/MinioPingError.js
index f73f9423f3407fe828ba99db556b2f8367483fa3..d03e31c2a223b6182c659b0c13beb7e1f11751d4 100644
--- a/src/errors/MinioPingError.js
+++ b/src/errors/MinioPingError.js
@@ -1,4 +1,5 @@
-const {MoleculerRetryableError} = require("moleculer/src/errors");
+const {Errors} = require('moleculer');
+const MoleculerRetryableError = Errors.MoleculerRetryableError;
/**
* Error that should be thrown when the Minio Backend can not be pinged

@ -20,9 +20,6 @@ overrides:
mongoose-findorcreate: 3.0.0
patchedDependencies:
moleculer-minio@2.0.0:
hash: 77awcwzrgh47fhn6qqq4ghcfau
path: patches/moleculer-minio@2.0.0.patch
moleculer@0.14.23:
hash: ahhlgpfy57fntn2aftq6beaeja
path: patches/moleculer@0.14.23.patch
@ -320,7 +317,7 @@ importers:
specifier: ^11.1.0
version: 11.1.0
tailchat-server-sdk:
specifier: workspace:^0.0.14
specifier: workspace:^
version: link:../../server/packages/sdk
devDependencies:
'@types/express':
@ -1391,9 +1388,6 @@ importers:
mkdirp:
specifier: ^1.0.4
version: 1.0.4
moleculer-minio:
specifier: ^2.0.0
version: 2.0.0(patch_hash=77awcwzrgh47fhn6qqq4ghcfau)(moleculer@0.14.23)
moment:
specifier: ^2.29.1
version: 2.29.4
@ -1723,6 +1717,9 @@ importers:
lodash:
specifier: ^4.17.21
version: 4.17.21
minio:
specifier: ^7.1.1
version: 7.1.1
moleculer:
specifier: 0.14.23
version: 0.14.23(patch_hash=ahhlgpfy57fntn2aftq6beaeja)(ioredis@4.28.5)(nats@1.4.12)(redlock@4.2.0)
@ -1744,6 +1741,9 @@ importers:
path-to-regexp:
specifier: ^6.2.1
version: 6.2.1
ramda-adjunct:
specifier: ^4.0.0
version: 4.0.0(ramda@0.29.0)
tailchat-types:
specifier: workspace:^
version: link:../../../packages/types
@ -13668,15 +13668,6 @@ packages:
/asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
/asn1.js@5.4.1:
resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
dependencies:
bn.js: 4.12.0
inherits: 2.0.4
minimalistic-assert: 1.0.1
safer-buffer: 2.1.2
dev: false
/asn1@0.2.6:
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
dependencies:
@ -14265,14 +14256,6 @@ packages:
resolution: {integrity: sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==}
dev: false
/bn.js@4.12.0:
resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
dev: false
/bn.js@5.2.1:
resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==}
dev: false
/body-parser@1.20.1:
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@ -14422,67 +14405,14 @@ packages:
dependencies:
fill-range: 7.0.1
/brorand@1.1.0:
resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==}
dev: false
/browser-or-node@1.3.0:
resolution: {integrity: sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==}
/browser-or-node@2.1.1:
resolution: {integrity: sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==}
dev: false
/browser-process-hrtime@1.0.0:
resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
dev: true
/browserify-aes@1.2.0:
resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==}
dependencies:
buffer-xor: 1.0.3
cipher-base: 1.0.4
create-hash: 1.2.0
evp_bytestokey: 1.0.3
inherits: 2.0.4
safe-buffer: 5.2.1
dev: false
/browserify-cipher@1.0.1:
resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==}
dependencies:
browserify-aes: 1.2.0
browserify-des: 1.0.2
evp_bytestokey: 1.0.3
dev: false
/browserify-des@1.0.2:
resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==}
dependencies:
cipher-base: 1.0.4
des.js: 1.0.1
inherits: 2.0.4
safe-buffer: 5.2.1
dev: false
/browserify-rsa@4.1.0:
resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==}
dependencies:
bn.js: 5.2.1
randombytes: 2.1.0
dev: false
/browserify-sign@4.2.1:
resolution: {integrity: sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==}
dependencies:
bn.js: 5.2.1
browserify-rsa: 4.1.0
create-hash: 1.2.0
create-hmac: 1.1.7
elliptic: 6.5.4
inherits: 2.0.4
parse-asn1: 5.1.6
readable-stream: 3.6.1
safe-buffer: 5.2.1
dev: false
/browserslist@4.21.3:
resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@ -14556,10 +14486,6 @@ packages:
/buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
/buffer-xor@1.0.3:
resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==}
dev: false
/buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
dependencies:
@ -15060,13 +14986,6 @@ packages:
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
engines: {node: '>=8'}
/cipher-base@1.0.4:
resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==}
dependencies:
inherits: 2.0.4
safe-buffer: 5.2.1
dev: false
/cjs-module-lexer@1.2.2:
resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
dev: true
@ -16147,34 +16066,6 @@ packages:
react: 18.2.0
dev: false
/create-ecdh@4.0.4:
resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
dependencies:
bn.js: 4.12.0
elliptic: 6.5.4
dev: false
/create-hash@1.2.0:
resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
dependencies:
cipher-base: 1.0.4
inherits: 2.0.4
md5.js: 1.3.5
ripemd160: 2.0.2
sha.js: 2.4.11
dev: false
/create-hmac@1.1.7:
resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
dependencies:
cipher-base: 1.0.4
create-hash: 1.2.0
inherits: 2.0.4
ripemd160: 2.0.2
safe-buffer: 5.2.1
sha.js: 2.4.11
dev: false
/create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
@ -16217,22 +16108,6 @@ packages:
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
dev: false
/crypto-browserify@3.12.0:
resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==}
dependencies:
browserify-cipher: 1.0.1
browserify-sign: 4.2.1
create-ecdh: 4.0.4
create-hash: 1.2.0
create-hmac: 1.1.7
diffie-hellman: 5.0.3
inherits: 2.0.4
pbkdf2: 3.1.2
public-encrypt: 4.0.3
randombytes: 2.1.0
randomfill: 1.0.4
dev: false
/crypto-random-string@2.0.0:
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
engines: {node: '>=8'}
@ -17075,13 +16950,6 @@ packages:
engines: {node: '>=6'}
dev: false
/des.js@1.0.1:
resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==}
dependencies:
inherits: 2.0.4
minimalistic-assert: 1.0.1
dev: false
/destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@ -17184,14 +17052,6 @@ packages:
engines: {node: '>=0.3.1'}
dev: false
/diffie-hellman@5.0.3:
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
dependencies:
bn.js: 4.12.0
miller-rabin: 4.0.1
randombytes: 2.1.0
dev: false
/dir-glob@2.2.2:
resolution: {integrity: sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==}
engines: {node: '>=4'}
@ -17509,18 +17369,6 @@ packages:
/electron-to-chromium@1.4.310:
resolution: {integrity: sha512-/xlATgfwkm5uDDwLw5nt/MNEf7c1oazLURMZLy39vOioGYyYzLWIDT8fZMJak6qTiAJ7udFTy7JG7ziyjNutiA==}
/elliptic@6.5.4:
resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
dependencies:
bn.js: 4.12.0
brorand: 1.1.0
hash.js: 1.1.7
hmac-drbg: 1.0.1
inherits: 2.0.4
minimalistic-assert: 1.0.1
minimalistic-crypto-utils: 1.0.1
dev: false
/emittery@0.8.1:
resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==}
engines: {node: '>=10'}
@ -18435,13 +18283,6 @@ packages:
engines: {node: '>=12.0.0'}
dev: false
/evp_bytestokey@1.0.3:
resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==}
dependencies:
md5.js: 1.3.5
safe-buffer: 5.2.1
dev: false
/exec-sh@0.3.6:
resolution: {integrity: sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==}
dev: true
@ -18748,8 +18589,8 @@ packages:
punycode: 1.4.1
dev: false
/fast-xml-parser@3.21.1:
resolution: {integrity: sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==}
/fast-xml-parser@4.2.6:
resolution: {integrity: sha512-Xo1qV++h/Y3Ng8dphjahnYe+rGHaaNdsYOBWL9Y9GCPKpNKilJtilvWkLcI9f9X2DoKTLsZsGYAls5+JL5jfLA==}
hasBin: true
dependencies:
strnum: 1.0.5
@ -20044,22 +19885,6 @@ packages:
dependencies:
function-bind: 1.1.1
/hash-base@3.1.0:
resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==}
engines: {node: '>=4'}
dependencies:
inherits: 2.0.4
readable-stream: 3.6.1
safe-buffer: 5.2.1
dev: false
/hash.js@1.1.7:
resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
dependencies:
inherits: 2.0.4
minimalistic-assert: 1.0.1
dev: false
/hast-to-hyperscript@9.0.1:
resolution: {integrity: sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==}
dependencies:
@ -20243,14 +20068,6 @@ packages:
value-equal: 1.0.1
dev: false
/hmac-drbg@1.0.1:
resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
dependencies:
hash.js: 1.1.7
minimalistic-assert: 1.0.1
minimalistic-crypto-utils: 1.0.1
dev: false
/hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
dependencies:
@ -23362,14 +23179,6 @@ packages:
resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==}
dev: false
/md5.js@1.3.5:
resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
dependencies:
hash-base: 3.1.0
inherits: 2.0.4
safe-buffer: 5.2.1
dev: false
/md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
dependencies:
@ -23968,14 +23777,6 @@ packages:
braces: 3.0.2
picomatch: 2.3.1
/miller-rabin@4.0.1:
resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==}
hasBin: true
dependencies:
bn.js: 4.12.0
brorand: 1.1.0
dev: false
/mime-db@1.33.0:
resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==}
engines: {node: '>= 0.6'}
@ -24117,10 +23918,6 @@ packages:
/minimalistic-assert@1.0.1:
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
/minimalistic-crypto-utils@1.0.1:
resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
dev: false
/minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@ -24159,27 +23956,24 @@ packages:
/minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
/minio@7.0.32:
resolution: {integrity: sha512-txa7Vr0N24MKzeAybP/wY1jxbLnfGHXwZYyfFXuMW55HX2+HOcKEIgH4hU6Qj/kiMgyXs/ozHjAuLIDrR8nwLg==}
engines: {node: '>8 <=18'}
/minio@7.1.1:
resolution: {integrity: sha512-HBLRFXs1CkNwAkahU+j1ilB9YS/Tmkdc6orpxVW1YN11NlEJyLjarIpBYu/inF+dj+tJIsA8PSKNnRmUNm+9qQ==}
engines: {node: ^16 || ^18 || >=20}
dependencies:
async: 3.2.4
block-stream2: 2.1.0
browser-or-node: 1.3.0
browser-or-node: 2.1.1
buffer-crc32: 0.2.13
crypto-browserify: 3.12.0
es6-error: 4.1.1
fast-xml-parser: 3.21.1
fast-xml-parser: 4.2.6
ipaddr.js: 2.0.1
json-stream: 1.0.0
lodash: 4.17.21
mime-types: 2.1.35
mkdirp: 0.5.6
query-string: 7.1.3
through2: 3.0.2
through2: 4.0.2
web-encoding: 1.1.5
xml: 1.0.1
xml2js: 0.4.23
xml2js: 0.5.0
dev: false
/minipass-collect@1.0.2:
@ -24370,19 +24164,6 @@ packages:
moleculer: 0.14.23(patch_hash=ahhlgpfy57fntn2aftq6beaeja)(ioredis@4.28.5)(nats@1.4.12)(redlock@4.2.0)
dev: false
/moleculer-minio@2.0.0(patch_hash=77awcwzrgh47fhn6qqq4ghcfau)(moleculer@0.14.23):
resolution: {integrity: sha512-2INKAtdgboR8VQC9Vp0W4u5yy6XVxUW5UmP8akoggMa1BuvJ8m6hUlY/Y2VpcQ6FJIrQa9hHz5B38IMmXd7ZqQ==}
engines: {node: '>= 14.x.x'}
peerDependencies:
moleculer: '>= 0.13.0'
dependencies:
minio: 7.0.32
moleculer: 0.14.23(patch_hash=ahhlgpfy57fntn2aftq6beaeja)(ioredis@4.28.5)(nats@1.4.12)(redlock@4.2.0)
ramda: 0.27.1
ramda-adjunct: 2.36.0(ramda@0.27.1)
dev: false
patched: true
/moleculer-repl@0.6.6:
resolution: {integrity: sha512-XQuz6PdosVgm8SkqJ21gba+VlDGvDHMd2yGhZyeZaoacWEe1wovA8Smr+JuAwFCz6YEpv6Gp6NVLMyHoG5DcsQ==}
engines: {node: '>= 12.x.x'}
@ -25685,16 +25466,6 @@ packages:
resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==}
dev: false
/parse-asn1@5.1.6:
resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==}
dependencies:
asn1.js: 5.4.1
browserify-aes: 1.2.0
evp_bytestokey: 1.0.3
pbkdf2: 3.1.2
safe-buffer: 5.2.1
dev: false
/parse-entities@2.0.0:
resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==}
dependencies:
@ -25940,17 +25711,6 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
/pbkdf2@3.1.2:
resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
engines: {node: '>=0.12'}
dependencies:
create-hash: 1.2.0
create-hmac: 1.1.7
ripemd160: 2.0.2
safe-buffer: 5.2.1
sha.js: 2.4.11
dev: false
/peek-readable@4.1.0:
resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==}
engines: {node: '>=8'}
@ -27375,17 +27135,6 @@ packages:
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
dev: true
/public-encrypt@4.0.3:
resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==}
dependencies:
bn.js: 4.12.0
browserify-rsa: 4.1.0
create-hash: 1.2.0
parse-asn1: 5.1.6
randombytes: 2.1.0
safe-buffer: 5.2.1
dev: false
/pump@2.0.1:
resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
dependencies:
@ -27521,35 +27270,28 @@ packages:
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
engines: {node: '>=10'}
/ramda-adjunct@2.36.0(ramda@0.27.1):
resolution: {integrity: sha512-8w+/Hx73oByS+vo+BfAPOG3HYL2ay6O5fjrJpR7NFxMoFWksKz6vSOtvjqdfMM6MfAimHizq9tpdI0OD4xbKog==}
/ramda-adjunct@4.0.0(ramda@0.29.0):
resolution: {integrity: sha512-W/NiJAlZdwZ/iUkWEQQgRdH5Szqqet1WoVH9cdqDVjFbVaZHuJfJRvsxqHhvq6tZse+yVbFatLDLdVa30wBlGQ==}
engines: {node: '>=0.10.3'}
peerDependencies:
ramda: '>= 0.19.0 <= 0.27.2'
ramda: '>= 0.29.0'
dependencies:
ramda: 0.27.1
dev: false
/ramda@0.27.1:
resolution: {integrity: sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==}
ramda: 0.29.0
dev: false
/ramda@0.28.0:
resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==}
dev: true
/ramda@0.29.0:
resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==}
dev: false
/randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
dependencies:
safe-buffer: 5.2.1
/randomfill@1.0.4:
resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==}
dependencies:
randombytes: 2.1.0
safe-buffer: 5.2.1
dev: false
/range-parser@1.2.0:
resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==}
engines: {node: '>= 0.6'}
@ -29708,13 +29450,6 @@ packages:
dependencies:
glob: 7.2.3
/ripemd160@2.0.2:
resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
dependencies:
hash-base: 3.1.0
inherits: 2.0.4
dev: false
/rollup-plugin-copy@3.4.0:
resolution: {integrity: sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==}
engines: {node: '>=8.3'}
@ -30287,14 +30022,6 @@ packages:
/setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
/sha.js@2.4.11:
resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
hasBin: true
dependencies:
inherits: 2.0.4
safe-buffer: 5.2.1
dev: false
/shallow-clone@3.0.1:
resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
engines: {node: '>=8'}
@ -31970,18 +31697,10 @@ packages:
xtend: 4.0.2
dev: true
/through2@3.0.2:
resolution: {integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==}
dependencies:
inherits: 2.0.4
readable-stream: 3.6.1
dev: false
/through2@4.0.2:
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
dependencies:
readable-stream: 3.6.1
dev: true
/through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
@ -34363,8 +34082,8 @@ packages:
resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
dev: true
/xml2js@0.4.23:
resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==}
/xml2js@0.5.0:
resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
engines: {node: '>=4.0.0'}
dependencies:
sax: 1.2.4

@ -58,7 +58,6 @@
"lodash": "^4.17.21",
"mime": "^2.5.2",
"mkdirp": "^1.0.4",
"moleculer-minio": "^2.0.0",
"moment": "^2.29.1",
"mongodb": "4.2.1",
"mongoose": "6.1.1",

@ -1,6 +1,6 @@
{
"name": "tailchat-server-sdk",
"version": "0.0.14",
"version": "0.0.15",
"description": "",
"main": "dist/index.js",
"bin": {
@ -49,6 +49,7 @@
"isstream": "^0.1.2",
"kleur": "^4.1.4",
"lodash": "^4.17.21",
"minio": "^7.1.1",
"moleculer": "0.14.23",
"moleculer-db": "0.8.19",
"moleculer-repl": "^0.7.2",
@ -56,6 +57,7 @@
"mongodb": "4.2.1",
"mongoose": "6.1.1",
"path-to-regexp": "^6.2.1",
"ramda-adjunct": "^4.0.0",
"tailchat-types": "workspace:^"
}
}

@ -2,6 +2,7 @@ export { defaultBrokerConfig } from './runner/moleculer.config';
export { TcService } from './services/base';
export { TcBroker } from './services/broker';
export type { TcDbService } from './services/mixins/db.mixin';
export { TcMinioService } from './services/mixins/minio.mixin';
export type {
TcContext,
TcPureContext,

@ -9,6 +9,7 @@ dotenv.config();
const port = process.env.PORT ? Number(process.env.PORT) : 11000;
const apiUrl = process.env.API_URL || `http://127.0.0.1:${port}`;
const staticHost = process.env.STATIC_HOST || '{BACKEND}';
const staticUrl = process.env.STATIC_URL || `${staticHost}/static/`;
export const config = {
port,
secret: process.env.SECRET || 'tailchat',
@ -25,6 +26,7 @@ export const config = {
user: process.env.MINIO_USER,
pass: process.env.MINIO_PASS,
bucketName: process.env.MINIO_BUCKET_NAME || 'tailchat',
pathStyle: process.env.MINIO_PATH_STYLE === 'VirtualHosted' ? false : true,
/**
*
@ -36,7 +38,7 @@ export const config = {
: 1 * 1024 * 1024,
},
apiUrl,
staticUrl: `${staticHost}/static/`,
staticUrl,
enableOpenapi: true, // 是否开始openapi
emailVerification: checkEnvTrusty(process.env.EMAIL_VERIFY) || false, // 是否在注册后验证邮箱可用性

@ -0,0 +1,822 @@
import { Client as MinioClient, CopyConditions } from 'minio';
import { isString, isUndefined } from 'ramda-adjunct';
import { Errors } from 'moleculer';
class MinioInitializationError extends Errors.MoleculerError {
/**
* Creates an instance of MinioInitializationError.
*
* @param {String?} message
* @param {Number?} code
* @param {String?} type
* @param {any} data
*
* @memberof MinioInitializationError
*/
constructor(
message = 'Minio can not be initialized',
code = 500,
type = 'MINIO_INITIALIZATION_ERROR',
data = {}
) {
super(message);
this.code = code;
this.type = type;
this.data = data;
}
}
class MinioPingError extends Errors.MoleculerRetryableError {
/**
* Creates an instance of MinioPingError.
*
* @param {String?} message
* @param {Number?} code
* @param {String?} type
* @param {any} data
*
* @memberof MinioPingError
*/
constructor(
message = 'Minio Backend not reachable',
code = 502,
type = 'MINIO_PING_ERROR',
data = {}
) {
super(message);
this.code = code;
this.type = type;
this.data = data;
}
}
/**
* Service mixin for managing files in a Minio S3 backend
*
* @name moleculer-minio
* @module Service
*/
export const TcMinioService = {
// Service name
name: 'minio',
// Default settings
settings: {
/** @type {String} The Hostname minio is running on and available at. Hostname or IP-Address */
endPoint: undefined,
/** @type {Number} TCP/IP port number minio is listening on. Default value set to 80 for HTTP and 443 for HTTPs.*/
port: undefined,
/** @type {Boolean?} If set to true, https is used instead of http. Default is true.*/
useSSL: true,
/** @type {String} The AccessKey to use when connecting to minio */
accessKey: undefined,
/** @type {String} The SecretKey to use when connecting to minio */
secretKey: undefined,
/** @type {String?} Set this value to override region cache*/
region: undefined,
/** @type {String?} Set this value to pass in a custom transport. (Optional)*/
transport: undefined,
/** @type {String?} Set this value to provide x-amz-security-token (AWS S3 specific). (Optional)*/
sessionToken: undefined,
/** @type {Number?} This service will perform a periodic healthcheck of Minio. Use this setting to configure the inverval in which the healthcheck is performed. Set to `0` to turn healthcheks of */
minioHealthCheckInterval: 5000,
/**
* Path Style: <Schema>://<S3 Endpoint>/<Bucket>/<Object>
* Virtual hosted style: <Schema>://<Bucket>.<S3 Endpoint>/<Object>
*/
pathStyle: true,
},
methods: {
/**
* Creates and returns a new Minio client
*
* @methods
*
* @returns {Client}
*/
createMinioClient() {
return new MinioClient({
endPoint: this.settings.endPoint,
port: this.settings.port,
useSSL: this.settings.useSSL,
accessKey: this.settings.accessKey,
secretKey: this.settings.secretKey,
region: this.settings.region,
transport: this.settings.transport,
sessionToken: this.settings.sessionToken,
pathStyle: this.settings.pathStyle,
});
},
/**
* Pings the configured minio backend
*
* @param {number} timeout - Amount of miliseconds to wait for a ping response
* @returns {PromiseLike<boolean|MinioPingError>}
*/
ping({ timeout = 5000 } = {}) {
return this.Promise.race([
this.client.listBuckets().then(() => true),
this.Promise.delay(timeout).then(() => {
throw new MinioPingError();
}),
]);
},
},
actions: {
/**
* Creates a new Bucket
*
* @actions
*
* @param {string} bucketName - The name of the bucket
* @param {string} region - The region to create the bucket in. Defaults to "us-east-1"
*
* @returns {PromiseLike<undefined|Error>}
*/
makeBucket: {
params: {
bucketName: { type: 'string' },
region: { type: 'string', optional: true },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, region = '' }) =>
this.client.makeBucket(bucketName, region)
);
},
},
/**
* Lists all buckets.
*
* @actions
*
* @returns {PromiseLike<Bucket[]|Error>}
*/
listBuckets: {
handler() {
return this.client
.listBuckets()
.then((buckets) => (isUndefined(buckets) ? [] : buckets));
},
},
/**
* Checks if a bucket exists.
*
* @actions
* @param {string} bucketName - Name of the bucket
*
* @returns {PromiseLike<boolean|Error>}
*/
bucketExists: {
params: {
bucketName: { type: 'string' },
},
handler(ctx) {
return this.client.bucketExists(ctx.params.bucketName);
},
},
/**
* Removes a bucket.
*
* @actions
* @param {string} bucketName - Name of the bucket
*
* @returns {PromiseLike<boolean|Error>}
*/
removeBucket: {
params: {
bucketName: { type: 'string' },
},
handler(ctx) {
return this.client.removeBucket(ctx.params.bucketName);
},
},
/**
* Lists all objects in a bucket.
*
* @actions
* @param {string} bucketName - Name of the bucket
* @param {string} prefix - The prefix of the objects that should be listed (optional, default '').
* @param {boolean} recursive - `true` indicates recursive style listing and false indicates directory style listing delimited by '/'. (optional, default `false`).
*
* @returns {PromiseLike<Object[]|Error>}
*/
listObjects: {
params: {
bucketName: { type: 'string' },
prefix: { type: 'string', optional: true },
recursive: { type: 'boolean', optional: true },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, prefix = '', recursive = false }) => {
return new this.Promise((resolve, reject) => {
try {
const stream = this.client.listObjects(
bucketName,
prefix,
recursive
);
const objects = [];
stream.on('data', (el) => objects.push(el));
stream.on('end', () => resolve(objects));
stream.on('error', reject);
} catch (e) {
reject(e);
}
});
}
);
},
},
/**
* Lists all objects in a bucket using S3 listing objects V2 API
*
* @actions
* @param {string} bucketName - Name of the bucket
* @param {string} prefix - The prefix of the objects that should be listed (optional, default '').
* @param {boolean} recursive - `true` indicates recursive style listing and false indicates directory style listing delimited by '/'. (optional, default `false`).
* @param {string} startAfter - Specifies the object name to start after when listing objects in a bucket. (optional, default '').
*
* @returns {PromiseLike<Object[]|Error>}
*/
listObjectsV2: {
params: {
bucketName: { type: 'string' },
prefix: { type: 'string', optional: true },
recursive: { type: 'boolean', optional: true },
startAfter: { type: 'string', optional: true },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, prefix = '', recursive = false, startAfter = '' }) => {
return new this.Promise((resolve, reject) => {
try {
const stream = this.client.listObjectsV2(
bucketName,
prefix,
recursive,
startAfter
);
const objects = [];
stream.on('data', (el) => objects.push(el));
stream.on('end', () => resolve(objects));
stream.on('error', reject);
} catch (e) {
reject(e);
}
});
}
);
},
},
/**
* Lists partially uploaded objects in a bucket.
*
* @actions
* @param {string} bucketName - Name of the bucket
* @param {string} prefix - The prefix of the objects that should be listed (optional, default '').
* @param {boolean} recursive - `true` indicates recursive style listing and false indicates directory style listing delimited by '/'. (optional, default `false`).
*
* @returns {PromiseLike<Object[]|Error>}
*/
listIncompleteUploads: {
params: {
bucketName: { type: 'string' },
prefix: { type: 'string', optional: true },
recursive: { type: 'boolean', optional: true },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, prefix = '', recursive = false }) => {
return new this.Promise((resolve, reject) => {
try {
const stream = this.client.listIncompleteUploads(
bucketName,
prefix,
recursive
);
const objects = [];
stream.on('data', (el) => objects.push(el));
stream.on('end', () => resolve(objects));
stream.on('error', reject);
} catch (e) {
reject(e);
}
});
}
);
},
},
/**
* Downloads an object as a stream.
*
* @actions
* @param {string} bucketName - Name of the bucket
* @param {string} objectName - Name of the object.
*
* @returns {PromiseLike<ReadableStream|Error>}
*/
getObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
},
handler(ctx) {
return this.client.getObject(
ctx.params.bucketName,
ctx.params.objectName
);
},
},
/**
* Downloads the specified range bytes of an object as a stream.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {number} offset - `offset` of the object from where the stream will start.
* @param {number} length - `length` of the object that will be read in the stream (optional, if not specified we read the rest of the file from the offset).
*
* @returns {PromiseLike<ReadableStream|Error>}
*/
getPartialObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
offset: { type: 'number' },
length: { type: 'number', optional: true },
},
handler(ctx) {
return this.client.getPartialObject(
ctx.params.bucketName,
ctx.params.objectName,
ctx.params.offset,
ctx.params.length
);
},
},
/**
* Downloads and saves the object as a file in the local filesystem.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {string} filePath - Path on the local filesystem to which the object data will be written.
*
* @returns {PromiseLike<undefined|Error>}
*/
fGetObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
filePath: { type: 'string' },
},
handler(ctx) {
return this.client.fGetObject(
ctx.params.bucketName,
ctx.params.objectName,
ctx.params.filePath
);
},
},
/**
* Uploads an object from a stream/Buffer.
*
* @actions
* @param {ReadableStream} params - Readable stream.
*
* @meta
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {number} size - Size of the object (optional).
* @param {object} metaData - metaData of the object (optional).
*
* @returns {PromiseLike<undefined|Error>}
*/
putObject: {
handler(ctx) {
return this.Promise.resolve({
stream: ctx.params,
meta: ctx.meta,
}).then(({ stream, meta }) =>
this.client.putObject(
meta.bucketName,
meta.objectName,
stream,
meta.size,
meta.metaData
)
);
},
},
/**
* Uploads contents from a file to objectName.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {string} filePath - Path of the file to be uploaded.
* @param {object} metaData - metaData of the object (optional).
*
* @returns {PromiseLike<undefined|Error>}
*/
fPutObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
filePath: { type: 'string' },
metaData: { type: 'object', optional: true },
},
handler(ctx) {
return this.client.fPutObject(
ctx.params.bucketName,
ctx.params.objectName,
ctx.params.filePath,
ctx.params.metaData
);
},
},
/**
* Copy a source object into a new object in the specified bucket.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {string} sourceObject - Path of the file to be copied.
* @param {object} conditions - Conditions to be satisfied before allowing object copy.
* @param {object} metaData - metaData of the object (optional).
*
* @returns {PromiseLike<{etag: {string}, lastModified: {string}}|Error>}
*/
copyObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
sourceObject: { type: 'string' },
conditions: {
type: 'object',
properties: {
modified: { type: 'string', optional: true },
unmodified: { type: 'string', optional: true },
matchETag: { type: 'string', optional: true },
matchETagExcept: { type: 'string', optional: true },
},
},
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, objectName, sourceObject, conditions }) => {
const _conditions = new CopyConditions();
if (conditions.modified) {
_conditions.setModified(new Date(conditions.modified));
}
if (conditions.unmodified) {
_conditions.setUnmodified(new Date(conditions.unmodified));
}
if (conditions.matchETag) {
_conditions.setMatchETag(conditions.matchETag);
}
if (conditions.matchETagExcept) {
_conditions.setMatchETagExcept(conditions.matchETagExcept);
}
conditions = _conditions;
return this.client.copyObject(
bucketName,
objectName,
sourceObject,
conditions
);
}
);
},
},
/**
* Gets metadata of an object.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
*
* @returns {PromiseLike<{size: {number}, metaData: {object}, lastModified: {string}, etag: {string}}|Error>}
*/
statObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
},
handler(ctx) {
return this.client.statObject(
ctx.params.bucketName,
ctx.params.objectName
);
},
},
/**
* Removes an Object
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
*
* @returns {PromiseLike<undefined|Error>}
*/
removeObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
},
handler(ctx) {
return this.client.removeObject(
ctx.params.bucketName,
ctx.params.objectName
);
},
},
/**
* Removes a list of Objects
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string[]} objectNames - Names of the objects.
*
* @returns {PromiseLike<undefined|Error>}
*/
removeObjects: {
params: {
bucketName: { type: 'string' },
objectNames: { type: 'array', items: 'string' },
},
handler(ctx) {
return this.client.removeObjects(
ctx.params.bucketName,
ctx.params.objectNames
);
},
},
/**
* Removes a partially uploaded object.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
*
* @returns {PromiseLike<undefined|Error>}
*/
removeIncompleteUpload: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, objectName }) =>
this.client.removeIncompleteUpload(bucketName, objectName)
);
},
},
/**
* Generates a presigned URL for the provided HTTP method, 'httpMethod'. Browsers/Mobile clients may point to this URL to directly download objects even if the bucket is private. This
* presigned URL can have an associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.
*
* @actions
* @param {string} httpMethod - The HTTP-Method (eg. `GET`).
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {number} expires - Expiry time in seconds. Default value is 7 days. (optional)
* @param {object} reqParams - request parameters. (optional)
* @param {string} requestDate - An ISO date string, the url will be issued at. Default value is now. (optional)
* @returns {PromiseLike<String|Error>}
*/
presignedUrl: {
params: {
httpMethod: { type: 'string' },
bucketName: { type: 'string' },
objectName: { type: 'string' },
expires: { type: 'number', integer: true, optional: true },
reqParams: { type: 'object', optional: true },
requestDate: { type: 'string', optional: true },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({
httpMethod,
bucketName,
objectName,
expires,
reqParams,
requestDate,
}) => {
if (isString(requestDate)) {
requestDate = new Date(requestDate);
}
return new this.Promise((resolve, reject) => {
this.client.presignedUrl(
httpMethod,
bucketName,
objectName,
expires,
reqParams,
requestDate,
(error, url) => {
if (error) {
reject(error);
} else {
resolve(url);
}
}
);
});
}
);
},
},
/**
* Generates a presigned URL for HTTP GET operations. Browsers/Mobile clients may point to this URL to directly download objects even if the bucket is private. This presigned URL can have an
* associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {number} expires - Expiry time in seconds. Default value is 7 days. (optional)
* @param {object} reqParams - request parameters. (optional)
* @param {string} requestDate - An ISO date string, the url will be issued at. Default value is now. (optional)
* @returns {PromiseLike<String|Error>}
*/
presignedGetObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
expires: { type: 'number', integer: true, optional: true },
reqParams: { type: 'object', optional: true },
requestDate: { type: 'string', optional: true },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, objectName, expires, reqParams, requestDate }) => {
if (isString(requestDate)) {
requestDate = new Date(requestDate);
}
return new this.Promise((resolve, reject) => {
this.client.presignedGetObject(
bucketName,
objectName,
expires,
reqParams,
requestDate,
(error, url) => {
if (error) {
reject(error);
} else {
resolve(url);
}
}
);
});
}
);
},
},
/**
* Generates a presigned URL for HTTP PUT operations. Browsers/Mobile clients may point to this URL to upload objects directly to a bucket even if it is private. This presigned URL can have
* an associated expiration time in seconds after which the URL is no longer valid. The default value is 7 days.
*
* @actions
* @param {string} bucketName - Name of the bucket.
* @param {string} objectName - Name of the object.
* @param {number} expires - Expiry time in seconds. Default value is 7 days. (optional)
* @returns {PromiseLike<String|Error>}
*/
presignedPutObject: {
params: {
bucketName: { type: 'string' },
objectName: { type: 'string' },
expires: { type: 'number', integer: true, optional: true },
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(
({ bucketName, objectName, expires }) => {
return new this.Promise((resolve, reject) => {
this.client.presignedPutObject(
bucketName,
objectName,
expires,
(error, url) => {
if (error) {
reject(error);
} else {
resolve(url);
}
}
);
});
}
);
},
},
/**
* Allows setting policy conditions to a presigned URL for POST operations. Policies such as bucket name to receive object uploads, key name prefixes, expiry policy may be set.
*
* @actions
* @param {object} policy - Policy object created by minioClient.newPostPolicy()
* @returns {PromiseLike<{postURL: {string}, formData: {object}}|Error>}
*/
presignedPostPolicy: {
params: {
policy: {
type: 'object',
properties: {
expires: { type: 'string', optional: true },
key: { type: 'string', optional: true },
keyStartsWith: { type: 'string', optional: true },
bucket: { type: 'string', optional: true },
contentType: { type: 'string', optional: true },
contentLengthRangeMin: {
type: 'number',
integer: true,
optional: true,
},
contentLengthRangeMax: {
type: 'number',
integer: true,
optional: true,
},
},
},
},
handler(ctx) {
return this.Promise.resolve(ctx.params).then(({ policy }) => {
const _policy = this.client.newPostPolicy();
if (policy.expires) {
_policy.setExpires(new Date(policy.expires));
}
if (policy.key) {
_policy.setKey(policy.key);
}
if (policy.keyStartsWith) {
_policy.setKeyStartsWith(policy.keyStartsWith);
}
if (policy.bucket) {
_policy.setBucket(policy.bucket);
}
if (policy.contentType) {
_policy.setContentType(policy.contentType);
}
if (policy.contentLengthRangeMin && policy.contentLengthRangeMax) {
_policy.setContentLengthRange(
policy.contentLengthRangeMin,
policy.contentLengthRangeMax
);
}
return this.client.presignedPostPolicy(_policy);
});
},
},
},
/**
* Service created lifecycle event handler.
* Constructs a new minio client entity
*/
created() {
this.client = this.createMinioClient();
},
/**
* Service started lifecycle event handler. Resolves when:
* * ping of S3 backend has been successful
* * a healthCheck has been registered, given minioHealthCheckInterval > 0
* @returns {PromiseLike<undefined|MinioInitializationError>}
*/
started() {
/* istanbul ignore next */
return this.Promise.resolve()
.then(() => this.ping())
.then(() => {
this.settings.minioHealthCheckInterval
? (this.healthCheckInterval = setInterval(
() =>
this.ping().catch((e) =>
this.logger.error('Minio backend can not be reached', e)
),
this.settings.minioHealthCheckInterval
))
: undefined;
return undefined;
})
.catch((e) => {
throw new MinioInitializationError(e.message);
});
},
/**
* Service stopped lifecycle event handler.
* Removes the healthCheckInterval
*/
stopped() {
this.healthCheckInterval && clearInterval(this.healthCheckInterval);
},
};

@ -6,8 +6,8 @@ import {
config,
TcDbService,
NoPermissionError,
TcMinioService,
} from 'tailchat-server-sdk';
import MinioService from 'moleculer-minio';
import _ from 'lodash';
import mime from 'mime';
import type { Client as MinioClient } from 'minio';
@ -34,7 +34,7 @@ class FileService extends TcService {
onInit(): void {
this.registerLocalDb(require('../../models/file').default);
this.registerMixin(MinioService);
this.registerMixin(TcMinioService);
const minioUrl = config.storage.minioUrl;
const [endPoint, port] = minioUrl.split(':');
@ -44,6 +44,7 @@ class FileService extends TcService {
this.registerSetting('useSSL', false);
this.registerSetting('accessKey', config.storage.user);
this.registerSetting('secretKey', config.storage.pass);
this.registerSetting('pathStyle', config.storage.pathStyle);
this.registerAction('save', this.save);
this.registerAction('saveFileWithUrl', this.saveFileWithUrl, {

@ -9,7 +9,8 @@ title: Environment Variable
| ----- | ------ | --- |
| PORT | 11000 | Gateway service port number |
| SECRET | tailchat | encryption key, used for JWT |
| STATIC_HOST | "{BACKEND}" | Externally accessible static service address, used for file service access, the default is the dynamic server address inferred from the front-end request, if it is expected to be stored in a third-party OSS, it needs to be modified |
| STATIC_HOST | "{BACKEND}" | Externally accessible static service host, used for file service access, the default is the dynamic server address inferred from the front-end request, if it is expected to be stored in a third-party OSS, it needs to be modified |
| STATIC_URL | "{BACKEND}/static/" | Externally accessible static service complete address prefix, used for file service access, the default is the dynamic server address inferred from the front-end request, if it is expected to be stored in a third-party OSS Modify, if this variable is set, the above `STATIC_HOST` value is invalid |
| API_URL | http://127.0.0.1:11000 | Externally accessible url address, used for issuer issuance on open platforms or as a fallback for file services |
| MONGO_URL | - | Database service address |
| REDIS_URL | - | Redis service address |
@ -17,6 +18,7 @@ title: Environment Variable
| MINIO_USER | - | File service username |
| MINIO_PASS | - | File service password |
| MINIO_BUCKET_NAME | tailchat | file service bucket name |
| MINIO_PATH_STYLE | false | Whether to use path-style s3 communication format, `true` is `Path Style`, `false` is `Virtual hosted style` |
| SMTP_SENDER | - | Mail service sender (example: `"Tailchat" example@163.com`) |
| SMTP_URI | - | mail service connection address (example: `smtp://username:password@smtp.example.com/?pool=true`) |
| FILE_LIMIT | 1048576 | File/image upload size limit, the default is 1m, please enter a number(unit: byte) |

@ -9,7 +9,8 @@ title: 环境变量
| ----- | ------ | --- |
| PORT | 11000 | 网关服务端口号 |
| SECRET | tailchat | 加密秘钥, 用于JWT |
| STATIC_HOST | "{BACKEND}" | 对外可访问的静态服务地址,用于文件服务访问, 默认为动态根据前端请求推断出的服务端地址如果期望存储在第三方OSS中需要进行修改 |
| STATIC_HOST | "{BACKEND}" | 对外可访问的静态服务主机,用于文件服务访问, 默认为动态根据前端请求推断出的服务端地址如果期望存储在第三方OSS中需要进行修改 |
| STATIC_URL | "{BACKEND}/static/" | 对外可访问的静态服务完整地址前缀,用于文件服务访问, 默认为动态根据前端请求推断出的服务端地址如果期望存储在第三方OSS中需要进行修改, 如果设置了本变量则上面的 `STATIC_HOST` 值无效 |
| API_URL | http://127.0.0.1:11000 | 对外可访问的url地址用于开放平台的issuer签发或者作为文件服务的fallback |
| MONGO_URL | - | 数据库服务地址 |
| REDIS_URL | - | Redis服务地址 |
@ -17,6 +18,7 @@ title: 环境变量
| MINIO_USER | - | 文件服务用户名 |
| MINIO_PASS | - | 文件服务密码 |
| MINIO_BUCKET_NAME | tailchat | 文件服务存储桶名 |
| MINIO_PATH_STYLE | "Path" | 是否使用路径形式的s3通信格式, `Path``Path Style`, `VirtualHosted``Virtual hosted style` |
| SMTP_SENDER | - | 邮件服务发件人(示例: `"Tailchat" example@163.com`) |
| SMTP_URI | - | 邮件服务连接地址(示例: `smtp://username:password@smtp.example.com/?pool=true`) |
| FILE_LIMIT | 1048576 | 文件/图片上传的大小限制默认为1m请输入数字单位: 字节) |

Loading…
Cancel
Save