Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2bc616e18 | ||
|
|
934f8ba4d7 | ||
|
|
477616ce38 | ||
|
|
f66d0e5cd7 | ||
|
|
2bb6de1198 | ||
|
|
6570d71234 | ||
|
|
64c7da70e7 | ||
|
|
a2eadf5a78 | ||
|
|
7fb040ac1a | ||
|
|
b575274680 | ||
|
|
8bfe3583b9 | ||
|
|
ab3882e1d4 | ||
|
|
0cbb9fd1a4 | ||
|
|
115a597038 | ||
|
|
a8fde271ea | ||
|
|
b0143bb5bf | ||
|
|
025af5eae0 | ||
|
|
ce16f87e12 | ||
|
|
b47084f61f | ||
|
|
936e3581b4 | ||
|
|
cc756842c4 | ||
|
|
9aa29685ef | ||
|
|
c1bb45fe14 | ||
|
|
928555b36e | ||
|
|
744d516932 | ||
|
|
8721c80a9f | ||
|
|
06698a54e1 | ||
|
|
7f48b54b73 | ||
|
|
cf619ebeee | ||
|
|
9fdc6c2e1b | ||
|
|
1d309816ba | ||
|
|
21aa248d49 | ||
|
|
90c8e49808 | ||
|
|
2326850538 | ||
|
|
cef0cb1076 | ||
|
|
9b34e51494 | ||
|
|
61dc1e2c2d | ||
|
|
2458782c73 | ||
|
|
0f05d63a3b | ||
|
|
b82f7dea3d | ||
|
|
6df626afd3 | ||
|
|
ac2d2a530a | ||
|
|
99a0590c7e | ||
|
|
58c98bbbfa | ||
|
|
9560b9c202 | ||
|
|
24ebe429cc | ||
|
|
8ef92d0c1b | ||
|
|
e3e122edc4 | ||
|
|
ab0e51feac | ||
|
|
c6c981f4aa | ||
|
|
cc82c5b420 | ||
|
|
0f15a3840e | ||
|
|
f9781363ff | ||
|
|
ea5e7c7eb9 | ||
|
|
25a4a54e52 | ||
|
|
abd6668a7a | ||
|
|
a98e95a49c | ||
|
|
d3d24f2ce6 | ||
|
|
a9386ea275 | ||
|
|
773bf2c255 | ||
|
|
71d58dd99a | ||
|
|
ec8cc14aa6 | ||
|
|
cab3ca0916 | ||
|
|
e59217bd10 | ||
|
|
26e43c519e | ||
|
|
b827ad446d | ||
|
|
8bd0dc7852 | ||
|
|
3f7c8fe3ea | ||
|
|
ee8dc65b2a | ||
|
|
d7a2decf32 | ||
|
|
5620f091bb | ||
|
|
c6040446db | ||
|
|
9a13c50e91 | ||
|
|
07e6ab324f | ||
|
|
ea063159f0 | ||
|
|
1de5322a00 | ||
|
|
2416347bea | ||
|
|
b4418c040c | ||
|
|
3080050c4d | ||
|
|
213851335a | ||
|
|
80daadff47 | ||
|
|
f3daad5c28 | ||
|
|
af624557c8 | ||
|
|
4bd41aa135 | ||
|
|
724b26aab4 | ||
|
|
f165fd968f | ||
|
|
9df60774a7 | ||
|
|
626d6e786f | ||
|
|
c4d928bbd7 | ||
|
|
1141e9f7a1 | ||
|
|
cfa478067e | ||
|
|
a36d926460 | ||
|
|
1ba9eb3613 | ||
|
|
58fd90d33d | ||
|
|
8f7b7df124 | ||
|
|
8e17e99428 | ||
|
|
284417ef50 | ||
|
|
9ceb26307e | ||
|
|
11bb0c6fa1 | ||
|
|
dd8bad7480 | ||
|
|
e10619813a | ||
|
|
24e9d9f83c | ||
|
|
5efbe8e8bf | ||
|
|
176b986c47 | ||
|
|
a2266e5bbe | ||
|
|
0ad6ffdd3c | ||
|
|
0f3b3f9ee6 | ||
|
|
5d6435cf40 | ||
|
|
ba8ad02289 | ||
|
|
7546a509da | ||
|
|
baecbbcb31 | ||
|
|
6bba40ff1c | ||
|
|
8a03dd8131 | ||
|
|
241a659f35 | ||
|
|
5afc874eed | ||
|
|
add5e8968e | ||
|
|
5e4c70edce | ||
|
|
1aa6d9ca93 | ||
|
|
ce018fe6a0 | ||
|
|
88e0ccf20e | ||
|
|
99226aadbd | ||
|
|
ea0ad14374 | ||
|
|
a6f133e058 | ||
|
|
bb52d2c6d5 | ||
|
|
96703690e6 | ||
|
|
b761928654 | ||
|
|
d50a291366 | ||
|
|
0fae292946 | ||
|
|
63e27c2700 | ||
|
|
e824a22ae6 | ||
|
|
0bf35a7b8d | ||
|
|
08d3e8d278 | ||
|
|
ed7d596f12 | ||
|
|
0186de26c2 | ||
|
|
3c9cc5fa8e | ||
|
|
b1aa0019b2 | ||
|
|
34a2aca45e | ||
|
|
6bbeba1612 | ||
|
|
99f0612209 | ||
|
|
d8e5998f21 | ||
|
|
513d4d7996 | ||
|
|
cf5d619d34 | ||
|
|
fc59b51558 | ||
|
|
c839c9eadd | ||
|
|
5456431dd4 | ||
|
|
08949370e6 | ||
|
|
033a428184 | ||
|
|
1a6dd8a259 | ||
|
|
4307a90333 | ||
|
|
7267264503 | ||
|
|
2fb475ae95 | ||
|
|
b780b71e5e | ||
|
|
5d02ecdf94 | ||
|
|
f8c1a95b63 | ||
|
|
95a8b313e5 | ||
|
|
1bc6bdbbce | ||
|
|
3293478dd7 | ||
|
|
ba2b8b44d3 | ||
|
|
f7b213a677 | ||
|
|
a49792adf5 | ||
|
|
1b272e5849 | ||
|
|
6bacce6cec | ||
|
|
6f533d23c6 | ||
|
|
c367100e6b | ||
|
|
e8b105fa90 | ||
|
|
7c4f902dbd | ||
|
|
4d59a54ad0 | ||
|
|
dbd1310eb3 | ||
|
|
52415c5561 | ||
|
|
ed87abaaaa | ||
|
|
89174178c6 | ||
|
|
d6aca1d2a1 | ||
|
|
898ebab6d8 | ||
|
|
a62f0aa6f3 | ||
|
|
55168e24db | ||
|
|
d7023ae81b | ||
|
|
c81fab62e6 | ||
|
|
fcfce6f2ea | ||
|
|
14091f142d | ||
|
|
67412b082f | ||
|
|
23b903e2f2 | ||
|
|
03c4874a50 | ||
|
|
83be327bd3 | ||
|
|
4e14655f45 | ||
|
|
8e8a295200 | ||
|
|
458840a0d7 | ||
|
|
0480260a56 | ||
|
|
ae9c6c7a48 | ||
|
|
3ff3d44d35 | ||
|
|
c243d518a9 | ||
|
|
ec951738b5 | ||
|
|
04a82c4a63 | ||
|
|
53ec8e5b60 | ||
|
|
1f3d39f4b1 | ||
|
|
fb9765304c | ||
|
|
555893d287 | ||
|
|
e0104d99f9 | ||
|
|
16a9a0ccd0 | ||
|
|
e848ecf9bc | ||
|
|
949e531448 | ||
|
|
f4afd451ab | ||
|
|
d5a8e3f7b4 | ||
|
|
fcb7870a38 | ||
|
|
46a215bd2c | ||
|
|
d9ff81131c | ||
|
|
c18c915dd2 | ||
|
|
29ef00f24e | ||
|
|
76c2857d48 | ||
|
|
2f25863578 | ||
|
|
85a787993c | ||
|
|
406ffd10db | ||
|
|
67ab24570e | ||
|
|
0247cfef9e | ||
|
|
44a7c4d000 | ||
|
|
3a0eb3bea1 | ||
|
|
ce0265c7e7 | ||
|
|
c1694ec6e2 | ||
|
|
a5a56e59c7 | ||
|
|
9d23bf934e | ||
|
|
9221a87812 | ||
|
|
6dc762f47f | ||
|
|
5c6889fbd5 | ||
|
|
7ffb8fb43b | ||
|
|
9440352e56 | ||
|
|
f8282eff7e | ||
|
|
dd106434ce | ||
|
|
6ca8108f1e | ||
|
|
451b5db941 | ||
|
|
76fb5cee72 | ||
|
|
c659a54158 | ||
|
|
22b1fa2b0e | ||
|
|
63d2f2dd69 | ||
|
|
ec6ec09cee | ||
|
|
96fb6301a8 | ||
|
|
46d27e3bea | ||
|
|
e5c46e7a55 | ||
|
|
b0095aa3fd | ||
|
|
a2e51b2c31 | ||
|
|
44fecb3b5a | ||
|
|
97f57a9322 | ||
|
|
8688a9d098 | ||
|
|
e2bcd3aac7 | ||
|
|
80c9448c71 | ||
|
|
b7207e2c2b | ||
|
|
065ad04699 | ||
|
|
725220a99b | ||
|
|
ab282cea23 | ||
|
|
324721e155 | ||
|
|
42dca4a42e | ||
|
|
e42e1257c9 | ||
|
|
2b9a21d475 | ||
|
|
9e6cfe4002 | ||
|
|
3351b92ea6 | ||
|
|
a946357ea3 | ||
|
|
f4120433d9 | ||
|
|
25f9bdd9b6 | ||
|
|
a0a758949e | ||
|
|
5125132f23 | ||
|
|
5206cc3df7 | ||
|
|
65faa69852 | ||
|
|
83dc0f99b6 | ||
|
|
edc2515caa | ||
|
|
be8a2f4ca4 | ||
|
|
0bf059afcf | ||
|
|
47c76fafc1 | ||
|
|
3ebb9c7d48 | ||
|
|
e68c5ce4b0 | ||
|
|
0fc231909c | ||
|
|
fde2a77aae | ||
|
|
6d4f076969 | ||
|
|
6f2796f851 | ||
|
|
478ec61cb0 | ||
|
|
667505b13a | ||
|
|
4f810fc7fd | ||
|
|
dab5efea30 | ||
|
|
5823bdae46 | ||
|
|
139db5e5b8 | ||
|
|
4e9e7a8bc1 | ||
|
|
7b57f04db9 | ||
|
|
42bd234c76 | ||
|
|
59af0f34ec | ||
|
|
57aaac8a8f | ||
|
|
dd41de430e | ||
|
|
ae43010da3 | ||
|
|
00d7480e9b | ||
|
|
687c66d7ec | ||
|
|
997da1f497 | ||
|
|
f2011e5032 | ||
|
|
dbefe8e33a | ||
|
|
a279863e80 | ||
|
|
f85d45f796 | ||
|
|
47101ca894 | ||
|
|
4db4663274 | ||
|
|
fdf5a83386 | ||
|
|
3bbec46585 | ||
|
|
92f4492fc3 | ||
|
|
04fdb31ef6 | ||
|
|
64694a4409 | ||
|
|
2257ae6919 | ||
|
|
579cdafb77 | ||
|
|
fae67394a3 | ||
|
|
b020580a40 | ||
|
|
f21f0ba54d | ||
|
|
9b911339d7 | ||
|
|
609cd5c8f8 | ||
|
|
127894b2f9 | ||
|
|
b7f3e1beba | ||
|
|
a7d476589a | ||
|
|
e23ac97e8e | ||
|
|
db31f77f9b | ||
|
|
6eb635f437 | ||
|
|
a308780acd | ||
|
|
91ca7fd1c2 | ||
|
|
0655f20543 | ||
|
|
4167861071 | ||
|
|
c19f15ae14 | ||
|
|
d684726ea7 | ||
|
|
12b2819e45 | ||
|
|
d2d77eb608 | ||
|
|
fdcc0f810b | ||
|
|
c2655df12d | ||
|
|
b6949ead26 | ||
|
|
f75e140ddf | ||
|
|
30da8c043d | ||
|
|
74edc3031f | ||
|
|
723a1caf05 | ||
|
|
745f36bea3 | ||
|
|
2bb362825d | ||
|
|
01cef587ec | ||
|
|
539427ddd2 | ||
|
|
798fe1e171 | ||
|
|
3ff03ef50e | ||
|
|
8a7175b011 | ||
|
|
2adee4054c | ||
|
|
4483e554e9 | ||
|
|
4d15d92e46 | ||
|
|
c29249a7de | ||
|
|
f8f1e83986 | ||
|
|
03f103bcff | ||
|
|
0b4c812e43 | ||
|
|
a1dd613232 | ||
|
|
f5d74d8d0e | ||
|
|
19a200707b | ||
|
|
a156a1d084 | ||
|
|
4236bc2af5 | ||
|
|
36c09c02ab | ||
|
|
5c9d0808dd | ||
|
|
6821548d50 | ||
|
|
a198247d13 | ||
|
|
255eed49a7 | ||
|
|
6cdcee0c58 | ||
|
|
96df597507 | ||
|
|
5054c72b32 | ||
|
|
16ee560f4c | ||
|
|
73bce495e1 | ||
|
|
25bb6f50a5 | ||
|
|
fdce1bbc59 | ||
|
|
48afd160d5 | ||
|
|
c48534b0ba | ||
|
|
e81f8ff96c | ||
|
|
2d9f93bdf8 | ||
|
|
65ba222b89 | ||
|
|
1d89578ab5 | ||
|
|
852a87a516 | ||
|
|
8aa4a77c4a | ||
|
|
d72b2611d7 | ||
|
|
4ea5d290c7 | ||
|
|
3e27beb825 | ||
|
|
526bcc61cf | ||
|
|
0e7cbd76aa | ||
|
|
e2aa30d139 | ||
|
|
99fea68bb8 | ||
|
|
682e905c49 | ||
|
|
ff4c5dff3b | ||
|
|
acc00863a4 | ||
|
|
94d5afbff7 | ||
|
|
191387f42d | ||
|
|
bdaac34a33 | ||
|
|
94475d2b04 | ||
|
|
f25af642b0 | ||
|
|
c5b7fd52c5 | ||
|
|
f460d6a230 | ||
|
|
504ac3055e | ||
|
|
1bc4ff4581 | ||
|
|
8bfe79ae87 | ||
|
|
1f12a9e529 | ||
|
|
a309361a2e | ||
|
|
abef299633 | ||
|
|
2c291fc1fc | ||
|
|
1c64186a00 | ||
|
|
07093ab0cb | ||
|
|
43b73501d7 | ||
|
|
e3c4360b62 | ||
|
|
01e7465f0c | ||
|
|
6f7011c9f2 | ||
|
|
716c9ac0a0 | ||
|
|
e295fcf9f2 | ||
|
|
60c28f1ecb | ||
|
|
9b164019ec | ||
|
|
e777c54414 | ||
|
|
97ddd1cd8b | ||
|
|
ebdee0bb44 | ||
|
|
d4f977c0af | ||
|
|
aed1a80532 | ||
|
|
e7f26e402b | ||
|
|
d7d97a74d8 | ||
|
|
964efd341e | ||
|
|
03ae299e36 | ||
|
|
a7b8c07d1a | ||
|
|
4162aae3a0 | ||
|
|
8b3f158a43 | ||
|
|
aac5b37485 | ||
|
|
731c7859ce | ||
|
|
953e038223 | ||
|
|
8be6e1e86b | ||
|
|
1f1bfff4f7 | ||
|
|
4512eeff23 | ||
|
|
e7ba2aee8e | ||
|
|
4ae6893f81 | ||
|
|
1592a0676c | ||
|
|
7666a1634a | ||
|
|
63d106e414 | ||
|
|
8481fada93 | ||
|
|
933763486c | ||
|
|
c90893d817 | ||
|
|
aea4f3709a | ||
|
|
46fcdc0894 | ||
|
|
1d75a25aa3 | ||
|
|
0786de54b2 | ||
|
|
ac8a11fefa | ||
|
|
081631242f | ||
|
|
3f0ab75efb | ||
|
|
12a3c65c9b | ||
|
|
480e36579c | ||
|
|
2c43c74fe3 | ||
|
|
4029ddd2b1 | ||
|
|
0f41c9ea08 | ||
|
|
a146ffb30e | ||
|
|
ac93f04dde | ||
|
|
c36ebc648b | ||
|
|
dd61be59b3 | ||
|
|
ac2086a0e0 | ||
|
|
ea043bc743 | ||
|
|
3dce95cc50 | ||
|
|
1ad5f4aef9 | ||
|
|
db7a597bcc | ||
|
|
d3116dc685 | ||
|
|
9bf909b4db | ||
|
|
be42cfaae1 | ||
|
|
4e2a0dbf28 | ||
|
|
8aa683452a | ||
|
|
642fa4a4aa | ||
|
|
686c5f4c65 | ||
|
|
43f56a8f39 | ||
|
|
68a4093e1a | ||
|
|
631049877e | ||
|
|
7b97855a3b | ||
|
|
08b5cd8333 | ||
|
|
e812efd6b7 | ||
|
|
dfd985da95 | ||
|
|
1831560bd6 | ||
|
|
f7e8da93d8 | ||
|
|
57586ce7c0 | ||
|
|
633365672c | ||
|
|
32244e9482 | ||
|
|
012e0687a8 | ||
|
|
1c6cbd5d30 | ||
|
|
3100d835df | ||
|
|
da94a6a014 | ||
|
|
2640a5354c | ||
|
|
57d9eeef64 | ||
|
|
0bfdc88e21 | ||
|
|
0e0afe7c9b | ||
|
|
c76db85ae7 | ||
|
|
2b554ca475 | ||
|
|
b58cd086ea | ||
|
|
968e021711 | ||
|
|
105a06537c | ||
|
|
5d02239dc1 | ||
|
|
5b254cfe75 | ||
|
|
a54b122094 | ||
|
|
1e04292ba2 | ||
|
|
1c2fe8c8fe | ||
|
|
a53772f094 | ||
|
|
e6bb883334 | ||
|
|
a9f53a4a95 | ||
|
|
d65886343b | ||
|
|
81a447dce9 | ||
|
|
12ed805737 | ||
|
|
c62f549176 | ||
|
|
296af8b4f6 | ||
|
|
3355e7c334 | ||
|
|
c727ee87d3 | ||
|
|
c28673b83b | ||
|
|
5ca48ddd73 | ||
|
|
8df43991a6 | ||
|
|
558b4a4c64 | ||
|
|
b6239b3c5c | ||
|
|
3ac692a7c7 | ||
|
|
9a7e643742 | ||
|
|
18fa8f4baf | ||
|
|
38746ad0dd | ||
|
|
40de41adc8 | ||
|
|
9c62e3d05c | ||
|
|
2bc5df8703 | ||
|
|
7508275cf7 | ||
|
|
8e43f75f70 | ||
|
|
b51b3214ba | ||
|
|
b7dec8f65e | ||
|
|
ccdb50e760 | ||
|
|
e136caef91 | ||
|
|
502deb840b | ||
|
|
3e89bc5d2c | ||
|
|
b1850e1dfe | ||
|
|
03c0097641 | ||
|
|
0c5f0f293f | ||
|
|
90998c447b | ||
|
|
81a1d8d2b2 | ||
|
|
cf67d5ac52 | ||
|
|
912a88ec0a | ||
|
|
8215e89d77 | ||
|
|
ac419adbd6 | ||
|
|
ac5a4daa6f | ||
|
|
4d637a349b | ||
|
|
d664227436 | ||
|
|
5efad350d8 | ||
|
|
4790325f0e | ||
|
|
181303b58b | ||
|
|
55ea2d7c12 | ||
|
|
40f2d87434 | ||
|
|
36daf153db | ||
|
|
8b7458a6c4 | ||
|
|
3c614910e5 | ||
|
|
807debb622 | ||
|
|
bc6525e431 | ||
|
|
32b6b9b1da | ||
|
|
8cc588d766 | ||
|
|
29fd6d03b9 | ||
|
|
81100e13b4 | ||
|
|
3085ee54d8 | ||
|
|
fedd2965cc | ||
|
|
7886bf7103 | ||
|
|
6a4aa28489 | ||
|
|
486b4fe8ba | ||
|
|
3d2e69cc21 | ||
|
|
ed9d91e527 | ||
|
|
1ef2a4fd3c | ||
|
|
2dd18c2b70 | ||
|
|
eec4dc1779 | ||
|
|
8fceb20a9e | ||
|
|
d622adb4ef | ||
|
|
693a9a454f | ||
|
|
0c4f7fa393 | ||
|
|
d5399995c3 | ||
|
|
e41e55f5aa | ||
|
|
04303eda75 | ||
|
|
4bf2f60f42 | ||
|
|
b5b16b5dff | ||
|
|
5f003ccb05 | ||
|
|
d396edad12 | ||
|
|
4bcecd5aa3 | ||
|
|
cad1165071 | ||
|
|
d24f14a8f2 | ||
|
|
9fec341589 | ||
|
|
dbe9c7bcaa | ||
|
|
5029da8763 | ||
|
|
ae734ded8e | ||
|
|
ed6ad315d1 | ||
|
|
1e7f6d98e7 | ||
|
|
1b77e43b61 | ||
|
|
7dab8a367f | ||
|
|
f44283d5f0 | ||
|
|
0b883d1f9a | ||
|
|
48031dd6da | ||
|
|
f32c8f5fe6 | ||
|
|
9b75a6a0a9 | ||
|
|
cd908f7e44 | ||
|
|
4e8f72a34b | ||
|
|
6cadaebb0c | ||
|
|
efbfab7914 | ||
|
|
7bb02d9a2a | ||
|
|
1604c71c33 | ||
|
|
f5a3d57afd | ||
|
|
368100f77c | ||
|
|
04f6878696 | ||
|
|
62c5985a34 | ||
|
|
91505c9350 | ||
|
|
5487303095 | ||
|
|
5c7e2b357f | ||
|
|
d36de24349 | ||
|
|
a19af18a61 | ||
|
|
69c1219f2b | ||
|
|
8c14efb7d8 | ||
|
|
25b6413be1 | ||
|
|
dfa75e145f | ||
|
|
43f7223a15 | ||
|
|
3be2667844 | ||
|
|
43371d3068 | ||
|
|
aa94d47148 | ||
|
|
7b1db3e1db | ||
|
|
beb556c73c | ||
|
|
c7e82497dd | ||
|
|
ff93fcdc9c | ||
|
|
5b6d0ced6e | ||
|
|
a44d6b09a8 | ||
|
|
4c1f36cf67 | ||
|
|
5daf1362cc | ||
|
|
cf252da519 | ||
|
|
5f83465529 | ||
|
|
9c8e96df16 | ||
|
|
163858e47b | ||
|
|
571eaa1378 | ||
|
|
4a2b25e5cc | ||
|
|
71d811c031 | ||
|
|
8e891da49f | ||
|
|
4f65a59324 | ||
|
|
446c846572 | ||
|
|
f44518ff27 | ||
|
|
51627ea2f6 | ||
|
|
b280089225 | ||
|
|
db66f19109 | ||
|
|
6cb67eb6c8 | ||
|
|
16a5556fa3 | ||
|
|
6805cf89ee | ||
|
|
8c88567c78 | ||
|
|
f41f7dafb9 | ||
|
|
5a1cd97b71 | ||
|
|
80569d5dfe | ||
|
|
585bf7523e | ||
|
|
9ce9a6597f | ||
|
|
490c6a763d | ||
|
|
7325c8e7bc | ||
|
|
1cc1807aec | ||
|
|
810d8a1af8 | ||
|
|
e3b6391653 | ||
|
|
72f628888e | ||
|
|
371c740321 | ||
|
|
f438ef7f49 | ||
|
|
892eecfa2b | ||
|
|
9f9d01ec58 | ||
|
|
faf1abb896 | ||
|
|
fdf5b03983 | ||
|
|
9236def453 | ||
|
|
dbf86c9408 | ||
|
|
7fb9198373 | ||
|
|
02b23ef926 | ||
|
|
2cd6077287 | ||
|
|
191a94e19d | ||
|
|
ce1618cb52 | ||
|
|
608a72d3ac | ||
|
|
19dac6e32b | ||
|
|
635c87389d | ||
|
|
9a18739a7f | ||
|
|
c793ded63e | ||
|
|
0b5c95ee57 | ||
|
|
874141e13b | ||
|
|
165a0aebd5 | ||
|
|
1d25839f53 | ||
|
|
a7aba651b8 | ||
|
|
c96ce9d37e | ||
|
|
afd710fccc | ||
|
|
770b93d275 | ||
|
|
85872eec19 | ||
|
|
ef9af6edae | ||
|
|
b35eb9ec81 | ||
|
|
54c68a61c0 | ||
|
|
6ab413de68 | ||
|
|
7fb9d2d55a | ||
|
|
f45c0a76e1 | ||
|
|
46f9a38056 | ||
|
|
b7169271ac | ||
|
|
9effc1edb5 | ||
|
|
a697e77e6c | ||
|
|
db6d296c20 | ||
|
|
3e37df1275 | ||
|
|
76bdd00a18 | ||
|
|
a9404bdf31 | ||
|
|
cc4b3715c5 | ||
|
|
ea2daa688f | ||
|
|
4e32988661 | ||
|
|
767e2ba4b7 | ||
|
|
5e397ea6d9 | ||
|
|
ca99a00ac2 | ||
|
|
33bc23b4e1 | ||
|
|
67087a6a7e | ||
|
|
fae4685082 | ||
|
|
f7466fd968 | ||
|
|
56f122b8b9 | ||
|
|
709f6d7c4a | ||
|
|
573bf273ff | ||
|
|
6e1524817e | ||
|
|
f1e2aff331 | ||
|
|
e68a485e16 | ||
|
|
e08cf95135 | ||
|
|
9a7ec7fd62 | ||
|
|
05446ddf1a | ||
|
|
de642bf3e6 | ||
|
|
fc1ac5973f | ||
|
|
d8e432b4cf | ||
|
|
d8875dd8b9 | ||
|
|
560559fad2 | ||
|
|
b6554eb0ac | ||
|
|
fdbee3a21f | ||
|
|
28b7529516 | ||
|
|
96c63b5e6f | ||
|
|
89790291a5 | ||
|
|
bafcca4c58 | ||
|
|
74aaf603e2 | ||
|
|
c3224c2a5b | ||
|
|
68ac417f65 | ||
|
|
0c114d3dab | ||
|
|
ccc5ceb3b9 | ||
|
|
dc55ff4078 | ||
|
|
9daff2b738 | ||
|
|
f876ae49ad | ||
|
|
b644d560d8 | ||
|
|
8a3fa2956c | ||
|
|
67ed07549e | ||
|
|
58f6191a8e | ||
|
|
66ce5b6e0e | ||
|
|
5bfc73fafb | ||
|
|
8b48a82968 | ||
|
|
fd1df47fec | ||
|
|
14f14e20df | ||
|
|
4a7c90e5bc | ||
|
|
90cf8eabd8 | ||
|
|
e690f97c8d | ||
|
|
5860a5a027 | ||
|
|
5a362aa8f9 | ||
|
|
9e7f2b76f7 | ||
|
|
f0a8346ef4 | ||
|
|
a1fb2115cd | ||
|
|
0423dd32c4 | ||
|
|
ea98ccb8f1 | ||
|
|
44f51e5e60 | ||
|
|
f4ec180d85 | ||
|
|
4663fa507b | ||
|
|
f2410f962b | ||
|
|
ac2eb6dce0 | ||
|
|
b8af814940 | ||
|
|
5416a87149 | ||
|
|
7dece38b57 | ||
|
|
336c452ef3 | ||
|
|
78b3f1faf8 | ||
|
|
932d2fb4cb | ||
|
|
3deb903f3c | ||
|
|
867d78b650 | ||
|
|
c4ebd75008 | ||
|
|
92a76c9de1 | ||
|
|
d20940f110 | ||
|
|
a3f06f7e33 | ||
|
|
5f73cded22 | ||
|
|
e699d5ff2b | ||
|
|
3f4a5586de | ||
|
|
3ceabfabdd | ||
|
|
47d1f94a13 | ||
|
|
0602325974 | ||
|
|
1eb47dec6a | ||
|
|
d1a8a79525 | ||
|
|
53aa74b493 | ||
|
|
78eddadac4 | ||
|
|
db4b7ca192 | ||
|
|
092e30a552 | ||
|
|
059fd89b69 | ||
|
|
9a97b00750 | ||
|
|
443bf7ab85 | ||
|
|
1aecc3d922 | ||
|
|
75e67dcbd0 | ||
|
|
302f3a96ce | ||
|
|
8b8058dc47 | ||
|
|
f3ed351c61 | ||
|
|
6d3fbc4591 | ||
|
|
6519691e79 | ||
|
|
c150a89d13 | ||
|
|
29bdd2f8fb | ||
|
|
1cd044ffda | ||
|
|
a4ef26b1a6 | ||
|
|
dcaf90e394 | ||
|
|
e6098e0cf5 | ||
|
|
8822a7baf7 | ||
|
|
d60b4d7363 | ||
|
|
9dc457125e | ||
|
|
9d8e9df44e | ||
|
|
966e6399f6 | ||
|
|
bd1b804026 | ||
|
|
87ee72ad9d | ||
|
|
546117253d | ||
|
|
1a2dade994 | ||
|
|
8574299ed5 | ||
|
|
647773cd2e | ||
|
|
fc046003d5 | ||
|
|
d850262e6c | ||
|
|
94e0e3778e | ||
|
|
331244ddd2 | ||
|
|
0aad3cbbf3 | ||
|
|
34c668714a | ||
|
|
9fa667845e | ||
|
|
d388ae01bb | ||
|
|
d215ba8c8b | ||
|
|
a661a94d80 | ||
|
|
d7f2493811 | ||
|
|
6cc5b0edb9 | ||
|
|
8aad95525c | ||
|
|
ea4b5b057a | ||
|
|
8943445efa | ||
|
|
4195b0d62c | ||
|
|
a94cfe92d3 | ||
|
|
85d8f1c0d5 | ||
|
|
bf3fcadd28 | ||
|
|
715937a394 | ||
|
|
262b712d5b | ||
|
|
a8d60ab38a | ||
|
|
d271e23f99 | ||
|
|
65e3e77a59 | ||
|
|
c5b3bb05dd | ||
|
|
5267ef0fb7 | ||
|
|
56eea049e6 | ||
|
|
4baf407657 | ||
|
|
443682f24f | ||
|
|
f5f9b793c4 | ||
|
|
528c263930 | ||
|
|
50e1005b29 | ||
|
|
24845e6077 | ||
|
|
3ea9e3f6e0 | ||
|
|
fb39e64417 | ||
|
|
08506617b6 | ||
|
|
f6b044ebdd | ||
|
|
1bc2fe75ed | ||
|
|
bbe1acb143 | ||
|
|
4b836b441c | ||
|
|
c30e053ab1 | ||
|
|
fffa9afbfa | ||
|
|
d731fddaf1 | ||
|
|
9562f56a3a | ||
|
|
97dd07e1d4 | ||
|
|
155daa0335 | ||
|
|
aa79fe3145 | ||
|
|
b56d94d0bf | ||
|
|
a3d505dd97 | ||
|
|
72c8445693 | ||
|
|
5fe9554965 | ||
|
|
95107e1624 | ||
|
|
1e63652d32 | ||
|
|
6bde0e7c9b | ||
|
|
405f800908 | ||
|
|
62edfd19f9 | ||
|
|
176ebf9409 | ||
|
|
275d63de18 | ||
|
|
11c6c89c7d | ||
|
|
e6dba99aa8 | ||
|
|
5b4a45a487 | ||
|
|
aac84677e4 | ||
|
|
99148db07a | ||
|
|
e4e5e7c681 | ||
|
|
263b59dae2 | ||
|
|
5dbd71b565 | ||
|
|
7bcd437664 | ||
|
|
a599c38537 | ||
|
|
af79794300 | ||
|
|
acc1167fe1 | ||
|
|
fa5b953010 | ||
|
|
10081b3880 | ||
|
|
d944a0e7c6 | ||
|
|
0940ac84a0 | ||
|
|
cc45b87ebe | ||
|
|
1a66bc4645 | ||
|
|
359556909c | ||
|
|
2e4e060b46 | ||
|
|
c624c040b7 | ||
|
|
bae1987704 | ||
|
|
04f6150399 | ||
|
|
fe66116dde | ||
|
|
24282fe0ab | ||
|
|
26ff8070ac | ||
|
|
7040567aab | ||
|
|
d581db23f8 | ||
|
|
b792e3695d | ||
|
|
27c5d75871 | ||
|
|
f3e6b71e09 | ||
|
|
c83baae50d | ||
|
|
7314dbee73 | ||
|
|
2be0fd84fe | ||
|
|
0593b19d51 | ||
|
|
c0ebbb0b44 | ||
|
|
81a084984b | ||
|
|
5c5e640b04 | ||
|
|
aee85a7cb0 | ||
|
|
98f04859ab | ||
|
|
4fe7b6754c | ||
|
|
43d19e02a6 | ||
|
|
2c90b1386d | ||
|
|
b737cde803 | ||
|
|
9f3d7baf05 | ||
|
|
0e729b51bf | ||
|
|
5446d25528 | ||
|
|
43677cca89 | ||
|
|
dbe270539b | ||
|
|
e724a1ee9e | ||
|
|
161421a00c | ||
|
|
e40e19cd1e | ||
|
|
b7fc92e343 | ||
|
|
f343ce3094 | ||
|
|
d1b54eb8cf | ||
|
|
53f87a6e4f | ||
|
|
b0ef8114b6 | ||
|
|
d4ef81a7ef | ||
|
|
482ffe0c65 | ||
|
|
bd3c4cb40c | ||
|
|
8185811188 | ||
|
|
465d7123f6 | ||
|
|
d4e13e8543 | ||
|
|
c79f2d0a96 | ||
|
|
aa24b19d2b | ||
|
|
883ac4590c | ||
|
|
6e360bef93 | ||
|
|
5df734fcf7 | ||
|
|
bfb2b56599 | ||
|
|
08e12494ec | ||
|
|
d8ec0f2e94 | ||
|
|
665e60a646 | ||
|
|
90cfe1ac23 | ||
|
|
1afa7601c9 | ||
|
|
d22ec1cf7a | ||
|
|
fdc7f6237e | ||
|
|
17d2ab505c | ||
|
|
666cc29d47 | ||
|
|
0bbe719f16 | ||
|
|
c23147185e | ||
|
|
4191ea8a18 | ||
|
|
4358c4e64c | ||
|
|
97a2bd3854 | ||
|
|
7bba7c321b | ||
|
|
e6353b5d44 | ||
|
|
775acff846 | ||
|
|
cd771f1432 | ||
|
|
80eb7d7cea | ||
|
|
b8a15d9a5d | ||
|
|
6755723e0b | ||
|
|
17548eeff7 | ||
|
|
8a7db6ee15 | ||
|
|
4d2bf38a66 | ||
|
|
01bbbd3bda | ||
|
|
1897154cfd | ||
|
|
b5eb75d607 | ||
|
|
1ad5acef7c | ||
|
|
b0d9bc2d6a | ||
|
|
16ccfeda1d | ||
|
|
37ec617801 | ||
|
|
6be1f99cf9 | ||
|
|
3ba72cbad5 | ||
|
|
9624815c82 | ||
|
|
bbb9924c4b | ||
|
|
6da45fc139 | ||
|
|
e73a2a8b9e | ||
|
|
5163b094ae | ||
|
|
4ff6b2aeae | ||
|
|
56a178c786 | ||
|
|
c9dbf58737 | ||
|
|
95d276f7a0 | ||
|
|
a1448597af | ||
|
|
99e161b05b | ||
|
|
03ea5d4e3b | ||
|
|
adf41722f5 | ||
|
|
c69ff17048 | ||
|
|
ccbfe04988 | ||
|
|
b027e49f33 | ||
|
|
39f048910a | ||
|
|
df6140c282 | ||
|
|
7790ab1b53 | ||
|
|
ba155feb6c | ||
|
|
1d9e2ccad2 | ||
|
|
a7619e889b | ||
|
|
a6118b2942 | ||
|
|
373b9657c2 | ||
|
|
06c0eb3e87 | ||
|
|
3f0724d1dc | ||
|
|
a6f94cb0a3 | ||
|
|
5be2fa9cfe | ||
|
|
224bcc1721 | ||
|
|
33705c52e0 | ||
|
|
2c28c0b771 | ||
|
|
ffeb794303 | ||
|
|
06eb509bc3 | ||
|
|
6428d39ce5 | ||
|
|
7695c14598 | ||
|
|
d259d7c5e7 | ||
|
|
42311b2c54 | ||
|
|
a020d83c8a | ||
|
|
9639639420 | ||
|
|
d59f5b9473 | ||
|
|
7e3b6a841f | ||
|
|
ee3aab938a | ||
|
|
43ce8d6372 | ||
|
|
0ac76e3452 | ||
|
|
a50cabc572 | ||
|
|
072c4fef86 | ||
|
|
4226f4134f | ||
|
|
6c3deb392d | ||
|
|
e9adffec54 | ||
|
|
3ce82c5150 | ||
|
|
233a466dd6 | ||
|
|
527e045c69 | ||
|
|
bd3a1f6c4d | ||
|
|
215b85c407 | ||
|
|
f592db516f | ||
|
|
d9d2d13d0d | ||
|
|
e9bb34898b | ||
|
|
d596e41716 | ||
|
|
101a1060e7 | ||
|
|
f649ebff24 | ||
|
|
d4cc804624 | ||
|
|
56ad66c04f | ||
|
|
9a27304a9e | ||
|
|
a1b1417acb | ||
|
|
9603310b4e | ||
|
|
056ab10121 | ||
|
|
9cec0af484 | ||
|
|
1fa810362f | ||
|
|
817ca8e176 | ||
|
|
7a7c5cc53e | ||
|
|
49d1b2c948 | ||
|
|
2c18ee92c7 | ||
|
|
136b7e25c3 | ||
|
|
af8c0e3b9a | ||
|
|
f4162c2868 | ||
|
|
64e332f3d5 | ||
|
|
7bcaadf6ee | ||
|
|
56faccd2fc | ||
|
|
04478e77e1 | ||
|
|
1e83f3c035 | ||
|
|
4e27ee638a | ||
|
|
a6164f7464 | ||
|
|
81b6516cac | ||
|
|
f09fcd6cd5 | ||
|
|
1dfc6ff77c | ||
|
|
ecfe3e0936 | ||
|
|
576704562e | ||
|
|
8f10f3ed15 | ||
|
|
3d3432762e | ||
|
|
ac5fb3c9ac | ||
|
|
63842088a4 | ||
|
|
3ce369ab43 | ||
|
|
6269330364 | ||
|
|
466e0946d6 | ||
|
|
51f0ba7e1c | ||
|
|
30e165c8ce | ||
|
|
70befbcd77 | ||
|
|
5382a9c467 | ||
|
|
8ade5f68aa | ||
|
|
6fb4682d8b | ||
|
|
47bdb27261 | ||
|
|
dcffda5c4f | ||
|
|
fa8a12a6f2 | ||
|
|
3066cfdd04 | ||
|
|
3fa3705d55 | ||
|
|
ef8f794b93 | ||
|
|
52402536d9 | ||
|
|
daa637c557 | ||
|
|
58017f45a1 | ||
|
|
2c9858c261 | ||
|
|
9f2497e0f9 | ||
|
|
c3f5a69d68 | ||
|
|
1b1cc715be | ||
|
|
facec04ac2 | ||
|
|
28b37f68ea | ||
|
|
4dd633cef8 | ||
|
|
4335a566f1 | ||
|
|
ecfc5e645a | ||
|
|
1baa16b8e3 | ||
|
|
93859d1a63 | ||
|
|
22fe25da32 | ||
|
|
6afaa46cf4 | ||
|
|
24e34278aa | ||
|
|
103d772533 | ||
|
|
3f44eaee39 | ||
|
|
557c682a5e | ||
|
|
2ee84797ba | ||
|
|
2886aa0bab | ||
|
|
d9131b4b07 | ||
|
|
23b4958592 | ||
|
|
779a0a9d26 | ||
|
|
13b211e813 | ||
|
|
78daf36418 | ||
|
|
0694f28f32 | ||
|
|
2c11cade06 | ||
|
|
ff0139d809 | ||
|
|
2b7858de7c | ||
|
|
e579180abf | ||
|
|
635d37112e | ||
|
|
63333b11ad | ||
|
|
d2554788ea | ||
|
|
22269ccfb1 | ||
|
|
fb7c137a11 | ||
|
|
dd4ca33ed0 | ||
|
|
90e2277490 | ||
|
|
b3293a031f | ||
|
|
353b70d6c7 | ||
|
|
e74f6d5655 | ||
|
|
7d0cb70584 | ||
|
|
82e998e939 | ||
|
|
49de06e500 | ||
|
|
55beac9386 | ||
|
|
0cf31ae364 | ||
|
|
693f498d05 | ||
|
|
f46cce493f | ||
|
|
8c04526c62 | ||
|
|
cdf76080c5 | ||
|
|
bc513dcba9 | ||
|
|
016d7e8926 | ||
|
|
2cc1878cd6 | ||
|
|
81b4c3fb8d | ||
|
|
76a889f160 | ||
|
|
a8758b7f04 | ||
|
|
2259c115c4 | ||
|
|
54af962ea3 | ||
|
|
53cb3dda73 | ||
|
|
a010371a8b | ||
|
|
afcc20f483 | ||
|
|
b1ee353ea8 | ||
|
|
4037a0c0b0 | ||
|
|
2cc474dd26 | ||
|
|
9df2669839 | ||
|
|
ecea97f409 | ||
|
|
522a0ecb4a | ||
|
|
74dfdc7702 | ||
|
|
501c3fffe6 | ||
|
|
34e5177da6 | ||
|
|
fe022cb19c | ||
|
|
ea62a684d0 | ||
|
|
3410f0f0ce | ||
|
|
48b0b95e58 | ||
|
|
633688f184 | ||
|
|
b7c2585742 | ||
|
|
c4f186b3c8 | ||
|
|
87a51cf4c2 | ||
|
|
db161599e9 | ||
|
|
de2937d4dd | ||
|
|
d9d58e498f | ||
|
|
562c99b9ed | ||
|
|
2f67532fdd | ||
|
|
fd0c336600 | ||
|
|
0e3e1bf80b | ||
|
|
ff5c60a608 | ||
|
|
1dafa02f53 | ||
|
|
d1d7c9d9df | ||
|
|
2cc30e6ce1 | ||
|
|
fc223d0b0b | ||
|
|
59c7a85355 | ||
|
|
8fdec31c35 | ||
|
|
481b8a5fff | ||
|
|
626f12e619 | ||
|
|
37916f7cbe | ||
|
|
f00e5494fb | ||
|
|
8f3041fe56 | ||
|
|
0e1a3cef6c | ||
|
|
70ad0c0475 | ||
|
|
ddc835d1f3 | ||
|
|
d6f6892d37 | ||
|
|
598efd26f3 | ||
|
|
342a6a6187 | ||
|
|
bfc4193b76 | ||
|
|
a52845a4c2 | ||
|
|
e56662fbef | ||
|
|
7d5e91afae | ||
|
|
ab65cd250c | ||
|
|
588374646e | ||
|
|
1337d8dd5f | ||
|
|
9d0aaa0eb5 | ||
|
|
870eb12390 | ||
|
|
a2c5c83a48 | ||
|
|
babad2942a | ||
|
|
bddd8e0745 | ||
|
|
41f11a14e3 | ||
|
|
cfa0e19205 | ||
|
|
c69802ef91 | ||
|
|
217e754de2 | ||
|
|
21d6d5880c | ||
|
|
3c99be6d25 | ||
|
|
8519b47dc2 | ||
|
|
a5d28b67e9 | ||
|
|
ae8a2afb8b | ||
|
|
6f88060ffe | ||
|
|
b092d15c12 | ||
|
|
2ed87b2c1a | ||
|
|
2f9c957310 | ||
|
|
2c2539e457 | ||
|
|
25efc898a0 | ||
|
|
9285691c62 | ||
|
|
9276ea8c98 | ||
|
|
42c04b074c | ||
|
|
e5fbf5c655 | ||
|
|
599afa76e6 | ||
|
|
4ef628d0e6 | ||
|
|
5644b8e048 | ||
|
|
ba7f5284cf | ||
|
|
3be2ccca8f | ||
|
|
04872e07f7 | ||
|
|
c889a4ef45 | ||
|
|
65e34ddc40 | ||
|
|
cb95ecc7cb | ||
|
|
60a79ba9b7 | ||
|
|
585008e29d | ||
|
|
376140cab8 | ||
|
|
90259d4148 | ||
|
|
043b1b2095 | ||
|
|
6ab4560887 | ||
|
|
2b77aa1b54 | ||
|
|
ecaf50a1a8 | ||
|
|
1b683328e0 | ||
|
|
6888d37fb6 | ||
|
|
824cc56802 | ||
|
|
802d07c894 | ||
|
|
87791963b6 | ||
|
|
323811adb2 | ||
|
|
bb3e8170d5 | ||
|
|
f2220e664a | ||
|
|
a8773c3331 | ||
|
|
d34201d0d9 | ||
|
|
739f63bc40 | ||
|
|
56431a161e | ||
|
|
f0064357f5 | ||
|
|
d2940741f8 | ||
|
|
719b8f1894 | ||
|
|
ce7bf469f3 | ||
|
|
758301149a | ||
|
|
b5e673d209 | ||
|
|
1ee08d4847 | ||
|
|
57137da4bc | ||
|
|
61f0e261bf | ||
|
|
ed78372fd0 | ||
|
|
fcc37cc3d2 | ||
|
|
d0677f1c44 | ||
|
|
4122f1861b | ||
|
|
f691767fd5 | ||
|
|
54e0b1520d | ||
|
|
0fb5d8f7a0 | ||
|
|
34af828653 | ||
|
|
96f24bf8d4 | ||
|
|
0c084ec463 | ||
|
|
aef6991eb4 | ||
|
|
7b0ac3a373 | ||
|
|
02e9f8dadc | ||
|
|
8cde7e24f2 | ||
|
|
10e9d4ce37 | ||
|
|
2f9931eb6e | ||
|
|
e8efbfce42 | ||
|
|
6c8fa400d9 | ||
|
|
af9fff2911 | ||
|
|
d2a667ef9e | ||
|
|
025b5c77ff | ||
|
|
389c906d8f | ||
|
|
943a962bb9 | ||
|
|
8a21aae034 | ||
|
|
bc6189c65e | ||
|
|
1fcd0290d5 | ||
|
|
2dbf10d905 | ||
|
|
732031a624 | ||
|
|
8e3f83403f | ||
|
|
5be34ec932 | ||
|
|
e15e5721fb | ||
|
|
a0139317e8 | ||
|
|
50724aa7fd | ||
|
|
5540805570 | ||
|
|
937a7b7060 | ||
|
|
343f36c4f5 | ||
|
|
c1af1d9c31 | ||
|
|
1173406b0a | ||
|
|
2ec0c76ba2 | ||
|
|
372af61076 | ||
|
|
b9d4356487 | ||
|
|
fdedfecfba | ||
|
|
f8d75bf004 | ||
|
|
bf1db7b207 | ||
|
|
3be3f614c2 | ||
|
|
c54b35c79c | ||
|
|
279b16fd26 | ||
|
|
03ff484702 | ||
|
|
5a5f723d5f | ||
|
|
c70163f7ef | ||
|
|
147ef71e06 | ||
|
|
8ded145b96 | ||
|
|
e6756b3c06 | ||
|
|
f18ba2e248 | ||
|
|
41f03b2d3e | ||
|
|
f0dd3f1f2e | ||
|
|
3ad7366077 | ||
|
|
683ac4e6f6 | ||
|
|
ae404f047e | ||
|
|
80466f6c5f | ||
|
|
3169ac165a | ||
|
|
b32bbe4629 | ||
|
|
397aea8e6a | ||
|
|
a65026dd03 | ||
|
|
c3176256dc | ||
|
|
13c905ec48 | ||
|
|
1e4541c784 | ||
|
|
5ca7d18f7d | ||
|
|
530704d31e | ||
|
|
275b6fc1e1 | ||
|
|
b5070a160a | ||
|
|
5eecc1f78d | ||
|
|
be9d70855f | ||
|
|
4cd366e634 | ||
|
|
a17c07ad46 | ||
|
|
98b9d5ce90 | ||
|
|
11ff2fa0a1 | ||
|
|
6d1d4a9bb1 | ||
|
|
51a6ea6164 | ||
|
|
0e787e758a | ||
|
|
949a00de14 | ||
|
|
a7ad0720bc | ||
|
|
b5903bbf84 | ||
|
|
061985a336 | ||
|
|
911286ab14 | ||
|
|
a95a633586 | ||
|
|
2cbf237887 | ||
|
|
0dc403d278 | ||
|
|
8d48e37aa0 | ||
|
|
aec70c8250 | ||
|
|
17e8e55b08 | ||
|
|
ee76a1c360 | ||
|
|
463aa25693 | ||
|
|
aadff9c9bd | ||
|
|
45ae42c4fc | ||
|
|
556eec95d8 | ||
|
|
422fbc4d2c |
@@ -0,0 +1,16 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
max_line_length=120
|
||||
|
||||
[*.gradle]
|
||||
indent_size = 2
|
||||
@@ -0,0 +1,2 @@
|
||||
*.cpp linguist-language=java
|
||||
*.cc linguist-language=java
|
||||
@@ -0,0 +1,13 @@
|
||||
### Expected behavior
|
||||
|
||||
### Actual behavior
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
### Minimal yet complete reproducer code (or URL to code) or complete log file
|
||||
|
||||
### Moquette MQTT version
|
||||
|
||||
### JVM version (e.g. `java -version`)
|
||||
|
||||
### OS version (e.g. `uname -a`)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/build
|
||||
*/build/
|
||||
/target/
|
||||
/parser/target/
|
||||
/benchmarking/target/
|
||||
.idea/
|
||||
*.iml
|
||||
broker/target/
|
||||
client/target/
|
||||
parser_commons/target/
|
||||
netty_parser/target/
|
||||
netty_parser/target/
|
||||
parser_commons/target/
|
||||
distribution/target/
|
||||
broker/moquette.log
|
||||
broker/runner
|
||||
/bundle/target/
|
||||
/bundle/runner/
|
||||
/osgi_test/target/
|
||||
/perf/target/
|
||||
/mapdb_storage/target/
|
||||
*~
|
||||
*.log*
|
||||
.DS_Store
|
||||
.settings/
|
||||
.project
|
||||
.classpath
|
||||
/*/bin/
|
||||
*.swp
|
||||
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
broker/*moquette_store.*
|
||||
embedding_moquette/target/
|
||||
broker/nb-configuration.xml
|
||||
broker/nbactions-Server.xml
|
||||
|
||||
maven-metadata.xml
|
||||
/.gradle/
|
||||
/bin/
|
||||
broker/config/git.properties
|
||||
distribution/src/main/resources/git.properties
|
||||
broker/h2db
|
||||
broker/media
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
script:
|
||||
- ./gradlew install test
|
||||
@@ -0,0 +1,55 @@
|
||||
Creative Commons Attribution-NoDerivs 3.0 Unported
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
|
||||
|
||||
License
|
||||
|
||||
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
|
||||
|
||||
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
|
||||
|
||||
1. Definitions
|
||||
a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
|
||||
b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
|
||||
c. "Distribute" means to make available to the public the original and copies of the Work through sale or other transfer of ownership.
|
||||
d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
|
||||
e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
|
||||
f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
|
||||
g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
|
||||
h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
|
||||
i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
|
||||
2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
|
||||
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
|
||||
a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; and,
|
||||
b. to Distribute and Publicly Perform the Work including as incorporated in Collections.
|
||||
c. For the avoidance of doubt:
|
||||
i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
|
||||
ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
|
||||
iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
|
||||
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Adaptations. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
|
||||
|
||||
4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
|
||||
a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested.
|
||||
b. If You Distribute, or Publicly Perform the Work or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(b) may be implemented in any reasonable manner; provided, however, that in the case of a Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
|
||||
c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation.
|
||||
5. Representations, Warranties and Disclaimer
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
|
||||
|
||||
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
7. Termination
|
||||
a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
|
||||
b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
|
||||
8. Miscellaneous
|
||||
a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
|
||||
b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
|
||||
d. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
|
||||
e. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
|
||||
Creative Commons Notice
|
||||
|
||||
Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
|
||||
|
||||
Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
|
||||
|
||||
Creative Commons may be contacted at http://creativecommons.org/.
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
Copyright (c) <2019> <wildfirechat>
|
||||
|
||||
Anti 996 License Version 1.0 (Draft)
|
||||
|
||||
Permission is hereby granted to any individual or legal entity
|
||||
obtaining a copy of this licensed work (including the source code,
|
||||
documentation and/or related items, hereinafter collectively referred
|
||||
to as the "licensed work"), free of charge, to deal with the licensed
|
||||
work for any purpose, including without limitation, the rights to use,
|
||||
reproduce, modify, prepare derivative works of, distribute, publish
|
||||
and sublicense the licensed work, subject to the following conditions:
|
||||
|
||||
1. The individual or the legal entity must conspicuously display,
|
||||
without modification, this License and the notice on each redistributed
|
||||
or derivative copy of the Licensed Work.
|
||||
|
||||
2. The individual or the legal entity must strictly comply with all
|
||||
applicable laws, regulations, rules and standards of the jurisdiction
|
||||
relating to labor and employment where the individual is physically
|
||||
located or where the individual was born or naturalized; or where the
|
||||
legal entity is registered or is operating (whichever is stricter). In
|
||||
case that the jurisdiction has no such laws, regulations, rules and
|
||||
standards or its laws, regulations, rules and standards are
|
||||
unenforceable, the individual or the legal entity are required to
|
||||
comply with Core International Labor Standards.
|
||||
|
||||
3. The individual or the legal entity shall not induce or force its
|
||||
employee(s), whether full-time or part-time, or its independent
|
||||
contractor(s), in any methods, to agree in oral or written form, to
|
||||
directly or indirectly restrict, weaken or relinquish his or her
|
||||
rights or remedies under such laws, regulations, rules and standards
|
||||
relating to labor and employment as mentioned above, no matter whether
|
||||
such written or oral agreement are enforceable under the laws of the
|
||||
said jurisdiction, nor shall such individual or the legal entity
|
||||
limit, in any methods, the rights of its employee(s) or independent
|
||||
contractor(s) from reporting or complaining to the copyright holder or
|
||||
relevant authorities monitoring the compliance of the license about
|
||||
its violation(s) of the said license.
|
||||
|
||||
THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE
|
||||
LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK.
|
||||
Executable
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,2 +1,69 @@
|
||||
## 野火IM解决方案
|
||||
|
||||
野火IM是一套跨平台、全开源的即时通讯解决方案,主要包含以下内容。
|
||||
|
||||
| 仓库 | 说明 | 备注 |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------- | ---------------------------------------------- |
|
||||
| [android-chat](https://github.com/wildfirechat/android-chat) | 野火IM Android App | 可以很方便地进行二次开发,或集成到现有应用当中 |
|
||||
| [ios-chat](https://github.com/wildfirechat/ios-chat) | 野火IM iOS App | 可以很方便地进行二次开发,或集成到现有应用当中 |
|
||||
| [pc-chat](https://github.com/wildfirechat/pc-chat) | 基于[Electron](https://electronjs.org/)开发的PC平台应用 | |
|
||||
| [proto](https://github.com/wildfirechat/proto) | 野火IM的协议栈实现 | |
|
||||
| [server](https://github.com/wildfirechat/server) | IM server | |
|
||||
| [app server](https://github.com/wildfirechat/app_server) | 应用服务端 | |
|
||||
| [robot_server](https://github.com/wildfirechat/robot_server) | 机器人服务端 | |
|
||||
| [push_server](https://github.com/wildfirechat/push_server) | 推送服务器 | |
|
||||
| [docs](https://github.com/wildfirechat/docs) | 野火IM相关文档,包含设计、概念、开发、使用说明 | |
|
||||
|
||||
# server
|
||||
本系统有4部分组成,[服务器](https://github.com/wildfirechat/server)/[iOS客户端](https://github.com/wildfirechat/ios-chat)/[Android客户端](https://github.com/wildfirechat/android-chat)/[协议栈库](https://github.com/wildfirechat/proto)。其中iOS和Android都依赖于协议栈库。本工程为服务器的发布工程,请阅读[docs](http://docs.wildfirechat.cn)或下载服务器[发布版本](https://github.com/wildfirechat/server/releases)
|
||||
本工程为野火IM 社区版IM服务软件。野火IM作为一个通用的即时通讯解决方案,可以集成到各种应用中。请阅读[docs](http://docs.wildfirechat.cn)或下载服务器[发布版本](https://github.com/wildfirechat/server/releases)
|
||||
|
||||
|
||||
开发一套IM系统真的很艰辛,请路过的朋友们给点个star,支持我们坚持下去🙏🙏🙏🙏🙏
|
||||
|
||||
|
||||
### 联系我们
|
||||
问题讨论请加群:822762829
|
||||
|
||||
<img src="http://static.wildfirechat.cn/qr_qqgroup.jpeg" width = 50% height = 50% />
|
||||
|
||||
#### 体验Demo
|
||||
我们提供了体验demo,请使用微信扫码下载安装体验
|
||||
|
||||

|
||||
|
||||
#### 应用截图
|
||||

|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-message-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-contact-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-discover-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-settings-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-messagelist-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-chat-setting-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-takephoto-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-record-voice-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-location-view.png" width = 50% height = 50% />
|
||||
|
||||
<img src="http://static.wildfirechat.cn/ios-voip-view.png" width = 50% height = 50% />
|
||||
|
||||
## 编译
|
||||
在安装JDK1.8以上及maven的前提下,在命令行中执行```mvn clean compile package```,生成的目标文件在```./distribution/target/distribution-xxxx-bundle-tar.tar.gz```
|
||||
|
||||
## 特别感谢
|
||||
1. [moquette](https://github.com/moquette-io/moquette) 本项目是基于此项目二次开发而来,处理MQTT相关业务。
|
||||
2. [loServer](https://github.com/looly/loServer) 本项目使用loServer处理HTTP相关业务。
|
||||
|
||||
*** 对他们表示诚挚的感谢🙏 ***
|
||||
|
||||
## License
|
||||
|
||||
1. Under the Creative Commons Attribution-NoDerivs 3.0 Unported license. See the [LICENSE](https://github.com/wildfirechat/server/blob/wildfirechat/LICENSE) file for details.
|
||||
2. Under the Anti 996 License. See the [Anti 996 LICENSE](https://github.com/wildfirechat/server/blob/wildfirechat/LICENSE_996) file for details.
|
||||
|
||||
Executable
+130
@@ -0,0 +1,130 @@
|
||||
<hazelcast
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.hazelcast.com/schema/config
|
||||
http://www.hazelcast.com/schema/config/hazelcast-config-3.5.xsd"
|
||||
xmlns="http://www.hazelcast.com/schema/config">
|
||||
|
||||
<group>
|
||||
<!--<name>wfc-dev</name>-->
|
||||
<name>wfc-pro</name>
|
||||
</group>
|
||||
|
||||
<network>
|
||||
<join>
|
||||
<multicast enabled="true"/>
|
||||
</join>
|
||||
</network>
|
||||
|
||||
<!-- map eviction -->
|
||||
<!-- http://docs.hazelcast.org/docs/latest-development/manual/html/Distributed_Data_Structures/Map/Map_Eviction.html -->
|
||||
<map name="messages_map">
|
||||
<!-- 7 days -->
|
||||
<time-to-live-seconds>604800</time-to-live-seconds>
|
||||
<eviction-policy>LRU</eviction-policy>
|
||||
<max-size policy="PER_NODE">1000000</max-size>
|
||||
<eviction-percentage>10</eviction-percentage>
|
||||
<map-store enabled="true">
|
||||
<class-name>io.moquette.persistence.MessageLoader</class-name>
|
||||
<write-delay-seconds>0</write-delay-seconds>
|
||||
</map-store>
|
||||
</map>
|
||||
|
||||
<map name="groups_map">
|
||||
<!-- 7 days -->
|
||||
<time-to-live-seconds>604800</time-to-live-seconds>
|
||||
<eviction-policy>LRU</eviction-policy>
|
||||
<max-size policy="PER_NODE">1000000</max-size>
|
||||
<eviction-percentage>10</eviction-percentage>
|
||||
<map-store enabled="true">
|
||||
<class-name>io.moquette.persistence.GroupLoader</class-name>
|
||||
<write-delay-seconds>0</write-delay-seconds>
|
||||
</map-store>
|
||||
</map>
|
||||
|
||||
<map name="users">
|
||||
<time-to-live-seconds>604800</time-to-live-seconds>
|
||||
<eviction-policy>LRU</eviction-policy>
|
||||
<max-size policy="PER_NODE">1000000</max-size>
|
||||
<eviction-percentage>10</eviction-percentage>
|
||||
<map-store enabled="true">
|
||||
<class-name>io.moquette.persistence.UserLoader</class-name>
|
||||
<write-delay-seconds>0</write-delay-seconds>
|
||||
</map-store>
|
||||
</map>
|
||||
|
||||
<map name="user_status">
|
||||
<time-to-live-seconds>604800</time-to-live-seconds>
|
||||
<eviction-policy>LRU</eviction-policy>
|
||||
<max-size policy="PER_NODE">1000000</max-size>
|
||||
<eviction-percentage>10</eviction-percentage>
|
||||
<map-store enabled="true">
|
||||
<class-name>io.moquette.persistence.UserStatusLoader</class-name>
|
||||
<write-delay-seconds>0</write-delay-seconds>
|
||||
</map-store>
|
||||
</map>
|
||||
|
||||
<set name="node_ids"/>
|
||||
|
||||
<multimap name="user_setting">
|
||||
</multimap>
|
||||
|
||||
<multimap name="group_members">
|
||||
</multimap>
|
||||
|
||||
<map name="chatrooms">
|
||||
<time-to-live-seconds>604800</time-to-live-seconds>
|
||||
<eviction-policy>LRU</eviction-policy>
|
||||
<map-store enabled="true">
|
||||
<class-name>io.moquette.persistence.ChatroomLoader</class-name>
|
||||
<write-delay-seconds>0</write-delay-seconds>
|
||||
</map-store>
|
||||
</map>
|
||||
|
||||
<map name="user_chatroom">
|
||||
</map>
|
||||
|
||||
<map name="robots">
|
||||
<!-- 7 days -->
|
||||
<time-to-live-seconds>604800</time-to-live-seconds>
|
||||
<eviction-policy>LRU</eviction-policy>
|
||||
<max-size policy="PER_NODE">1000000</max-size>
|
||||
<eviction-percentage>10</eviction-percentage>
|
||||
<map-store enabled="true">
|
||||
<class-name>io.moquette.persistence.RobotLoader</class-name>
|
||||
<write-delay-seconds>0</write-delay-seconds>
|
||||
</map-store>
|
||||
</map>
|
||||
|
||||
<map name="things">
|
||||
</map>
|
||||
|
||||
<multimap name="user_robots">
|
||||
</multimap>
|
||||
|
||||
<multimap name="user_things">
|
||||
</multimap>
|
||||
|
||||
<multimap name="chatroom_members">
|
||||
</multimap>
|
||||
|
||||
<multimap name="user_friends">
|
||||
</multimap>
|
||||
|
||||
<multimap name="user_friends_request">
|
||||
</multimap>
|
||||
|
||||
<map name="channels_map">
|
||||
<!-- 7 days -->
|
||||
<time-to-live-seconds>604800</time-to-live-seconds>
|
||||
<eviction-policy>LRU</eviction-policy>
|
||||
<max-size policy="PER_NODE">1000000</max-size>
|
||||
<eviction-percentage>10</eviction-percentage>
|
||||
<map-store enabled="true">
|
||||
<class-name>io.moquette.persistence.ChannelLoader</class-name>
|
||||
<write-delay-seconds>0</write-delay-seconds>
|
||||
</map-store>
|
||||
</map>
|
||||
|
||||
<multimap name="channel_listeners">
|
||||
</multimap>
|
||||
</hazelcast>
|
||||
Executable
+104
@@ -0,0 +1,104 @@
|
||||
#*********************************************************************
|
||||
# Server configuration
|
||||
#*********************************************************************
|
||||
#服务器的接入IP。给客户端提供是${server.ip}和${http_port}。
|
||||
#客户端会从${http_port}端口获取到长链接端口。
|
||||
server.ip 0.0.0.0
|
||||
|
||||
##原生客户端长链接端口
|
||||
port 1883
|
||||
|
||||
##客户端短链接端口
|
||||
http_port 80
|
||||
|
||||
##WEB客户端长链接端口
|
||||
websocket_port 8083
|
||||
|
||||
#管理端口
|
||||
http.admin.port 18080
|
||||
|
||||
##节点ID,当集群部署时,一定不能有重复。
|
||||
node_id 1
|
||||
|
||||
##绑定IP,保持不变即可
|
||||
host 0.0.0.0
|
||||
|
||||
##本地绑定端口
|
||||
local_port 80
|
||||
|
||||
##是否使用内置DB。0使用mysql;1使用h2db
|
||||
embed.db 1
|
||||
|
||||
##服务器管理接口密钥
|
||||
http.admin.secret_key 123456
|
||||
|
||||
##客户端协议栈密钥,需要与客户端协议栈文件libemqq.cc文件中的aes_key值保持一致,16进制byte用逗号分割。这个密钥对安全性帮助不大,客户不要修改
|
||||
client.proto.secret_key 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F
|
||||
|
||||
##用来生产im token的私钥,只在服务器使用,客户端不用。正式使用时为了安全一定要修改这个值,切记切记
|
||||
token.key testim
|
||||
|
||||
#*********************************************************************
|
||||
# hazelcase configuration
|
||||
#*********************************************************************
|
||||
hazelcast.configuration config/hazelcast.xml
|
||||
|
||||
#*********************************************************************
|
||||
# Media server configuration
|
||||
#*********************************************************************
|
||||
##是否使用七牛云存储。1使用七牛;0使用内存文件服务器。
|
||||
media.server.use_qiniu 0
|
||||
|
||||
##媒体类型分类
|
||||
#Media_Type_GENERAL = 0,
|
||||
#Media_Type_IMAGE = 1,
|
||||
#Media_Type_VOICE = 2,
|
||||
#Media_Type_VIDEO = 3,
|
||||
#Media_Type_FILE = 4,
|
||||
#Media_Type_PORTRAIT = 5,
|
||||
#Media_Type_FAVORITE = 6
|
||||
|
||||
# qiniu media server configuration
|
||||
qiniu.server_url http://up.qbox.me
|
||||
qiniu.access_key tU3vdBK5BL5j4N7jI5N5uZgq_HQDo170w5C9Amnn
|
||||
qiniu.secret_key YfQIJdgp5YGhwEw14vGpaD2HJZsuJldWtqens7i5
|
||||
qiniu.bucket_general_name media
|
||||
qiniu.bucket_general_domain http://pghnpyzos.bkt.clouddn.com
|
||||
qiniu.bucket_image_name media
|
||||
qiniu.bucket_image_domain http://pghnpyzos.bkt.clouddn.com
|
||||
qiniu.bucket_voice_name media
|
||||
qiniu.bucket_voice_domain http://pghnpyzos.bkt.clouddn.com
|
||||
qiniu.bucket_video_name media
|
||||
qiniu.bucket_video_domain http://pghnpyzos.bkt.clouddn.com
|
||||
qiniu.bucket_file_name media
|
||||
qiniu.bucket_file_domain http://pghnpyzos.bkt.clouddn.com
|
||||
qiniu.bucket_portrait_name storage
|
||||
qiniu.bucket_portrait_domain http://cdn2.wildfirechat.cn
|
||||
qiniu.bucket_favorite_name storage
|
||||
qiniu.bucket_favorite_domain http://cdn2.wildfirechat.cn
|
||||
|
||||
|
||||
# local media server configuration
|
||||
# 本地媒体服务器配置。
|
||||
local.media.storage.root media
|
||||
|
||||
#*********************************************************************
|
||||
# Push server configuration
|
||||
#*********************************************************************
|
||||
##安卓推送服务器地址
|
||||
push.android.server.address http://localhost:8085/android/push
|
||||
##苹果推送服务器地址
|
||||
push.ios.server.address http://localhost:8085/ios/push
|
||||
|
||||
|
||||
#*********************************************************************
|
||||
# Sensitive configuration
|
||||
#*********************************************************************
|
||||
##文本敏感词过滤处理方法,0 发送失败;1 发送成功但消息被服务器直接丢弃;2 命中的敏感词被替换成***
|
||||
sensitive.filter.type 0
|
||||
|
||||
#*********************************************************************
|
||||
# Message forward configuration
|
||||
#*********************************************************************
|
||||
##消息转发地址
|
||||
#message.forward.url http://localhost:8087/message/forward
|
||||
Executable
Executable
+200
@@ -0,0 +1,200 @@
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_group`;
|
||||
CREATE TABLE `t_group` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_gid` varchar(64) NOT NULL,
|
||||
`_name` varchar(64) DEFAULT '',
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_type` tinyint NOT NULL DEFAULT 0,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
`_member_count` int(11) DEFAULT 0,
|
||||
`_member_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `group_gid_index` (`_gid` ASC),
|
||||
INDEX `group_name_index` (`_name` ASC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_group_member`;
|
||||
CREATE TABLE `t_group_member` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_gid` varchar(64) NOT NULL,
|
||||
`_mid` varchar(64) DEFAULT '',
|
||||
`_alias` varchar(64) DEFAULT '',
|
||||
`_type` tinyint DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_gid_mid_index` (`_gid`, `_mid`)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_user`;
|
||||
CREATE TABLE `t_user` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_name` varchar(64) DEFAULT '',
|
||||
`_display_name` varchar(64) DEFAULT '',
|
||||
`_gender` int(11) NOT NULL DEFAULT 0,
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_mobile` varchar(64) DEFAULT '',
|
||||
`_email` varchar(64) DEFAULT '',
|
||||
`_address` varchar(64) DEFAULT '',
|
||||
`_company` varchar(64) DEFAULT '',
|
||||
`_social` varchar(64) DEFAULT '',
|
||||
`_passwd_md5` varchar(64) DEFAULT '',
|
||||
`_salt` varchar(64) DEFAULT '',
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_type` tinyint DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_uid_index` (`_uid` ASC),
|
||||
UNIQUE INDEX `user_name_index` (`_name` ASC),
|
||||
INDEX `user_display_name_index` (`_display_name` ASC),
|
||||
INDEX `user_mobile_index` (`_mobile` ASC),
|
||||
INDEX `user_email_index` (`_email` ASC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_status`;
|
||||
CREATE TABLE `t_user_status` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_status` int(11) NOT NULL DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_status_uid_index` (`_uid` ASC),
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_friend_request`;
|
||||
CREATE TABLE `t_friend_request` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_friend_uid` varchar(64) NOT NULL,
|
||||
`_reason` TEXT DEFAULT NULL,
|
||||
`_status` tinyint NOT NULL DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
`_from_read_status` tinyint DEFAULT 0,
|
||||
`_to_read_status` tinyint DEFAULT 0,
|
||||
UNIQUE INDEX `fr_user_target_index` (`_uid` ASC, `_friend_uid` ASC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_friend`;
|
||||
CREATE TABLE `t_friend` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_friend_uid` varchar(64) NOT NULL,
|
||||
`_state` tinyint DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `f_user_target_index` (`_uid` ASC, `_friend_uid` ASC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_setting`;
|
||||
CREATE TABLE `t_user_setting` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_scope` int NOT NULL,
|
||||
`_key` varchar(64) NOT NULL,
|
||||
`_value` varchar(4096) NOT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_setting_index` (`_uid` ASC, `_scope` ASC, `_key` ASC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_id_generator`;
|
||||
CREATE TABLE `t_id_generator` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT
|
||||
)
|
||||
AUTO_INCREMENT = 3;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_channel`;
|
||||
CREATE TABLE `t_channel` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_name` varchar(64) DEFAULT '',
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_status` tinyint NOT NULL DEFAULT 0,
|
||||
`_desc` TEXT DEFAULT NULL,
|
||||
`_secret` varchar(64) DEFAULT '',
|
||||
`_callback` varchar(1024) DEFAULT '',
|
||||
`_automatic` tinyint NOT NULL DEFAULT 0,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `channel_cid_index` (`_cid` DESC),
|
||||
INDEX `channel_name_index` (`_name` DESC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_channel_listener`;
|
||||
CREATE TABLE `t_channel_listener` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_mid` varchar(64) DEFAULT '',
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `channel_cid_mid_index` (`_cid`, `_mid`)
|
||||
);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_session`;
|
||||
CREATE TABLE `t_user_session` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_token` varchar(64) DEFAULT '',
|
||||
`_voip_token` varchar(64) DEFAULT '',
|
||||
`_secret` varchar(64) NOT NULL,
|
||||
`_db_secret` varchar(64) NOT NULL,
|
||||
`_platform` tinyint DEFAULT 0,
|
||||
`_push_type` tinyint DEFAULT 0,
|
||||
`_package_name` varchar(64) DEFAULT '',
|
||||
`_device_name` varchar(64) DEFAULT '',
|
||||
`_device_version` varchar(64) DEFAULT '',
|
||||
`_phone_name` varchar(64) DEFAULT '',
|
||||
`_language` varchar(64) DEFAULT '',
|
||||
`_carrier_name` varchar(64) DEFAULT '',
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `session_uid_cid_index` (`_cid`, `_uid`)
|
||||
);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_robot`;
|
||||
CREATE TABLE `t_robot` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_secret` varchar(64) DEFAULT '',
|
||||
`_callback` varchar(1024) DEFAULT '',
|
||||
`_state` tinyint DEFAULT 0,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `robot_uid_index` (`_uid` ASC),
|
||||
INDEX `robot_owner_index` (`_owner` ASC)
|
||||
);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_thing`;
|
||||
CREATE TABLE `t_thing` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_token` varchar(64) DEFAULT '',
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `thing_uid_index` (`_uid` ASC),
|
||||
INDEX `thing_owner_index` (`_owner` ASC)
|
||||
);
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_chatroom`;
|
||||
CREATE TABLE `t_chatroom` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_title` varchar(64) DEFAULT '',
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_state` tinyint NOT NULL DEFAULT 0,
|
||||
`_desc` TEXT DEFAULT NULL,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `chatroom_cid_index` (`_cid` DESC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_sensitiveword`;
|
||||
CREATE TABLE `t_sensitiveword` (
|
||||
`_word` TEXT DEFAULT NULL
|
||||
);
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
|
||||
DROP TABLE IF EXISTS `t_messages`;
|
||||
CREATE TABLE `t_messages` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_mid` bigint(20) NOT NULL,
|
||||
`_from` varchar(64) NOT NULL,
|
||||
`_type` tinyint NOT NULL DEFAULT 0,
|
||||
`_target` varchar(64) NOT NULL,
|
||||
`_line` int(11) NOT NULL DEFAULT 0,
|
||||
`_data` BLOB NOT NULL,
|
||||
`_searchable_key` TEXT DEFAULT NULL,
|
||||
`_dt` DATETIME NOT NULL DEFAULT NOW(),
|
||||
UNIQUE INDEX `message_uid_index` (`_mid` ASC)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_messages`;
|
||||
CREATE TABLE `t_user_messages` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_mid` bigint(20) NOT NULL,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_seq` bigint(20) NOT NULL,
|
||||
`_dt` DATETIME NOT NULL DEFAULT NOW(),
|
||||
UNIQUE INDEX `message_seq_uid_index` (`_uid` DESC, `_seq` DESC)
|
||||
);
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
|
||||
insert into t_chatroom (`_cid`, `_title`, `_desc`,`_dt`) values ('chatroom1','火信聊天室1','火信测试聊天室1,用来演示聊天室功能', 1);
|
||||
|
||||
insert into t_chatroom (`_cid`, `_title`, `_desc`,`_dt`) values ('chatroom2','火信聊天室2','火信测试聊天室2,用来演示聊天室功能', 1);
|
||||
|
||||
insert into t_chatroom (`_cid`, `_title`, `_desc`,`_dt`) values ('chatroom3','火信聊天室3','火信测试聊天室3,用来演示聊天室功能', 1);
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
|
||||
insert into t_user (`_uid`,`_name`,`_display_name`,`_portrait`,`_type`,`_dt`) values ('FireRobot','FireRobot','小火','http://cdn2.wildfirechat.cn/robot.png',1,1);
|
||||
|
||||
insert into t_robot (`_uid`,`_owner`,`_secret`,`_callback`,`_state`,`_dt`) values ('FireRobot', 'FireRobot', '123456', 'http://127.0.0.1:8883/robot/recvmsg', 0, 1);
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
|
||||
alter table t_friend add column `_alias` varchar(64) DEFAULT NULL;
|
||||
Executable
Executable
+275
@@ -0,0 +1,275 @@
|
||||
|
||||
DROP TABLE IF EXISTS `t_messages`;
|
||||
CREATE TABLE `t_messages` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_mid` bigint(20) NOT NULL,
|
||||
`_from` varchar(64) NOT NULL,
|
||||
`_type` tinyint NOT NULL DEFAULT 0,
|
||||
`_target` varchar(64) NOT NULL,
|
||||
`_line` int(11) NOT NULL DEFAULT 0,
|
||||
`_data` BLOB NOT NULL,
|
||||
`_searchable_key` TEXT DEFAULT NULL,
|
||||
`_dt` DATETIME NOT NULL,
|
||||
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_messages`;
|
||||
CREATE TABLE `t_user_messages` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_mid` bigint(20) NOT NULL,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_seq` bigint(20) NOT NULL,
|
||||
`_dt` DATETIME NOT NULL DEFAULT NOW(),
|
||||
INDEX `message_seq_uid_index` (`_uid` DESC, `_seq` DESC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_group`;
|
||||
CREATE TABLE `t_group` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_gid` varchar(64) NOT NULL,
|
||||
`_name` varchar(64) DEFAULT '',
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_type` tinyint NOT NULL DEFAULT 0,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
`_member_count` int(11) DEFAULT 0,
|
||||
`_member_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `group_gid_index` (`_gid` DESC),
|
||||
INDEX `group_name_index` (`_name` DESC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_group_member`;
|
||||
CREATE TABLE `t_group_member` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_gid` varchar(64) NOT NULL,
|
||||
`_mid` varchar(64) DEFAULT '',
|
||||
`_alias` varchar(64) DEFAULT '',
|
||||
`_type` tinyint DEFAULT 0 COMMENT "0普通成员;1,管理员;2,群主,与Owner相同",
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_gid_mid_index` (`_gid`, `_mid`)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_user`;
|
||||
CREATE TABLE `t_user` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_name` varchar(64) DEFAULT '',
|
||||
`_display_name` varchar(64) DEFAULT '',
|
||||
`_gender` int(11) NOT NULL DEFAULT 0,
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_mobile` varchar(64) DEFAULT '',
|
||||
`_email` varchar(64) DEFAULT '',
|
||||
`_address` varchar(64) DEFAULT '',
|
||||
`_company` varchar(64) DEFAULT '',
|
||||
`_social` varchar(64) DEFAULT '',
|
||||
`_passwd_md5` varchar(64) DEFAULT '',
|
||||
`_salt` varchar(64) DEFAULT '',
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_type` tinyint DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_uid_index` (`_uid` ASC),
|
||||
UNIQUE INDEX `user_name_index` (`_name` ASC),
|
||||
INDEX `user_display_name_index` (`_display_name` ASC),
|
||||
INDEX `user_mobile_index` (`_mobile` ASC),
|
||||
INDEX `user_email_index` (`_email` ASC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_status`;
|
||||
CREATE TABLE `t_user_status` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_status` int(11) NOT NULL DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_status_uid_index` (`_uid` ASC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_friend_request`;
|
||||
CREATE TABLE `t_friend_request` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_friend_uid` varchar(64) NOT NULL,
|
||||
`_reason` TEXT DEFAULT NULL,
|
||||
`_status` tinyint NOT NULL DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
`_from_read_status` tinyint DEFAULT 0,
|
||||
`_to_read_status` tinyint DEFAULT 0,
|
||||
UNIQUE INDEX `fr_user_target_index` (`_uid` ASC, `_friend_uid` ASC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_friend`;
|
||||
CREATE TABLE `t_friend` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_friend_uid` varchar(64) NOT NULL,
|
||||
`_state` tinyint DEFAULT 0 COMMENT "0, normal; 1, deleted; 2, blacked",
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `f_user_target_index` (`_uid` ASC, `_friend_uid` ASC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_setting`;
|
||||
CREATE TABLE `t_user_setting` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_scope` int NOT NULL,
|
||||
`_key` varchar(64) NOT NULL,
|
||||
`_value` varchar(4096) NOT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `user_setting_index` (`_uid` ASC, `_scope` ASC, `_key` ASC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_id_generator`;
|
||||
CREATE TABLE `t_id_generator` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci
|
||||
AUTO_INCREMENT = 3;
|
||||
|
||||
DROP TABLE IF EXISTS `t_channel`;
|
||||
CREATE TABLE `t_channel` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_name` varchar(64) DEFAULT '',
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_status` tinyint NOT NULL DEFAULT 0,
|
||||
`_desc` TEXT DEFAULT NULL,
|
||||
`_secret` varchar(64) DEFAULT '',
|
||||
`_callback` varchar(1024) DEFAULT '',
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_automatic` tinyint NOT NULL DEFAULT 0,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `channel_cid_index` (`_cid` DESC),
|
||||
INDEX `channel_name_index` (`_name` DESC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_channel_listener`;
|
||||
CREATE TABLE `t_channel_listener` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_mid` varchar(64) DEFAULT '',
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `channel_cid_mid_index` (`_cid`, `_mid`)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_user_session`;
|
||||
CREATE TABLE `t_user_session` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_token` varchar(64) DEFAULT '',
|
||||
`_voip_token` varchar(64) DEFAULT '',
|
||||
`_secret` varchar(64) NOT NULL,
|
||||
`_db_secret` varchar(64) NOT NULL,
|
||||
`_platform` tinyint DEFAULT 0,
|
||||
`_push_type` tinyint DEFAULT 0,
|
||||
`_package_name` varchar(64) DEFAULT '',
|
||||
`_device_name` varchar(64) DEFAULT '',
|
||||
`_device_version` varchar(64) DEFAULT '',
|
||||
`_phone_name` varchar(64) DEFAULT '',
|
||||
`_language` varchar(64) DEFAULT '',
|
||||
`_carrier_name` varchar(64) DEFAULT '',
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `session_uid_cid_index` (`_cid`, `_uid`)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_robot`;
|
||||
CREATE TABLE `t_robot` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_secret` varchar(64) DEFAULT '',
|
||||
`_callback` varchar(1024) DEFAULT '',
|
||||
`_state` tinyint DEFAULT 0,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `robot_uid_index` (`_uid` ASC),
|
||||
INDEX `robot_owner_index` (`_owner` ASC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_thing`;
|
||||
CREATE TABLE `t_thing` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_uid` varchar(64) NOT NULL,
|
||||
`_owner` varchar(64) DEFAULT '',
|
||||
`_token` varchar(64) DEFAULT '',
|
||||
`_state` tinyint DEFAULT 0,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `thing_uid_index` (`_uid` ASC),
|
||||
INDEX `thing_owner_index` (`_owner` ASC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `t_chatroom`;
|
||||
CREATE TABLE `t_chatroom` (
|
||||
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
`_cid` varchar(64) NOT NULL,
|
||||
`_title` varchar(64) DEFAULT '',
|
||||
`_portrait` varchar(1024) DEFAULT '',
|
||||
`_state` tinyint NOT NULL DEFAULT 0,
|
||||
`_desc` TEXT DEFAULT NULL,
|
||||
`_extra` TEXT DEFAULT NULL,
|
||||
`_dt` bigint(20) NOT NULL,
|
||||
UNIQUE INDEX `chatroom_cid_index` (`_cid` DESC)
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
|
||||
DROP TABLE IF EXISTS `t_sensitiveword`;
|
||||
CREATE TABLE `t_sensitiveword` (
|
||||
`_word` TEXT DEFAULT NULL
|
||||
)
|
||||
ENGINE = InnoDB
|
||||
DEFAULT CHARACTER SET = utf8mb4
|
||||
COLLATE = utf8mb4_unicode_ci;
|
||||
+2353
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
insert into t_chatroom (`_cid`, `_title`, `_desc`,`_dt`) values ('chatroom1','火信聊天室1','火信测试聊天室1,用来演示聊天室功能', 1);
|
||||
|
||||
insert into t_chatroom (`_cid`, `_title`, `_desc`,`_dt`) values ('chatroom2','火信聊天室2','火信测试聊天室2,用来演示聊天室功能', 1);
|
||||
|
||||
insert into t_chatroom (`_cid`, `_title`, `_desc`,`_dt`) values ('chatroom3','火信聊天室3','火信测试聊天室3,用来演示聊天室功能', 1);
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
insert into t_user (`_uid`,`_name`,`_display_name`,`_portrait`,`_type`,`_dt`) values ('FireRobot','FireRobot','小火','http://cdn2.wildfirechat.cn/robot.png',1,1);
|
||||
|
||||
insert into t_robot (`_uid`,`_owner`,`_secret`,`_callback`,`_state`,`_dt`) values ('FireRobot', 'FireRobot', '123456', 'http://127.0.0.1:8883/robot/recvmsg', 0, 1);
|
||||
+1
@@ -0,0 +1 @@
|
||||
alter table t_friend add column `_alias` varchar(64) DEFAULT NULL;
|
||||
@@ -0,0 +1,27 @@
|
||||
upstream imserver {
|
||||
server 127.0.0.1:8080; #Apache
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name www.wildfirechat.cn;
|
||||
|
||||
root html;
|
||||
index index.html index.htm index.php;
|
||||
|
||||
## send request back to im server ##
|
||||
location /im {
|
||||
proxy_pass http://imserver;
|
||||
}
|
||||
|
||||
location /route {
|
||||
proxy_pass http://imserver;
|
||||
}
|
||||
|
||||
location /api {
|
||||
proxy_pass http://imserver;
|
||||
}
|
||||
location / {
|
||||
proxy_pass https://wildfirechat.github.io/docs/;
|
||||
}
|
||||
}
|
||||
Executable
+375
@@ -0,0 +1,375 @@
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<relativePath>../</relativePath>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moquette-broker</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Moquette - broker</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<bintray.package>io.moquette.moquette-broker</bintray.package>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<artifactId>client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.14</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.11</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
<version>4.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
<!--
|
||||
<classifier>${os.detected.name}-${os.detected.arch}</classifier>
|
||||
-->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-core</artifactId>
|
||||
<version>3.2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-jvm</artifactId>
|
||||
<version>3.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.librato.metrics</groupId>
|
||||
<artifactId>metrics-librato</artifactId>
|
||||
<version>5.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.bugsnag</groupId>
|
||||
<artifactId>bugsnag</artifactId>
|
||||
<version>[3.0,4.0)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.hazelcast</groupId>
|
||||
<artifactId>hazelcast</artifactId>
|
||||
<version>3.9.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.xiaoleilu/hutool -->
|
||||
<dependency>
|
||||
<groupId>com.xiaoleilu</groupId>
|
||||
<artifactId>hutool</artifactId>
|
||||
<version>2.15.11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
|
||||
<dependency>
|
||||
<groupId>com.mchange</groupId>
|
||||
<artifactId>c3p0</artifactId>
|
||||
<version>[0.9.5.3,)</version>
|
||||
</dependency>
|
||||
|
||||
<!-- needs extra dependencies: objenesis & hamcrest -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.fusesource.mqtt-client</groupId>
|
||||
<artifactId>mqtt-client</artifactId>
|
||||
<version>1.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>websocket-client</artifactId>
|
||||
<version>9.2.0.M1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<!-- use 2.6.0 for Java 7 projects -->
|
||||
<version>2.6.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.191</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.46</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.197</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.13.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.flywaydb/flyway-core -->
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<version>5.2.4</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Generate the test-jar -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Includes to generate OSGi headers -->
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bundle-manifest</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>2.1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>revision</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<!--日期格式;默认值:dd.MM.yyyy '@' HH:mm:ss z;-->
|
||||
<dateFormat>yyyyMMddHHmmss</dateFormat>
|
||||
<!--,构建过程中,是否打印详细信息;默认值:false;-->
|
||||
<verbose>true</verbose>
|
||||
<!-- ".git"文件路径;默认值:${project.basedir}/.git; -->
|
||||
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
|
||||
<!--若项目打包类型为pom,是否取消构建;默认值:true;-->
|
||||
<skipPoms>false</skipPoms>
|
||||
<!--是否生成"git.properties"文件;默认值:false;-->
|
||||
<generateGitPropertiesFile>true</generateGitPropertiesFile>
|
||||
<!--指定"git.properties"文件的存放路径(相对于${project.basedir}的一个路径);-->
|
||||
<generateGitPropertiesFilename>config/git.properties</generateGitPropertiesFilename>
|
||||
<!--".git"文件夹未找到时,构建是否失败;若设置true,则构建失败;若设置false,则跳过执行该目标;默认值:true;-->
|
||||
<failOnNoGitDirectory>true</failOnNoGitDirectory>
|
||||
|
||||
<!--git描述配置,可选;由JGit提供实现;-->
|
||||
<gitDescribe>
|
||||
<!--是否生成描述属性-->
|
||||
<skip>false</skip>
|
||||
<!--提交操作未发现tag时,仅打印提交操作ID,-->
|
||||
<always>false</always>
|
||||
<!--提交操作ID显式字符长度,最大值为:40;默认值:7;
|
||||
0代表特殊意义;后面有解释;
|
||||
-->
|
||||
<abbrev>7</abbrev>
|
||||
<!--构建触发时,代码有修改时(即"dirty state"),添加指定后缀;默认值:"";-->
|
||||
<dirty>-dirty</dirty>
|
||||
<!--always print using the "tag-commits_from_tag-g_commit_id-maybe_dirty" format, even if "on" a tag.
|
||||
The distance will always be 0 if you're "on" the tag.
|
||||
-->
|
||||
<forceLongFormat>false</forceLongFormat>
|
||||
</gitDescribe>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package cn.wildfirechat.push;
|
||||
|
||||
|
||||
public class PushMessage {
|
||||
public String sender;
|
||||
public String senderName;
|
||||
public int convType;
|
||||
public String target;
|
||||
public String targetName;
|
||||
public int line;
|
||||
public int cntType;
|
||||
public long serverTime;
|
||||
//消息的类型,普通消息通知栏;voip要透传。
|
||||
public int pushMessageType;
|
||||
//推送类型,android推送分为小米/华为/魅族等。ios分别为开发和发布。
|
||||
public int pushType;
|
||||
public String pushContent;
|
||||
public int unReceivedMsg;
|
||||
public int mentionedType;
|
||||
public String packageName;
|
||||
public String deviceToken;
|
||||
public String voipDeviceToken;
|
||||
public boolean isHiddenDetail;
|
||||
public String language;
|
||||
|
||||
public PushMessage(String sender, int conversationType, String target, int line, int messageContentType, long serverTime, String senderName, String targetName, int unReceivedMsg, int mentionedType, boolean isHiddenDetail, String language) {
|
||||
this.sender = sender;
|
||||
this.convType = conversationType;
|
||||
this.target = target;
|
||||
this.senderName = senderName;
|
||||
this.targetName = targetName;
|
||||
this.line = line;
|
||||
this.cntType = messageContentType;
|
||||
this.serverTime = serverTime;
|
||||
this.unReceivedMsg = unReceivedMsg;
|
||||
this.pushMessageType = (cntType == 400 ? PushServer.PushMessageType.PUSH_MESSAGE_TYPE_VOIP_INVITE : (cntType == 402 ? PushServer.PushMessageType.PUSH_MESSAGE_TYPE_VOIP_BYE : PushServer.PushMessageType.PUSH_MESSAGE_TYPE_NORMAL));
|
||||
this.mentionedType = mentionedType;
|
||||
this.isHiddenDetail = isHiddenDetail;
|
||||
this.language = language;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package cn.wildfirechat.push;
|
||||
|
||||
import cn.wildfirechat.proto.ProtoConstants;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import io.moquette.persistence.MemorySessionStore;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.ClientSession;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import win.liyufan.im.HttpUtils;
|
||||
import win.liyufan.im.Utility;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static io.moquette.BrokerConstants.PUSH_ANDROID_SERVER_ADDRESS;
|
||||
import static io.moquette.BrokerConstants.PUSH_IOS_SERVER_ADDRESS;
|
||||
|
||||
public class PushServer {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PushServer.class);
|
||||
|
||||
public interface PushMessageType {
|
||||
int PUSH_MESSAGE_TYPE_NORMAL = 0;
|
||||
int PUSH_MESSAGE_TYPE_VOIP_INVITE = 1;
|
||||
int PUSH_MESSAGE_TYPE_VOIP_BYE = 2;
|
||||
}
|
||||
|
||||
private static PushServer INSTANCE = new PushServer();
|
||||
private ISessionsStore sessionsStore;
|
||||
private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*5);
|
||||
|
||||
private String androidPushServerUrl;
|
||||
private String iOSPushServerUrl;
|
||||
|
||||
private PushServer() {
|
||||
}
|
||||
|
||||
public static PushServer getServer() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void init(IConfig config, ISessionsStore sessionsStore) {
|
||||
this.sessionsStore = sessionsStore;
|
||||
this.androidPushServerUrl = config.getProperty(PUSH_ANDROID_SERVER_ADDRESS);
|
||||
this.iOSPushServerUrl = config.getProperty(PUSH_IOS_SERVER_ADDRESS);
|
||||
}
|
||||
|
||||
public void pushMessage(PushMessage pushMessage, String deviceId, String pushContent) {
|
||||
LOG.info("try to delivery push diviceId = {}, pushContent", deviceId, pushContent);
|
||||
executorService.execute(() ->{
|
||||
try {
|
||||
pushMessageInternel(pushMessage, deviceId, pushContent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Utility.printExecption(LOG, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void pushMessageInternel(PushMessage pushMessage, String deviceId, String pushContent) {
|
||||
if (pushMessage.pushMessageType == PushMessageType.PUSH_MESSAGE_TYPE_NORMAL && StringUtil.isNullOrEmpty(pushContent)) {
|
||||
LOG.info("push content empty, deviceId {}", deviceId);
|
||||
return;
|
||||
}
|
||||
|
||||
MemorySessionStore.Session session = sessionsStore.getSession(deviceId);
|
||||
int badge = session.getUnReceivedMsgs();
|
||||
if (badge <= 0) {
|
||||
badge = 1;
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(session.getDeviceToken())) {
|
||||
LOG.warn("Device token is empty for device {}", deviceId);
|
||||
return;
|
||||
}
|
||||
|
||||
pushMessage.packageName = session.getAppName();
|
||||
pushMessage.pushType = session.getPushType();
|
||||
pushMessage.pushContent = pushContent;
|
||||
pushMessage.deviceToken = session.getDeviceToken();
|
||||
pushMessage.unReceivedMsg = badge;
|
||||
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS || session.getPlatform() == ProtoConstants.Platform.Platform_Android) {
|
||||
String url = androidPushServerUrl;
|
||||
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS) {
|
||||
url = iOSPushServerUrl;
|
||||
pushMessage.voipDeviceToken = session.getVoipDeviceToken();
|
||||
}
|
||||
HttpUtils.httpJsonPost(url, new Gson().toJson(pushMessage, pushMessage.getClass()));
|
||||
} else {
|
||||
LOG.info("Not mobile platform {}", session.getPlatform());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package cn.wildfirechat.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Server {
|
||||
public static void main(String[] args) throws IOException {
|
||||
io.moquette.server.Server.start(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package cn.wildfirechat.server;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ThreadPoolExecutorWrapper {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolExecutorWrapper.class);
|
||||
private final ScheduledExecutorService executor;
|
||||
private final int count;
|
||||
private final AtomicInteger runCounter;
|
||||
private final String name;
|
||||
|
||||
public ThreadPoolExecutorWrapper(ScheduledExecutorService executor, int count, String name) {
|
||||
this.executor = executor;
|
||||
this.count = count;
|
||||
this.runCounter = new AtomicInteger();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void execute(Runnable task) {
|
||||
int startCount = runCounter.incrementAndGet();
|
||||
LOG.debug("Submit task and current task count {}", startCount);
|
||||
final long startTime = System.currentTimeMillis();
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
task.run();
|
||||
} finally {
|
||||
int endCount = runCounter.decrementAndGet();
|
||||
LOG.debug("Finish task and current task count {} use time {}", endCount, System.currentTimeMillis()-startTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.xiaoleilu.loServer;
|
||||
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.hutool.util.DateUtil;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.action.ClassUtil;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.ActionHandler;
|
||||
import com.xiaoleilu.loServer.handler.HttpFileServerHandler;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* LoServer starter<br>
|
||||
* 用于启动服务器的主对象<br>
|
||||
* 使用LoServer.start()启动服务器<br>
|
||||
* 服务的Action类和端口等设置在ServerSetting中设置
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class LoFileServer {
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(LoFileServer.class);
|
||||
private int port;
|
||||
private IMessagesStore messagesStore;
|
||||
private Channel channel;
|
||||
|
||||
public LoFileServer(int port, IMessagesStore messagesStore) {
|
||||
this.port = port;
|
||||
this.messagesStore = messagesStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public void start() throws InterruptedException {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
// Configure the server.
|
||||
final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
final EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
try {
|
||||
final ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.SO_BACKLOG, 1024) // 服务端可连接队列大小
|
||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||
.option(ChannelOption.SO_REUSEADDR, true)
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.option(ChannelOption.SO_SNDBUF, 1024*1024*10)
|
||||
.option(ChannelOption.SO_RCVBUF, 1024*1024*10)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
socketChannel.pipeline().addLast(new HttpRequestDecoder());
|
||||
socketChannel.pipeline().addLast(new HttpResponseEncoder());
|
||||
socketChannel.pipeline().addLast(new ChunkedWriteHandler());
|
||||
socketChannel.pipeline().addLast(new HttpObjectAggregator(100 * 1024 * 1024));
|
||||
socketChannel.pipeline().addLast(new HttpFileServerHandler());
|
||||
}
|
||||
});
|
||||
|
||||
channel = b.bind(port).sync().channel();
|
||||
Logger.info("***** Welcome To LoServer on port [{}], startting spend {}ms *****", port, DateUtil.spendMs(start));
|
||||
} finally {
|
||||
|
||||
}
|
||||
}
|
||||
public void shutdown() {
|
||||
if (this.channel != null) {
|
||||
this.channel.close();
|
||||
try {
|
||||
this.channel.closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.xiaoleilu.loServer;
|
||||
|
||||
import com.xiaoleilu.hutool.util.DateUtil;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.AdminActionHandler;
|
||||
import com.xiaoleilu.loServer.handler.IMActionHandler;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import com.xiaoleilu.loServer.action.ClassUtil;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* LoServer starter<br>
|
||||
* 用于启动服务器的主对象<br>
|
||||
* 使用LoServer.start()启动服务器<br>
|
||||
* 服务的Action类和端口等设置在ServerSetting中设置
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class LoServer {
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(LoServer.class);
|
||||
private int port;
|
||||
private int adminPort;
|
||||
private IMessagesStore messagesStore;
|
||||
private ISessionsStore sessionsStore;
|
||||
private Channel channel;
|
||||
private Channel adminChannel;
|
||||
|
||||
public LoServer(int port, int adminPort, IMessagesStore messagesStore, ISessionsStore sessionsStore) {
|
||||
this.port = port;
|
||||
this.adminPort = adminPort;
|
||||
this.messagesStore = messagesStore;
|
||||
this.sessionsStore = sessionsStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动服务
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public void start() throws InterruptedException {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
// Configure the server.
|
||||
final EventLoopGroup bossGroup = new NioEventLoopGroup(2);
|
||||
final EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
|
||||
registerAllAction();
|
||||
|
||||
try {
|
||||
final ServerBootstrap b = new ServerBootstrap();
|
||||
final ServerBootstrap adminB = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.option(ChannelOption.SO_REUSEADDR, true)
|
||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||
.childOption(ChannelOption.SO_SNDBUF, 1024*64)
|
||||
.childOption(ChannelOption.SO_RCVBUF, 1024*64)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
socketChannel.pipeline().addLast(new HttpRequestDecoder());
|
||||
socketChannel.pipeline().addLast(new HttpResponseEncoder());
|
||||
socketChannel.pipeline().addLast(new ChunkedWriteHandler());
|
||||
socketChannel.pipeline().addLast(new HttpObjectAggregator(100 * 1024 * 1024));
|
||||
socketChannel.pipeline().addLast(new IMActionHandler(messagesStore, sessionsStore));
|
||||
}
|
||||
});
|
||||
|
||||
channel = b.bind(port).sync().channel();
|
||||
|
||||
|
||||
adminB.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.option(ChannelOption.SO_REUSEADDR, true)
|
||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||
.childOption(ChannelOption.SO_SNDBUF, 1024*64)
|
||||
.childOption(ChannelOption.SO_RCVBUF, 1024*64)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) throws Exception {
|
||||
socketChannel.pipeline().addLast(new HttpRequestDecoder());
|
||||
socketChannel.pipeline().addLast(new HttpResponseEncoder());
|
||||
socketChannel.pipeline().addLast(new ChunkedWriteHandler());
|
||||
socketChannel.pipeline().addLast(new HttpObjectAggregator(100 * 1024 * 1024));
|
||||
socketChannel.pipeline().addLast(new AdminActionHandler(messagesStore, sessionsStore));
|
||||
}
|
||||
});
|
||||
|
||||
adminChannel = adminB.bind(adminPort).sync().channel();
|
||||
Logger.info("***** Welcome To LoServer on port [{},{}], startting spend {}ms *****", port, adminPort, DateUtil.spendMs(start));
|
||||
} finally {
|
||||
|
||||
}
|
||||
}
|
||||
public void shutdown() {
|
||||
if (this.channel!= null) {
|
||||
this.channel.close();
|
||||
try {
|
||||
this.channel.closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.adminChannel != null) {
|
||||
this.adminChannel.close();
|
||||
try {
|
||||
this.adminChannel.closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerAllAction() {
|
||||
try {
|
||||
for (Class cls:ClassUtil.getAllAssignedClass(Action.class)
|
||||
) {
|
||||
if(cls.getAnnotation(Route.class) != null) {
|
||||
ServerSetting.setAction((Class<? extends Action>)cls);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer;
|
||||
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
public class RestResult {
|
||||
|
||||
|
||||
int code;
|
||||
String msg;
|
||||
Object result;
|
||||
|
||||
public static RestResult ok(Object object) {
|
||||
return resultOf(ErrorCode.ERROR_CODE_SUCCESS, ErrorCode.ERROR_CODE_SUCCESS.getMsg(), object);
|
||||
}
|
||||
|
||||
public static RestResult ok() {
|
||||
return resultOf(ErrorCode.ERROR_CODE_SUCCESS, ErrorCode.ERROR_CODE_SUCCESS.getMsg(), null);
|
||||
}
|
||||
|
||||
public static RestResult resultOf(ErrorCode errorCode) {
|
||||
return resultOf(errorCode, errorCode.msg, null);
|
||||
}
|
||||
|
||||
public static RestResult resultOf(ErrorCode errorCode, String msg) {
|
||||
return resultOf(errorCode, msg, null);
|
||||
}
|
||||
|
||||
public static RestResult resultOf(ErrorCode errorCode, String msg, Object object) {
|
||||
RestResult result = new RestResult();
|
||||
result.code = errorCode.code;
|
||||
result.msg = msg;
|
||||
result.result = object;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setErrorCode(ErrorCode errorCode) {
|
||||
setCode(errorCode.code);
|
||||
}
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(Object result) {
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package com.xiaoleilu.loServer;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.xiaoleilu.hutool.lang.Singleton;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.hutool.util.FileUtil;
|
||||
import com.xiaoleilu.hutool.util.StrUtil;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.action.DefaultIndexAction;
|
||||
import com.xiaoleilu.loServer.action.UnknownErrorAction;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.exception.ServerSettingException;
|
||||
import com.xiaoleilu.loServer.filter.Filter;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 全局设定文件
|
||||
* @author xiaoleilu
|
||||
*
|
||||
*/
|
||||
public class ServerSetting {
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(ServerSetting.class);
|
||||
|
||||
//-------------------------------------------------------- Default value start
|
||||
/** 默认的字符集编码 */
|
||||
public final static String DEFAULT_CHARSET = "utf-8";
|
||||
|
||||
public final static String MAPPING_ALL = "/*";
|
||||
|
||||
public final static String MAPPING_ERROR = "/_error";
|
||||
//-------------------------------------------------------- Default value end
|
||||
|
||||
/** 字符编码 */
|
||||
private static String charset = DEFAULT_CHARSET;
|
||||
/** 端口 */
|
||||
private static int port = 8090;
|
||||
/** 根目录 */
|
||||
private static File root;
|
||||
/** Filter映射表 */
|
||||
private static Map<String, Filter> filterMap;
|
||||
/** Action映射表 */
|
||||
private static Map<String, Action> getActionMap;
|
||||
private static Map<String, Action> postActionMap;
|
||||
private static Map<String, Action> putActionMap;
|
||||
private static Map<String, Action> deleteActionMap;
|
||||
private static Map<String, Action> errorActionMap;
|
||||
|
||||
static{
|
||||
filterMap = new ConcurrentHashMap<String, Filter>();
|
||||
|
||||
getActionMap = new ConcurrentHashMap<String, Action>();
|
||||
postActionMap = new ConcurrentHashMap<String, Action>();
|
||||
|
||||
putActionMap = new ConcurrentHashMap<String, Action>();
|
||||
|
||||
deleteActionMap = new ConcurrentHashMap<String, Action>();
|
||||
|
||||
errorActionMap = new ConcurrentHashMap<String, Action>();
|
||||
errorActionMap.put(StrUtil.SLASH, new DefaultIndexAction());
|
||||
errorActionMap.put(MAPPING_ERROR, new UnknownErrorAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取编码
|
||||
*/
|
||||
public static String getCharset() {
|
||||
return charset;
|
||||
}
|
||||
/**
|
||||
* @return 字符集
|
||||
*/
|
||||
public static Charset charset() {
|
||||
return Charset.forName(getCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置编码
|
||||
* @param charset 编码
|
||||
*/
|
||||
public static void setCharset(String charset) {
|
||||
ServerSetting.charset = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 监听端口
|
||||
*/
|
||||
public static int getPort() {
|
||||
return port;
|
||||
}
|
||||
/**
|
||||
* 设置监听端口
|
||||
* @param port 端口
|
||||
*/
|
||||
public static void setPort(int port) {
|
||||
ServerSetting.port = port;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------- Root start
|
||||
/**
|
||||
* @return 根目录
|
||||
*/
|
||||
public static File getRoot() {
|
||||
return root;
|
||||
}
|
||||
/**
|
||||
* @return 根目录
|
||||
*/
|
||||
public static boolean isRootAvailable() {
|
||||
if(root != null && root.isDirectory() && root.isHidden() == false && root.canRead()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return 根目录
|
||||
*/
|
||||
public static String getRootPath() {
|
||||
return FileUtil.getAbsolutePath(root);
|
||||
}
|
||||
/**
|
||||
* 根目录
|
||||
* @param root 根目录绝对路径
|
||||
*/
|
||||
public static void setRoot(String root) {
|
||||
ServerSetting.root = FileUtil.mkdir(root);
|
||||
Logger.debug("Set root to [{}]", ServerSetting.root.getAbsolutePath());
|
||||
}
|
||||
/**
|
||||
* 根目录
|
||||
* @param root 根目录绝对路径
|
||||
*/
|
||||
public static void setRoot(File root) {
|
||||
if(root.exists() == false){
|
||||
root.mkdirs();
|
||||
}else if(root.isDirectory() == false){
|
||||
throw new ServerSettingException(StrUtil.format("{} is not a directory!", root.getPath()));
|
||||
}
|
||||
ServerSetting.root = root;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------- Root end
|
||||
|
||||
//----------------------------------------------------------------------------------------------- Filter start
|
||||
/**
|
||||
* @return 获取FilterMap
|
||||
*/
|
||||
public static Map<String, Filter> getFilterMap() {
|
||||
return filterMap;
|
||||
}
|
||||
/**
|
||||
* 获得路径对应的Filter
|
||||
* @param path 路径,为空时将获得 根目录对应的Action
|
||||
* @return Filter
|
||||
*/
|
||||
public static Filter getFilter(String path){
|
||||
if(StrUtil.isBlank(path)){
|
||||
path = StrUtil.SLASH;
|
||||
}
|
||||
return getFilterMap().get(path.trim());
|
||||
}
|
||||
/**
|
||||
* 设置FilterMap
|
||||
* @param filterMap FilterMap
|
||||
*/
|
||||
public static void setFilterMap(Map<String, Filter> filterMap) {
|
||||
ServerSetting.filterMap = filterMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Filter类,已有的Filter类将被覆盖
|
||||
* @param path 拦截路径(必须以"/"开头)
|
||||
* @param filter Action类
|
||||
*/
|
||||
public static void setFilter(String path, Filter filter) {
|
||||
if(StrUtil.isBlank(path)){
|
||||
path = StrUtil.SLASH;
|
||||
}
|
||||
|
||||
if(null == filter) {
|
||||
Logger.warn("Added blank action, pass it.");
|
||||
return;
|
||||
}
|
||||
//所有路径必须以 "/" 开头,如果没有则补全之
|
||||
if(false == path.startsWith(StrUtil.SLASH)) {
|
||||
path = StrUtil.SLASH + path;
|
||||
}
|
||||
|
||||
ServerSetting.filterMap.put(path, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Filter类,已有的Filter类将被覆盖
|
||||
* @param path 拦截路径(必须以"/"开头)
|
||||
* @param filterClass Filter类
|
||||
*/
|
||||
public static void setFilter(String path, Class<? extends Filter> filterClass) {
|
||||
setFilter(path, (Filter)Singleton.get(filterClass));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------- Filter end
|
||||
|
||||
//----------------------------------------------------------------------------------------------- Action start
|
||||
/**
|
||||
* @return 获取ActionMap
|
||||
*/
|
||||
public static Map<String, Action> getActionMap(String method) {
|
||||
if (method.equals("GET")) {
|
||||
return getActionMap;
|
||||
} else if(method.equals("POST")) {
|
||||
return postActionMap;
|
||||
} else if(method.equals("PUT")) {
|
||||
return putActionMap;
|
||||
} else if(method.equals("DELETE")) {
|
||||
return deleteActionMap;
|
||||
}
|
||||
return getActionMap;
|
||||
}
|
||||
|
||||
public static Action getErrorAction(String path) {
|
||||
return errorActionMap.get(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得路径对应的Action
|
||||
* @param path 路径,为空时将获得 根目录对应的Action
|
||||
* @return Action
|
||||
*/
|
||||
public static Action getAction(String path, String method){
|
||||
if(StrUtil.isBlank(path)){
|
||||
path = StrUtil.SLASH;
|
||||
}
|
||||
return getActionMap(method).get(path.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Action类,已有的Action类将被覆盖
|
||||
* @param path 拦截路径(必须以"/"开头)
|
||||
* @param action Action类
|
||||
*/
|
||||
public static void setAction(String path, Action action) {
|
||||
if(StrUtil.isBlank(path)){
|
||||
path = StrUtil.SLASH;
|
||||
}
|
||||
|
||||
if(null == action) {
|
||||
Logger.warn("Added blank action, pass it.");
|
||||
return;
|
||||
}
|
||||
//所有路径必须以 "/" 开头,如果没有则补全之
|
||||
if(false == path.startsWith(StrUtil.SLASH)) {
|
||||
path = StrUtil.SLASH + path;
|
||||
}
|
||||
String method = "GET";
|
||||
HttpMethod methodAnnotation = action.getClass().getAnnotation(HttpMethod.class);
|
||||
if (methodAnnotation != null) {
|
||||
method = methodAnnotation.value();
|
||||
}
|
||||
ServerSetting.getActionMap(method).put(path, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加Action类,已有的Action类将被覆盖<br>
|
||||
* 所有Action都是以单例模式存在的!
|
||||
* @param path 拦截路径(必须以"/"开头)
|
||||
* @param actionClass Action类
|
||||
*/
|
||||
public static void setAction(String path, Class<? extends Action> actionClass) {
|
||||
setAction(path, (Action)Singleton.get(actionClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加Action类,已有的Action类将被覆盖<br>
|
||||
* 自动读取Route的注解来获得Path路径
|
||||
* @param action 带注解的Action对象
|
||||
*/
|
||||
public static void setAction(Action action) {
|
||||
final Route route = action.getClass().getAnnotation(Route.class);
|
||||
if(route != null){
|
||||
final String path = route.value();
|
||||
if(StrUtil.isNotBlank(path)){
|
||||
setAction(path, action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new ServerSettingException("Can not find Route annotation,please add annotation to Action class!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加Action类,已有的Action类将被覆盖<br>
|
||||
* 所有Action都是以单例模式存在的!
|
||||
* @param actionClass 带注解的Action类
|
||||
*/
|
||||
public static void setAction(Class<? extends Action> actionClass) {
|
||||
setAction((Action)Singleton.get(actionClass));
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------- Action start
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.action.admin.GetIMTokenAction;
|
||||
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.DBUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
import static win.liyufan.im.ErrorCode.ERROR_CODE_SUCCESS;
|
||||
|
||||
/**
|
||||
* 请求处理接口<br>
|
||||
* 当用户请求某个Path,则调用相应Action的doAction方法
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
|
||||
abstract public class Action {
|
||||
public static IMessagesStore messagesStore = null;
|
||||
public static ISessionsStore sessionsStore = null;
|
||||
|
||||
public ChannelHandlerContext ctx;
|
||||
|
||||
public ErrorCode preAction(Request request, Response response) {
|
||||
if (getClass().getAnnotation(RequireAuthentication.class) != null) {
|
||||
//do authentication
|
||||
}
|
||||
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
public boolean doAction(Request request, Response response) {
|
||||
ErrorCode errorCode = preAction(request, response);
|
||||
boolean isSync = true;
|
||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
//事务逻辑有缺陷,先注释掉
|
||||
// if (isTransactionAction() && !(this instanceof IMAction)) {
|
||||
// DBUtil.beginTransaction();
|
||||
// try {
|
||||
// action(request, response);
|
||||
// DBUtil.commit();
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// DBUtil.roolback();
|
||||
// throw e;
|
||||
// }
|
||||
// } else {
|
||||
isSync = action(request, response);
|
||||
// }
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if (errorCode == null) {
|
||||
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), RestResult.resultOf(errorCode));
|
||||
response.setContent(new Gson().toJson(result));
|
||||
response.send();
|
||||
}
|
||||
|
||||
return isSync;
|
||||
}
|
||||
public boolean isTransactionAction() {
|
||||
return false;
|
||||
}
|
||||
abstract public boolean action(Request request, Response response);
|
||||
|
||||
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
|
||||
if (request instanceof FullHttpRequest) {
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||
String content = new String(bytes);
|
||||
Gson gson = new Gson();
|
||||
T t = gson.fromJson(content, cls);
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class ClassUtil {
|
||||
|
||||
/**
|
||||
* 获取同一路径下所有子类或接口实现类
|
||||
*
|
||||
* @param cls
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static List<Class<?>> getAllAssignedClass(Class<?> cls) throws IOException,
|
||||
ClassNotFoundException {
|
||||
List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||
for (Class<?> c : getClasses(cls)) {
|
||||
if (cls.isAssignableFrom(c) && !cls.equals(c)) {
|
||||
classes.add(c);
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得当前类路径下的所有类
|
||||
*
|
||||
* @param cls
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static List<Class<?>> getClasses(Class<?> cls) throws IOException,
|
||||
ClassNotFoundException {
|
||||
String pk = cls.getPackage().getName();
|
||||
// String path = pk.replace('.', '/');
|
||||
// ClassLoader classloader = Thread.currentThread().getContextClassLoader();
|
||||
// URL url = classloader.getResource(path);
|
||||
// String type = url.getProtocol();
|
||||
//
|
||||
// System.out.println("the type is " + type);
|
||||
// System.out.println(url.getPath());
|
||||
//
|
||||
// return getClasses(new File(url.getFile()), pk);
|
||||
List<String> classNames = getClassName(pk, true);
|
||||
List<Class<?>> classes = new ArrayList<>();
|
||||
for (String className :
|
||||
classNames) {
|
||||
if (className.endsWith(".class")) {
|
||||
className = className.substring(0, className.length() - 6);
|
||||
className = className.substring(className.lastIndexOf("/")+1, className.length());
|
||||
}
|
||||
|
||||
classes.add(Class.forName(className));
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迭代查找类
|
||||
*
|
||||
* @param dir
|
||||
* @param pk
|
||||
* @return
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
private static List<Class<?>> getClasses(File dir, String pk) throws ClassNotFoundException {
|
||||
System.out.println(dir.getAbsolutePath());
|
||||
List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||
if (!dir.exists()) {
|
||||
return classes;
|
||||
}
|
||||
for (File f : dir.listFiles()) {
|
||||
if (f.isDirectory()) {
|
||||
classes.addAll(getClasses(f, pk + "." + f.getName()));
|
||||
}
|
||||
String name = f.getName();
|
||||
if (name.endsWith(".class")) {
|
||||
classes.add(Class.forName(pk + "." + name.substring(0, name.length() - 6)));
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取某包下所有类
|
||||
* @param packageName 包名
|
||||
* @param childPackage 是否遍历子包
|
||||
* @return 类的完整名称
|
||||
*/
|
||||
public static List<String> getClassName(String packageName, boolean childPackage) {
|
||||
List<String> fileNames = null;
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
String packagePath = packageName.replace(".", "/");
|
||||
URL url = loader.getResource(packagePath);
|
||||
if (url != null) {
|
||||
String type = url.getProtocol();
|
||||
if (type.equals("file")) {
|
||||
fileNames = getClassNameByFile(url.getPath(), null, childPackage);
|
||||
} else if (type.equals("jar")) {
|
||||
fileNames = getClassNameByJar(url.getPath(), childPackage);
|
||||
}
|
||||
} else {
|
||||
fileNames = getClassNameByJars(((URLClassLoader) loader).getURLs(), packagePath, childPackage);
|
||||
}
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从项目文件获取某包下所有类
|
||||
* @param filePath 文件路径
|
||||
* @param className 类名集合
|
||||
* @param childPackage 是否遍历子包
|
||||
* @return 类的完整名称
|
||||
*/
|
||||
private static List<String> getClassNameByFile(String filePath, List<String> className, boolean childPackage) {
|
||||
List<String> myClassName = new ArrayList<String>();
|
||||
File file = new File(filePath);
|
||||
File[] childFiles = file.listFiles();
|
||||
for (File childFile : childFiles) {
|
||||
if (childFile.isDirectory()) {
|
||||
if (childPackage) {
|
||||
myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage));
|
||||
}
|
||||
} else {
|
||||
String childFilePath = childFile.getPath();
|
||||
if (childFilePath.endsWith(".class")) {
|
||||
if (childFilePath.indexOf("\\classes") >= 0 || childFilePath.indexOf("/classes") >= 0) {
|
||||
int start = childFilePath.indexOf("\\classes");
|
||||
if (start == -1) {
|
||||
start = childFilePath.indexOf("/classes");
|
||||
}
|
||||
childFilePath = childFilePath.substring(start + 9, childFilePath.lastIndexOf("."));
|
||||
}
|
||||
childFilePath = childFilePath.replace("\\", ".");
|
||||
childFilePath = childFilePath.replace("/", ".");
|
||||
myClassName.add(childFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return myClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从jar获取某包下所有类
|
||||
* @param jarPath jar文件路径
|
||||
* @param childPackage 是否遍历子包
|
||||
* @return 类的完整名称
|
||||
*/
|
||||
private static List<String> getClassNameByJar(String jarPath, boolean childPackage) {
|
||||
List<String> myClassName = new ArrayList<String>();
|
||||
String[] jarInfo = jarPath.split("!");
|
||||
String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));
|
||||
String packagePath = jarInfo[1].substring(1);
|
||||
try {
|
||||
JarFile jarFile = new JarFile(jarFilePath);
|
||||
Enumeration<JarEntry> entrys = jarFile.entries();
|
||||
while (entrys.hasMoreElements()) {
|
||||
JarEntry jarEntry = entrys.nextElement();
|
||||
String entryName = jarEntry.getName();
|
||||
|
||||
if (entryName.endsWith(".class")) {
|
||||
if (childPackage) {
|
||||
if (entryName.startsWith(packagePath)) {
|
||||
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
|
||||
myClassName.add(entryName);
|
||||
}
|
||||
} else {
|
||||
int index = entryName.lastIndexOf("/");
|
||||
String myPackagePath;
|
||||
if (index != -1) {
|
||||
myPackagePath = entryName.substring(0, index);
|
||||
} else {
|
||||
myPackagePath = entryName;
|
||||
}
|
||||
if (myPackagePath.equals(packagePath)) {
|
||||
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
|
||||
myClassName.add(entryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return myClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从所有jar中搜索该包,并获取该包下所有类
|
||||
* @param urls URL集合
|
||||
* @param packagePath 包路径
|
||||
* @param childPackage 是否遍历子包
|
||||
* @return 类的完整名称
|
||||
*/
|
||||
private static List<String> getClassNameByJars(URL[] urls, String packagePath, boolean childPackage) {
|
||||
List<String> myClassName = new ArrayList<String>();
|
||||
if (urls != null) {
|
||||
for (int i = 0; i < urls.length; i++) {
|
||||
URL url = urls[i];
|
||||
String urlPath = url.getPath();
|
||||
// 不必搜索classes文件夹
|
||||
if (urlPath.endsWith("classes/")) {
|
||||
continue;
|
||||
}
|
||||
String jarPath = urlPath + "!/" + packagePath;
|
||||
myClassName.addAll(getClassNameByJar(jarPath, childPackage));
|
||||
}
|
||||
}
|
||||
return myClassName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
|
||||
/**
|
||||
* 默认的主页Action,当访问主页且没有定义主页Action时,调用此Action
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class DefaultIndexAction extends Action{
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
response.setContent("Welcome to LoServer.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.hutool.util.DateUtil;
|
||||
import com.xiaoleilu.hutool.util.FileUtil;
|
||||
import com.xiaoleilu.hutool.util.ReUtil;
|
||||
import com.xiaoleilu.hutool.util.StrUtil;
|
||||
import com.xiaoleilu.loServer.ServerSetting;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 默认的主页Action,当访问主页且没有定义主页Action时,调用此Action
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class FileAction extends Action {
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(FileAction.class);
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (false == Request.METHOD_GET.equalsIgnoreCase(request.getMethod())) {
|
||||
response.sendError(HttpResponseStatus.METHOD_NOT_ALLOWED, "Please use GET method to request file!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(ServerSetting.isRootAvailable() == false){
|
||||
response.sendError(HttpResponseStatus.NOT_FOUND, "404 Root dir not avaliable!");
|
||||
return true;
|
||||
}
|
||||
|
||||
final File file = getFileByPath(request.getPath());
|
||||
Logger.debug("Client [{}] get file [{}]", request.getIp(), file.getPath());
|
||||
|
||||
// 隐藏文件,跳过
|
||||
if (file.isHidden() || !file.exists()) {
|
||||
response.sendError(HttpResponseStatus.NOT_FOUND, "404 File not found!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 非文件,跳过
|
||||
if (false == file.isFile()) {
|
||||
response.sendError(HttpResponseStatus.FORBIDDEN, "403 Forbidden!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cache Validation
|
||||
String ifModifiedSince = request.getHeader(HttpHeaderNames.IF_MODIFIED_SINCE.toString());
|
||||
if (StrUtil.isNotBlank(ifModifiedSince)) {
|
||||
Date ifModifiedSinceDate = null;
|
||||
try {
|
||||
ifModifiedSinceDate = DateUtil.parse(ifModifiedSince, HTTP_DATE_FORMATER);
|
||||
} catch (Exception e) {
|
||||
Logger.warn("If-Modified-Since header parse error: {}", e.getMessage());
|
||||
}
|
||||
if(ifModifiedSinceDate != null) {
|
||||
// 只对比到秒一级别
|
||||
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
|
||||
long fileLastModifiedSeconds = file.lastModified() / 1000;
|
||||
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
|
||||
Logger.debug("File {} not modified.", file.getPath());
|
||||
response.sendNotModified();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.setContent(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
|
||||
private static final SimpleDateFormat HTTP_DATE_FORMATER = new SimpleDateFormat(DateUtil.HTTP_DATETIME_PATTERN, Locale.US);
|
||||
|
||||
|
||||
/**
|
||||
* 通过URL中的path获得文件的绝对路径
|
||||
*
|
||||
* @param httpPath Http请求的Path
|
||||
* @return 文件绝对路径
|
||||
*/
|
||||
public static File getFileByPath(String httpPath) {
|
||||
// Decode the path.
|
||||
try {
|
||||
httpPath = URLDecoder.decode(httpPath, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
if (httpPath.isEmpty() || httpPath.charAt(0) != '/') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 路径安全检查
|
||||
if (httpPath.contains("/.") || httpPath.contains("./") || httpPath.charAt(0) == '.' || httpPath.charAt(httpPath.length() - 1) == '.' || ReUtil.isMatch(INSECURE_URI, httpPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 转换为绝对路径
|
||||
return FileUtil.file(ServerSetting.getRoot(), httpPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.MemorySessionStore;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.moquette.spi.impl.security.AES;
|
||||
import io.moquette.spi.security.Tokenor;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route("/im")
|
||||
@HttpMethod("POST")
|
||||
public class IMAction extends Action {
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||
|
||||
String str = new String(bytes);
|
||||
try {
|
||||
bytes = Base64.getDecoder().decode(str);
|
||||
} catch (IllegalArgumentException e) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
String cid = fullHttpRequest.headers().get("cid");
|
||||
byte[] cbytes = Base64.getDecoder().decode(cid);
|
||||
cbytes = AES.AESDecrypt(cbytes, "", true);
|
||||
if (cbytes == null) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
|
||||
return true;
|
||||
}
|
||||
cid = new String(cbytes);
|
||||
|
||||
MemorySessionStore.Session session = sessionsStore.getSession(cid);
|
||||
|
||||
if (session != null) {
|
||||
bytes = AES.AESDecrypt(bytes, session.getSecret(), true);
|
||||
} else {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (bytes == null) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
WFCMessage.IMHttpWrapper wrapper = WFCMessage.IMHttpWrapper.parseFrom(bytes);
|
||||
String token = wrapper.getToken();
|
||||
String userId = Tokenor.getUserId(token.getBytes());
|
||||
if (userId == null) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null);
|
||||
} else {
|
||||
RPCCenter.getInstance().sendRequest(userId, wrapper.getClientId(), wrapper.getRequest(), wrapper.getData().toByteArray(), userId, TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
sendResponse(response, null, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, false);
|
||||
return false;
|
||||
}
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendResponse(Response response, ErrorCode errorCode, byte[] contents) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if(contents == null) {
|
||||
ByteBuf ackPayload = Unpooled.buffer();
|
||||
ackPayload.ensureWritable(1).writeByte(errorCode.getCode());
|
||||
response.setContent(ackPayload);
|
||||
} else {
|
||||
response.setContent(contents);
|
||||
}
|
||||
response.send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.hutool.util.StrUtil;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* 错误堆栈Action类
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class NotFoundAction extends Action{
|
||||
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(NotFoundAction.class);
|
||||
|
||||
public final static String ERROR_PARAM_NAME = "_e";
|
||||
|
||||
private final static String TEMPLATE_ERROR = "<!DOCTYPE html><html><head><title>LoServer - Error report</title><style>h1,h3 {color:white; background-color: gray;}</style></head><body><h1>HTTP Status {} - {}</h1><hr size=\"1\" noshade=\"noshade\" /><p>{}</p><hr size=\"1\" noshade=\"noshade\" /><h3>LoServer</h3></body></html>";
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
response.sendError(HttpResponseStatus.NOT_FOUND, "404 Not found!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.persistence.MemorySessionStore;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.moquette.spi.impl.security.AES;
|
||||
import io.moquette.spi.security.Tokenor;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route("/route")
|
||||
@HttpMethod("POST")
|
||||
public class RouteAction extends Action {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RouteAction.class);
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
|
||||
response.setContentType("application/octet-stream");
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
||||
boolean b = false;
|
||||
if ("web".equalsIgnoreCase(fullHttpRequest.headers().get("p"))) {
|
||||
b = true;
|
||||
}
|
||||
|
||||
final boolean base64Response = b;
|
||||
|
||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||
|
||||
String str = new String(bytes);
|
||||
try {
|
||||
bytes = Base64.getDecoder().decode(str);
|
||||
} catch (IllegalArgumentException e) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
|
||||
return true;
|
||||
}
|
||||
|
||||
String cid = fullHttpRequest.headers().get("cid");
|
||||
byte[] cbytes = Base64.getDecoder().decode(cid);
|
||||
cbytes = AES.AESDecrypt(cbytes, "", true);
|
||||
if (cbytes == null) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
|
||||
return true;
|
||||
}
|
||||
cid = new String(cbytes);
|
||||
|
||||
String uid = fullHttpRequest.headers().get("uid");
|
||||
byte[] ubytes = Base64.getDecoder().decode(uid);
|
||||
ubytes = AES.AESDecrypt(ubytes, "", true);
|
||||
if (ubytes == null) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
|
||||
return true;
|
||||
}
|
||||
uid = new String(ubytes);
|
||||
|
||||
|
||||
MemorySessionStore.Session session = sessionsStore.sessionForClientAndUser(uid, cid);
|
||||
if (session == null) {
|
||||
ErrorCode errorCode = sessionsStore.createNewSession(uid, cid, true);
|
||||
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
sendResponse(response, errorCode, null, base64Response);
|
||||
return true;
|
||||
}
|
||||
session = sessionsStore.sessionForClientAndUser(uid, cid);
|
||||
}
|
||||
|
||||
|
||||
if (session != null) {
|
||||
bytes = AES.AESDecrypt(bytes, session.getSecret(), true);
|
||||
} else {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (bytes == null) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
WFCMessage.IMHttpWrapper wrapper = WFCMessage.IMHttpWrapper.parseFrom(bytes);
|
||||
String token = wrapper.getToken();
|
||||
String userId = Tokenor.getUserId(token.getBytes());
|
||||
LOG.info("RouteAction token={}, userId={}", token, userId);
|
||||
if (userId == null) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null, base64Response);
|
||||
} else {
|
||||
RPCCenter.getInstance().sendRequest(userId, wrapper.getClientId(), wrapper.getRequest(), wrapper.getData().toByteArray(), userId, TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
sendResponse(response, null, result, base64Response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null, base64Response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null, base64Response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, false);
|
||||
return false;
|
||||
}
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendResponse(Response response, ErrorCode errorCode, byte[] contents, boolean base64Response) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if(contents == null) {
|
||||
ByteBuf ackPayload = Unpooled.buffer();
|
||||
ackPayload.ensureWritable(1).writeByte(errorCode.getCode());
|
||||
response.setContent(ackPayload);
|
||||
} else {
|
||||
response.setContent(contents);
|
||||
}
|
||||
response.send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.hutool.util.StrUtil;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 错误堆栈Action类
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class UnknownErrorAction extends Action{
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(UnknownErrorAction.class);
|
||||
|
||||
public final static String ERROR_PARAM_NAME = "_e";
|
||||
|
||||
private final static String TEMPLATE_ERROR = "<!DOCTYPE html><html><head><title>LoServer - Error report</title><style>h1,h3 {color:white; background-color: gray;}</style></head><body><h1>HTTP Status {} - {}</h1><hr size=\"1\" noshade=\"noshade\" /><p>{}</p><hr size=\"1\" noshade=\"noshade\" /><h3>LoServer</h3></body></html>";
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import com.hazelcast.util.StringUtil;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.*;
|
||||
import io.moquette.server.config.MediaServerConfig;
|
||||
import io.moquette.spi.impl.security.AES;
|
||||
import io.moquette.spi.security.DES;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.multipart.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.xiaoleilu.loServer.handler.HttpResponseHelper.getFileExt;
|
||||
|
||||
@Route("/fs")
|
||||
@HttpMethod("POST")
|
||||
@RequireAuthentication
|
||||
public class UploadFileAction extends Action {
|
||||
private static final String KEY = "imfile";
|
||||
private static final Logger logger = LoggerFactory.getLogger(UploadFileAction.class);
|
||||
private static final HttpDataFactory factory = new DefaultHttpDataFactory(false);
|
||||
|
||||
public static class InvalidateTokenExecption extends Exception {
|
||||
|
||||
}
|
||||
|
||||
public static String getToken(int type) {
|
||||
String signKey = KEY + "|" + (System.currentTimeMillis()) + "|" + type;
|
||||
try {
|
||||
return DES.encryptDES(signKey);
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int validateToken(String token) throws InvalidateTokenExecption {
|
||||
try {
|
||||
String signKey = DES.decryptDES(token);
|
||||
String[] parts = signKey.split("\\|");
|
||||
if(parts.length == 3) {
|
||||
if(parts[0].equals(KEY)) {
|
||||
long timestamp = Long.parseLong(parts[1]);
|
||||
if(Math.abs(System.currentTimeMillis() - timestamp) < 2 * 60 * 60 * 1000) {
|
||||
return Integer.parseInt(parts[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
//e.printStackTrace();
|
||||
}
|
||||
throw new InvalidateTokenExecption();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request r, Response response) {
|
||||
if (r.getNettyRequest() instanceof FullHttpRequest) {
|
||||
|
||||
FullHttpRequest request = (FullHttpRequest) r.getNettyRequest();
|
||||
String requestId = UUID.randomUUID().toString().replace("-", "");
|
||||
logger.info("HttpFileServerHandler received a request: method=" + request.getMethod() + ", uri=" + request.getUri() + ", requestId=" + requestId);
|
||||
|
||||
if (!request.getDecoderResult().isSuccess()) {
|
||||
logger.warn("http decode failed!");
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("http decode failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
multipartUpload(request, requestId, response);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* multipart上传
|
||||
*/
|
||||
private void multipartUpload(FullHttpRequest request, String requestId, Response response) {
|
||||
HttpPostRequestDecoder decoder = null;
|
||||
try {
|
||||
decoder = new HttpPostRequestDecoder(factory, request);
|
||||
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
|
||||
logger.error("Failed to decode file data!", e1);
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("Failed to decode file data!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (decoder != null) {
|
||||
if (request instanceof HttpContent) {
|
||||
HttpContent chunk = (HttpContent) request;
|
||||
try {
|
||||
decoder.offer(chunk);
|
||||
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
|
||||
logger.warn("BAD_REQUEST, Failed to decode file data");
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("Failed to decode file data!");
|
||||
return;
|
||||
}
|
||||
|
||||
long fileTotalSize = 0;
|
||||
if (request.headers().contains("X-File-Total-Size")) {
|
||||
try {
|
||||
fileTotalSize = Integer.parseInt(request.headers().get("X-File-Total-Size"));
|
||||
} catch (Exception e) {
|
||||
logger.warn("invalid X-File-Total-Size value!");
|
||||
}
|
||||
}
|
||||
|
||||
readHttpDataChunkByChunk(response, decoder, requestId, HttpHeaders.isKeepAlive(request));
|
||||
|
||||
if (chunk instanceof LastHttpContent) {
|
||||
|
||||
}
|
||||
} else {
|
||||
logger.warn("BAD_REQUEST, Not a http request");
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("Not a http request");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* readHttpDataChunkByChunk
|
||||
*/
|
||||
private void readHttpDataChunkByChunk(Response response, HttpPostRequestDecoder decoder, String requestId, boolean isKeepAlive) {
|
||||
try {
|
||||
int[] bucket = new int[1];
|
||||
bucket[0] = -1;
|
||||
while (decoder.hasNext()) {
|
||||
InterfaceHttpData data = decoder.next();
|
||||
if (data != null) {
|
||||
try {
|
||||
if(!writeFileUploadData(data, response, requestId, isKeepAlive, bucket)) {
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("chunk end");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writeFileUploadData
|
||||
*/
|
||||
private boolean writeFileUploadData(InterfaceHttpData data, Response response, String requestId, boolean isKeepAlive, int[] bucket) {
|
||||
try {
|
||||
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
|
||||
FileUpload fileUpload = (FileUpload) data;
|
||||
|
||||
String remoteFileName = fileUpload.getFilename();
|
||||
long remoteFileSize = fileUpload.length();
|
||||
|
||||
if(bucket[0] == -1) {
|
||||
logger.warn("Not authenticated!");
|
||||
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("Not authenticated!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringUtil.isNullOrEmpty(remoteFileName)) {
|
||||
logger.warn("remoteFileName is empty!");
|
||||
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("file name is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringUtil.isNullOrEmpty(requestId)) {
|
||||
logger.warn("requestId is empty!");
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("requestId is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remoteFileSize > 50 * 1024 * 1024) {
|
||||
logger.warn("file over limite!(" + remoteFileSize + ")");
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("file over limite!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
String remoteFileExt = "";
|
||||
if (remoteFileName.lastIndexOf(".") == -1) {
|
||||
remoteFileExt = "octetstream";
|
||||
remoteFileName = remoteFileName + "." + remoteFileExt;
|
||||
|
||||
} else {
|
||||
remoteFileExt = getFileExt(remoteFileName);
|
||||
}
|
||||
|
||||
if (StringUtil.isNullOrEmpty(remoteFileExt) || remoteFileExt.equals("ing")) {
|
||||
logger.warn("Invalid file extention name");
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("Invalid file extention name");
|
||||
return false;
|
||||
}
|
||||
|
||||
int remoteFileTotalSize = (int) remoteFileSize;
|
||||
|
||||
|
||||
ByteBuf byteBuf = null;
|
||||
int savedThunkSize = 0; // 分片接收保存的大小
|
||||
int offset = 0; // 断点续传开始位置
|
||||
|
||||
Date nowTime=new Date();
|
||||
SimpleDateFormat time=new SimpleDateFormat("yyyy/MM/dd/HH");
|
||||
String datePath = time.format(nowTime);
|
||||
|
||||
datePath = "fs/" + bucket[0] + "/" + datePath; //add bucket
|
||||
|
||||
String dir = "./" + MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
|
||||
|
||||
File dirFile = new File(dir);
|
||||
boolean bFile = dirFile.exists();
|
||||
|
||||
if(!bFile) {
|
||||
bFile = dirFile.mkdirs();
|
||||
if (!bFile) {
|
||||
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
response.setContent("服务器错误:无法创建文件");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String filePath = dir + "/" + requestId;
|
||||
logger.info("the file path is " + filePath);
|
||||
|
||||
File tmpFile = new File(filePath);
|
||||
|
||||
logger.info("before write the file");
|
||||
boolean isError = false;
|
||||
while (true) {
|
||||
byte[] thunkData;
|
||||
try {
|
||||
byteBuf = fileUpload.getChunk(128 * 1024);
|
||||
int readableBytesSize = byteBuf.readableBytes();
|
||||
thunkData = new byte[readableBytesSize];
|
||||
byteBuf.readBytes(thunkData);
|
||||
|
||||
put(tmpFile, offset, thunkData);
|
||||
|
||||
savedThunkSize += readableBytesSize;
|
||||
offset += readableBytesSize;
|
||||
|
||||
if (savedThunkSize >= remoteFileSize) {
|
||||
byteBuf.release();
|
||||
fileUpload.release();
|
||||
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
String relativePath = datePath + "/" + requestId;
|
||||
response.setContent("{\"key\":\"" + relativePath + "\"}");
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("save thunckData error!", e);
|
||||
if (fileUpload != null)
|
||||
fileUpload.release();
|
||||
|
||||
if (byteBuf != null)
|
||||
byteBuf.release();
|
||||
|
||||
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
response.setContent("服务器错误:" + e.getMessage());
|
||||
isError = true;
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
thunkData = null;
|
||||
if (isError) {
|
||||
tmpFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
|
||||
Attribute attribute = (Attribute)data;
|
||||
if(attribute.getName().equals("token")) {
|
||||
String token = attribute.getValue();
|
||||
|
||||
try {
|
||||
bucket[0] = validateToken(token);
|
||||
} catch (InvalidateTokenExecption e) {
|
||||
logger.error("无效的token!", e);
|
||||
response.setStatus(HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent("无效的token:" + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("writeHttpData error!", e);
|
||||
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
response.setContent("服务器错误:" + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void put(File file, long pos, byte[] data) throws Exception {
|
||||
RandomAccessFile raf = null;
|
||||
try {
|
||||
raf = new RandomAccessFile(file, "rwd");
|
||||
raf.seek(pos);
|
||||
raf.write(data);
|
||||
} finally {
|
||||
try {
|
||||
if (raf != null)
|
||||
raf.close();
|
||||
} catch (Exception e) {
|
||||
logger.warn("release error!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputUserLogin;
|
||||
import com.xiaoleilu.loServer.pojos.OutputLoginData;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.moquette.spi.impl.security.TokenAuthenticator;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.GitRepositoryState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Route("/api/version")
|
||||
@HttpMethod("GET")
|
||||
public class VersionAction extends Action {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
|
||||
try {
|
||||
response.setContent(new Gson().toJson(GitRepositoryState.getGitRepositoryState()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
response.setContent("{\"version\":\"unknown\"}");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputCreateUser;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.RateLimiter;
|
||||
|
||||
abstract public class AdminAction extends Action {
|
||||
private static String SECRET_KEY = "123456";
|
||||
private final RateLimiter mLimitCounter = new RateLimiter(10, 500);
|
||||
public static void setSecretKey(String secretKey) {
|
||||
SECRET_KEY = secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorCode preAction(Request request, Response response) {
|
||||
if (!mLimitCounter.isGranted("admin")) {
|
||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
}
|
||||
String nonce = request.getHeader("nonce");
|
||||
String timestamp = request.getHeader("timestamp");
|
||||
String sign = request.getHeader("sign");
|
||||
if (StringUtil.isNullOrEmpty(nonce) || StringUtil.isNullOrEmpty(timestamp) || StringUtil.isNullOrEmpty(sign)) {
|
||||
return ErrorCode.INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Long ts;
|
||||
try {
|
||||
ts = Long.parseLong(timestamp);
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return ErrorCode.INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - ts > 2 * 60 * 60 * 1000) {
|
||||
// return ErrorCode.ERROR_CODE_SIGN_EXPIRED;
|
||||
}
|
||||
|
||||
String str = nonce + "|" + SECRET_KEY + "|" + timestamp;
|
||||
String localSign = DigestUtils.sha1Hex(str);
|
||||
return localSign.equals(sign) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if (errorCode == null) {
|
||||
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
response.send();
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputOutputUserBlockStatus;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("admin/user/status")
|
||||
@HttpMethod("POST")
|
||||
public class BlockUserAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputOutputUserBlockStatus inputUserBlock = getRequestBody(request.getNettyRequest(), InputOutputUserBlockStatus.class);
|
||||
if (inputUserBlock != null
|
||||
&& !StringUtil.isNullOrEmpty(inputUserBlock.getUserId())) {
|
||||
|
||||
ErrorCode errorCode = messagesStore.modifyUserStatus(inputUserBlock.getUserId(), inputUserBlock.getStatus());
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result;
|
||||
result = RestResult.resultOf(errorCode);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
|
||||
RPCCenter.getInstance().sendRequest(null, null, RPCCenter.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), inputUserBlock.getUserId(), TargetEntry.Type.TARGET_TYPE_USER, null, true);
|
||||
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, null);
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("/admin/user/onlinestatus")
|
||||
@HttpMethod("POST")
|
||||
public class CheckUserOnlineAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
|
||||
if (inputUserId == null || !StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
|
||||
RPCCenter.getInstance().sendRequest(null, null, RPCCenter.CHECK_USER_ONLINE_REQUEST, inputUserId.getUserId().getBytes(), inputUserId.getUserId(), TargetEntry.Type.TARGET_TYPE_USER, null, true);
|
||||
return true;
|
||||
} else {
|
||||
sendResponse(response, ErrorCode.INVALID_PARAMETER, null);
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputCreateChatroom;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateChatroom;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.UUIDGenerator;
|
||||
|
||||
@Route("/admin/chatroom")
|
||||
@HttpMethod("PUT")
|
||||
public class CreateChatroomAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||
String content = new String(bytes);
|
||||
Gson gson = new Gson();
|
||||
InputCreateChatroom inputCreateChatroom = gson.fromJson(content, InputCreateChatroom.class);
|
||||
if (inputCreateChatroom != null
|
||||
&& !StringUtil.isNullOrEmpty(inputCreateChatroom.getTitle())) {
|
||||
|
||||
if (StringUtil.isNullOrEmpty(inputCreateChatroom.getChatroomId())) {
|
||||
inputCreateChatroom.setChatroomId(messagesStore.getShortUUID());
|
||||
}
|
||||
|
||||
if (inputCreateChatroom.getPortrait() == null || inputCreateChatroom.getPortrait().length() == 0) {
|
||||
inputCreateChatroom.setPortrait("https://avatars.io/gravatar/" + inputCreateChatroom.getChatroomId());
|
||||
}
|
||||
|
||||
WFCMessage.ChatroomInfo info = inputCreateChatroom.toChatroomInfo();
|
||||
|
||||
messagesStore.createChatroom(inputCreateChatroom.getChatroomId(), info);
|
||||
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.ok(new OutputCreateChatroom(inputCreateChatroom.getChatroomId()));
|
||||
response.setContent(new Gson().toJson(result));
|
||||
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputCreateGroup;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateGroupResult;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route("admin/group")
|
||||
@HttpMethod("PUT")
|
||||
public class CreateGroupAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputCreateGroup inputCreateGroup = getRequestBody(request.getNettyRequest(), InputCreateGroup.class);
|
||||
if (inputCreateGroup.isValide()) {
|
||||
RPCCenter.getInstance().sendRequest(inputCreateGroup.getOperator(), null, IMTopic.CreateGroupTopic, inputCreateGroup.toProtoGroupRequest().toByteArray(), inputCreateGroup.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
byteBuf.writeBytes(result);
|
||||
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
|
||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
byte[] data = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(data);
|
||||
String groupId = new String(data);
|
||||
sendResponse(response, null, new OutputCreateGroupResult(groupId));
|
||||
} else {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
return false;
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputCreateRobot;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateRobot;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.UUIDGenerator;
|
||||
|
||||
@Route("admin/robot/create")
|
||||
@HttpMethod("POST")
|
||||
public class CreateRobotAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputCreateRobot inputCreateRobot = getRequestBody(request.getNettyRequest(), InputCreateRobot.class);
|
||||
if (inputCreateRobot != null
|
||||
&& !StringUtil.isNullOrEmpty(inputCreateRobot.getName())) {
|
||||
|
||||
if(StringUtil.isNullOrEmpty(inputCreateRobot.getPassword())) {
|
||||
inputCreateRobot.setPassword(UUIDGenerator.getUUID());
|
||||
}
|
||||
|
||||
if(StringUtil.isNullOrEmpty(inputCreateRobot.getUserId())) {
|
||||
inputCreateRobot.setUserId(messagesStore.getShortUUID());
|
||||
}
|
||||
|
||||
if (inputCreateRobot.getPortrait() == null || inputCreateRobot.getPortrait().length() == 0) {
|
||||
inputCreateRobot.setPortrait("https://avatars.io/gravatar/" + inputCreateRobot.getUserId());
|
||||
}
|
||||
|
||||
WFCMessage.User newUser = inputCreateRobot.toUser();
|
||||
|
||||
|
||||
messagesStore.addUserInfo(newUser, inputCreateRobot.getPassword());
|
||||
|
||||
if (StringUtil.isNullOrEmpty(inputCreateRobot.getOwner())) {
|
||||
inputCreateRobot.setOwner(inputCreateRobot.getUserId());
|
||||
}
|
||||
|
||||
if (StringUtil.isNullOrEmpty(inputCreateRobot.getSecret())) {
|
||||
inputCreateRobot.setSecret(UUIDGenerator.getUUID());
|
||||
}
|
||||
|
||||
messagesStore.addRobot(inputCreateRobot.toRobot());
|
||||
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.ok(new OutputCreateRobot(inputCreateRobot.getUserId(), inputCreateRobot.getSecret()));
|
||||
response.setContent(new Gson().toJson(result));
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputCreateUser;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.UUIDGenerator;
|
||||
|
||||
@Route("admin/user/create")
|
||||
@HttpMethod("POST")
|
||||
public class CreateUserAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputCreateUser inputCreateUser = getRequestBody(request.getNettyRequest(), InputCreateUser.class);
|
||||
if (inputCreateUser != null
|
||||
&& !StringUtil.isNullOrEmpty(inputCreateUser.getName())) {
|
||||
|
||||
if(StringUtil.isNullOrEmpty(inputCreateUser.getPassword())) {
|
||||
inputCreateUser.setPassword(UUIDGenerator.getUUID());
|
||||
}
|
||||
|
||||
if(StringUtil.isNullOrEmpty(inputCreateUser.getUserId())) {
|
||||
inputCreateUser.setUserId(messagesStore.getShortUUID());
|
||||
}
|
||||
|
||||
if (inputCreateUser.getPortrait() == null || inputCreateUser.getPortrait().length() == 0) {
|
||||
inputCreateUser.setPortrait("https://avatars.io/gravatar/" + inputCreateUser.getUserId());
|
||||
}
|
||||
|
||||
WFCMessage.User.Builder newUserBuilder = WFCMessage.User.newBuilder()
|
||||
.setUid(StringUtil.isNullOrEmpty(inputCreateUser.getUserId()) ? "" : inputCreateUser.getUserId());
|
||||
if (inputCreateUser.getName() != null)
|
||||
newUserBuilder.setName(inputCreateUser.getName());
|
||||
if (inputCreateUser.getDisplayName() != null)
|
||||
newUserBuilder.setDisplayName(StringUtil.isNullOrEmpty(inputCreateUser.getDisplayName()) ? inputCreateUser.getName() : inputCreateUser.getDisplayName());
|
||||
if (inputCreateUser.getPortrait() != null)
|
||||
newUserBuilder.setPortrait(inputCreateUser.getPortrait());
|
||||
if (inputCreateUser.getEmail() != null)
|
||||
newUserBuilder.setEmail(inputCreateUser.getEmail());
|
||||
if (inputCreateUser.getAddress() != null)
|
||||
newUserBuilder.setAddress(inputCreateUser.getAddress());
|
||||
if (inputCreateUser.getCompany() != null)
|
||||
newUserBuilder.setCompany(inputCreateUser.getCompany());
|
||||
|
||||
if (inputCreateUser.getSocial() != null)
|
||||
newUserBuilder.setSocial(inputCreateUser.getSocial());
|
||||
|
||||
|
||||
if (inputCreateUser.getMobile() != null)
|
||||
newUserBuilder.setMobile(inputCreateUser.getMobile());
|
||||
newUserBuilder.setGender(inputCreateUser.getGender());
|
||||
if (inputCreateUser.getExtra() != null)
|
||||
newUserBuilder.setExtra(inputCreateUser.getExtra());
|
||||
|
||||
newUserBuilder.setUpdateDt(System.currentTimeMillis());
|
||||
|
||||
messagesStore.addUserInfo(newUserBuilder.build(), inputCreateUser.getPassword());
|
||||
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.ok(new OutputCreateUser(inputCreateUser.getUserId(), inputCreateUser.getName()));
|
||||
response.setContent(new Gson().toJson(result));
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputDestoryChatroom;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("/admin/chatroom")
|
||||
@HttpMethod("DELETE")
|
||||
public class DestoryChatroomAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
|
||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||
String content = new String(bytes);
|
||||
Gson gson = new Gson();
|
||||
InputDestoryChatroom inputDestoryChatroom = gson.fromJson(content, InputDestoryChatroom.class);
|
||||
if (inputDestoryChatroom != null
|
||||
&& !StringUtil.isNullOrEmpty(inputDestoryChatroom.getChatroomId())) {
|
||||
|
||||
messagesStore.destoryChatroom(inputDestoryChatroom.getChatroomId());
|
||||
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.ok();
|
||||
response.setContent(new Gson().toJson(result));
|
||||
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.OutputGetChatroomInfo;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("/admin/chatroom")
|
||||
@HttpMethod("GET")
|
||||
public class GetChatroomInfoAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
|
||||
String chatroomid = request.getParam("id");
|
||||
if (!StringUtil.isNullOrEmpty(chatroomid)) {
|
||||
|
||||
WFCMessage.ChatroomInfo info = messagesStore.getChatroomInfo(chatroomid);
|
||||
|
||||
RestResult result;
|
||||
if (info != null) {
|
||||
result = RestResult.ok(new OutputGetChatroomInfo(chatroomid, messagesStore.getChatroomMemberCount(chatroomid), info));
|
||||
} else {
|
||||
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
|
||||
}
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
|
||||
response.setContent(new Gson().toJson(result));
|
||||
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputGetToken;
|
||||
import com.xiaoleilu.loServer.pojos.OutputGetIMTokenData;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route("/admin/user/token")
|
||||
@HttpMethod("POST")
|
||||
public class GetIMTokenAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputGetToken input = getRequestBody(request.getNettyRequest(), InputGetToken.class);
|
||||
String userId = input.getUserId();
|
||||
|
||||
|
||||
WFCMessage.GetTokenRequest getTokenRequest = WFCMessage.GetTokenRequest.newBuilder().setUserId(userId).setClientId(input.getClientId()).build();
|
||||
RPCCenter.getInstance().sendRequest(userId, input.getClientId(), IMTopic.GetTokenTopic, getTokenRequest.toByteArray(), userId, TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
ErrorCode errorCode1 = ErrorCode.fromCode(result[0]);
|
||||
if (errorCode1 == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
//ba errorcode qudiao
|
||||
byte[] data = new byte[result.length -1];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = result[i+1];
|
||||
}
|
||||
String token = Base64.getEncoder().encodeToString(data);
|
||||
|
||||
sendResponse(response, null, new OutputGetIMTokenData(userId, token));
|
||||
} else {
|
||||
sendResponse(response, errorCode1, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputCreateUser;
|
||||
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("admin/user/info")
|
||||
@HttpMethod("POST")
|
||||
public class GetUserAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
|
||||
if (inputUserId != null
|
||||
&& (!StringUtil.isNullOrEmpty(inputUserId.getUserId()) || !StringUtil.isNullOrEmpty(inputUserId.getName()))) {
|
||||
|
||||
WFCMessage.User user = null;
|
||||
if(!StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
|
||||
user = messagesStore.getUserInfo(inputUserId.getUserId());
|
||||
} else if(!StringUtil.isNullOrEmpty(inputUserId.getName())) {
|
||||
user = messagesStore.getUserInfoByName(inputUserId.getName());
|
||||
} else if(!StringUtil.isNullOrEmpty(inputUserId.getMobile())) {
|
||||
user = messagesStore.getUserInfoByMobile(inputUserId.getMobile());
|
||||
}
|
||||
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result;
|
||||
if (user == null) {
|
||||
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
|
||||
} else {
|
||||
result = RestResult.ok(InputCreateUser.fromPbUser(user));
|
||||
}
|
||||
|
||||
response.setContent(new Gson().toJson(result));
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
|
||||
@Route("admin/user/statuslist")
|
||||
@HttpMethod("POST")
|
||||
public class GetUserBlockListAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.ok(messagesStore.getUserStatusList());
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
|
||||
import com.xiaoleilu.loServer.pojos.OutputUserStatus;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("admin/user/checkstatus")
|
||||
@HttpMethod("POST")
|
||||
public class GetUserBlockStatusAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
|
||||
if (inputUserId != null
|
||||
&& !StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
|
||||
|
||||
int status = messagesStore.getUserStatus(inputUserId.getUserId());
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.ok(new OutputUserStatus(status));
|
||||
response.setContent(new Gson().toJson(result));
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.RecallMessageData;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route("admin/message/recall")
|
||||
@HttpMethod("POST")
|
||||
public class RecallMessageAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
RecallMessageData recallMessageData = getRequestBody(request.getNettyRequest(), RecallMessageData.class);
|
||||
if (recallMessageData != null && !StringUtil.isNullOrEmpty(recallMessageData.getOperator())) {
|
||||
|
||||
WFCMessage.INT64Buf idBuf = WFCMessage.INT64Buf.newBuilder().setId(recallMessageData.getMessageUid()).build();
|
||||
RPCCenter.getInstance().sendRequest(recallMessageData.getOperator(), null, IMTopic.RecallMessageTopic, idBuf.toByteArray(), recallMessageData.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
byteBuf.writeBytes(result);
|
||||
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
return false;
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.hazelcast.util.StringUtil;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.SendMessageData;
|
||||
import com.xiaoleilu.loServer.pojos.SendMessageResult;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route("admin/message/send")
|
||||
@HttpMethod("POST")
|
||||
public class SendMessageAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
SendMessageData sendMessageData = getRequestBody(request.getNettyRequest(), SendMessageData.class);
|
||||
if (SendMessageData.isValide(sendMessageData) && !StringUtil.isNullOrEmpty(sendMessageData.getSender())) {
|
||||
RPCCenter.getInstance().sendRequest(sendMessageData.getSender(), null, IMTopic.SendMessageTopic, sendMessageData.toProtoMessage().toByteArray(), sendMessageData.getSender(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
byteBuf.writeBytes(result);
|
||||
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
|
||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
long messageId = byteBuf.readLong();
|
||||
long timestamp = byteBuf.readLong();
|
||||
sendResponse(response, null, new SendMessageResult(messageId, timestamp));
|
||||
} else {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
return false;
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputOutputSensitiveWords;
|
||||
import com.xiaoleilu.loServer.pojos.InputOutputUserBlockStatus;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("admin/sensitive/add")
|
||||
@HttpMethod("POST")
|
||||
public class SensitiveWordAddAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputOutputSensitiveWords input = getRequestBody(request.getNettyRequest(), InputOutputSensitiveWords.class);
|
||||
if (input != null && input.getWords() != null && !input.getWords().isEmpty()) {
|
||||
ErrorCode errorCode = messagesStore.addSensitiveWords(input.getWords()) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_SERVER_ERROR;
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
sendResponse(response, errorCode, null);
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputOutputSensitiveWords;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("admin/sensitive/del")
|
||||
@HttpMethod("POST")
|
||||
public class SensitiveWordDeleteAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputOutputSensitiveWords input = getRequestBody(request.getNettyRequest(), InputOutputSensitiveWords.class);
|
||||
if (input != null && input.getWords() != null && !input.getWords().isEmpty()) {
|
||||
ErrorCode errorCode = messagesStore.removeSensitiveWords(input.getWords()) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_SERVER_ERROR;
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
|
||||
sendResponse(response, errorCode, null);
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.admin;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputOutputSensitiveWords;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Route("admin/sensitive/query")
|
||||
@HttpMethod("POST")
|
||||
public class SensitiveWordQueryAction extends AdminAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
List<String> words = messagesStore.getAllSensitiveWords();
|
||||
InputOutputSensitiveWords out = new InputOutputSensitiveWords();
|
||||
out.setWords(words);
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, out);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.channel;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.RateLimiter;
|
||||
|
||||
abstract public class ChannelAction extends Action {
|
||||
private final RateLimiter mLimitCounter = new RateLimiter(10, 200);
|
||||
protected WFCMessage.ChannelInfo channelInfo;
|
||||
|
||||
@Override
|
||||
public ErrorCode preAction(Request request, Response response) {
|
||||
String nonce = request.getHeader("nonce");
|
||||
String timestamp = request.getHeader("timestamp");
|
||||
String sign = request.getHeader("sign");
|
||||
String cid = request.getHeader("cid");
|
||||
|
||||
if (StringUtil.isNullOrEmpty(nonce) || StringUtil.isNullOrEmpty(timestamp) || StringUtil.isNullOrEmpty(sign) || StringUtil.isNullOrEmpty(cid)) {
|
||||
return ErrorCode.INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!mLimitCounter.isGranted(cid)) {
|
||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
}
|
||||
|
||||
Long ts;
|
||||
try {
|
||||
ts = Long.parseLong(timestamp);
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return ErrorCode.INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - ts > 2 * 60 * 60 * 1000) {
|
||||
return ErrorCode.ERROR_CODE_SIGN_EXPIRED;
|
||||
}
|
||||
|
||||
channelInfo = messagesStore.getChannelInfo(cid);
|
||||
if (channelInfo == null) {
|
||||
return ErrorCode.INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
String str = nonce + "|" + channelInfo.getSecret() + "|" + timestamp;
|
||||
String localSign = DigestUtils.sha1Hex(str);
|
||||
return localSign.equals(sign) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if (errorCode == null) {
|
||||
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
response.send();
|
||||
}
|
||||
|
||||
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
|
||||
if (request instanceof FullHttpRequest) {
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||
String content = new String(bytes);
|
||||
Gson gson = new Gson();
|
||||
T t = gson.fromJson(content, cls);
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.channel;
|
||||
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.action.admin.AdminAction;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.Conversation;
|
||||
import com.xiaoleilu.loServer.pojos.SendMessageData;
|
||||
import com.xiaoleilu.loServer.pojos.SendMessageResult;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static cn.wildfirechat.proto.ProtoConstants.ConversationType.ConversationType_Channel;
|
||||
|
||||
@Route("channel/message/send")
|
||||
@HttpMethod("POST")
|
||||
public class SendMessageAction extends ChannelAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
SendMessageData sendMessageData = getRequestBody(request.getNettyRequest(), SendMessageData.class);
|
||||
sendMessageData.setConv(new Conversation());
|
||||
sendMessageData.getConv().setType(ConversationType_Channel);
|
||||
sendMessageData.getConv().setTarget(channelInfo.getTargetId());
|
||||
sendMessageData.setSender(channelInfo.getOwner());
|
||||
if (SendMessageData.isValide(sendMessageData)) {
|
||||
RPCCenter.getInstance().sendRequest(sendMessageData.getSender(), null, IMTopic.SendMessageTopic, sendMessageData.toProtoMessage().toByteArray(), sendMessageData.getSender(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
byteBuf.writeBytes(result);
|
||||
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
|
||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
long messageId = byteBuf.readLong();
|
||||
long timestamp = byteBuf.readLong();
|
||||
sendResponse(response, null, new SendMessageResult(messageId, timestamp));
|
||||
} else {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, false);
|
||||
return false;
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.robot;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.InputCreateUser;
|
||||
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
|
||||
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
|
||||
@Route("admin/robot/user_info")
|
||||
@HttpMethod("POST")
|
||||
public class GetUserAction extends RobotAction {
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
|
||||
if (inputUserId != null
|
||||
&& (!StringUtil.isNullOrEmpty(inputUserId.getUserId()) || !StringUtil.isNullOrEmpty(inputUserId.getName()))) {
|
||||
|
||||
WFCMessage.User user = null;
|
||||
if(!StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
|
||||
user = messagesStore.getUserInfo(inputUserId.getUserId());
|
||||
} else if(!StringUtil.isNullOrEmpty(inputUserId.getName())) {
|
||||
user = messagesStore.getUserInfoByName(inputUserId.getName());
|
||||
} else if(!StringUtil.isNullOrEmpty(inputUserId.getMobile())) {
|
||||
user = messagesStore.getUserInfoByMobile(inputUserId.getMobile());
|
||||
}
|
||||
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result;
|
||||
if (user == null) {
|
||||
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
|
||||
} else {
|
||||
result = RestResult.ok(InputCreateUser.fromPbUser(user));
|
||||
}
|
||||
|
||||
response.setContent(new Gson().toJson(result));
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.robot;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.RateLimiter;
|
||||
|
||||
abstract public class RobotAction extends Action {
|
||||
private final RateLimiter mLimitCounter = new RateLimiter(10, 1000);
|
||||
|
||||
protected WFCMessage.Robot robot;
|
||||
|
||||
@Override
|
||||
public ErrorCode preAction(Request request, Response response) {
|
||||
String nonce = request.getHeader("nonce");
|
||||
String timestamp = request.getHeader("timestamp");
|
||||
String sign = request.getHeader("sign");
|
||||
String rid = request.getHeader("rid");
|
||||
if (StringUtil.isNullOrEmpty(nonce) || StringUtil.isNullOrEmpty(timestamp) || StringUtil.isNullOrEmpty(sign) || StringUtil.isNullOrEmpty(rid)) {
|
||||
return ErrorCode.INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!mLimitCounter.isGranted(rid)) {
|
||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
}
|
||||
|
||||
Long ts;
|
||||
try {
|
||||
ts = Long.parseLong(timestamp);
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return ErrorCode.INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - ts > 2 * 60 * 60 * 1000) {
|
||||
return ErrorCode.ERROR_CODE_SIGN_EXPIRED;
|
||||
}
|
||||
|
||||
robot = messagesStore.getRobot(rid);
|
||||
if (robot == null) {
|
||||
return ErrorCode.ERROR_CODE_NOT_EXIST;
|
||||
}
|
||||
|
||||
String str = nonce + "|" + robot.getSecret() + "|" + timestamp;
|
||||
String localSign = DigestUtils.sha1Hex(str);
|
||||
return localSign.equals(sign) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if (errorCode == null) {
|
||||
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
response.send();
|
||||
}
|
||||
|
||||
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
|
||||
if (request instanceof FullHttpRequest) {
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
||||
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
|
||||
String content = new String(bytes);
|
||||
Gson gson = new Gson();
|
||||
T t = gson.fromJson(content, cls);
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.action.robot;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.ProtoConstants;
|
||||
import com.google.gson.Gson;
|
||||
import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.action.admin.AdminAction;
|
||||
import com.xiaoleilu.loServer.annotation.HttpMethod;
|
||||
import com.xiaoleilu.loServer.annotation.Route;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import com.xiaoleilu.loServer.pojos.SendMessageData;
|
||||
import com.xiaoleilu.loServer.pojos.SendMessageResult;
|
||||
import io.moquette.persistence.RPCCenter;
|
||||
import io.moquette.persistence.TargetEntry;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import win.liyufan.im.ErrorCode;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Route("admin/robot/send")
|
||||
@HttpMethod("POST")
|
||||
public class SendMessageAction extends RobotAction {
|
||||
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
SendMessageData sendMessageData = getRequestBody(request.getNettyRequest(), SendMessageData.class);
|
||||
sendMessageData.setSender(robot.getUid());
|
||||
if (SendMessageData.isValide(sendMessageData)) {
|
||||
RPCCenter.getInstance().sendRequest(sendMessageData.getSender(), null, IMTopic.SendMessageTopic, sendMessageData.toProtoMessage().toByteArray(), sendMessageData.getSender(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
|
||||
@Override
|
||||
public void onSuccess(byte[] result) {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
byteBuf.writeBytes(result);
|
||||
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
|
||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
long messageId = byteBuf.readLong();
|
||||
long timestamp = byteBuf.readLong();
|
||||
sendResponse(response, null, new SendMessageResult(messageId, timestamp));
|
||||
} else {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorCode errorCode) {
|
||||
sendResponse(response, errorCode, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout() {
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getResponseExecutor() {
|
||||
return command -> {
|
||||
ctx.executor().execute(command);
|
||||
};
|
||||
}
|
||||
}, false);
|
||||
return false;
|
||||
} else {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)/*保留的时间长短*/
|
||||
@Inherited/*只用于class,可被子类继承*/
|
||||
public @interface HttpMethod {
|
||||
String value() default "GET";
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.annotation;
|
||||
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)/*保留的时间长短*/
|
||||
@Inherited/*只用于class,可被子类继承*/
|
||||
public @interface RequireAuthentication {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.annotation;
|
||||
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* 注解,用于自定义访问的URL路径<br>
|
||||
* 值可以是一个请求路径,如果需要指定HTTP方法,在前面加方法名用":"分隔既可<br>
|
||||
* @author loolly
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)/*保留的时间长短*/
|
||||
@Inherited/*只用于class,可被子类继承*/
|
||||
public @interface Route {
|
||||
String value();
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.xiaoleilu.loServer.exception;
|
||||
|
||||
import com.xiaoleilu.hutool.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 设置异常
|
||||
* @author xiaoleilu
|
||||
*/
|
||||
public class ServerSettingException extends RuntimeException{
|
||||
private static final long serialVersionUID = -4134588858314744501L;
|
||||
|
||||
public ServerSettingException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public ServerSettingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ServerSettingException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public ServerSettingException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public ServerSettingException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.xiaoleilu.loServer.filter;
|
||||
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
|
||||
/**
|
||||
* 过滤器接口<br>
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public interface Filter {
|
||||
|
||||
/**
|
||||
* 执行过滤
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
* @return 如果返回true,则继续执行下一步内容,否则中断
|
||||
*/
|
||||
public boolean doFilter(Request request, Response response);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.xiaoleilu.hutool.lang.Singleton;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.loServer.ServerSetting;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.action.UnknownErrorAction;
|
||||
import com.xiaoleilu.loServer.action.FileAction;
|
||||
import com.xiaoleilu.loServer.filter.Filter;
|
||||
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Action处理单元
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
private static final Logger Logger = LoggerFactory.getLogger(ActionHandler.class);
|
||||
|
||||
public ActionHandler(IMessagesStore messagesStore, ISessionsStore sessionsStore) {
|
||||
Action.messagesStore = messagesStore;
|
||||
Action.sessionsStore = sessionsStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {
|
||||
Logger.info("Http request whit url {}", fullHttpRequest.uri());
|
||||
final Request request = Request.build(ctx, fullHttpRequest);
|
||||
final Response response = Response.build(ctx, request);
|
||||
|
||||
response.setContentType("application/json");
|
||||
try {
|
||||
//do filter
|
||||
boolean isPass = this.doFilter(request, response);
|
||||
|
||||
if(isPass){
|
||||
//do action
|
||||
this.doAction(ctx, request, response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Action errorAction = ServerSetting.getErrorAction(ServerSetting.MAPPING_ERROR);
|
||||
request.putParam(UnknownErrorAction.ERROR_PARAM_NAME, e);
|
||||
response.setContent(e.toString());
|
||||
errorAction.doAction(request, response);
|
||||
|
||||
//如果发送请求未被触发,则触发之,否则跳过。
|
||||
if(!response.isSent()){
|
||||
response.send();
|
||||
}
|
||||
|
||||
ctx.fireExceptionCaught(e);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
Logger.warn("{}", cause.getMessage());
|
||||
Channel channel = ctx.channel();
|
||||
if(channel.isActive())ctx.close();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------- Private method start
|
||||
/**
|
||||
* 执行过滤
|
||||
* @param request 请求
|
||||
* @param response 响应
|
||||
* @@return 是否继续
|
||||
*/
|
||||
private boolean doFilter(Request request, Response response) {
|
||||
//全局过滤器
|
||||
Filter filter = ServerSetting.getFilter(ServerSetting.MAPPING_ALL);
|
||||
if(null != filter){
|
||||
if(false == filter.doFilter(request, response)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//自定义Path过滤器
|
||||
filter = ServerSetting.getFilter(request.getPath());
|
||||
if(null != filter){
|
||||
if(false == filter.doFilter(request, response)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract boolean isValidePath(String path);
|
||||
/**
|
||||
* 执行Action
|
||||
* @param request 请求对象
|
||||
* @param response 响应对象
|
||||
*/
|
||||
private void doAction(ChannelHandlerContext ctx, Request request, Response response){
|
||||
Action action;
|
||||
if (isValidePath(request.getPath())) {
|
||||
action = ServerSetting.getAction(request.getPath(), request.getMethod().toUpperCase());
|
||||
} else {
|
||||
action = ServerSetting.getErrorAction(ServerSetting.MAPPING_ERROR);
|
||||
}
|
||||
|
||||
if (action == null && (request.getPath().startsWith("/api") || request.getPath().startsWith("/admin"))) {
|
||||
action = ServerSetting.getErrorAction(ServerSetting.MAPPING_ERROR);
|
||||
}
|
||||
|
||||
if (null == action) {
|
||||
//查找匹配所有路径的Action
|
||||
action = ServerSetting.getAction(ServerSetting.MAPPING_ALL, request.getMethod());
|
||||
if(null == action){
|
||||
// 非Action方法,调用静态文件读取
|
||||
action = Singleton.get(FileAction.class);
|
||||
}
|
||||
}
|
||||
action.ctx = ctx;
|
||||
|
||||
boolean isSync = action.doAction(request, response);
|
||||
|
||||
if(isSync) {
|
||||
//如果发送请求未被触发,则触发之,否则跳过。
|
||||
if(!response.isSent()){
|
||||
response.send();
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------- Private method start
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import com.xiaoleilu.hutool.lang.Singleton;
|
||||
import com.xiaoleilu.loServer.ServerSetting;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.action.FileAction;
|
||||
import com.xiaoleilu.loServer.action.UnknownErrorAction;
|
||||
import com.xiaoleilu.loServer.filter.Filter;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Action处理单元
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class AdminActionHandler extends ActionHandler {
|
||||
private static final Logger Logger = LoggerFactory.getLogger(AdminActionHandler.class);
|
||||
|
||||
public AdminActionHandler(IMessagesStore messagesStore, ISessionsStore sessionsStore) {
|
||||
super(messagesStore, sessionsStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isValidePath(String path) {
|
||||
if (path.startsWith("/admin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.FileRegion;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpContentCompressor;
|
||||
|
||||
/**
|
||||
* 解决大文件传输与Gzip压缩冲突问题
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class HttpChunkContentCompressor extends HttpContentCompressor {
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
StaticLog.debug("Write object [{}]", msg);
|
||||
|
||||
if (msg instanceof FileRegion || msg instanceof DefaultHttpResponse) {
|
||||
//文件传输不经过Gzip压缩
|
||||
ctx.write(msg, promise);
|
||||
}else{
|
||||
super.write(ctx, msg, promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class HttpFileServerController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HttpFileServerController.class);
|
||||
private Map<String, ChannelHandlerContext> contextMap = null; //
|
||||
|
||||
private static class BrokenUploadInfoHandlerHolder {
|
||||
private static HttpFileServerController singleton = new HttpFileServerController();
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
private HttpFileServerController() {
|
||||
contextMap = new ConcurrentHashMap<String, ChannelHandlerContext>();
|
||||
}
|
||||
|
||||
/**
|
||||
* getInstance
|
||||
*/
|
||||
public static HttpFileServerController getInstance() {
|
||||
return BrokenUploadInfoHandlerHolder.singleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* mapChannelHandlerContext
|
||||
*
|
||||
* @param requestId
|
||||
* @param ctx
|
||||
*/
|
||||
public synchronized void mapChannelHandlerContext(String requestId, ChannelHandlerContext ctx) {
|
||||
if (contextMap.containsKey(requestId)) {
|
||||
logger.warn("contextMap has already contained the key:" + requestId);
|
||||
}
|
||||
|
||||
contextMap.put(requestId, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* getContext
|
||||
*
|
||||
* @param requestId
|
||||
* @return
|
||||
*/
|
||||
public ChannelHandlerContext getChannelHandlerContext(String requestId) {
|
||||
if (!contextMap.containsKey(requestId)) {
|
||||
logger.warn("contextMap not contains the key:" + requestId);
|
||||
return null;
|
||||
}
|
||||
|
||||
return contextMap.get(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* removeContext
|
||||
*
|
||||
* @param requestId
|
||||
*/
|
||||
public void removeChannelHandlerContext(String requestId) {
|
||||
if (!contextMap.containsKey(requestId)) {
|
||||
logger.warn("contextMap not contains the key:" + requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
contextMap.remove(requestId);
|
||||
// logger.info("contextMap remove done.(" + requestId + ")");
|
||||
}
|
||||
}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.moquette.server.config.MediaServerConfig;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.multipart.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.xiaoleilu.loServer.handler.HttpResponseHelper.getFileExt;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
|
||||
public class HttpFileServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HttpFileServerHandler.class);
|
||||
private static final HttpDataFactory factory = new DefaultHttpDataFactory(false);
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
try {
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
FullHttpRequest request = (FullHttpRequest) msg;
|
||||
String requestId = UUID.randomUUID().toString().replace("-", "");
|
||||
logger.info("HttpFileServerHandler received a request: method=" + request.getMethod() + ", uri=" + request.getUri() + ", requestId=" + requestId);
|
||||
|
||||
if (!request.getDecoderResult().isSuccess()) {
|
||||
logger.warn("http decode failed!");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.getMethod() == HttpMethod.GET) {
|
||||
download(ctx, request, requestId);
|
||||
} else if (request.getMethod() == HttpMethod.POST) {
|
||||
multipartUpload(ctx, request, requestId);
|
||||
} else if (request.getMethod() == HttpMethod.DELETE) {
|
||||
delete(ctx, request, requestId);
|
||||
} else if (request.getMethod() == HttpMethod.OPTIONS) {
|
||||
doOptions(ctx, request);
|
||||
} else {
|
||||
logger.warn("METHOD_NOT_ALLOWED!(methodName=" + request.getMethod().name() + ")");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.METHOD_NOT_ALLOWED);
|
||||
}
|
||||
} else {
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "Not a http request");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("HttpFileServerHandler.channelRead0() process error!", e);
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
try {
|
||||
if (ctx.channel().isActive()) {
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
logger.warn("exceptionCaught:" + cause.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("exceptionCaught error!" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* multipart上传
|
||||
*/
|
||||
private void multipartUpload(ChannelHandlerContext ctx, FullHttpRequest request, String requestId) {
|
||||
HttpPostRequestDecoder decoder = null;
|
||||
try {
|
||||
decoder = new HttpPostRequestDecoder(factory, request);
|
||||
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
|
||||
logger.error("Failed to decode file data!", e1);
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "Failed to decode file data!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (decoder != null) {
|
||||
if (request instanceof HttpContent) {
|
||||
HttpContent chunk = (HttpContent) request;
|
||||
try {
|
||||
decoder.offer(chunk);
|
||||
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
|
||||
logger.warn("BAD_REQUEST, Failed to decode file data");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "Failed to decode file data");
|
||||
return;
|
||||
}
|
||||
|
||||
long fileTotalSize = 0;
|
||||
if (request.headers().contains("X-File-Total-Size")) {
|
||||
try {
|
||||
fileTotalSize = Integer.parseInt(request.headers().get("X-File-Total-Size"));
|
||||
} catch (Exception e) {
|
||||
logger.warn("invalid X-File-Total-Size value!");
|
||||
}
|
||||
}
|
||||
|
||||
readHttpDataChunkByChunk(ctx, decoder, requestId, HttpHeaders.isKeepAlive(request));
|
||||
|
||||
if (chunk instanceof LastHttpContent) {
|
||||
releaseRequest(request, decoder);
|
||||
}
|
||||
} else {
|
||||
logger.warn("BAD_REQUEST, Not a http request");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "Not a http request");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* readHttpDataChunkByChunk
|
||||
*/
|
||||
private void readHttpDataChunkByChunk(ChannelHandlerContext ctx, HttpPostRequestDecoder decoder, String requestId, boolean isKeepAlive) {
|
||||
try {
|
||||
while (decoder.hasNext()) {
|
||||
InterfaceHttpData data = decoder.next();
|
||||
if (data != null) {
|
||||
try {
|
||||
writeFileUploadData(data, ctx, requestId, isKeepAlive);
|
||||
} finally {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.info("chunk end");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writeFileUploadData
|
||||
*/
|
||||
private void writeFileUploadData(InterfaceHttpData data, ChannelHandlerContext ctx, String requestId, boolean isKeepAlive) {
|
||||
try {
|
||||
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
|
||||
FileUpload fileUpload = (FileUpload) data;
|
||||
|
||||
String remoteFileName = fileUpload.getFilename();
|
||||
long remoteFileSize = fileUpload.length();
|
||||
if (StringUtil.isNullOrEmpty(remoteFileName)) {
|
||||
logger.warn("remoteFileName is empty!");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "file name is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtil.isNullOrEmpty(requestId)) {
|
||||
logger.warn("requestId is empty!");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "requestId is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteFileSize > 10 * 1024 * 1024) {
|
||||
logger.warn("file over limite!(" + remoteFileSize + ")");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "file over limite!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
String remoteFileExt = "";
|
||||
if (remoteFileName.lastIndexOf(".") == -1) {
|
||||
remoteFileExt = "octetstream";
|
||||
remoteFileName = remoteFileName + "." + remoteFileExt;
|
||||
|
||||
} else {
|
||||
remoteFileExt = getFileExt(remoteFileName);
|
||||
}
|
||||
|
||||
if (StringUtil.isNullOrEmpty(remoteFileExt) || remoteFileExt.equals("ing")) {
|
||||
logger.warn("Invalid file extention name");
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "Invalid file extention name");
|
||||
return;
|
||||
}
|
||||
|
||||
int remoteFileTotalSize = (int) remoteFileSize;
|
||||
|
||||
HttpFileServerController.getInstance().mapChannelHandlerContext(requestId, ctx);
|
||||
|
||||
ByteBuf byteBuf = null;
|
||||
int savedThunkSize = 0; // 分片接收保存的大小
|
||||
int offset = 0; // 断点续传开始位置
|
||||
|
||||
File tmpFile = new File("./" + MediaServerConfig.FILE_STROAGE_ROOT + "/" + requestId);
|
||||
|
||||
boolean isError = false;
|
||||
while (true) {
|
||||
byte[] thunkData;
|
||||
try {
|
||||
byteBuf = fileUpload.getChunk(128 * 1024);
|
||||
int readableBytesSize = byteBuf.readableBytes();
|
||||
thunkData = new byte[readableBytesSize];
|
||||
byteBuf.readBytes(thunkData);
|
||||
|
||||
put(tmpFile, offset, thunkData);
|
||||
|
||||
savedThunkSize += readableBytesSize;
|
||||
offset += readableBytesSize;
|
||||
|
||||
if (savedThunkSize >= remoteFileSize) {
|
||||
byteBuf.release();
|
||||
fileUpload.release();
|
||||
HttpResponseHelper.sendResponse(requestId, ctx, HttpResponseStatus.OK, HttpHeaders.Values.APPLICATION_JSON, "{\"key\":\"" + requestId + "\"}", true, false);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("save thunckData error!", e);
|
||||
if (fileUpload != null)
|
||||
fileUpload.release();
|
||||
|
||||
if (byteBuf != null)
|
||||
byteBuf.release();
|
||||
|
||||
HttpResponseHelper.sendResponse(requestId, ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
isError = true;
|
||||
|
||||
return;
|
||||
} finally {
|
||||
thunkData = null;
|
||||
if (isError) {
|
||||
tmpFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("writeHttpData error!", e);
|
||||
HttpResponseHelper.sendResponse(requestId, ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public static void put(File file, long pos, byte[] data) throws Exception {
|
||||
RandomAccessFile raf = null;
|
||||
try {
|
||||
raf = new RandomAccessFile(file, "rwd");
|
||||
raf.seek(pos);
|
||||
raf.write(data);
|
||||
} finally {
|
||||
try {
|
||||
if (raf != null)
|
||||
raf.close();
|
||||
} catch (Exception e) {
|
||||
logger.warn("release error!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*/
|
||||
private void download(ChannelHandlerContext ctx, FullHttpRequest request, String requestId) {
|
||||
// try {
|
||||
// final String httpUri = request.getUri();
|
||||
// if (httpUri.toLowerCase().equals("/favicon.ico")) {
|
||||
// logger.warn("invalid httpUri(" + httpUri + ", requestId=" + requestId + ")");
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "invalid httpUri");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// String typedDownloadInfo = MetaDataIndexHandler.getTypedDownloadInfo(httpUri);
|
||||
// if (StringUtil.isNullOrEmpty(typedDownloadInfo)) {
|
||||
// logger.warn("invalid httpUri(" + httpUri + ", requestId=" + requestId + ")");
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "invalid httpUri");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// MetaDataIndex metadataIndex = MetaDataIndexHandler.parseTypedDownloadInfo(typedDownloadInfo);
|
||||
// if (metadataIndex == null) {
|
||||
// logger.warn("invalid typedDownloadInfo!(" + typedDownloadInfo + ", requestId=" + requestId + ")");
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "invalid downloadFileName");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// int fileSize = MetaDataHandler.getDataFieldLength(metadataIndex);
|
||||
// int chunkSize = FileStorageConst.Stroage_Remote_Invoke_Thunk_Size;
|
||||
// int chunkCount = fileSize % chunkSize == 0 ? (int) (fileSize / chunkSize) : (int) (fileSize / chunkSize + 1);
|
||||
//
|
||||
// ClusterNode toNode = StorageClusterRouterManager.getInstance().getDownloadNode(metadataIndex.getClusterNode());
|
||||
// if(toNode == null) {
|
||||
// logger.warn("toNode is null!(" + typedDownloadInfo + ", requestId=" + requestId + ")");
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.NOT_FOUND, "File Not Existed");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// ClusterNode fromNode = StorageClusterRouterManager.getInstance().getSelfNode();
|
||||
// ActorSystem actorSystem = StorageClusterRouterManager.getInstance().getStorageActorSystem();
|
||||
// ActorRef toActorRef = actorSystem.actorOf(toNode.getIp(), toNode.getRpcPort(), ActorType.DownloadActor.getName());
|
||||
// ActorRef fromActorRef = actorSystem.actorOf(fromNode.getIp(),fromNode.getRpcPort(), ActorType.HttpFileServerActor.getName());
|
||||
//
|
||||
// HttpFileServerController.getInstance().mapChannelHandlerContext(requestId, ctx);
|
||||
//
|
||||
// DownloadMsg downloadMsg = new DownloadMsg();
|
||||
// downloadMsg.setRequestId(requestId);
|
||||
// downloadMsg.setIsKeepAlive(HttpHeaders.isKeepAlive(request));
|
||||
// downloadMsg.setClusterNode(metadataIndex.getClusterNode());
|
||||
// downloadMsg.setFileSize(fileSize);
|
||||
// downloadMsg.setChunkIndex(0);
|
||||
// downloadMsg.setChunkSize(chunkSize);
|
||||
// downloadMsg.setChunkCount(chunkCount);
|
||||
// downloadMsg.setTime(metadataIndex.getTime());
|
||||
// downloadMsg.setBigFileIndex(metadataIndex.getBigFileIndex());
|
||||
// downloadMsg.setOffset(metadataIndex.getOffset());
|
||||
// downloadMsg.setMetaDataTotalLength(metadataIndex.getMetaDataTotalLength());
|
||||
// downloadMsg.setFileType(metadataIndex.getFileType());
|
||||
// downloadMsg.setBigFilePath(metadataIndex.getBigFilePath());
|
||||
// downloadMsg.setStorageEngineVersion(metadataIndex.getStorageEngineVersion());
|
||||
// toActorRef.tell(downloadMsg, fromActorRef);
|
||||
// } catch (Exception e) {
|
||||
// logger.error("download error!", e);
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
// } finally {
|
||||
// releaseRequest(request, null);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
private void delete(ChannelHandlerContext ctx, FullHttpRequest request, String requestId) {
|
||||
// try {
|
||||
// final String httpUri = request.getUri();
|
||||
// String typedDownloadInfo = MetaDataIndexHandler.getTypedDownloadInfo(httpUri);
|
||||
// if (StringUtil.isNullOrEmpty(typedDownloadInfo)) {
|
||||
// logger.warn("invalid httpUri(" + httpUri + ")");
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "invalid httpUri");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// MetaDataIndex metadataIndex = MetaDataIndexHandler.parseTypedDownloadInfo(typedDownloadInfo);
|
||||
// if (metadataIndex == null) {
|
||||
// logger.warn("invalid typedDownloadInfo!(" + typedDownloadInfo + ")");
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.BAD_REQUEST, "invalid downloadFileName");
|
||||
// }
|
||||
//
|
||||
// ClusterNode toNode = StorageClusterRouterManager.getInstance().getClusterNode(metadataIndex.getClusterNode());
|
||||
// ClusterNode fromNode = StorageClusterRouterManager.getInstance().getSelfNode();
|
||||
// ActorSystem actorSystem = StorageClusterRouterManager.getInstance().getStorageActorSystem();
|
||||
// ActorRef toActorRef = actorSystem.actorOf(toNode.getIp(), toNode.getRpcPort(), ActorType.DeleteActor.getName());
|
||||
// ActorRef fromActorRef = actorSystem.actorOf(fromNode.getIp(),fromNode.getRpcPort(), ActorType.HttpFileServerActor.getName());
|
||||
//
|
||||
// HttpFileServerController.getInstance().mapChannelHandlerContext(requestId, ctx);
|
||||
//
|
||||
// DeleteMsg downloadMsg = new DeleteMsg();
|
||||
// downloadMsg.setRequestId(requestId);
|
||||
// downloadMsg.setIsKeepAlive(HttpHeaders.isKeepAlive(request));
|
||||
// downloadMsg.setClusterNode(metadataIndex.getClusterNode());
|
||||
// downloadMsg.setTime(metadataIndex.getTime());
|
||||
// downloadMsg.setBigFileIndex(metadataIndex.getBigFileIndex());
|
||||
// downloadMsg.setOffset(metadataIndex.getOffset());
|
||||
// downloadMsg.setMetaDataTotalLength(metadataIndex.getMetaDataTotalLength());
|
||||
// downloadMsg.setFileType(metadataIndex.getFileType());
|
||||
// downloadMsg.setBigFilePath(metadataIndex.getBigFilePath());
|
||||
// downloadMsg.setStorageEngineVersion(metadataIndex.getStorageEngineVersion());
|
||||
// toActorRef.tell(downloadMsg, fromActorRef);
|
||||
// } catch (Exception e) {
|
||||
// logger.error("delete error!", e);
|
||||
// HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
// } finally {
|
||||
// releaseRequest(request, null);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP能力探测
|
||||
*/
|
||||
private void doOptions(ChannelHandlerContext ctx, FullHttpRequest request) {
|
||||
try {
|
||||
boolean isKeepAlive = HttpHeaders.isKeepAlive(request);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.NO_CONTENT);
|
||||
response.headers().set(CONTENT_TYPE, HttpHeaders.Values.APPLICATION_JSON);
|
||||
response.headers().set(TRANSFER_ENCODING, "chunked");
|
||||
if (isKeepAlive)
|
||||
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, DELETE, OPTIONS");
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_HEADERS, "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization");
|
||||
|
||||
ChannelFuture future = ctx.channel().writeAndFlush(response);
|
||||
if (!isKeepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("doOptions error!", e);
|
||||
HttpResponseHelper.sendResponse("", ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
releaseRequest(request, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* releaseRequest
|
||||
*/
|
||||
private void releaseRequest(FullHttpRequest request, HttpPostRequestDecoder decoder) {
|
||||
try {
|
||||
if (request != null)
|
||||
request.release();
|
||||
|
||||
request = null;
|
||||
|
||||
if (decoder != null)
|
||||
decoder.destroy();
|
||||
|
||||
decoder = null;
|
||||
} catch (Exception e) {
|
||||
logger.error("reset error!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import com.xiaoleilu.hutool.util.FileUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_RANGE;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
|
||||
|
||||
public class HttpResponseHelper {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HttpResponseHelper.class);
|
||||
private static final MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();
|
||||
|
||||
/**
|
||||
* sendResponse
|
||||
*
|
||||
* @param ctx
|
||||
* @param status
|
||||
*/
|
||||
public static void sendResponse(String requestId, ChannelHandlerContext ctx, HttpResponseStatus status) {
|
||||
sendResponse(requestId, ctx, status, "status: " + status.toString() + "\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* sendResponse
|
||||
*
|
||||
* @param ctx
|
||||
* @param status
|
||||
* @param msg
|
||||
*/
|
||||
public static void sendResponse(String requestId, ChannelHandlerContext ctx, HttpResponseStatus status, String msg) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
|
||||
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
|
||||
removeChannelHandlerContext(requestId, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* sendResponse(成功应答需要注意isKeepAlive和isCloseChannel的值)
|
||||
*
|
||||
* @param requestId
|
||||
* @param ctx
|
||||
* @param status
|
||||
* @param contentType
|
||||
* @param respBodyString
|
||||
* @param isKeepAlive
|
||||
* @param isCloseChannel
|
||||
*/
|
||||
public static void sendResponse(String requestId, ChannelHandlerContext ctx, HttpResponseStatus status, String contentType, String respBodyString, boolean isKeepAlive, boolean isCloseChannel) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(respBodyString, CharsetUtil.UTF_8));
|
||||
if (!StringUtil.isNullOrEmpty(contentType)) {
|
||||
response.headers().set(CONTENT_TYPE, contentType);
|
||||
} else {
|
||||
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
}
|
||||
|
||||
if (isKeepAlive) {
|
||||
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
|
||||
ChannelFuture future = ctx.channel().writeAndFlush(response);
|
||||
if (!isKeepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
removeChannelHandlerContext(requestId, ctx, isCloseChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* sendResponse
|
||||
*
|
||||
* @param ctx
|
||||
* @param statusCode
|
||||
* @param resultDesc
|
||||
*/
|
||||
public static void sendResponse(String requestId, ChannelHandlerContext ctx, int statusCode, String resultDesc) {
|
||||
HttpResponseStatus httpResponseStatus = new HttpResponseStatus(statusCode, resultDesc);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, httpResponseStatus, Unpooled.copiedBuffer(resultDesc, CharsetUtil.UTF_8));
|
||||
response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
|
||||
removeChannelHandlerContext(requestId, ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 断点上传204应答
|
||||
*
|
||||
* @param ctx
|
||||
* @param contentRange
|
||||
*/
|
||||
public static void sendBrokenUpload204Resonse(String requestId, ChannelHandlerContext ctx, String contentRange, boolean isKeepAlive, boolean isCloseChannel) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.NO_CONTENT, Unpooled.copiedBuffer("", CharsetUtil.UTF_8));
|
||||
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
response.headers().set(CONTENT_RANGE, contentRange);
|
||||
|
||||
ChannelFuture future = ctx.channel().writeAndFlush(response);
|
||||
if (!isKeepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
removeChannelHandlerContext(requestId, ctx, isCloseChannel);
|
||||
}
|
||||
|
||||
|
||||
public static String getMultipartUploadResponseBody(String remoteFileName, String fileUrl) throws Exception {
|
||||
|
||||
return "{\"rc_url\":{\"path\":\"" + fileUrl + "\",\"type\":0}}";
|
||||
}
|
||||
|
||||
/**
|
||||
* getContentType
|
||||
*
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
|
||||
public static String getFileExt(String fileName) {
|
||||
int index = fileName.lastIndexOf(".");
|
||||
if (index == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return fileName.substring(index + 1).toLowerCase();
|
||||
}
|
||||
|
||||
public static String getContentType(String fileName) {
|
||||
String fileExtName = getFileExt(fileName);
|
||||
if (fileExtName.equals("png")) { // MimetypesFileTypeMap目前缺少png类型
|
||||
return "image/png";
|
||||
} else {
|
||||
return mimetypesFileTypeMap.getContentType(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* removeChannelHandlerContext
|
||||
*
|
||||
* @param requestId
|
||||
* @param ctx
|
||||
*/
|
||||
private static void removeChannelHandlerContext(String requestId, ChannelHandlerContext ctx) {
|
||||
removeChannelHandlerContext(requestId, ctx, true);
|
||||
}
|
||||
|
||||
private static void removeChannelHandlerContext(String requestId, ChannelHandlerContext ctx, boolean isCloseChannel) {
|
||||
try {
|
||||
if (!StringUtil.isNullOrEmpty(requestId)) {
|
||||
// HttpFileServerController.getInstance().removeChannelHandlerContext(requestId);
|
||||
}
|
||||
|
||||
if (isCloseChannel)
|
||||
ctx.channel().close();
|
||||
} catch (Exception e) {
|
||||
logger.error("close error!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import com.xiaoleilu.hutool.lang.Singleton;
|
||||
import com.xiaoleilu.loServer.ServerSetting;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.action.FileAction;
|
||||
import com.xiaoleilu.loServer.action.UnknownErrorAction;
|
||||
import com.xiaoleilu.loServer.filter.Filter;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Action处理单元
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class IMActionHandler extends ActionHandler {
|
||||
private static final Logger Logger = LoggerFactory.getLogger(IMActionHandler.class);
|
||||
|
||||
public IMActionHandler(IMessagesStore messagesStore, ISessionsStore sessionsStore) {
|
||||
super(messagesStore, sessionsStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isValidePath(String path) {
|
||||
if (!path.startsWith("/admin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.xiaoleilu.hutool.http.HttpUtil;
|
||||
import com.xiaoleilu.hutool.lang.Conver;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.hutool.util.CharsetUtil;
|
||||
import com.xiaoleilu.hutool.util.DateUtil;
|
||||
import com.xiaoleilu.hutool.util.StrUtil;
|
||||
import com.xiaoleilu.hutool.util.URLUtil;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
import io.netty.handler.codec.http.cookie.Cookie;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
|
||||
import io.netty.handler.codec.http.multipart.Attribute;
|
||||
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
|
||||
import io.netty.handler.codec.http.multipart.FileUpload;
|
||||
import io.netty.handler.codec.http.multipart.HttpDataFactory;
|
||||
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
|
||||
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
|
||||
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Http请求对象
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class Request {
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(Request.class);
|
||||
|
||||
public static final String METHOD_DELETE = HttpMethod.DELETE.name();
|
||||
public static final String METHOD_HEAD = HttpMethod.HEAD.name();
|
||||
public static final String METHOD_GET = HttpMethod.GET.name();
|
||||
public static final String METHOD_OPTIONS = HttpMethod.OPTIONS.name();
|
||||
public static final String METHOD_POST = HttpMethod.POST.name();
|
||||
public static final String METHOD_PUT = HttpMethod.PUT.name();
|
||||
public static final String METHOD_TRACE = HttpMethod.TRACE.name();
|
||||
|
||||
private static final HttpDataFactory HTTP_DATA_FACTORY = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
|
||||
|
||||
private FullHttpRequest nettyRequest;
|
||||
|
||||
private String path;
|
||||
private String ip;
|
||||
private Map<String, String> headers = new HashMap<String, String>();
|
||||
private Map<String, Object> params = new HashMap<String, Object>();
|
||||
private Map<String, Cookie> cookies = new HashMap<String, Cookie>();
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param ctx ChannelHandlerContext
|
||||
* @param nettyRequest HttpRequest
|
||||
*/
|
||||
private Request(ChannelHandlerContext ctx, FullHttpRequest nettyRequest) {
|
||||
this.nettyRequest = nettyRequest;
|
||||
final String uri = nettyRequest.uri();
|
||||
this.path = URLUtil.getPath(getUri());
|
||||
|
||||
this.putHeadersAndCookies(nettyRequest.headers());
|
||||
|
||||
// request URI parameters
|
||||
this.putParams(new QueryStringDecoder(uri));
|
||||
if(nettyRequest.method() != HttpMethod.GET){
|
||||
HttpPostRequestDecoder decoder = null;
|
||||
try {
|
||||
decoder = new HttpPostRequestDecoder(HTTP_DATA_FACTORY, nettyRequest);
|
||||
this.putParams(decoder);
|
||||
} finally {
|
||||
if(null != decoder){
|
||||
decoder.destroy();
|
||||
decoder = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IP
|
||||
this.putIp(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Netty的HttpRequest
|
||||
*/
|
||||
public HttpRequest getNettyRequest() {
|
||||
return this.nettyRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得版本信息
|
||||
*
|
||||
* @return 版本
|
||||
*/
|
||||
public String getProtocolVersion() {
|
||||
return nettyRequest.protocolVersion().text();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得URI(带参数的路径)
|
||||
*
|
||||
* @return URI
|
||||
*/
|
||||
public String getUri() {
|
||||
return nettyRequest.uri();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获得path(不带参数的路径)
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Http方法
|
||||
*
|
||||
* @return Http method
|
||||
*/
|
||||
public String getMethod() {
|
||||
return nettyRequest.method().name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得IP地址
|
||||
*
|
||||
* @return IP地址
|
||||
*/
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有头信息
|
||||
*
|
||||
* @return 头信息Map
|
||||
*/
|
||||
public Map<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用ISO8859_1字符集获得Header内容<br>
|
||||
* 由于Header中很少有中文,故一般情况下无需转码
|
||||
*
|
||||
* @param headerKey 头信息的KEY
|
||||
* @return 值
|
||||
*/
|
||||
public String getHeader(String headerKey) {
|
||||
return headers.get(headerKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否为普通表单(application/x-www-form-urlencoded)
|
||||
*/
|
||||
public boolean isXWwwFormUrlencoded() {
|
||||
return "application/x-www-form-urlencoded".equals(getHeader("Content-Type"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定的Cookie
|
||||
*
|
||||
* @param name cookie名
|
||||
* @return Cookie对象
|
||||
*/
|
||||
public Cookie getCookie(String name) {
|
||||
return cookies.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获得所有Cookie信息
|
||||
*/
|
||||
public Map<String, Cookie> getCookies() {
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 客户浏览器是否为IE
|
||||
*/
|
||||
public boolean isIE() {
|
||||
String userAgent = getHeader("User-Agent");
|
||||
if (StrUtil.isNotBlank(userAgent)) {
|
||||
userAgent = userAgent.toUpperCase();
|
||||
if (userAgent.contains("MSIE") || userAgent.contains("TRIDENT")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @return 获得请求参数
|
||||
*/
|
||||
public String getParam(String name) {
|
||||
final Object value = params.get(name);
|
||||
if(null == value){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(value instanceof String){
|
||||
return (String)value;
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @return 获得请求参数
|
||||
*/
|
||||
public Object getObjParam(String name) {
|
||||
return params.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得GET请求参数<br>
|
||||
* 会根据浏览器类型自动识别GET请求的编码方式从而解码<br>
|
||||
* charsetOfServlet为null则默认的ISO_8859_1
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param charset 字符集
|
||||
* @return 获得请求参数
|
||||
*/
|
||||
public String getParam(String name, Charset charset) {
|
||||
if (null == charset) {
|
||||
charset = Charset.forName(CharsetUtil.ISO_8859_1);
|
||||
}
|
||||
|
||||
String destCharset = CharsetUtil.UTF_8;
|
||||
if (isIE()) {
|
||||
// IE浏览器GET请求使用GBK编码
|
||||
destCharset = CharsetUtil.GBK;
|
||||
}
|
||||
|
||||
String value = getParam(name);
|
||||
if (METHOD_GET.equalsIgnoreCase(getMethod())) {
|
||||
value = CharsetUtil.convert(value, charset.toString(), destCharset);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得请求参数
|
||||
*/
|
||||
public String getParam(String name, String defaultValue) {
|
||||
String param = getParam(name);
|
||||
return StrUtil.isBlank(param) ? defaultValue : param;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得Integer类型请求参数
|
||||
*/
|
||||
public Integer getIntParam(String name, Integer defaultValue) {
|
||||
return Conver.toInt(getParam(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得long类型请求参数
|
||||
*/
|
||||
public Long getLongParam(String name, Long defaultValue) {
|
||||
return Conver.toLong(getParam(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得Double类型请求参数
|
||||
*/
|
||||
public Double getDoubleParam(String name, Double defaultValue) {
|
||||
return Conver.toDouble(getParam(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得Float类型请求参数
|
||||
*/
|
||||
public Float getFloatParam(String name, Float defaultValue) {
|
||||
return Conver.toFloat(getParam(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得Boolean类型请求参数
|
||||
*/
|
||||
public Boolean getBoolParam(String name, Boolean defaultValue) {
|
||||
return Conver.toBool(getParam(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式:<br>
|
||||
* 1、yyyy-MM-dd HH:mm:ss <br>
|
||||
* 2、yyyy-MM-dd <br>
|
||||
* 3、HH:mm:ss <br>
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得Date类型请求参数,默认格式:
|
||||
*/
|
||||
public Date getDateParam(String name, Date defaultValue) {
|
||||
String param = getParam(name);
|
||||
return StrUtil.isBlank(param) ? defaultValue : DateUtil.parse(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 参数名
|
||||
* @param format 格式
|
||||
* @param defaultValue 当客户端未传参的默认值
|
||||
* @return 获得Date类型请求参数
|
||||
*/
|
||||
public Date getDateParam(String name, String format, Date defaultValue) {
|
||||
String param = getParam(name);
|
||||
return StrUtil.isBlank(param) ? defaultValue : DateUtil.parse(param, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求参数<br>
|
||||
* 列表类型值,常用于表单中的多选框
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return 数组
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> getArrayParam(String name) {
|
||||
Object value = params.get(name);
|
||||
if(null == value){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(value instanceof List){
|
||||
return (List<String>) value;
|
||||
}else if(value instanceof String){
|
||||
return StrUtil.split((String)value, ',');
|
||||
}else{
|
||||
throw new RuntimeException("Value is not a List type!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @return Map
|
||||
*/
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否为长连接
|
||||
*/
|
||||
public boolean isKeepAlive() {
|
||||
final String connectionHeader = getHeader(HttpHeaderNames.CONNECTION.toString());
|
||||
// 无论任何版本Connection为close时都关闭连接
|
||||
if (HttpHeaderValues.CLOSE.toString().equalsIgnoreCase(connectionHeader)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// HTTP/1.0只有Connection为Keep-Alive时才会保持连接
|
||||
if (HttpVersion.HTTP_1_0.text().equals(getProtocolVersion())) {
|
||||
if (false == HttpHeaderValues.KEEP_ALIVE.toString().equalsIgnoreCase(connectionHeader)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// HTTP/1.1默认打开Keep-Alive
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- Protected method start
|
||||
/**
|
||||
* 填充参数(GET请求的参数)
|
||||
*
|
||||
* @param decoder QueryStringDecoder
|
||||
*/
|
||||
protected void putParams(QueryStringDecoder decoder) {
|
||||
if (null != decoder) {
|
||||
List<String> valueList;
|
||||
for (Entry<String, List<String>> entry : decoder.parameters().entrySet()) {
|
||||
valueList = entry.getValue();
|
||||
if(null != valueList){
|
||||
this.putParam(entry.getKey(), 1 == valueList.size() ? valueList.get(0) : valueList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充参数(POST请求的参数)
|
||||
*
|
||||
* @param decoder QueryStringDecoder
|
||||
*/
|
||||
protected void putParams(HttpPostRequestDecoder decoder) {
|
||||
if (null == decoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (InterfaceHttpData data : decoder.getBodyHttpDatas()) {
|
||||
putParam(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充参数
|
||||
*
|
||||
* @param data InterfaceHttpData
|
||||
*/
|
||||
protected void putParam(InterfaceHttpData data) {
|
||||
final HttpDataType dataType = data.getHttpDataType();
|
||||
if (dataType == HttpDataType.Attribute) {
|
||||
//普通参数
|
||||
Attribute attribute = (Attribute) data;
|
||||
try {
|
||||
this.putParam(attribute.getName(), attribute.getValue());
|
||||
} catch (IOException e) {
|
||||
Logger.error(e.toString());
|
||||
}
|
||||
}else if(dataType == HttpDataType.FileUpload){
|
||||
//文件
|
||||
FileUpload fileUpload = (FileUpload) data;
|
||||
if(fileUpload.isCompleted()){
|
||||
try {
|
||||
this.putParam(data.getName(), fileUpload.getFile());
|
||||
} catch (IOException e) {
|
||||
Logger.error(e.toString(), "Get file param [{}] error!", data.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充参数
|
||||
*
|
||||
* @param key 参数名
|
||||
* @param value 参数值
|
||||
*/
|
||||
protected void putParam(String key, Object value) {
|
||||
this.params.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充头部信息和Cookie信息
|
||||
*
|
||||
* @param headers HttpHeaders
|
||||
*/
|
||||
protected void putHeadersAndCookies(HttpHeaders headers) {
|
||||
for (Entry<String, String> entry : headers) {
|
||||
this.headers.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// Cookie
|
||||
final String cookieString = this.headers.get(HttpHeaderNames.COOKIE);
|
||||
if (StrUtil.isNotBlank(cookieString)) {
|
||||
final Set<Cookie> cookies = ServerCookieDecoder.LAX.decode(cookieString);
|
||||
for (Cookie cookie : cookies) {
|
||||
this.cookies.put(cookie.name(), cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置客户端IP
|
||||
*
|
||||
* @param ctx ChannelHandlerContext
|
||||
*/
|
||||
protected void putIp(ChannelHandlerContext ctx) {
|
||||
String ip = getHeader("X-Forwarded-For");
|
||||
if (StrUtil.isNotBlank(ip)) {
|
||||
ip = HttpUtil.getMultistageReverseProxyIp(ip);
|
||||
} else {
|
||||
final InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
|
||||
ip = insocket.getAddress().getHostAddress();
|
||||
}
|
||||
this.ip = ip;
|
||||
}
|
||||
// --------------------------------------------------------- Protected method end
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("\r\nprotocolVersion: ").append(getProtocolVersion()).append("\r\n");
|
||||
sb.append("uri: ").append(getUri()).append("\r\n");
|
||||
sb.append("path: ").append(path).append("\r\n");
|
||||
sb.append("method: ").append(getMethod()).append("\r\n");
|
||||
sb.append("ip: ").append(ip).append("\r\n");
|
||||
sb.append("headers:\r\n ");
|
||||
for (Entry<String, String> entry : headers.entrySet()) {
|
||||
sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
|
||||
}
|
||||
sb.append("params: \r\n");
|
||||
for (Entry<String, Object> entry : params.entrySet()) {
|
||||
sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Request对象
|
||||
*
|
||||
* @param ctx ChannelHandlerContext
|
||||
* @param nettyRequest Netty的HttpRequest
|
||||
* @return Request
|
||||
*/
|
||||
protected final static Request build(ChannelHandlerContext ctx, FullHttpRequest nettyRequest) {
|
||||
return new Request(ctx, nettyRequest);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,592 @@
|
||||
package com.xiaoleilu.loServer.handler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.xiaoleilu.hutool.http.HttpUtil;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
import com.xiaoleilu.hutool.util.CharsetUtil;
|
||||
import com.xiaoleilu.hutool.util.DateUtil;
|
||||
import com.xiaoleilu.hutool.util.StrUtil;
|
||||
import com.xiaoleilu.loServer.ServerSetting;
|
||||
import com.xiaoleilu.loServer.listener.FileProgressiveFutureListener;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.DefaultFileRegion;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import io.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import io.netty.handler.codec.http.cookie.Cookie;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 响应对象
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class Response {
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(Response.class);
|
||||
|
||||
/** 返回内容类型:普通文本 */
|
||||
public final static String CONTENT_TYPE_TEXT = "text/plain";
|
||||
/** 返回内容类型:HTML */
|
||||
public final static String CONTENT_TYPE_HTML = "text/html";
|
||||
/** 返回内容类型:XML */
|
||||
public final static String CONTENT_TYPE_XML = "text/xml";
|
||||
/** 返回内容类型:JAVASCRIPT */
|
||||
public final static String CONTENT_TYPE_JAVASCRIPT = "application/javascript";
|
||||
/** 返回内容类型:JSON */
|
||||
public final static String CONTENT_TYPE_JSON = "application/json";
|
||||
public final static String CONTENT_TYPE_JSON_IE = "text/json";
|
||||
|
||||
private ChannelHandlerContext ctx;
|
||||
private Request request;
|
||||
|
||||
private HttpVersion httpVersion = HttpVersion.HTTP_1_1;
|
||||
private HttpResponseStatus status = HttpResponseStatus.OK;
|
||||
private String contentType = CONTENT_TYPE_HTML;
|
||||
private String charset = ServerSetting.getCharset();
|
||||
private HttpHeaders headers = new DefaultHttpHeaders();
|
||||
private Set<Cookie> cookies = new HashSet<Cookie>();
|
||||
private Object content = Unpooled.EMPTY_BUFFER;
|
||||
//发送完成标记
|
||||
private boolean isSent;
|
||||
|
||||
public Response(ChannelHandlerContext ctx, Request request) {
|
||||
this.ctx = ctx;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应的Http版本号
|
||||
*
|
||||
* @param httpVersion http版本号对象
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setHttpVersion(HttpVersion httpVersion) {
|
||||
this.httpVersion = httpVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应状态码<br>
|
||||
* 使用io.netty.handler.codec.http.HttpResponseStatus对象
|
||||
*
|
||||
* @param status 状态码
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setStatus(HttpResponseStatus status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应状态码
|
||||
*
|
||||
* @param status 状态码
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setStatus(int status) {
|
||||
return setStatus(HttpResponseStatus.valueOf(status));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Content-Type
|
||||
*
|
||||
* @param contentType Content-Type
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置返回内容的字符集编码
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加响应的Header<br>
|
||||
* 重复的Header将被叠加
|
||||
*
|
||||
* @param name 名
|
||||
* @param value 值,可以是String,Date, int
|
||||
* @return 自己
|
||||
*/
|
||||
public Response addHeader(String name, Object value) {
|
||||
headers.add(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应的Header<br>
|
||||
* 重复的Header将被替换
|
||||
*
|
||||
* @param name 名
|
||||
* @param value 值,可以是String,Date, int
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setHeader(String name, Object value) {
|
||||
headers.set(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应体长度
|
||||
*
|
||||
* @param contentLength 响应体长度
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setContentLength(long contentLength) {
|
||||
setHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), contentLength);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否长连接
|
||||
*
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setKeepAlive() {
|
||||
setHeader(HttpHeaderNames.CONNECTION.toString(), HttpHeaderValues.KEEP_ALIVE.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- Cookie start
|
||||
/**
|
||||
* 设定返回给客户端的Cookie
|
||||
*
|
||||
* @param cookie
|
||||
* @return 自己
|
||||
*/
|
||||
public Response addCookie(Cookie cookie) {
|
||||
cookies.add(cookie);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设定返回给客户端的Cookie
|
||||
*
|
||||
* @param name Cookie名
|
||||
* @param value Cookie值
|
||||
* @return 自己
|
||||
*/
|
||||
public Response addCookie(String name, String value) {
|
||||
return addCookie(new DefaultCookie(name, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设定返回给客户端的Cookie
|
||||
*
|
||||
* @param name cookie名
|
||||
* @param value cookie值
|
||||
* @param maxAgeInSeconds -1: 关闭浏览器清除Cookie. 0: 立即清除Cookie. n>0 : Cookie存在的秒数.
|
||||
* @param path Cookie的有效路径
|
||||
* @param domain the Cookie可见的域,依据 RFC 2109 标准
|
||||
* @return 自己
|
||||
*/
|
||||
public Response addCookie(String name, String value, int maxAgeInSeconds, String path, String domain) {
|
||||
Cookie cookie = new DefaultCookie(name, value);
|
||||
if (domain != null) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
cookie.setMaxAge(maxAgeInSeconds);
|
||||
cookie.setPath(path);
|
||||
return addCookie(cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设定返回给客户端的Cookie<br>
|
||||
* Path: "/"<br>
|
||||
* No Domain
|
||||
*
|
||||
* @param name cookie名
|
||||
* @param value cookie值
|
||||
* @param maxAgeInSeconds -1: 关闭浏览器清除Cookie. 0: 立即清除Cookie. n>0 : Cookie存在的秒数.
|
||||
* @return 自己
|
||||
*/
|
||||
public Response addCookie(String name, String value, int maxAgeInSeconds) {
|
||||
return addCookie(name, value, maxAgeInSeconds, "/", null);
|
||||
}
|
||||
// --------------------------------------------------------- Cookie end
|
||||
|
||||
/**
|
||||
* 设置响应HTML文本内容
|
||||
*
|
||||
* @param contentText 响应的文本
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setContent(String contentText) {
|
||||
this.content = Unpooled.copiedBuffer(contentText, Charset.forName(charset));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应文本内容
|
||||
*
|
||||
* @param contentText 响应的文本
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setTextContent(String contentText) {
|
||||
setContentType(CONTENT_TYPE_TEXT);
|
||||
return setContent(contentText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应JSON文本内容
|
||||
*
|
||||
* @param contentText 响应的JSON文本
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setJsonContent(String contentText) {
|
||||
setContentType(request.isIE() ? CONTENT_TYPE_JSON : CONTENT_TYPE_JSON);
|
||||
return setContent(contentText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应XML文本内容
|
||||
*
|
||||
* @param contentText 响应的XML文本
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setXmlContent(String contentText) {
|
||||
setContentType(CONTENT_TYPE_XML);
|
||||
return setContent(contentText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应文本内容
|
||||
*
|
||||
* @param contentBytes 响应的字节
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setContent(byte[] contentBytes) {
|
||||
return setContent(Unpooled.copiedBuffer(contentBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应文本内容
|
||||
*
|
||||
* @param byteBuf 响应的字节
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setContent(ByteBuf byteBuf) {
|
||||
this.content = byteBuf;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应到客户端的文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 自己
|
||||
*/
|
||||
public Response setContent(File file) {
|
||||
this.content = file;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Date and Cache headers for the HTTP Response
|
||||
*
|
||||
* @param response HTTP response
|
||||
* @param fileToCache file to extract content type
|
||||
*/
|
||||
/**
|
||||
* 设置日期和过期时间
|
||||
*
|
||||
* @param lastModify 上一次修改时间
|
||||
* @param httpCacheSeconds 缓存时间,单位秒
|
||||
*/
|
||||
public void setDateAndCache(long lastModify, int httpCacheSeconds) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat(DateUtil.HTTP_DATETIME_PATTERN, Locale.US);
|
||||
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
// Date header
|
||||
Calendar time = new GregorianCalendar();
|
||||
setHeader(HttpHeaderNames.DATE.toString(), formatter.format(time.getTime()));
|
||||
|
||||
// Add cache headers
|
||||
time.add(Calendar.SECOND, httpCacheSeconds);
|
||||
|
||||
setHeader(HttpHeaderNames.EXPIRES.toString(), formatter.format(time.getTime()));
|
||||
setHeader(HttpHeaderNames.CACHE_CONTROL.toString(), "private, max-age=" + httpCacheSeconds);
|
||||
setHeader(HttpHeaderNames.LAST_MODIFIED.toString(), formatter.format(DateUtil.date(lastModify)));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------- build HttpResponse start
|
||||
/**
|
||||
* 转换为Netty所用Response<br>
|
||||
* 不包括content,一般用于返回文件类型的响应
|
||||
*
|
||||
* @return DefaultHttpResponse
|
||||
*/
|
||||
private DefaultHttpResponse toDefaultHttpResponse() {
|
||||
final DefaultHttpResponse defaultHttpResponse = new DefaultHttpResponse(httpVersion, status);
|
||||
|
||||
// headers
|
||||
final HttpHeaders httpHeaders = defaultHttpResponse.headers().add(headers);
|
||||
|
||||
// Cookies
|
||||
for (Cookie cookie : cookies) {
|
||||
httpHeaders.add(HttpHeaderNames.SET_COOKIE.toString(), ServerCookieEncoder.LAX.encode(cookie));
|
||||
}
|
||||
|
||||
return defaultHttpResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Netty所用Response<br>
|
||||
* 用于返回一般类型响应(文本)
|
||||
*
|
||||
* @return FullHttpResponse
|
||||
*/
|
||||
private FullHttpResponse toFullHttpResponse() {
|
||||
final ByteBuf byteBuf = (ByteBuf)content;
|
||||
final FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(httpVersion, status, byteBuf);
|
||||
|
||||
// headers
|
||||
final HttpHeaders httpHeaders = fullHttpResponse.headers().add(headers);
|
||||
if ("application/octet-stream".equals(contentType)) {
|
||||
httpHeaders.set(HttpHeaderNames.CONTENT_TYPE.toString(), contentType);
|
||||
} else {
|
||||
httpHeaders.set(HttpHeaderNames.CONTENT_TYPE.toString(), StrUtil.format("{};charset={}", contentType, charset));
|
||||
httpHeaders.set(HttpHeaderNames.CONTENT_ENCODING.toString(), charset);
|
||||
}
|
||||
|
||||
httpHeaders.set(HttpHeaderNames.CONTENT_LENGTH.toString(), byteBuf.readableBytes());
|
||||
|
||||
// Cookies
|
||||
for (Cookie cookie : cookies) {
|
||||
httpHeaders.add(HttpHeaderNames.SET_COOKIE.toString(), ServerCookieEncoder.LAX.encode(cookie));
|
||||
}
|
||||
|
||||
return fullHttpResponse;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------- build HttpResponse end
|
||||
|
||||
// -------------------------------------------------------------------------------------- send start
|
||||
/**
|
||||
* 发送响应到客户端<br>
|
||||
*
|
||||
* @return ChannelFuture
|
||||
* @throws IOException
|
||||
*/
|
||||
public ChannelFuture send() {
|
||||
ChannelFuture channelFuture;
|
||||
if(content instanceof File){
|
||||
//文件
|
||||
File file = (File)content;
|
||||
try {
|
||||
channelFuture = sendFile(file);
|
||||
} catch (IOException e) {
|
||||
Logger.error(StrUtil.format("Send {} error!", file), e.toString());
|
||||
channelFuture = sendError(HttpResponseStatus.FORBIDDEN, "");
|
||||
}
|
||||
}else{
|
||||
//普通文本
|
||||
channelFuture = sendFull();
|
||||
}
|
||||
|
||||
this.isSent = true;
|
||||
return channelFuture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否已经出发发送请求,内部使用<br>
|
||||
*/
|
||||
protected boolean isSent(){
|
||||
return this.isSent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送响应到客户端
|
||||
*
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
private ChannelFuture sendFull() {
|
||||
if (request != null && request.isKeepAlive()) {
|
||||
setKeepAlive();
|
||||
return ctx.writeAndFlush(this.toFullHttpResponse());
|
||||
} else {
|
||||
return sendAndCloseFull();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送给到客户端并关闭ChannelHandlerContext
|
||||
*
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
private ChannelFuture sendAndCloseFull() {
|
||||
return ctx.writeAndFlush(this.toFullHttpResponse()).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @return ChannelFuture
|
||||
* @throws IOException
|
||||
*/
|
||||
private ChannelFuture sendFile(File file) throws IOException {
|
||||
final RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||
|
||||
// 内容长度
|
||||
long fileLength = raf.length();
|
||||
this.setContentLength(fileLength);
|
||||
|
||||
//文件类型
|
||||
String contentType = HttpUtil.getMimeType(file.getName());
|
||||
if(StrUtil.isBlank(contentType)){
|
||||
//无法识别默认使用数据流
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
this.setContentType(contentType);
|
||||
|
||||
ctx.write(this.toDefaultHttpResponse());
|
||||
ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise())
|
||||
.addListener(FileProgressiveFutureListener.build(raf));
|
||||
|
||||
return sendEmptyLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送结尾标记,表示发送结束
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
private ChannelFuture sendEmptyLast(){
|
||||
final ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
if (false == request.isKeepAlive()) {
|
||||
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
|
||||
return lastContentFuture;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------- send end
|
||||
|
||||
// ---------------------------------------------------------------------------- special response start
|
||||
|
||||
/**
|
||||
* 302 重定向
|
||||
*
|
||||
* @param uri 重定向到的URI
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
public ChannelFuture sendRedirect(String uri) {
|
||||
return this.setStatus(HttpResponseStatus.FOUND).setHeader(HttpHeaderNames.LOCATION.toString(), uri).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* 304 文件未修改
|
||||
*
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
public ChannelFuture sendNotModified() {
|
||||
return this.setStatus(HttpResponseStatus.NOT_MODIFIED).setHeader(HttpHeaderNames.DATE.toString(), DateUtil.formatHttpDate(DateUtil.date())).send();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误消息
|
||||
*
|
||||
* @param status 错误状态码
|
||||
* @param msg 消息内容
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
public ChannelFuture sendError(HttpResponseStatus status, String msg) {
|
||||
if (ctx.channel().isActive()) {
|
||||
return this.setStatus(status).setContent(msg).send();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送404 Not Found
|
||||
*
|
||||
* @param msg 消息内容
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
public ChannelFuture sendNotFound(String msg) {
|
||||
return sendError(HttpResponseStatus.NOT_FOUND, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送500 Internal Server Error
|
||||
*
|
||||
* @param msg 消息内容
|
||||
* @return ChannelFuture
|
||||
*/
|
||||
public ChannelFuture sendServerError(String msg) {
|
||||
return sendError(HttpResponseStatus.INTERNAL_SERVER_ERROR, msg);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------- special response end
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("headers:\r\n ");
|
||||
for ( Entry<String, String> entry : headers.entries()) {
|
||||
sb.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
|
||||
}
|
||||
sb.append("content: ").append(StrUtil.str(content, CharsetUtil.UTF_8));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------- static method start
|
||||
/**
|
||||
* 构建Response对象
|
||||
*
|
||||
* @param ctx ChannelHandlerContext
|
||||
* @param request 请求对象
|
||||
* @return Response对象
|
||||
*/
|
||||
protected static Response build(ChannelHandlerContext ctx, Request request) {
|
||||
return new Response(ctx, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Response对象,Request对象为空,将无法获得某些信息<br>
|
||||
* 1. 无法使用长连接
|
||||
*
|
||||
* @param ctx ChannelHandlerContext
|
||||
* @return Response对象
|
||||
*/
|
||||
protected static Response build(ChannelHandlerContext ctx) {
|
||||
return new Response(ctx, null);
|
||||
}
|
||||
// ---------------------------------------------------------------------------- static method end
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.xiaoleilu.loServer.listener;
|
||||
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import com.xiaoleilu.hutool.io.IoUtil;
|
||||
import com.xiaoleilu.hutool.log.Log;
|
||||
import com.xiaoleilu.hutool.log.StaticLog;
|
||||
|
||||
import io.netty.channel.ChannelProgressiveFuture;
|
||||
import io.netty.channel.ChannelProgressiveFutureListener;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 文件进度指示监听
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class FileProgressiveFutureListener implements ChannelProgressiveFutureListener{
|
||||
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(FileProgressiveFutureListener.class);
|
||||
|
||||
private RandomAccessFile raf;
|
||||
|
||||
public FileProgressiveFutureListener(RandomAccessFile raf) {
|
||||
this.raf = raf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
|
||||
Logger.debug("Transfer progress: {} / {}", progress, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationComplete(ChannelProgressiveFuture future) {
|
||||
IoUtil.close(raf);
|
||||
Logger.debug("Transfer complete.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建文件进度指示监听
|
||||
* @param raf RandomAccessFile
|
||||
* @return 文件进度指示监听
|
||||
*/
|
||||
public static FileProgressiveFutureListener build(RandomAccessFile raf){
|
||||
return new FileProgressiveFutureListener(raf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class FriendData implements Serializable {
|
||||
private String userId;
|
||||
private String friendUid;
|
||||
private String alias;
|
||||
private int state;
|
||||
private long timestamp;
|
||||
|
||||
|
||||
public FriendData(String userId, String friendUid, String alias, int state, long timestamp) {
|
||||
this.userId = userId;
|
||||
this.friendUid = friendUid;
|
||||
this.alias = alias;
|
||||
this.state = state;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public FriendData() {
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getFriendUid() {
|
||||
return friendUid;
|
||||
}
|
||||
|
||||
public void setFriendUid(String friendUid) {
|
||||
this.friendUid = friendUid;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
public class Conversation {
|
||||
private int type;
|
||||
private String target;
|
||||
private int line;
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(String target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public void setLine(int line) {
|
||||
this.line = line;
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.moquette.BrokerConstants;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GroupNotificationBinaryContent {
|
||||
//groupId
|
||||
private String g;
|
||||
|
||||
//operator
|
||||
private String o;
|
||||
|
||||
//name
|
||||
private String n;
|
||||
|
||||
//members
|
||||
private List<String> ms;
|
||||
|
||||
//members
|
||||
private String m;
|
||||
|
||||
public GroupNotificationBinaryContent(String g, String o, String n, String m) {
|
||||
this.o = o;
|
||||
this.n = n;
|
||||
this.m = m;
|
||||
}
|
||||
|
||||
public GroupNotificationBinaryContent(String g, String operator, String name, List<String> members) {
|
||||
this.o = operator;
|
||||
this.n = name;
|
||||
this.ms = members;
|
||||
}
|
||||
|
||||
public String getG() {
|
||||
return g;
|
||||
}
|
||||
|
||||
public void setG(String g) {
|
||||
this.g = g;
|
||||
}
|
||||
|
||||
public String getM() {
|
||||
return m;
|
||||
}
|
||||
|
||||
public void setM(String m) {
|
||||
this.m = m;
|
||||
}
|
||||
|
||||
public String getO() {
|
||||
return o;
|
||||
}
|
||||
|
||||
public void setO(String o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
public String getN() {
|
||||
return n;
|
||||
}
|
||||
|
||||
public void setN(String n) {
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
public List<String> getMs() {
|
||||
return ms;
|
||||
}
|
||||
|
||||
public void setMs(List<String> ms) {
|
||||
this.ms = ms;
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getGroupNotifyContent(int groupContentType) {
|
||||
return WFCMessage.MessageContent.newBuilder().setType(groupContentType).setData(ByteString.copyFromUtf8(new Gson().toJson(this))).build();
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getAddGroupNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_ADD_GROUP_MEMBER);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getCreateGroupNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_CREATE_GROUP);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getDismissGroupNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_DISMISS_GROUP);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getKickokfMemberGroupNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_KICKOF_GROUP_MEMBER);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getQuitGroupNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_QUIT_GROUP);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getTransferGroupNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_TRANSFER_GROUP_OWNER);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getChangeGroupNameNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_CHANGE_GROUP_NAME);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getChangeGroupPortraitNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_CHANGE_GROUP_PORTRAIT);
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent getModifyGroupMemberAliasNotifyContent() {
|
||||
return getGroupNotifyContent(BrokerConstants.MESSAGE_CONTENT_TYPE_MODIFY_GROUP_ALIAS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InputAddGroupMember extends InputGroupBase {
|
||||
private String group_id;
|
||||
private List<PojoGroupMember> members;
|
||||
|
||||
public String getGroup_id() {
|
||||
return group_id;
|
||||
}
|
||||
|
||||
public void setGroup_id(String group_id) {
|
||||
this.group_id = group_id;
|
||||
}
|
||||
|
||||
public List<PojoGroupMember> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
public void setMembers(List<PojoGroupMember> members) {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
public boolean isValide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public WFCMessage.AddGroupMemberRequest toProtoGroupRequest() {
|
||||
WFCMessage.AddGroupMemberRequest.Builder addGroupBuilder = WFCMessage.AddGroupMemberRequest.newBuilder();
|
||||
addGroupBuilder.setGroupId(group_id);
|
||||
for (PojoGroupMember pojoGroupMember : getMembers()) {
|
||||
WFCMessage.GroupMember.Builder groupMemberBuilder = WFCMessage.GroupMember.newBuilder().setMemberId(pojoGroupMember.getMember_id());
|
||||
if (!StringUtil.isNullOrEmpty(pojoGroupMember.getAlias())) {
|
||||
groupMemberBuilder.setAlias(pojoGroupMember.getAlias());
|
||||
}
|
||||
groupMemberBuilder.setType(pojoGroupMember.getType());
|
||||
addGroupBuilder.addAddedMember(groupMemberBuilder);
|
||||
}
|
||||
|
||||
for (Integer line : to_lines
|
||||
) {
|
||||
addGroupBuilder.addToLine(line);
|
||||
}
|
||||
|
||||
addGroupBuilder.setNotifyContent(notify_message.toProtoMessageContent());
|
||||
return addGroupBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.ProtoConstants;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
public class InputCreateChatroom {
|
||||
private String chatroomId;
|
||||
private String title;
|
||||
private String desc;
|
||||
private String portrait;
|
||||
private String extra;
|
||||
private Integer state;
|
||||
|
||||
public WFCMessage.ChatroomInfo toChatroomInfo() {
|
||||
WFCMessage.ChatroomInfo.Builder builder = WFCMessage.ChatroomInfo.newBuilder().setTitle(title);
|
||||
if (!StringUtil.isNullOrEmpty(desc)) {
|
||||
builder.setDesc(desc);
|
||||
}
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(portrait)) {
|
||||
builder.setPortrait(portrait);
|
||||
}
|
||||
|
||||
long current = System.currentTimeMillis();
|
||||
builder.setCreateDt(current).setUpdateDt(current).setMemberCount(0);
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(extra)) {
|
||||
builder.setExtra(extra);
|
||||
}
|
||||
|
||||
if (state == null || state == 0) {
|
||||
builder.setState(ProtoConstants.ChatroomState.Chatroom_State_Normal);
|
||||
} else if(state == 1) {
|
||||
builder.setState(ProtoConstants.ChatroomState.Chatroom_State_NotStart);
|
||||
} else {
|
||||
builder.setState(ProtoConstants.ChatroomState.Chatroom_State_End);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
public InputCreateChatroom() {
|
||||
}
|
||||
|
||||
public Integer getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(Integer state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getChatroomId() {
|
||||
return chatroomId;
|
||||
}
|
||||
|
||||
public void setChatroomId(String chatroomId) {
|
||||
this.chatroomId = chatroomId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getPortrait() {
|
||||
return portrait;
|
||||
}
|
||||
|
||||
public void setPortrait(String portrait) {
|
||||
this.portrait = portrait;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InputCreateGroup extends InputGroupBase {
|
||||
private PojoGroup group;
|
||||
|
||||
public boolean isValide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public WFCMessage.CreateGroupRequest toProtoGroupRequest() {
|
||||
WFCMessage.Group.Builder groupBuilder = WFCMessage.Group.newBuilder();
|
||||
WFCMessage.GroupInfo.Builder groupInfoBuilder = WFCMessage.GroupInfo.newBuilder();
|
||||
if (!StringUtil.isNullOrEmpty(group.getGroup_info().target_id)) {
|
||||
groupInfoBuilder.setTargetId(group.getGroup_info().getTarget_id());
|
||||
}
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(group.getGroup_info().name)) {
|
||||
groupInfoBuilder.setName(group.getGroup_info().getName());
|
||||
}
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(group.getGroup_info().portrait)) {
|
||||
groupInfoBuilder.setPortrait(group.getGroup_info().getPortrait());
|
||||
}
|
||||
if (!StringUtil.isNullOrEmpty(group.getGroup_info().owner)) {
|
||||
groupInfoBuilder.setOwner(group.getGroup_info().getOwner());
|
||||
}
|
||||
|
||||
groupInfoBuilder.setType(group.getGroup_info().getType());
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(group.getGroup_info().extra)) {
|
||||
groupInfoBuilder.setExtra(group.getGroup_info().getExtra());
|
||||
}
|
||||
|
||||
|
||||
groupBuilder.setGroupInfo(groupInfoBuilder);
|
||||
for (PojoGroupMember pojoGroupMember : group.getMembers()) {
|
||||
WFCMessage.GroupMember.Builder groupMemberBuilder = WFCMessage.GroupMember.newBuilder().setMemberId(pojoGroupMember.getMember_id());
|
||||
if (!StringUtil.isNullOrEmpty(pojoGroupMember.getAlias())) {
|
||||
groupMemberBuilder.setAlias(pojoGroupMember.getAlias());
|
||||
}
|
||||
groupMemberBuilder.setType(pojoGroupMember.getType());
|
||||
groupBuilder.addMembers(groupMemberBuilder);
|
||||
}
|
||||
|
||||
WFCMessage.CreateGroupRequest.Builder createGroupReqBuilder = WFCMessage.CreateGroupRequest.newBuilder();
|
||||
createGroupReqBuilder.setGroup(groupBuilder);
|
||||
for (Integer line : to_lines
|
||||
) {
|
||||
createGroupReqBuilder.addToLine(line);
|
||||
}
|
||||
|
||||
createGroupReqBuilder.setNotifyContent(notify_message.toProtoMessageContent());
|
||||
return createGroupReqBuilder.build();
|
||||
}
|
||||
|
||||
public PojoGroup getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(PojoGroup group) {
|
||||
this.group = group;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.ProtoConstants;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
public class InputCreateRobot {
|
||||
private String userId;
|
||||
private String name;
|
||||
private String password;
|
||||
private String displayName;
|
||||
private String portrait;
|
||||
private int gender;
|
||||
private String mobile;
|
||||
private String email;
|
||||
private String address;
|
||||
private String company;
|
||||
private String social;
|
||||
private String extra;
|
||||
private long updateDt;
|
||||
|
||||
|
||||
private String owner;
|
||||
private String secret;
|
||||
private String callback;
|
||||
private String robotExtra;
|
||||
|
||||
public String getSocial() {
|
||||
return social;
|
||||
}
|
||||
|
||||
public void setSocial(String social) {
|
||||
this.social = social;
|
||||
}
|
||||
|
||||
public WFCMessage.Robot toRobot() {
|
||||
WFCMessage.Robot.Builder builder = WFCMessage.Robot.newBuilder();
|
||||
if (!StringUtil.isNullOrEmpty(userId))
|
||||
builder.setUid(userId);
|
||||
if (!StringUtil.isNullOrEmpty(owner))
|
||||
builder.setOwner(owner);
|
||||
if (!StringUtil.isNullOrEmpty(secret))
|
||||
builder.setSecret(secret);
|
||||
if (!StringUtil.isNullOrEmpty(callback))
|
||||
builder.setCallback(callback);
|
||||
if (!StringUtil.isNullOrEmpty(robotExtra))
|
||||
builder.setExtra(robotExtra);
|
||||
builder.setState(0);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public WFCMessage.User toUser() {
|
||||
WFCMessage.User.Builder newUserBuilder = WFCMessage.User.newBuilder()
|
||||
.setUid(userId);
|
||||
if (name != null)
|
||||
newUserBuilder.setName(name);
|
||||
if (displayName != null)
|
||||
newUserBuilder.setDisplayName(displayName);
|
||||
if (getPortrait() != null)
|
||||
newUserBuilder.setPortrait(getPortrait());
|
||||
if (getEmail() != null)
|
||||
newUserBuilder.setEmail(getEmail());
|
||||
if (getAddress() != null)
|
||||
newUserBuilder.setAddress(getAddress());
|
||||
if (getCompany() != null)
|
||||
newUserBuilder.setCompany(getCompany());
|
||||
if (getSocial() != null)
|
||||
newUserBuilder.setSocial(getSocial());
|
||||
|
||||
if (getMobile() != null)
|
||||
newUserBuilder.setMobile(getMobile());
|
||||
if (getExtra() != null)
|
||||
newUserBuilder.setExtra(getExtra());
|
||||
newUserBuilder.setType(ProtoConstants.UserType.UserType_Robot);
|
||||
newUserBuilder.setGender(gender);
|
||||
|
||||
newUserBuilder.setUpdateDt(System.currentTimeMillis());
|
||||
return newUserBuilder.build();
|
||||
}
|
||||
|
||||
public long getUpdateDt() {
|
||||
return updateDt;
|
||||
}
|
||||
|
||||
public void setUpdateDt(long updateDt) {
|
||||
this.updateDt = updateDt;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getPortrait() {
|
||||
return portrait;
|
||||
}
|
||||
|
||||
public void setPortrait(String portrait) {
|
||||
this.portrait = portrait;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
}
|
||||
|
||||
public void setCompany(String company) {
|
||||
this.company = company;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public int getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(int gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public String getCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
public void setCallback(String callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public String getRobotExtra() {
|
||||
return robotExtra;
|
||||
}
|
||||
|
||||
public void setRobotExtra(String robotExtra) {
|
||||
this.robotExtra = robotExtra;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
|
||||
public class InputCreateUser {
|
||||
private String userId;
|
||||
private String name;
|
||||
private String password;
|
||||
private String displayName;
|
||||
private String portrait;
|
||||
private int gender;
|
||||
private String mobile;
|
||||
private String email;
|
||||
private String address;
|
||||
private String company;
|
||||
private String social;
|
||||
private String extra;
|
||||
private long updateDt;
|
||||
|
||||
public static InputCreateUser fromPbUser(WFCMessage.User pbUser) {
|
||||
InputCreateUser inputCreateUser = new InputCreateUser();
|
||||
inputCreateUser.userId = pbUser.getUid();
|
||||
inputCreateUser.name = pbUser.getName();
|
||||
inputCreateUser.displayName = pbUser.getDisplayName();
|
||||
inputCreateUser.portrait = pbUser.getPortrait();
|
||||
inputCreateUser.gender = pbUser.getGender();
|
||||
inputCreateUser.mobile = pbUser.getMobile();
|
||||
inputCreateUser.email = pbUser.getEmail();
|
||||
inputCreateUser.address = pbUser.getAddress();
|
||||
inputCreateUser.company = pbUser.getCompany();
|
||||
inputCreateUser.social = pbUser.getSocial();
|
||||
inputCreateUser.extra = pbUser.getExtra();
|
||||
inputCreateUser.updateDt = pbUser.getUpdateDt();
|
||||
return inputCreateUser;
|
||||
}
|
||||
|
||||
public String getSocial() {
|
||||
return social;
|
||||
}
|
||||
|
||||
public void setSocial(String social) {
|
||||
this.social = social;
|
||||
}
|
||||
|
||||
public WFCMessage.User toUser() {
|
||||
WFCMessage.User.Builder newUserBuilder = WFCMessage.User.newBuilder()
|
||||
.setUid(userId);
|
||||
if (name != null)
|
||||
newUserBuilder.setName(name);
|
||||
if (displayName != null)
|
||||
newUserBuilder.setDisplayName(displayName);
|
||||
if (getPortrait() != null)
|
||||
newUserBuilder.setPortrait(getPortrait());
|
||||
if (getEmail() != null)
|
||||
newUserBuilder.setEmail(getEmail());
|
||||
if (getAddress() != null)
|
||||
newUserBuilder.setAddress(getAddress());
|
||||
if (getCompany() != null)
|
||||
newUserBuilder.setCompany(getCompany());
|
||||
if (getSocial() != null)
|
||||
newUserBuilder.setSocial(getSocial());
|
||||
|
||||
if (getMobile() != null)
|
||||
newUserBuilder.setMobile(getMobile());
|
||||
if (getExtra() != null)
|
||||
newUserBuilder.setExtra(getExtra());
|
||||
newUserBuilder.setGender(gender);
|
||||
|
||||
newUserBuilder.setUpdateDt(System.currentTimeMillis());
|
||||
return newUserBuilder.build();
|
||||
}
|
||||
|
||||
public long getUpdateDt() {
|
||||
return updateDt;
|
||||
}
|
||||
|
||||
public void setUpdateDt(long updateDt) {
|
||||
this.updateDt = updateDt;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getPortrait() {
|
||||
return portrait;
|
||||
}
|
||||
|
||||
public void setPortrait(String portrait) {
|
||||
this.portrait = portrait;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
}
|
||||
|
||||
public void setCompany(String company) {
|
||||
this.company = company;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public int getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(int gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
public class InputDestoryChatroom {
|
||||
private String chatroomId;
|
||||
|
||||
public InputDestoryChatroom() {
|
||||
}
|
||||
|
||||
public String getChatroomId() {
|
||||
return chatroomId;
|
||||
}
|
||||
|
||||
public void setChatroomId(String chatroomId) {
|
||||
this.chatroomId = chatroomId;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InputDismissGroupMember extends InputGroupBase {
|
||||
private String group_id;
|
||||
|
||||
|
||||
public String getGroup_id() {
|
||||
return group_id;
|
||||
}
|
||||
|
||||
public void setGroup_id(String group_id) {
|
||||
this.group_id = group_id;
|
||||
}
|
||||
|
||||
public boolean isValide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public WFCMessage.DismissGroupRequest toProtoGroupRequest() {
|
||||
WFCMessage.DismissGroupRequest.Builder dismissGroupBuilder = WFCMessage.DismissGroupRequest.newBuilder();
|
||||
dismissGroupBuilder.setGroupId(group_id);
|
||||
|
||||
for (Integer line : to_lines
|
||||
) {
|
||||
dismissGroupBuilder.addToLine(line);
|
||||
}
|
||||
|
||||
dismissGroupBuilder.setNotifyContent(notify_message.toProtoMessageContent());
|
||||
return dismissGroupBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
public class InputGetUserInfo {
|
||||
private String userId;
|
||||
private String name;
|
||||
private String mobile;
|
||||
|
||||
public InputGetUserInfo(String userId, String name) {
|
||||
this.userId = userId;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InputGroupBase {
|
||||
public String operator;
|
||||
public List<Integer> to_lines;
|
||||
public MessagePayload notify_message;
|
||||
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public List<Integer> getTo_lines() {
|
||||
return to_lines;
|
||||
}
|
||||
|
||||
public void setTo_lines(List<Integer> to_lines) {
|
||||
this.to_lines = to_lines;
|
||||
}
|
||||
|
||||
public MessagePayload getNotify_message() {
|
||||
return notify_message;
|
||||
}
|
||||
|
||||
public void setNotify_message(MessagePayload notify_message) {
|
||||
this.notify_message = notify_message;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InputKickoffGroupMember extends InputGroupBase {
|
||||
private String group_id;
|
||||
private List<String> members;
|
||||
|
||||
public String getGroup_id() {
|
||||
return group_id;
|
||||
}
|
||||
|
||||
public void setGroup_id(String group_id) {
|
||||
this.group_id = group_id;
|
||||
}
|
||||
|
||||
public List<String> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
public void setMembers(List<String> members) {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
public boolean isValide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public WFCMessage.RemoveGroupMemberRequest toProtoGroupRequest() {
|
||||
WFCMessage.RemoveGroupMemberRequest.Builder removedGroupBuilder = WFCMessage.RemoveGroupMemberRequest.newBuilder();
|
||||
removedGroupBuilder.setGroupId(group_id);
|
||||
removedGroupBuilder.addAllRemovedMember(members);
|
||||
|
||||
for (Integer line : to_lines
|
||||
) {
|
||||
removedGroupBuilder.addToLine(line);
|
||||
}
|
||||
|
||||
removedGroupBuilder.setNotifyContent(notify_message.toProtoMessageContent());
|
||||
return removedGroupBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InputOutputSensitiveWords {
|
||||
private List<String> words;
|
||||
|
||||
public List<String> getWords() {
|
||||
return words;
|
||||
}
|
||||
|
||||
public void setWords(List<String> words) {
|
||||
this.words = words;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
public class InputOutputUserBlockStatus {
|
||||
private String userId;
|
||||
private int status;
|
||||
|
||||
public InputOutputUserBlockStatus(String userId, int status) {
|
||||
this.userId = userId;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
public class InputTransferGroup extends InputGroupBase {
|
||||
private String group_id;
|
||||
private String new_owner;
|
||||
|
||||
|
||||
public boolean isValide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public WFCMessage.TransferGroupRequest toProtoGroupRequest() {
|
||||
WFCMessage.TransferGroupRequest.Builder groupBuilder = WFCMessage.TransferGroupRequest.newBuilder();
|
||||
|
||||
groupBuilder.setGroupId(group_id);
|
||||
groupBuilder.setNewOwner(new_owner);
|
||||
for (Integer line : to_lines
|
||||
) {
|
||||
groupBuilder.addToLine(line);
|
||||
}
|
||||
|
||||
groupBuilder.setNotifyContent(notify_message.toProtoMessageContent());
|
||||
return groupBuilder.build();
|
||||
}
|
||||
|
||||
public String getGroup_id() {
|
||||
return group_id;
|
||||
}
|
||||
|
||||
public void setGroup_id(String group_id) {
|
||||
this.group_id = group_id;
|
||||
}
|
||||
|
||||
public String getNew_owner() {
|
||||
return new_owner;
|
||||
}
|
||||
|
||||
public void setNew_owner(String new_owner) {
|
||||
this.new_owner = new_owner;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
|
||||
public class InputUserLogin {
|
||||
private String name;
|
||||
private String password;
|
||||
private String clientId;
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.hazelcast.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
public class MessagePayload {
|
||||
private int type;
|
||||
private String searchableContent;
|
||||
private String pushContent;
|
||||
private String content;
|
||||
private String base64edData;
|
||||
private int mediaType;
|
||||
private String remoteMediaUrl;
|
||||
private int persistFlag;
|
||||
private int expireDuration;
|
||||
private int mentionedType;
|
||||
private List<String> mentionedTarget;
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getSearchableContent() {
|
||||
return searchableContent;
|
||||
}
|
||||
|
||||
public void setSearchableContent(String searchableContent) {
|
||||
this.searchableContent = searchableContent;
|
||||
}
|
||||
|
||||
public String getPushContent() {
|
||||
return pushContent;
|
||||
}
|
||||
|
||||
public void setPushContent(String pushContent) {
|
||||
this.pushContent = pushContent;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getBase64edData() {
|
||||
return base64edData;
|
||||
}
|
||||
|
||||
public void setBase64edData(String base64edData) {
|
||||
this.base64edData = base64edData;
|
||||
}
|
||||
|
||||
public int getMediaType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
public void setMediaType(int mediaType) {
|
||||
this.mediaType = mediaType;
|
||||
}
|
||||
|
||||
public String getRemoteMediaUrl() {
|
||||
return remoteMediaUrl;
|
||||
}
|
||||
|
||||
public void setRemoteMediaUrl(String remoteMediaUrl) {
|
||||
this.remoteMediaUrl = remoteMediaUrl;
|
||||
}
|
||||
|
||||
public int getPersistFlag() {
|
||||
return persistFlag;
|
||||
}
|
||||
|
||||
public void setPersistFlag(int persistFlag) {
|
||||
this.persistFlag = persistFlag;
|
||||
}
|
||||
|
||||
public int getExpireDuration() {
|
||||
return expireDuration;
|
||||
}
|
||||
|
||||
public void setExpireDuration(int expireDuration) {
|
||||
this.expireDuration = expireDuration;
|
||||
}
|
||||
|
||||
public int getMentionedType() {
|
||||
return mentionedType;
|
||||
}
|
||||
|
||||
public void setMentionedType(int mentionedType) {
|
||||
this.mentionedType = mentionedType;
|
||||
}
|
||||
|
||||
public List<String> getMentionedTarget() {
|
||||
return mentionedTarget;
|
||||
}
|
||||
|
||||
public void setMentionedTarget(List<String> mentionedTarget) {
|
||||
this.mentionedTarget = mentionedTarget;
|
||||
}
|
||||
|
||||
public WFCMessage.MessageContent toProtoMessageContent() {
|
||||
WFCMessage.MessageContent.Builder builder = WFCMessage.MessageContent.newBuilder()
|
||||
.setType(type)
|
||||
.setMediaType(mediaType)
|
||||
.setPersistFlag(persistFlag)
|
||||
.setExpireDuration(expireDuration)
|
||||
.setMentionedType(mentionedType);
|
||||
|
||||
if (!StringUtil.isNullOrEmpty(searchableContent))
|
||||
builder.setSearchableContent(searchableContent);
|
||||
if (!StringUtil.isNullOrEmpty(pushContent))
|
||||
builder.setPushContent(pushContent);
|
||||
if (!StringUtil.isNullOrEmpty(searchableContent))
|
||||
builder.setContent(searchableContent);
|
||||
if (!StringUtil.isNullOrEmpty(base64edData))
|
||||
builder.setData(ByteString.copyFrom(Base64.getDecoder().decode(base64edData)));
|
||||
if (!StringUtil.isNullOrEmpty(remoteMediaUrl))
|
||||
builder.setRemoteMediaUrl(remoteMediaUrl);
|
||||
if (mentionedTarget != null && mentionedTarget.size() > 0)
|
||||
builder.addAllMentionedTarget(mentionedTarget);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static MessagePayload fromProtoMessageContent(WFCMessage.MessageContent protoContent) {
|
||||
if (protoContent == null)
|
||||
return null;
|
||||
|
||||
MessagePayload payload = new MessagePayload();
|
||||
payload.type = protoContent.getType();
|
||||
payload.searchableContent = protoContent.getSearchableContent();
|
||||
payload.pushContent = protoContent.getPushContent();
|
||||
payload.content = protoContent.getContent();
|
||||
if (protoContent.getData() != null && protoContent.getData().size() > 0)
|
||||
payload.base64edData = Base64.getEncoder().encodeToString(protoContent.getData().toByteArray());
|
||||
payload.mediaType = protoContent.getMediaType();
|
||||
payload.remoteMediaUrl = protoContent.getRemoteMediaUrl();
|
||||
payload.persistFlag = protoContent.getPersistFlag();
|
||||
payload.expireDuration = protoContent.getExpireDuration();
|
||||
payload.mentionedType = protoContent.getMentionedType();
|
||||
payload.mentionedTarget = new ArrayList<>();
|
||||
payload.mentionedTarget.addAll(protoContent.getMentionedTargetList());
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
package com.xiaoleilu.loServer.pojos;
|
||||
|
||||
public class OutputCheckUserOnline {
|
||||
private int status; //0 online, 1 have session offline, 2 no session
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user