mirror of
https://github.com/drogonframework/drogon.git
synced 2025-06-25 00:01:17 -04:00
Compare commits
929 Commits
v1.0.0-bet
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
a22956b82b | ||
|
3c5749bbc2 | ||
|
7cd1ae8940 | ||
|
c3f9192541 | ||
|
e46e05e94a | ||
|
26e7c6913c | ||
|
8d640bafb4 | ||
|
f6b5404dbb | ||
|
46b5c9044d | ||
|
ac0d4d0f89 | ||
|
5c4057331e | ||
|
95a518e7f2 | ||
|
c03a3df106 | ||
|
d6a33f93c9 | ||
|
59cd4366c7 | ||
|
c92d146374 | ||
|
3c7c66e310 | ||
|
1fb67d68be | ||
|
cbf63f8fc4 | ||
|
d68e8aa554 | ||
|
41537a6e86 | ||
|
a32dc67867 | ||
|
e155df9f66 | ||
|
f5de41f5d7 | ||
|
a3b4779540 | ||
|
686f68a12f | ||
|
152a69f1e9 | ||
|
38dd5fea31 | ||
|
3a6268f7e9 | ||
|
47f8af7ca1 | ||
|
44f796b796 | ||
|
df7e83ae74 | ||
|
99e816283d | ||
|
3e944d28d8 | ||
|
1765223755 | ||
|
71b6d57cae | ||
|
882c1d9ecd | ||
|
8541e67143 | ||
|
6d9ecb8d8d | ||
|
23c561f072 | ||
|
284d14b8ca | ||
|
ca2210331d | ||
|
3fce70b535 | ||
|
bf1fc03bff | ||
|
5225bb3295 | ||
|
ac0a1b873e | ||
|
912f1d803c | ||
|
13d7148764 | ||
|
b0c5331bc1 | ||
|
c9f5754423 | ||
|
31fb18fb46 | ||
|
f918ead0ae | ||
|
fee34095a2 | ||
|
2911a7c08a | ||
|
1b4653577f | ||
|
bbcad71458 | ||
|
beec858eba | ||
|
93d8fb425d | ||
|
73406d1225 | ||
|
6bafdf30fd | ||
|
59919f33ef | ||
|
1326205483 | ||
|
80ec7d9211 | ||
|
5b5d1906bf | ||
|
206ef0d881 | ||
|
c46f149c2c | ||
|
0546032edc | ||
|
f743cfd4d1 | ||
|
500d44faac | ||
|
e786907478 | ||
|
5d4523a3a6 | ||
|
dfacd1b454 | ||
|
85b918f9e9 | ||
|
7b8d0085b1 | ||
|
f6913f6328 | ||
|
a2f759e4cd | ||
|
ad2b7e13e1 | ||
|
b04dfd7f96 | ||
|
de5a4a5f57 | ||
|
8bdb9b2fa6 | ||
|
0a889e921d | ||
|
9a96a20c6e | ||
|
f37a1d036f | ||
|
c4c95918bf | ||
|
6726df139d | ||
|
150735848d | ||
|
155ae9ad65 | ||
|
306df8a8fb | ||
|
82c46f13f8 | ||
|
5f75222243 | ||
|
5b7cefd32c | ||
|
abbcf6023d | ||
|
b5cd748a12 | ||
|
439ddd8dbe | ||
|
e79d5170b4 | ||
|
519398c159 | ||
|
96919df488 | ||
|
294035beb9 | ||
|
46ac53adb3 | ||
|
9f2872639a | ||
|
4cbac301fb | ||
|
88d06684f2 | ||
|
da7f065a6f | ||
|
aa04d33e02 | ||
|
1a9ad1a2c9 | ||
|
22f4b4fad6 | ||
|
99d97df25f | ||
|
6aed658fab | ||
|
3d469112ca | ||
|
f63480674f | ||
|
0d178877f0 | ||
|
5f273d8744 | ||
|
3c8c273582 | ||
|
c2b8e7c624 | ||
|
56a53165b6 | ||
|
674137e89d | ||
|
ffc6e74f27 | ||
|
359b63fa77 | ||
|
baea2dce47 | ||
|
cca1c8f262 | ||
|
aeed963915 | ||
|
e640cc092d | ||
|
93c568bb95 | ||
|
af29e25b03 | ||
|
d745cfe765 | ||
|
6b36b3a4f9 | ||
|
8e4ffa71e2 | ||
|
25f874a278 | ||
|
5ecbd1f184 | ||
|
34d32a1ef0 | ||
|
1fd5c7ea5e | ||
|
2a0da80d5f | ||
|
01ad18d2d5 | ||
|
f4443dce44 | ||
|
021c89ec78 | ||
|
125dd0e69e | ||
|
38bde80aaa | ||
|
4eec56c49f | ||
|
ba9e9731d2 | ||
|
41b740f649 | ||
|
e76bf08eb2 | ||
|
c35e62ccd2 | ||
|
358de6e66f | ||
|
8026790e1a | ||
|
4e890f52d6 | ||
|
27f1a3d812 | ||
|
6370461896 | ||
|
6b20a9fa8d | ||
|
830ced8c5b | ||
|
6f6a03b14b | ||
|
f21b899e63 | ||
|
26840aa056 | ||
|
01385f4f33 | ||
|
2000a4279e | ||
|
1ec3c96cbb | ||
|
56f0102cfe | ||
|
a76c11cc34 | ||
|
e5daba6bf5 | ||
|
8586874c87 | ||
|
f215cb15a0 | ||
|
7599ae98a0 | ||
|
4323e7b6ef | ||
|
9ffe1b267b | ||
|
645c2d8aaf | ||
|
ab76e80089 | ||
|
d9afdf279a | ||
|
1efe89a719 | ||
|
fd7af8110f | ||
|
63b7f5eb13 | ||
|
94ca651cbd | ||
|
6cb8ac6f52 | ||
|
078f60ca03 | ||
|
cfa0de4389 | ||
|
112d19ff12 | ||
|
cedeeb59f4 | ||
|
4e5638fdcd | ||
|
e2e5d6d57f | ||
|
53c84305b2 | ||
|
85d7c068e4 | ||
|
5d0c70278e | ||
|
366311c196 | ||
|
5df9b48998 | ||
|
8d4c17702a | ||
|
9337571e1a | ||
|
816684e15d | ||
|
40aa034595 | ||
|
de4c811772 | ||
|
3ecb8b4917 | ||
|
f761c54aa8 | ||
|
58055ae39c | ||
|
bc4d740b0c | ||
|
f8f5283dff | ||
|
54b137d64f | ||
|
40579ae308 | ||
|
81bf767d89 | ||
|
f1426c6e9a | ||
|
17c80508c0 | ||
|
f361995035 | ||
|
3723ed5e0c | ||
|
83e08f4b27 | ||
|
3aa93e62e7 | ||
|
5c43b82dc2 | ||
|
4c44322221 | ||
|
47e700c77f | ||
|
a122725c3b | ||
|
5509091ab8 | ||
|
43b014cc24 | ||
|
4ad68db5e2 | ||
|
d3dbaed60a | ||
|
d7ae3a21b3 | ||
|
3fa480dd87 | ||
|
56f620717e | ||
|
ad99cf724d | ||
|
a91014a982 | ||
|
9b2716ec24 | ||
|
d00222883f | ||
|
0fb887cb07 | ||
|
f16017ee6e | ||
|
ec5dfdd9f7 | ||
|
8f37e526cc | ||
|
c1da9922eb | ||
|
ca92e32d55 | ||
|
cca5e5badd | ||
|
92e036874a | ||
|
cb2ae14bdf | ||
|
34a5c37974 | ||
|
3c82dcb491 | ||
|
6dbe650c74 | ||
|
74bb47c690 | ||
|
61073b4f74 | ||
|
eea916315e | ||
|
8e4474bf4c | ||
|
269399b701 | ||
|
586fd6d67a | ||
|
44b6916d7e | ||
|
7f04e63f25 | ||
|
5e245d08dc | ||
|
b9a699f866 | ||
|
3263b78ec3 | ||
|
07726dfcab | ||
|
415b34e06d | ||
|
75b106599d | ||
|
7fce0f1ff5 | ||
|
87a3132fd1 | ||
|
abf6f6cc86 | ||
|
bd66fa4f55 | ||
|
5baca75359 | ||
|
29984f26e0 | ||
|
fddbc0abb7 | ||
|
bf4ff759ad | ||
|
42bbc185e1 | ||
|
8b250f8638 | ||
|
178fb22b49 | ||
|
2caa8c4a34 | ||
|
4ec75e03e0 | ||
|
22c7567da3 | ||
|
f615d5e8df | ||
|
f14ba8de28 | ||
|
7c96c7fbf5 | ||
|
65b1715539 | ||
|
4358b71f55 | ||
|
c7912f246b | ||
|
d133b21804 | ||
|
ad2798e4aa | ||
|
abee656699 | ||
|
292d677446 | ||
|
0715a94a06 | ||
|
5df4db96a5 | ||
|
122a42cd4f | ||
|
120aaf249d | ||
|
f0a011b14d | ||
|
e25a162887 | ||
|
02742e4518 | ||
|
4f066258f2 | ||
|
ceab5f3037 | ||
|
21e207abe5 | ||
|
c63b021e7d | ||
|
2af8e47278 | ||
|
00debb056b | ||
|
af551bf2fb | ||
|
b229f74743 | ||
|
a039157587 | ||
|
97a5496fa4 | ||
|
314bab0b4c | ||
|
ab5259b290 | ||
|
d4c0e063f1 | ||
|
57ec87d38d | ||
|
b739a7fab8 | ||
|
394f9bd0d4 | ||
|
bc028776f7 | ||
|
d321bd4fc1 | ||
|
313392a9b6 | ||
|
29955becc1 | ||
|
54d96963f3 | ||
|
ae9d5f20b3 | ||
|
7d87d7e0b2 | ||
|
36d7435d1d | ||
|
479346822f | ||
|
3adf168a87 | ||
|
0b3147c157 | ||
|
007a6ffbe3 | ||
|
554939d7ee | ||
|
1618484d74 | ||
|
19f08786f0 | ||
|
80ea3c4f30 | ||
|
c5e596d942 | ||
|
28518b7bba | ||
|
1b11bfb668 | ||
|
ef93c91ec7 | ||
|
25ece89558 | ||
|
31fa010ca9 | ||
|
ab10d8cb22 | ||
|
c0d48da99f | ||
|
9dff8b296b | ||
|
990be54ea0 | ||
|
6208332de3 | ||
|
a8be56f9bb | ||
|
164972e2d3 | ||
|
37a10318ff | ||
|
29c67565a3 | ||
|
995b8e8b82 | ||
|
89588959b1 | ||
|
875bca0a86 | ||
|
4d63475203 | ||
|
cdd48686ab | ||
|
d3fe59432b | ||
|
1c8580a71b | ||
|
c913441bb4 | ||
|
302bb560b6 | ||
|
a1a0ef1af8 | ||
|
6a397efefe | ||
|
c97125b22f | ||
|
574a60f812 | ||
|
de1bd805fc | ||
|
ff1aac37de | ||
|
94c4f27d61 | ||
|
29b33a7fa0 | ||
|
c40bb2bc1f | ||
|
f582a16adb | ||
|
64b9484657 | ||
|
2465753ee9 | ||
|
bd9d290b82 | ||
|
4ef31d7c2d | ||
|
9abc9e5b6c | ||
|
5610bd2182 | ||
|
5371b967f6 | ||
|
2b75af82ee | ||
|
a1d2bd168b | ||
|
9f23560a29 | ||
|
bf36db1562 | ||
|
c9f5946eff | ||
|
82bd6bceac | ||
|
cfe1681b0f | ||
|
1bc6ee1170 | ||
|
a582a773c1 | ||
|
5373e51f92 | ||
|
474db0dd9a | ||
|
8b90403bae | ||
|
da18f21796 | ||
|
b32fa58d16 | ||
|
cf543716ce | ||
|
ee9ca895c7 | ||
|
9524a91479 | ||
|
b8f44aec2e | ||
|
e2b5a07921 | ||
|
46c00a317f | ||
|
81dd4ebd07 | ||
|
7570137138 | ||
|
a42f759248 | ||
|
3b5de8863a | ||
|
ebf87d69d7 | ||
|
664d97c185 | ||
|
346e2e6033 | ||
|
7af9853589 | ||
|
a4afc394dc | ||
|
3388c72343 | ||
|
8ab868ea22 | ||
|
bd4f3814a6 | ||
|
1bc38c7e22 | ||
|
fffb8f7e7c | ||
|
41d9d7ec58 | ||
|
3b11b80b10 | ||
|
cd55a198d2 | ||
|
3b0fd3d676 | ||
|
19d9ffcfc2 | ||
|
4f2cbd4135 | ||
|
c5a1888da3 | ||
|
f017b09947 | ||
|
e8b21502e2 | ||
|
a4ba8aff95 | ||
|
5892fa2f9f | ||
|
c9c2675ba9 | ||
|
bccf509ca0 | ||
|
86dbf9875b | ||
|
e53c81b229 | ||
|
d51bae1016 | ||
|
567e7c07ad | ||
|
8c4896dfec | ||
|
6971f84dae | ||
|
3d3daef3c5 | ||
|
740c34fce3 | ||
|
dd63a72dab | ||
|
b9bbe45642 | ||
|
b8a6188ad5 | ||
|
fc68b8c92c | ||
|
09a351d032 | ||
|
0e3ee5fad0 | ||
|
4b31f93767 | ||
|
3c785326c6 | ||
|
8ed0434ad8 | ||
|
26c8c0b527 | ||
|
92d39fb174 | ||
|
bbc31612fc | ||
|
bfb25a3765 | ||
|
88f4f090a8 | ||
|
68b2a46a29 | ||
|
e81662f29b | ||
|
29732a94ef | ||
|
e2842c9de5 | ||
|
720ce4e9ed | ||
|
9db332af65 | ||
|
6c8f8bac1f | ||
|
bc4e8faec2 | ||
|
52800006a0 | ||
|
03f34cb40b | ||
|
bba5253a0f | ||
|
080123ff96 | ||
|
6b677a3ff6 | ||
|
be3136ea26 | ||
|
719d50c0a7 | ||
|
754fd2da29 | ||
|
66e29dd9b2 | ||
|
7455355419 | ||
|
2a484536d1 | ||
|
6a55a3aa64 | ||
|
e9fafc643d | ||
|
1c44cf7e4c | ||
|
3ad9326e2a | ||
|
113d23494a | ||
|
7cf0a64ab6 | ||
|
8913abc400 | ||
|
d6b09c9e1b | ||
|
f522d2d70e | ||
|
bc18f56d57 | ||
|
7d137362bd | ||
|
6e6493299e | ||
|
faf3e0c17c | ||
|
ec8146774a | ||
|
895552dcae | ||
|
df331c8a74 | ||
|
953000e3fb | ||
|
e4ec2c36c5 | ||
|
29f2d431ab | ||
|
9e9bc7997e | ||
|
9448c19865 | ||
|
0431f38a4e | ||
|
7066b09edb | ||
|
dff4b6c5d9 | ||
|
e2eb674781 | ||
|
5a03c9aa9f | ||
|
a7f05c4214 | ||
|
0818384172 | ||
|
ac4a816a99 | ||
|
4a6f298661 | ||
|
3eb5bcd1dd | ||
|
22f810a71b | ||
|
fdcf294ef9 | ||
|
9b09abe274 | ||
|
c5effe9c51 | ||
|
8460249ab3 | ||
|
f49d71faba | ||
|
c9f98e1bf3 | ||
|
56cb305ae3 | ||
|
c7b6c8403f | ||
|
94c7add7a1 | ||
|
cdabca9de5 | ||
|
ba5187868d | ||
|
6b0e38fc8f | ||
|
30f06515fe | ||
|
b2bf247048 | ||
|
b68aeb43ae | ||
|
3620228843 | ||
|
133e6dc2ef | ||
|
588a7f0cd1 | ||
|
8049206b5f | ||
|
0783d6aa3e | ||
|
d60f962aa3 | ||
|
1551b66307 | ||
|
7e4174d537 | ||
|
b1bdc747c4 | ||
|
477c3dca7d | ||
|
88236f9279 | ||
|
6427fdedcc | ||
|
f46e3baeb1 | ||
|
0b9d114746 | ||
|
da87c124ae | ||
|
64f9f8b87f | ||
|
1c04b1a419 | ||
|
895ab63937 | ||
|
25cba9a2a5 | ||
|
629ae0bba1 | ||
|
dbfb99ccc8 | ||
|
35c2d123c0 | ||
|
991873cf60 | ||
|
c9035962fc | ||
|
5c1c81e828 | ||
|
5e8db234b9 | ||
|
d888816997 | ||
|
f87a6ca1b8 | ||
|
3b600232be | ||
|
a02d8e402c | ||
|
c5398b26cb | ||
|
0efd0c34c1 | ||
|
5245f136b7 | ||
|
0e7637daf6 | ||
|
04c7c7eb22 | ||
|
dbb51c1e6a | ||
|
b8d820fc2a | ||
|
065675486d | ||
|
6a882841f1 | ||
|
cd46b4a488 | ||
|
f84b709a61 | ||
|
a38d67aedb | ||
|
834e3eabdd | ||
|
a70a2844b1 | ||
|
e7ec973095 | ||
|
b22b7c6be3 | ||
|
945b26dc0c | ||
|
4abbf76214 | ||
|
6a3f72f2e5 | ||
|
b654e35e51 | ||
|
c6b65485e1 | ||
|
e1cbd1b987 | ||
|
ffc410a66e | ||
|
1bddbb117a | ||
|
802e2ae87a | ||
|
0b5075bfa9 | ||
|
afb7e853ec | ||
|
1e87c35b8f | ||
|
36fb3b3a40 | ||
|
65a7dadbfb | ||
|
dc732fc954 | ||
|
8052c38f49 | ||
|
3601992546 | ||
|
32970172f6 | ||
|
9a059aedef | ||
|
706fc70abc | ||
|
cd093fc97e | ||
|
08351ccf98 | ||
|
f736e12a05 | ||
|
63738bd578 | ||
|
74b3ca3db6 | ||
|
e478b63dda | ||
|
0e70be0a95 | ||
|
2c53cf086a | ||
|
471488eef1 | ||
|
f8e56d85dd | ||
|
6bfbf97eea | ||
|
f99c72bd5b | ||
|
cb1876f26b | ||
|
74d57ab7fa | ||
|
51814b76da | ||
|
60c877f920 | ||
|
685aaaa3da | ||
|
a33bf2bf34 | ||
|
88c6b6e7d4 | ||
|
1eea826a52 | ||
|
87d8123276 | ||
|
b067771fa4 | ||
|
b2c1c8de9e | ||
|
e83026230a | ||
|
df51674792 | ||
|
0ec2f51fbf | ||
|
d256e93cde | ||
|
ab5eb955b4 | ||
|
44a8a2d5f7 | ||
|
4eeba18088 | ||
|
8f2609d1bd | ||
|
f670f71484 | ||
|
d665bedb5c | ||
|
ba8c7b5bca | ||
|
8cb327606a | ||
|
c65051a8f1 | ||
|
4b8d08f20f | ||
|
54727a5dbe | ||
|
a81a5fa63e | ||
|
260c9a547f | ||
|
a19d0427ed | ||
|
564fc67649 | ||
|
686068065a | ||
|
49181a22b2 | ||
|
5f60c9fc6c | ||
|
f29a29f2ba | ||
|
e5c9c3a947 | ||
|
0bf37c6052 | ||
|
bd9a149059 | ||
|
9da122a189 | ||
|
d22ce4a848 | ||
|
4f0d29129e | ||
|
79f480c17d | ||
|
490948bb38 | ||
|
6d9aa3b44c | ||
|
df04c47f74 | ||
|
1dfaaac5d0 | ||
|
29a1659085 | ||
|
ed5ceb019d | ||
|
bcd8e27a36 | ||
|
1901801d59 | ||
|
71269e0179 | ||
|
5dc02c3476 | ||
|
e28b9aa59c | ||
|
b30c92a9a2 | ||
|
cd389aec13 | ||
|
98da3490e7 | ||
|
8bd1f5684e | ||
|
af2bd6ba69 | ||
|
ef51951785 | ||
|
0995749dec | ||
|
cde19a1f57 | ||
|
0f5721119c | ||
|
ffda84627b | ||
|
fd720d55d9 | ||
|
4210dbce07 | ||
|
eb2d24197a | ||
|
12cfdd5916 | ||
|
3b8b63d17d | ||
|
cfb71cc619 | ||
|
33044c823f | ||
|
0b5920a1f3 | ||
|
6542236b20 | ||
|
4c577e6fa9 | ||
|
a2142dd93e | ||
|
7ce5768372 | ||
|
5426100bde | ||
|
a060351f7a | ||
|
2919fdb7d4 | ||
|
0168cd0574 | ||
|
1bbc457f0d | ||
|
64e916ccb3 | ||
|
f26450f04b | ||
|
66fbb33956 | ||
|
4b5885e3c0 | ||
|
ec59dbbc3d | ||
|
1cb8b17709 | ||
|
61bb2cf130 | ||
|
f9d714ab74 | ||
|
ce704aff5b | ||
|
d2f291689d | ||
|
566297d4df | ||
|
28f6338271 | ||
|
64fe47acd8 | ||
|
35b4a86e06 | ||
|
292894c426 | ||
|
1abd8b3506 | ||
|
8b7ffb28d7 | ||
|
7dd2d6123b | ||
|
69d687dbcb | ||
|
4ca90f2415 | ||
|
4ce2d25d55 | ||
|
72a4cad9c1 | ||
|
3f0eec6427 | ||
|
3c0103d324 | ||
|
886dfc3648 | ||
|
4f9ee82153 | ||
|
7e8beacd1d | ||
|
fb17efe765 | ||
|
594911b7a2 | ||
|
dbf21f7dbc | ||
|
de37a0ae29 | ||
|
ed2818ed89 | ||
|
17b3ea471d | ||
|
43e7b3fec0 | ||
|
c264c91f38 | ||
|
34d2fe45c3 | ||
|
fb7d73be06 | ||
|
de0d793fc7 | ||
|
3d9278cb82 | ||
|
2ffd4738b2 | ||
|
0fe3f6fd8f | ||
|
465d2ebfe8 | ||
|
ada35c43fa | ||
|
bbb338bf12 | ||
|
5bca202c28 | ||
|
6fca7067da | ||
|
c320527f9d | ||
|
d023743fa3 | ||
|
c4ff98e620 | ||
|
3222c0df84 | ||
|
4c9463eeb7 | ||
|
6f7a062221 | ||
|
a32170b9a3 | ||
|
11cabfb546 | ||
|
e032f9bd0e | ||
|
3a00ffde47 | ||
|
19df3afb78 | ||
|
4a9ba2088c | ||
|
a10e6bfb1c | ||
|
4d8707df4b | ||
|
7117d96016 | ||
|
d59021ef9f | ||
|
2aebba9cc1 | ||
|
321405a93a | ||
|
f1a7462c4c | ||
|
f0110a642d | ||
|
168d2afb17 | ||
|
c4d727cbe6 | ||
|
3a10db99c7 | ||
|
857cacfda7 | ||
|
960309e615 | ||
|
fda2719dd4 | ||
|
80a8f62e30 | ||
|
dddc62a9fe | ||
|
43a21ddc9c | ||
|
820715cd62 | ||
|
5e35055379 | ||
|
34cefefce4 | ||
|
5bd85170bf | ||
|
dd66ba5a73 | ||
|
c8640700ea | ||
|
9d3efeac67 | ||
|
9c54fb8c69 | ||
|
f871d1607d | ||
|
630beed867 | ||
|
306c072af7 | ||
|
8f6269b208 | ||
|
ccd51d289e | ||
|
3424d3f2c4 | ||
|
56b5d03fed | ||
|
a9f4bff519 | ||
|
2607f35687 | ||
|
fda6a443a8 | ||
|
14b5ec08ee | ||
|
2457f9b413 | ||
|
4ebb72b0cf | ||
|
9e959397af | ||
|
e7b7618c37 | ||
|
be6f0966d5 | ||
|
b3d1f151b5 | ||
|
fd2a612945 | ||
|
bbef8780fd | ||
|
e015439740 | ||
|
ecb3d3f74f | ||
|
d4d5adf88b | ||
|
5faab6b414 | ||
|
adab48e187 | ||
|
c2f6aa0109 | ||
|
598d8c15b3 | ||
|
e286fe869a | ||
|
49472a3cc4 | ||
|
84e503a948 | ||
|
d43c2976ff | ||
|
7b1712003d | ||
|
375498a5a6 | ||
|
c754d65cf0 | ||
|
4423d836f4 | ||
|
26dca0a910 | ||
|
cddd17eb55 | ||
|
84eb05ce38 | ||
|
398b028046 | ||
|
91e7ead628 | ||
|
8ecfe9ef84 | ||
|
490def6742 | ||
|
eafdc5d357 | ||
|
6d6a7acd09 | ||
|
a7f49d893e | ||
|
4f8290b589 | ||
|
269acbc477 | ||
|
17b8c337f9 | ||
|
7d6be171c5 | ||
|
c43ba9e514 | ||
|
2fb0f845f6 | ||
|
3a2c472694 | ||
|
d7cb5b715a | ||
|
c46c7f3570 | ||
|
1969effd29 | ||
|
45d2969dcb | ||
|
f5e87acd9b | ||
|
98ab826cfc | ||
|
06bf158676 | ||
|
8551106d8f | ||
|
7af67dc4da | ||
|
4c8dbdbb85 | ||
|
d0dfa242b2 | ||
|
6c850ea886 | ||
|
2401c6a88a | ||
|
9ee00da431 | ||
|
2d87434bfb | ||
|
dd8fbe04cd | ||
|
3c7ac585a6 | ||
|
bb8b5ded79 | ||
|
77063e28d0 | ||
|
ebb8b7b70a | ||
|
56817978e6 | ||
|
49d1697bec | ||
|
686e30b250 | ||
|
8d17cc567e | ||
|
810896c94c | ||
|
4bd19773b5 | ||
|
e171874524 | ||
|
d5cd882248 | ||
|
53f61e9b66 | ||
|
2fb1e1ecb1 | ||
|
eb316d8263 | ||
|
ee77800821 | ||
|
cc3149dc58 | ||
|
978bd7c32f | ||
|
c9dd14bde5 | ||
|
71b60823da | ||
|
3c15f65a7f | ||
|
b733eee7e4 | ||
|
3d8c304a47 | ||
|
dd5e8f8159 | ||
|
ce675c1b87 | ||
|
58702dc41e | ||
|
4f319dd9c9 | ||
|
191acd0bd3 | ||
|
fb2343ac74 | ||
|
62fc82cba1 | ||
|
668533fbbd | ||
|
f07d9e03e1 | ||
|
cb95960e2d | ||
|
249a491d2f | ||
|
bc7ec6975a | ||
|
53e9dcca08 | ||
|
1c99a8a94e | ||
|
feb0b73e78 | ||
|
27c687a0af | ||
|
4809cc9508 | ||
|
cc04a013be | ||
|
832c6cb48d | ||
|
fd6df92bbc | ||
|
20c43b3c2d | ||
|
a67ab1db52 | ||
|
f1b5f2797c | ||
|
0a990f4331 | ||
|
ea43d8127d | ||
|
6571e55631 | ||
|
5bb54cd4cf | ||
|
d85d8f7821 | ||
|
f9d09d1554 | ||
|
5c1c73d9b6 | ||
|
2784a91dcb | ||
|
93c0d7e9da | ||
|
51aff1f5eb | ||
|
0fa61cded3 | ||
|
40ffb2d2af | ||
|
b34b3a6068 | ||
|
c89835cca2 | ||
|
d46a041cba | ||
|
a8ae91bee0 | ||
|
139d2db02b | ||
|
944d1e786d | ||
|
0133e47e8f | ||
|
fc8abadaeb | ||
|
a71e37d86e | ||
|
dbf20200fc | ||
|
ac377bd650 | ||
|
876e21f492 | ||
|
4868aa964d | ||
|
e7b6ba27bb | ||
|
3a3a5636e8 | ||
|
228bac1cfb | ||
|
d830c4f057 | ||
|
ddc41f7907 | ||
|
8f5c757ce2 | ||
|
df82728a45 | ||
|
c9076220ac | ||
|
f9998996a5 | ||
|
5b40410466 | ||
|
e8a6bdc245 | ||
|
656bd2462b | ||
|
d6e5c1959c | ||
|
323bf0693a | ||
|
daf72d3059 | ||
|
ffb8a9d0e4 | ||
|
f9d7f589a2 | ||
|
5605d7d351 | ||
|
56b9a320b5 | ||
|
70eda27427 | ||
|
2cdc060ed7 | ||
|
1414704b44 | ||
|
93573c99bf | ||
|
0b45135391 | ||
|
eed9c06250 | ||
|
795079531a | ||
|
ae01eb5ded | ||
|
569f1677ce | ||
|
b38a01bdea | ||
|
db18f565e1 | ||
|
543d1a8c80 | ||
|
9855fc9b4f | ||
|
456d003482 | ||
|
fff40952a3 | ||
|
1e83a5cc4e | ||
|
aaaba8aa97 | ||
|
6ef7cffabb | ||
|
5d3fda57a0 | ||
|
195bc5299e | ||
|
473b1c86f0 | ||
|
657dc840a9 | ||
|
eb894396c7 | ||
|
a8afbc8cee | ||
|
aa26e9a903 | ||
|
a0b68fb3c9 | ||
|
7186a74590 | ||
|
9f330f310d | ||
|
ba49a0e0e6 | ||
|
4e274b1a2e | ||
|
043c484a64 | ||
|
c3cb70f415 | ||
|
dcab7d54b1 | ||
|
b5a142a10e | ||
|
db99ef84ee | ||
|
f940b6f2cd | ||
|
bf88d08f78 | ||
|
10db494556 | ||
|
a6e2d4f007 | ||
|
da285cd4d2 | ||
|
673d74191e | ||
|
a0f5570f8b | ||
|
982e272703 | ||
|
16afa82688 | ||
|
f1409a2c2d | ||
|
c705990fe5 | ||
|
bf9313a164 | ||
|
b55d53458c | ||
|
89041dad2a | ||
|
36a31dc576 | ||
|
60ecbb945e |
@ -21,23 +21,24 @@ AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: Always
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
AfterCaseLabel: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Allman
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
@ -52,7 +53,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
@ -74,6 +75,7 @@ IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertNewlineAtEOF: true
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
@ -93,7 +95,8 @@ PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 2000
|
||||
PointerAlignment: Left
|
||||
PointerAlignment: Right
|
||||
ReferenceAlignment: Right
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
@ -123,7 +126,8 @@ RawStringFormats:
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SeparateDefinitionBlocks: Always
|
||||
SortIncludes: Never
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
|
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: drogonframework
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # ['https://paypal.me/antao2019']
|
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,10 +1,17 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
labels:
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Notice**
|
||||
If you need support or clarification regarding the usage of Drogon in your project, visit the official Drogon support channel at [gitter](https://gitter.im/drogon-web/community)
|
||||
|
||||
Please create a new issue only if you think you have found a bug or if have a feature request/enhancement.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
|
9
.github/ISSUE_TEMPLATE/feature_request.md
vendored
9
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,10 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels:
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Notice**
|
||||
If you need support or clarification regarding the usage of Drogon in your project, visit the official Drogon support channel at [gitter](https://gitter.im/drogon-web/community)
|
||||
|
||||
Please create a new issue only if you think you have found a bug or if have a feature request/enhancement.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
237
.github/workflows/cmake.yml
vendored
Normal file
237
.github/workflows/cmake.yml
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
name: Build & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
name: windows/msvc - ${{ matrix.link }}
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
link: ["STATIC", "SHARED"]
|
||||
steps:
|
||||
- name: Checkout Drogon source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install conan
|
||||
|
||||
- name: Create build directory
|
||||
run: mkdir build
|
||||
|
||||
- name: Install conan packages
|
||||
working-directory: ./build
|
||||
run: |
|
||||
conan profile detect
|
||||
conan install .. -s compiler="msvc" -sbuild_type=Debug --build=missing -s compiler.cppstd=17
|
||||
|
||||
- name: Create Build Environment & Configure Cmake
|
||||
shell: bash
|
||||
working-directory: ./build
|
||||
run: |
|
||||
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DBUILD_TESTING=on \
|
||||
-DBUILD_SHARED_LIBS=$shared \
|
||||
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \
|
||||
-DBUILD_CTL=ON \
|
||||
-DBUILD_EXAMPLES=ON \
|
||||
-DUSE_SPDLOG=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=../install \
|
||||
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW \
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build --target install --parallel
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: ./test.sh -w
|
||||
|
||||
macos:
|
||||
runs-on: macos-${{ matrix.osver }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
osver: [13, 14, 15]
|
||||
steps:
|
||||
- name: Checkout Drogon source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
# Already installed: brotli, zlib, lz4, sqlite3
|
||||
run: brew install ninja jsoncpp mariadb hiredis redis spdlog postgresql@14
|
||||
|
||||
- name: Create Build Environment & Configure Cmake
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DBUILD_TESTING=on \
|
||||
-DUSE_SPDLOG=ON \
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
|
||||
- name: Build
|
||||
working-directory: ./build
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: ninja && sudo ninja install
|
||||
|
||||
- name: Prepare for testing
|
||||
run: |
|
||||
brew services restart postgresql@14
|
||||
brew services start mariadb
|
||||
brew services start redis
|
||||
sleep 4
|
||||
mariadb -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('')"
|
||||
mariadb -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'"
|
||||
mariadb -e "FLUSH PRIVILEGES"
|
||||
brew services restart mariadb
|
||||
sleep 4
|
||||
psql -c 'create user postgres superuser;' postgres
|
||||
|
||||
- name: Test
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: ./test.sh -t
|
||||
|
||||
ubuntu:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
link: [SHARED, STATIC]
|
||||
compiler:
|
||||
- cxx: g++
|
||||
ver: 9
|
||||
- cxx: g++
|
||||
ver: 10
|
||||
- cxx: g++
|
||||
ver: 11
|
||||
- cxx: g++
|
||||
ver: 12
|
||||
- cxx: g++
|
||||
ver: 13
|
||||
- cxx: clang++
|
||||
ver: 11
|
||||
- cxx: clang++
|
||||
ver: 12
|
||||
- cxx: clang++
|
||||
ver: 13
|
||||
- cxx: clang++
|
||||
ver: 14
|
||||
- cxx: clang++
|
||||
ver: 15
|
||||
- cxx: clang++
|
||||
ver: 16
|
||||
- cxx: clang++
|
||||
ver: 17
|
||||
include:
|
||||
- link: STATIC
|
||||
compiler:
|
||||
cxx: g++
|
||||
ver: 13
|
||||
feature: coroutines
|
||||
env:
|
||||
CXX: ${{ matrix.compiler.cxx }}-${{ matrix.compiler.ver }}
|
||||
steps:
|
||||
- name: Checkout Drogon source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# Installing packages might fail as the github image becomes outdated
|
||||
sudo apt update
|
||||
# These aren't available or don't work well in vcpkg
|
||||
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
|
||||
sudo apt-get install -y ninja-build libbrotli-dev
|
||||
sudo apt-get install -y libspdlog-dev
|
||||
|
||||
- name: Install postgresql
|
||||
run: |
|
||||
sudo apt-get --purge remove postgresql postgresql-doc postgresql-common postgresql-client-common
|
||||
sudo apt-get -y install postgresql-all
|
||||
|
||||
- name: Install g++-13
|
||||
if: startsWith(matrix.compiler.cxx, 'g++') && matrix.compiler.ver == 13
|
||||
run: |
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get install g++-${{ matrix.compiler.ver }}
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13
|
||||
|
||||
- name: Install Clang
|
||||
if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver < 13
|
||||
run: sudo apt-get install clang-${{ matrix.compiler.ver }}
|
||||
|
||||
- name: Install Clang
|
||||
if: startsWith(matrix.compiler.cxx, 'clang') && matrix.compiler.ver >= 13
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh ${{ matrix.compiler.ver }}
|
||||
|
||||
- name: Export `shared`
|
||||
run: |
|
||||
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
|
||||
echo "shared=$shared" >> $GITHUB_ENV
|
||||
|
||||
- name: Create Build Environment & Configure Cmake
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
if: matrix.compiler.feature != 'coroutines'
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DBUILD_TESTING=on \
|
||||
-DUSE_SPDLOG=ON \
|
||||
-DBUILD_SHARED_LIBS=$shared
|
||||
- name: Create Build Environment & Configure Cmake (coroutines)
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
if: matrix.compiler.feature == 'coroutines'
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DBUILD_TESTING=on \
|
||||
-DUSE_SPDLOG=ON \
|
||||
-DCMAKE_CXX_FLAGS="-fcoroutines" \
|
||||
-DBUILD_SHARED_LIBS=$shared \
|
||||
|
||||
- name: Build
|
||||
working-directory: ./build
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: ninja && sudo ninja install
|
||||
|
||||
- name: Prepare for testing
|
||||
run: |
|
||||
sudo systemctl start postgresql
|
||||
sleep 1
|
||||
sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD '12345'" postgres
|
||||
|
||||
- name: Test
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: ./test.sh -t
|
80
.github/workflows/codeql.yml
vendored
Normal file
80
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'master' ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ 'master' ]
|
||||
schedule:
|
||||
- cron: '46 7 * * 5'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
env:
|
||||
SHARED: ON
|
||||
|
||||
steps:
|
||||
- name: Checkout Drogon source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
|
||||
sudo apt-get install -y ninja-build libbrotli-dev
|
||||
|
||||
- name: Create Build Environment & Configure Cmake
|
||||
run: |
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DBUILD_TESTING=on \
|
||||
-DBUILD_SHARED_LIBS=$SHARED
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Build
|
||||
working-directory: ./build
|
||||
run: ninja && sudo ninja install
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
15
.github/workflows/codespell.yml
vendored
Normal file
15
.github/workflows/codespell.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Look for typos in the codebase using codespell.
|
||||
# https://github.com/codespell-project/codespell#readme
|
||||
name: codespell
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
jobs:
|
||||
codespell:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: sudo apt-get install -y codespell
|
||||
- run: codespell --ignore-words-list="coo,folx,ot,statics,xwindows,NotIn,aNULL," --skip="*.csp"
|
41
.github/workflows/cpp.yml
vendored
Normal file
41
.github/workflows/cpp.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: C++
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dos2unix
|
||||
run: sudo apt-get install -y dos2unix
|
||||
|
||||
- name: Install clang-format-17
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
sudo apt-get install -y clang-format-17
|
||||
|
||||
- name: Check formatting
|
||||
run: ./format.sh && git diff --exit-code
|
||||
env:
|
||||
CLANG_FORMAT: clang-format-17
|
||||
|
||||
cpplint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install cpplint
|
||||
run: pip install cpplint
|
||||
|
||||
- name: Run lint
|
||||
run: cpplint --recursive .
|
28
.github/workflows/docker-publish.yml
vendored
Normal file
28
.github/workflows/docker-publish.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created] # 当新版本被创建时触发
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
cd docker/ubuntu
|
||||
docker build -t drogonframework/drogon:latest .
|
||||
|
||||
- name: Push Docker image
|
||||
run: |
|
||||
docker push drogonframework/drogon:latest
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -31,12 +31,19 @@
|
||||
*.out
|
||||
*.app
|
||||
|
||||
build
|
||||
cmake-build-debug
|
||||
.idea
|
||||
lib/inc/drogon/version.h
|
||||
lib/inc/drogon/config.h
|
||||
Doxyfile
|
||||
build/
|
||||
cmake-build-debug/
|
||||
cmake-build-debug-visual-studio/
|
||||
.idea/
|
||||
html/
|
||||
latex/
|
||||
.vscode
|
||||
*.kdev4
|
||||
.cproject
|
||||
.project
|
||||
.settings/
|
||||
.vs/
|
||||
CMakeSettings.json
|
||||
install
|
||||
trace.json
|
||||
.cache/
|
||||
|
1
.gitmodules
vendored
1
.gitmodules
vendored
@ -1,3 +1,4 @@
|
||||
[submodule "trantor"]
|
||||
path = trantor
|
||||
url = https://github.com/an-tao/trantor.git
|
||||
branch = master
|
||||
|
46
.travis.yml
46
.travis.yml
@ -1,46 +0,0 @@
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
osx_image: xcode10.1
|
||||
|
||||
sudo: required
|
||||
|
||||
dist: xenial
|
||||
|
||||
language: cpp
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- xenial
|
||||
- sourceline: 'deb http://archive.ubuntu.com/ubuntu xenial main'
|
||||
- sourceline: 'ppa:mhier/libboost-latest'
|
||||
packages:
|
||||
- gcc
|
||||
- g++
|
||||
- libjsoncpp-dev
|
||||
- uuid-dev
|
||||
- zlib1g-dev
|
||||
- postgresql-server-dev-10
|
||||
- openssl
|
||||
- libssl-dev
|
||||
- libsqlite3-dev
|
||||
- build-essential
|
||||
- cmake
|
||||
- boost1.67
|
||||
homebrew:
|
||||
packages:
|
||||
- jsoncpp
|
||||
- ossp-uuid
|
||||
- openssl
|
||||
- cmake
|
||||
- libtool
|
||||
- lz4
|
||||
- postgresql
|
||||
- mariadb
|
||||
- sqlite3
|
||||
update: true
|
||||
|
||||
script:
|
||||
- ./build.sh && ./test.sh
|
1110
CMakeLists.txt
Executable file → Normal file
1110
CMakeLists.txt
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
78
CONTRIBUTING.md
Normal file
78
CONTRIBUTING.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Contributing
|
||||
|
||||
**Drogon** is an open source project at its heart and every contribution is
|
||||
welcome. By participating in this project you agree to stick to common sense and
|
||||
contribute in an overall positive way.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Fork, then clone the repository: `git clone
|
||||
git@github.com:your-username/drogon.git`
|
||||
1. Follow the [official installation steps on
|
||||
Github](https://drogonframework.github.io/drogon-docs/#/ENG-02-Installation). It’s best to
|
||||
make sure to have the `drogon_ctl` executable in your shell’s `PATH`
|
||||
environment variable in case you use a terminal.
|
||||
|
||||
Now you can create branches, start adding features & bugfixes to the code, and
|
||||
[create pull requests](https://github.com/an-tao/drogon/compare).
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Feel free to [create a pull request](https://github.com/an-tao/drogon/compare)
|
||||
if you think you can contribute to the project. You will be listed as a
|
||||
[contributor](https://github.com/an-tao/drogon/graphs/contributors), agree to
|
||||
this document, and the
|
||||
[LICENSE](https://github.com/an-tao/drogon/blob/master/LICENSE).
|
||||
|
||||
There are also some recommendations you can follow. These aren’t requirements,
|
||||
but they will make the development more straightforward:
|
||||
|
||||
1. If you are unsure about a specific change, have questions, or want to get
|
||||
feedback about a feature you want to introduce, [open a new
|
||||
issue](https://github.com/an-tao/drogon/issues) (please make sure that there
|
||||
is no previous issue about a similar topic).
|
||||
1. You should branch off the current state of the `master` branch, and also
|
||||
merge it into your local branch before creating a pull request if there were
|
||||
other changes introduced in the meantime.
|
||||
1. You can use the following branch names to make your intent clearer:
|
||||
* `bugfix/123-fix-template-parser` when you want to fix a bug in the
|
||||
template parser.
|
||||
* `feature/123-add-l10n-and-i18n` if you want to add localization (l10n) and
|
||||
internationalization (i18n) as a new feature to the project.
|
||||
* If there’s no open issue and no need to open one you can skip the number,
|
||||
and just use the descriptive part: `bugfix/fix-typo-in-docs`.
|
||||
1. Write a brief, but good, and descriptive commit message / pull request title in English,
|
||||
e. g. “Added Internationalization and Localization”.
|
||||
|
||||
If you follow these recommendations your pull request will have more success:
|
||||
|
||||
1. Keep the style consistent to the project, when in doubt refer to the [Google
|
||||
C++ Style Guide](https://google.github.io/styleguide/cppguide.html#C++_Version).
|
||||
1. Please write all comments in English. Comments for new public API introduced by
|
||||
your pull request must be added and written in [Doxygen](http://www.doxygen.nl/) format.
|
||||
1. Format the code with `clang-format` (>= 8.0.0). The configuration is already
|
||||
provided in the `.clang-format` file, just run the `./format.sh` script
|
||||
before submitting your pull request.
|
||||
1. Install [Google Test](https://github.com/google/googletest), and write a test
|
||||
case.
|
||||
1. In case it is a bugfix, it’s best to write a test that breaks in the old
|
||||
version, but works in the new one. This way regressions can be tracked
|
||||
over time.
|
||||
1. If you add a feature, it is best to write the test as if it would be an
|
||||
example how to use the newly introduced feature and to test all major,
|
||||
newly introduced code.
|
||||
|
||||
## Project Maintainers & Collaborators
|
||||
|
||||
In addition to the guidelines mentioned above, collaborators with write access
|
||||
to the repository should also follow these guidelines:
|
||||
|
||||
1. If there are new tests as part of the pull request, you should make sure that
|
||||
they succeed.
|
||||
1. When merging **Pull Requests** you should use the option *Squash & Merge* and
|
||||
chose a descriptive commit message for the bugfix / feature (if not already
|
||||
done by the individual contributor).
|
||||
|
||||
This way the history in the `master` branch will be free of small
|
||||
corrections and easier to follow for people who aren’t engaged in the
|
||||
project on a day-to-day basis.
|
36
CPPLINT.cfg
Normal file
36
CPPLINT.cfg
Normal file
@ -0,0 +1,36 @@
|
||||
# Stop searching for additional config files.
|
||||
set noparent
|
||||
|
||||
exclude_files=trantor
|
||||
exclude_files=build
|
||||
|
||||
# Use non-const reference rather than a pointer.
|
||||
filter=-runtime/references
|
||||
|
||||
# CHECK macros are from Drogon, not Google Test.
|
||||
filter=-readability/check
|
||||
|
||||
# Don't warn about the use of C++11 or C++17 features.
|
||||
filter=-build/c++11
|
||||
filter=-build/c++17
|
||||
|
||||
filter=-build/include_subdir
|
||||
|
||||
# We prioritize clang-format for now.
|
||||
filter=-whitespace
|
||||
|
||||
# We don't require a username in TODO comments.
|
||||
filter=-readability/todo
|
||||
|
||||
# TODO: Fix these.
|
||||
filter=-legal/copyright
|
||||
filter=-build/namespaces
|
||||
filter=-build/include
|
||||
filter=-build/include_what_you_use
|
||||
filter=-runtime/explicit
|
||||
filter=-runtime/string
|
||||
filter=-runtime/int
|
||||
filter=-readability/casting
|
||||
filter=-readability/braces
|
||||
filter=-readability/fn_size
|
||||
filter=-runtime/threadsafe_fn
|
1868
ChangeLog.md
1868
ChangeLog.md
File diff suppressed because it is too large
Load Diff
31
Dockerfile
31
Dockerfile
@ -1,31 +0,0 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
RUN apt-get update -yqq && \
|
||||
apt-get install -yqq --no-install-recommends software-properties-common && \
|
||||
apt-get install -yqq --no-install-recommends sudo curl wget cmake locales git \
|
||||
openssl libssl-dev libjsoncpp-dev uuid-dev zlib1g-dev
|
||||
RUN apt-get install -yqq --no-install-recommends postgresql-server-dev-all
|
||||
RUN apt-get install -yqq --no-install-recommends libmariadbclient-dev
|
||||
RUN apt-get install -yqq --no-install-recommends libsqlite3-dev
|
||||
RUN apt-get install -yqq --no-install-recommends gcc-8 g++-8
|
||||
|
||||
RUN locale-gen en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
|
||||
ENV CC=gcc-8
|
||||
ENV CXX=g++-8
|
||||
ENV AR=gcc-ar-8
|
||||
ENV RANLIB=gcc-ranlib-8
|
||||
|
||||
ENV IROOT=/install
|
||||
ENV DROGON_ROOT=$IROOT/drogon
|
||||
|
||||
WORKDIR $IROOT
|
||||
ADD https://api.github.com/repos/an-tao/drogon/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/an-tao/drogon
|
||||
|
||||
WORKDIR $DROGON_ROOT
|
||||
RUN ./build.sh
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 An Tao
|
||||
Copyright (c) 2019-2023 An Tao
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
90
README.md
90
README.md
@ -1,17 +1,18 @@
|
||||

|
||||

|
||||
|
||||
[](https://travis-ci.com/an-tao/drogon)
|
||||
[](https://lgtm.com/projects/g/an-tao/drogon/alerts/)
|
||||
[](https://lgtm.com/projects/g/an-tao/drogon/context:cpp)
|
||||
[](https://gitter.im/drogon-web/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://github.com/drogonframework/drogon/actions)
|
||||
[](https://conan.io/center/recipes/drogon)
|
||||
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
||||
[](https://discord.gg/3DvHY6Ewuj)
|
||||
[](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
|
||||
|
||||
English | [简体中文](./README.zh-CN.md) | [繁體中文](./README.zh-TW.md)
|
||||
### Overview
|
||||
**Drogon** is a C++14/17-based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon in the American TV series "Game of Thrones" that I really like.
|
||||
**Drogon** is a C++17/20 based HTTP application framework. Drogon can be used to easily build various types of web application server programs using C++. **Drogon** is the name of a dragon from the American TV series *Game of Thrones*, which I really enjoy.
|
||||
|
||||
Drogon's main application platform is Linux. It also supports Mac OS and FreeBSD. Currently, it does not support windows. Its main features are as follows:
|
||||
Drogon is a cross-platform framework, It supports Linux, macOS, FreeBSD, OpenBSD, HaikuOS, and Windows. Its main features are as follows:
|
||||
|
||||
* Use a non-blocking I/O network lib based on epoll (kqueue under MacOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the [benchmarks](https://github.com/an-tao/drogon/wiki/13-Benchmarks) page for more details;
|
||||
* Use a non-blocking I/O network lib based on epoll (kqueue under macOS/FreeBSD) to provide high-concurrency, high-performance network IO, please visit the [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) for more details;
|
||||
* Provide a completely asynchronous programming mode;
|
||||
* Support Http1.0/1.1 (server side and client side);
|
||||
* Based on template, a simple reflection mechanism is implemented to completely decouple the main program framework, controllers and views.
|
||||
@ -24,15 +25,17 @@ Drogon's main application platform is Linux. It also supports Mac OS and FreeBSD
|
||||
* Support WebSocket (server side and client side);
|
||||
* Support JSON format request and response, very friendly to the Restful API application development;
|
||||
* Support file download and upload;
|
||||
* Support gzip compression transmission;
|
||||
* Support gzip, brotli compression transmission;
|
||||
* Support pipelining;
|
||||
* Provide a lightweight command line tool, drogon_ctl, to simplify the creation of various classes in Drogon and the generation of view code;
|
||||
* Support non-blocking I/O based asynchronously reading and writing database (PostgreSQL and MySQL(MariaDB) database);
|
||||
* Support asynchronously reading and writing sqlite3 database based on thread pool;
|
||||
* Support Redis with asynchronous reading and writing;
|
||||
* Support ARM Architecture;
|
||||
* Provide a convenient lightweight ORM implementation that supports for regular object-to-database bidirectional mapping;
|
||||
* Support plugins which can be installed by the configuration file at load time;
|
||||
* Support AOP with build-in joinpoints.
|
||||
* Support AOP with built-in joinpoints.
|
||||
* Support C++ coroutines
|
||||
|
||||
## A very simple example
|
||||
|
||||
@ -45,12 +48,12 @@ Below is the main program of a typical drogon application:
|
||||
using namespace drogon;
|
||||
int main()
|
||||
{
|
||||
app().setLogPath("./");
|
||||
app().setLogLevel(trantor::Logger::WARN);
|
||||
app().addListener("0.0.0.0", 80);
|
||||
app().setThreadNum(16);
|
||||
app().enableRunAsDaemon();
|
||||
app().run();
|
||||
app().setLogPath("./")
|
||||
.setLogLevel(trantor::Logger::kWarn)
|
||||
.addListener("0.0.0.0", 80)
|
||||
.setThreadNum(16)
|
||||
.enableRunAsDaemon()
|
||||
.run();
|
||||
}
|
||||
```
|
||||
|
||||
@ -61,15 +64,14 @@ It can be further simplified by using configuration file as follows:
|
||||
using namespace drogon;
|
||||
int main()
|
||||
{
|
||||
app().loadConfigFile("./config.json");
|
||||
app().run();
|
||||
app().loadConfigFile("./config.json").run();
|
||||
}
|
||||
```
|
||||
|
||||
Drogon provides some interfaces for adding controller logic directly in the main() function, for example, user can register a handler like this in Drogon:
|
||||
|
||||
```c++
|
||||
app.registerHandler("/test?username={1}",
|
||||
app().registerHandler("/test?username={name}",
|
||||
[](const HttpRequestPtr& req,
|
||||
std::function<void (const HttpResponsePtr &)> &&callback,
|
||||
const std::string &name)
|
||||
@ -93,7 +95,7 @@ using namespace drogon;
|
||||
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
|
||||
{
|
||||
public:
|
||||
virtual void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
|
||||
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
|
||||
PATH_LIST_BEGIN
|
||||
PATH_ADD("/test",Get);
|
||||
PATH_LIST_END
|
||||
@ -112,7 +114,7 @@ void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
|
||||
}
|
||||
```
|
||||
|
||||
**Most of the above programs can be automatically generated by the command line tool `drogon_ctl` provided by drogon** (The cammand is `drogon_ctl create controller TestCtrl`). All the user needs to do is add their own business logic. In the example, the controller returns a `Hello, world!` string when the client accesses the `http://ip/test` URL.
|
||||
**Most of the above programs can be automatically generated by the command line tool `drogon_ctl` provided by drogon** (The command is `drogon_ctl create controller TestCtrl`). All the user needs to do is add their own business logic. In the example, the controller returns a `Hello, world!` string when the client accesses the `http://ip/test` URL.
|
||||
|
||||
For JSON format response, we create the controller as follows:
|
||||
|
||||
@ -124,7 +126,7 @@ using namespace drogon;
|
||||
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
|
||||
{
|
||||
public:
|
||||
virtual void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
|
||||
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
|
||||
PATH_LIST_BEGIN
|
||||
//list path definitions here;
|
||||
PATH_ADD("/json", Get);
|
||||
@ -159,9 +161,9 @@ class User : public drogon::HttpController<User>
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
//use METHOD_ADD to add your custom processing function here;
|
||||
METHOD_ADD(User::getInfo, "/{1}", Get); //path is /api/v1/User/{arg1}
|
||||
METHOD_ADD(User::getDetailInfo, "/{1}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo
|
||||
METHOD_ADD(User::newUser, "/{1}", Post); //path is /api/v1/User/{arg1}
|
||||
METHOD_ADD(User::getInfo, "/{id}", Get); //path is /api/v1/User/{arg1}
|
||||
METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo
|
||||
METHOD_ADD(User::newUser, "/{name}", Post); //path is /api/v1/User/{arg1}
|
||||
METHOD_LIST_END
|
||||
//your declaration of processing function maybe like this:
|
||||
void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
|
||||
@ -181,4 +183,40 @@ As you can see, users can use the `HttpController` to map paths and parameters a
|
||||
|
||||
In addition, you can also find that all handler interfaces are in asynchronous mode, where the response is returned by a callback object. This design is for performance reasons because in asynchronous mode the drogon application can handle a large number of concurrent requests with a small number of threads.
|
||||
|
||||
After compiling all of the above source files, we get a very simple web application. This is a good start. **for more information, please visit the [wiki](https://github.com/an-tao/drogon/wiki/01-Overview) site**
|
||||
After compiling all of the above source files, we get a very simple web application. This is a good start. **For more information, please visit the [documentation](https://drogonframework.github.io/drogon-docs/#/) on GitHub**.
|
||||
|
||||
## Cross-compilation
|
||||
|
||||
Drogon supports cross-compilation, you should define the `CMAKE_SYSTEM_NAME` in toolchain file, for example:
|
||||
|
||||
```cmake
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
```
|
||||
|
||||
You can disable building options for examples and drogon_ctl by settings `BUILD_EXAMPLES` and `BUILD_CTL` to `OFF` in the toolchain file.
|
||||
|
||||
## Building options
|
||||
|
||||
Drogon provides some building options, you can enable or disable them by setting the corresponding variables to `ON` or `OFF` in the cmake command line, cmake file etc...
|
||||
|
||||
| Option name | Description | Default value |
|
||||
| :--- | :--- | :--- |
|
||||
| BUILD_CTL | Build drogon_ctl | ON |
|
||||
| BUILD_EXAMPLES | Build examples | ON |
|
||||
| BUILD_ORM | Build orm | ON |
|
||||
| COZ_PROFILING | Use coz for profiling | OFF |
|
||||
| BUILD_SHARED_LIBS | Build drogon as a shared lib | OFF |
|
||||
| BUILD_DOC | Build Doxygen documentation | OFF |
|
||||
| BUILD_BROTLI | Build Brotli | ON |
|
||||
| BUILD_YAML_CONFIG | Build yaml config | ON |
|
||||
| USE_SUBMODULE | Use trantor as a submodule | ON |
|
||||
|
||||
|
||||
## Contributions
|
||||
|
||||
This project exists thanks to all the people who contribute code.
|
||||
|
||||
<a href="https://github.com/drogonframework/drogon/graphs/contributors"><img src="https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
||||
|
||||
Every contribution is welcome. Please refer to the [contribution guidelines](CONTRIBUTING.md) for more information.
|
||||
|
188
README.zh-CN.md
188
README.zh-CN.md
@ -1,17 +1,19 @@
|
||||

|
||||

|
||||
|
||||
[](https://travis-ci.com/an-tao/drogon)
|
||||
[](https://lgtm.com/projects/g/an-tao/drogon/alerts/)
|
||||
[](https://lgtm.com/projects/g/an-tao/drogon/context:cpp)
|
||||
[](https://gitter.im/drogon-web/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://github.com/drogonframework/drogon/actions)
|
||||
[](https://conan.io/center/recipes/drogon)
|
||||
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
||||
[](https://discord.gg/3DvHY6Ewuj)
|
||||
[](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
|
||||
|
||||
**Drogon**是一个基于C++14/17的Http应用框架,使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。
|
||||
[English](./README.md) | 简体中文 | [繁體中文](./README.zh-TW.md)
|
||||
|
||||
**Drogon**是一个基于C++17/20的Http应用框架,使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序。
|
||||
本版本库是github上[Drogon工程](https://github.com/an-tao/drogon)的镜像库。**Drogon**是作者非常喜欢的美剧《权力的游戏》中的一条龙的名字(汉译作卓耿),和龙有关但并不是dragon的误写,为了不至于引起不必要的误会这里说明一下。
|
||||
|
||||
Drogon的主要应用平台是Linux,也支持Mac OS、FreeBSD,目前还不支持Windows。它的主要特点如下:
|
||||
Drogon是一个跨平台框架,它支持Linux,也支持macOS、FreeBSD,OpenBSD,HaikuOS,和Windows。它的主要特点如下:
|
||||
|
||||
* 网络层使用基于epoll(MacOS/FreeBSD下是kqueue)的非阻塞IO框架,提供高并发、高性能的网络IO。详细请见[性能测试](https://github.com/an-tao/drogon/wiki/13-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95);
|
||||
* 网络层使用基于epoll(macOS/FreeBSD下是kqueue)的非阻塞IO框架,提供高并发、高性能的网络IO。详细请见[TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite);
|
||||
* 全异步编程模式;
|
||||
* 支持Http1.0/1.1(server端和client端);
|
||||
* 基于template实现了简单的反射机制,使主程序框架、控制器(controller)和视图(view)完全解耦;
|
||||
@ -24,14 +26,180 @@ Drogon的主要应用平台是Linux,也支持Mac OS、FreeBSD,目前还不
|
||||
* 支持websocket(server端和client端);
|
||||
* 支持Json格式请求和应答, 对Restful API应用开发非常友好;
|
||||
* 支持文件下载和上传,支持sendfile系统调用;
|
||||
* 支持gzip压缩传输;
|
||||
* 支持gzip/brotli压缩传输;
|
||||
* 支持pipelining;
|
||||
* 提供一个轻量的命令行工具drogon_ctl,帮助简化各种类的创建和视图代码的生成过程;
|
||||
* 基于非阻塞IO实现的异步数据库读写,目前支持PostgreSQL和MySQL(MariaDB)数据库;
|
||||
* 基于线程池实现sqlite3数据库的异步读写,提供与上文数据库相同的接口;
|
||||
* 支持Redis异步读写;
|
||||
* 支持ARM架构;
|
||||
* 方便的轻量级ORM实现,支持常规的对象到数据库的双向映射操作;
|
||||
* 支持插件,可通过配置文件在加载期动态拆装;
|
||||
* 支持内建插入点的AOP
|
||||
* 支持C++协程
|
||||
|
||||
### 更多详情请浏览 [wiki](https://github.com/an-tao/drogon/wiki/01-概述)
|
||||
## 一个非常简单的例子
|
||||
|
||||
不像大多数C++框架那样,drogon的主程序可以保持非常简单。 Drogon使用了一些小技巧使主程序和控制器解耦合. 控制器的路由设置可以在控制器类中定义或者配置文件中完成.
|
||||
|
||||
下面是一个典型的主程序的样子:
|
||||
|
||||
```c++
|
||||
#include <drogon/drogon.h>
|
||||
using namespace drogon;
|
||||
int main()
|
||||
{
|
||||
app().setLogPath("./")
|
||||
.setLogLevel(trantor::Logger::kWarn)
|
||||
.addListener("0.0.0.0", 80)
|
||||
.setThreadNum(16)
|
||||
.enableRunAsDaemon()
|
||||
.run();
|
||||
}
|
||||
```
|
||||
|
||||
如果使用配置文件,可以进一步简化成如下的样子:
|
||||
|
||||
```c++
|
||||
#include <drogon/drogon.h>
|
||||
using namespace drogon;
|
||||
int main()
|
||||
{
|
||||
app().loadConfigFile("./config.json").run();
|
||||
}
|
||||
```
|
||||
|
||||
当然,Drogon也提供了一些接口,使用户可以在main()函数中直接添加控制器逻辑,比如,用户可以注册一个lambda处理器到drogon框架中,如下所示:
|
||||
|
||||
```c++
|
||||
app().registerHandler("/test?username={name}",
|
||||
[](const HttpRequestPtr& req,
|
||||
std::function<void (const HttpResponsePtr &)> &&callback,
|
||||
const std::string &name)
|
||||
{
|
||||
Json::Value json;
|
||||
json["result"]="ok";
|
||||
json["message"]=std::string("hello,")+name;
|
||||
auto resp=HttpResponse::newHttpJsonResponse(json);
|
||||
callback(resp);
|
||||
},
|
||||
{Get,"LoginFilter"});
|
||||
```
|
||||
|
||||
|
||||
这看起来是很方便,但是这并不适用于复杂的应用,试想假如有数十个或者数百个处理函数要注册进框架,main()函数将膨胀到不可读的程度。显然,让每个包含处理函数的类在自己的定义中完成注册是更好的选择。所以,除非你的应用逻辑非常简单,我们不推荐使用上述接口,更好的实践是,我们可以创建一个HttpSimpleController对象,如下:
|
||||
|
||||
|
||||
```c++
|
||||
/// The TestCtrl.h file
|
||||
#pragma once
|
||||
#include <drogon/HttpSimpleController.h>
|
||||
using namespace drogon;
|
||||
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
|
||||
{
|
||||
public:
|
||||
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
|
||||
PATH_LIST_BEGIN
|
||||
PATH_ADD("/test",Get);
|
||||
PATH_LIST_END
|
||||
};
|
||||
|
||||
/// The TestCtrl.cc file
|
||||
#include "TestCtrl.h"
|
||||
void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
|
||||
std::function<void (const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
//write your application logic here
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setBody("<p>Hello, world!</p>");
|
||||
resp->setExpiredTime(0);
|
||||
callback(resp);
|
||||
}
|
||||
```
|
||||
|
||||
**上面程序的大部分代码都可以由`drogon_ctl`命令创建**(这个命令是`drogon_ctl create controller TestCtr`)。用户所需做的就是添加自己的业务逻辑。在这个例子中,当客户端访问URL`http://ip/test`时,控制器简单的返回了一个`Hello, world!`页面。
|
||||
|
||||
对于JSON格式的响应,我们可以像下面这样创建控制器:
|
||||
|
||||
```c++
|
||||
/// The header file
|
||||
#pragma once
|
||||
#include <drogon/HttpSimpleController.h>
|
||||
using namespace drogon;
|
||||
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
|
||||
{
|
||||
public:
|
||||
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
|
||||
PATH_LIST_BEGIN
|
||||
//list path definitions here;
|
||||
PATH_ADD("/json", Get);
|
||||
PATH_LIST_END
|
||||
};
|
||||
|
||||
/// The source file
|
||||
#include "JsonCtrl.h"
|
||||
void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["message"] = "Hello, World!";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
callback(resp);
|
||||
}
|
||||
```
|
||||
|
||||
让我们更进一步,通过HttpController类创建一个RESTful API的例子,如下所示(忽略了实现文件):
|
||||
|
||||
```c++
|
||||
/// The header file
|
||||
#pragma once
|
||||
#include <drogon/HttpController.h>
|
||||
using namespace drogon;
|
||||
namespace api
|
||||
{
|
||||
namespace v1
|
||||
{
|
||||
class User : public drogon::HttpController<User>
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
//use METHOD_ADD to add your custom processing function here;
|
||||
METHOD_ADD(User::getInfo, "/{id}", Get); //path is /api/v1/User/{arg1}
|
||||
METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo
|
||||
METHOD_ADD(User::newUser, "/{name}", Post); //path is /api/v1/User/{arg1}
|
||||
METHOD_LIST_END
|
||||
//your declaration of processing function maybe like this:
|
||||
void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
|
||||
void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
|
||||
void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);
|
||||
public:
|
||||
User()
|
||||
{
|
||||
LOG_DEBUG << "User constructor!";
|
||||
}
|
||||
};
|
||||
} // namespace v1
|
||||
} // namespace api
|
||||
```
|
||||
|
||||
如你所见,通过`HttpController`类,用户可以同时映射路径和路径参数,这对RESTful API应用来说非常方便。
|
||||
|
||||
另外,你可以发现前面所有的处理函数接口都是异步的,处理器的响应是通过回调对象返回的。这种设计是出于对高性能的考虑,因为在异步模式下,可以使用少量的线程(比如和处理器核心数相等的线程)处理大量的并发请求。
|
||||
|
||||
编译上述的所有源文件后,我们得到了一个非常简单的web应用程序,这是一个不错的开始。**请访问GitHub上的[文档](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**
|
||||
|
||||
## 贡献方式
|
||||
|
||||
欢迎您的贡献。 请阅读[贡献指南](CONTRIBUTING.md)以获取更多的信息。
|
||||
|
||||
<a href="https://github.com/drogonframework/drogon/graphs/contributors"><img src="https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
||||
|
||||
## QQ交流群:1137909452
|
||||
|
||||
欢迎交流探讨。
|
||||
|
||||
## 微信公众号:
|
||||
|
||||

|
||||
|
||||
会不定期推送一些Drogon的使用技巧和更新信息,欢迎关注。
|
198
README.zh-TW.md
Normal file
198
README.zh-TW.md
Normal file
@ -0,0 +1,198 @@
|
||||

|
||||
|
||||
[](https://github.com/drogonframework/drogon/actions)
|
||||
[](https://conan.io/center/recipes/drogon)
|
||||
[](https://t.me/joinchat/_mMNGv0748ZkMDAx)
|
||||
[](https://discord.gg/3DvHY6Ewuj)
|
||||
[](https://cloud.docker.com/u/drogonframework/repository/docker/drogonframework/drogon)
|
||||
|
||||
[English](./README.md) | [简体中文](./README.zh-CN.md) | 繁體中文
|
||||
|
||||
**Drogon** 是一個基於 C++17/20 的 HTTP 應用程式框架,使用 Drogon 可以方便地用 C++ 建立各種類型的 Web App 伺服器端程式。
|
||||
|
||||
這個版本庫是 GitHub 上 [Drogon](https://github.com/an-tao/drogon) 的鏡像庫。**Drogon** 是作者非常喜歡的美劇《冰與火之歌:權力遊戲》中的一條龍的名字(中文譯作卓耿),和龍有關但並不是 dragon 的誤寫,為了避免不必要的誤會在此說明。
|
||||
|
||||
Drogon 是一個跨平台框架,支援 Linux、macOS、FreeBSD/OpenBSD、HaikuOS 和 Windows。主要特點如下:
|
||||
|
||||
* 網路層使用基於 epoll(macOS/FreeBSD 下是 kqueue)的非阻塞 IO 框架,提供高並行、高效能的網路 IO。詳細請見 [TFB Tests Results](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite);
|
||||
* 完全非同步的程式撰寫邏輯;
|
||||
* 支援 HTTP 1.0/1.1(伺服器端和用戶端);
|
||||
* 基於樣板(template)實作的簡單反射機制,使主程式框架、控制器(controller)和視圖(view)完全解耦;
|
||||
* 支援 cookies 和內建的 session;
|
||||
* 支援後端算繪,將控制器產生的資料交給視圖產生 HTML 頁面,視圖由 CSP 樣板檔案描述,透過 CSP 標籤將 C++ 程式碼嵌入 HTML 頁面,由 drogon 的命令列工具在編譯階段自動產生 C++ 程式碼並編譯;
|
||||
* 支援執行期的視圖頁面動態載入(動態編譯和載入 so 檔案);
|
||||
* 非常方便靈活的路徑(path)到控制器處理函式(handler)的對應方案;
|
||||
* 支援過濾器(filter)鏈,方便在控制器之前執行統一的邏輯(如登入驗證、HTTP Method 限制驗證等);
|
||||
* 支援 HTTPS(基於 OpenSSL);
|
||||
* 支援 WebSocket(伺服器端和用戶端);
|
||||
* 支援 JSON 格式的請求和回應,方便開發 RESTful API;
|
||||
* 支援檔案下載和上傳,支援 `sendfile` 系統呼叫;
|
||||
* 支援 Gzip/Brotli 壓縮傳輸;
|
||||
* 支援 pipelining;
|
||||
* 提供輕量的命令列工具 `drogon_ctl`,幫助簡化各種類別的建立和視圖程式碼的產生過程;
|
||||
* 非同步的讀寫資料庫,目前支援 PostgreSQL 和 MySQL(MariaDB)資料庫;
|
||||
* 支援非同步讀寫 Redis;
|
||||
* 基於執行緒池實作 sqlite3 資料庫的非同步讀寫,提供與上述資料庫相同的介面;
|
||||
* 支援 ARM 架構;
|
||||
* 方便的輕量級 ORM 實現,一般物件到資料庫的雙向對應;
|
||||
* 支援外掛,可透過設定檔案在載入時動態載入;
|
||||
* 支援內建插入點的 AOP;
|
||||
* 支援 C++ coroutine。
|
||||
|
||||
## 一個非常簡單的例子
|
||||
|
||||
不像大多數 C++ 框架,drogon 的主程式可以非常簡單。Drogon 使用了一些小技巧使主程式和控制器解耦。控制器的路由設定可以在控制器類別中定義或在設定檔案中完成。
|
||||
|
||||
下面是一個典型主程式的樣子:
|
||||
|
||||
```c++
|
||||
#include <drogon/drogon.h>
|
||||
using namespace drogon;
|
||||
int main()
|
||||
{
|
||||
app().setLogPath("./")
|
||||
.setLogLevel(trantor::Logger::kWarn)
|
||||
.addListener("0.0.0.0", 80)
|
||||
.setThreadNum(16)
|
||||
.enableRunAsDaemon()
|
||||
.run();
|
||||
}
|
||||
```
|
||||
|
||||
如果使用設定檔案,可以進一步簡化成:
|
||||
|
||||
```c++
|
||||
#include <drogon/drogon.h>
|
||||
using namespace drogon;
|
||||
int main()
|
||||
{
|
||||
app().loadConfigFile("./config.json").run();
|
||||
}
|
||||
```
|
||||
|
||||
當然,Drogon 也提供了一些函式,讓使用者可以在 `main()` 函式中直接加入控制器邏輯,例如,使用者可以註冊一個 lambda 處理常式到 drogon 框架中,如下所示:
|
||||
|
||||
```c++
|
||||
app().registerHandler("/test?username={name}",
|
||||
[](const HttpRequestPtr& req,
|
||||
std::function<void (const HttpResponsePtr &)> &&callback,
|
||||
const std::string &name)
|
||||
{
|
||||
Json::Value json;
|
||||
json["result"]="ok";
|
||||
json["message"]=std::string("hello,")+name;
|
||||
auto resp=HttpResponse::newHttpJsonResponse(json);
|
||||
callback(resp);
|
||||
},
|
||||
{Get,"LoginFilter"});
|
||||
```
|
||||
|
||||
這看起來很方便,但不適用於複雜的場景,試想如果有數十個或數百個處理函式要註冊進框架,`main()` 函式將變得難以閱讀。顯然,讓每個包含處理函式的類別在自己的定義中完成註冊是更好的選擇。所以,除非你的應用邏輯非常簡單,我們不建議使用上述介面,更好的做法是建立一個 HttpSimpleController 類別,如下:
|
||||
|
||||
```c++
|
||||
/// The TestCtrl.h file
|
||||
#pragma once
|
||||
#include <drogon/HttpSimpleController.h>
|
||||
using namespace drogon;
|
||||
class TestCtrl:public drogon::HttpSimpleController<TestCtrl>
|
||||
{
|
||||
public:
|
||||
void asyncHandleHttpRequest(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback) override;
|
||||
PATH_LIST_BEGIN
|
||||
PATH_ADD("/test",Get);
|
||||
PATH_LIST_END
|
||||
};
|
||||
|
||||
/// The TestCtrl.cc file
|
||||
#include "TestCtrl.h"
|
||||
void TestCtrl::asyncHandleHttpRequest(const HttpRequestPtr& req,
|
||||
std::function<void (const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
//write your application logic here
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setBody("<p>Hello, world!</p>");
|
||||
resp->setExpiredTime(0);
|
||||
callback(resp);
|
||||
}
|
||||
```
|
||||
|
||||
**上述程式的大部分程式碼都可以由 `drogon_ctl` 指令產生**(使用指令 `drogon_ctl create controller TestCtr`)。使用者只需要加入自己的業務邏輯。在這個範例中,當用戶端存取 URL `http://ip/test` 時,控制器簡單地回傳一個 `Hello, world!` 頁面。
|
||||
|
||||
對於 JSON 格式的回應,我們可以這樣建立控制器:
|
||||
|
||||
```c++
|
||||
/// The header file
|
||||
#pragma once
|
||||
#include <drogon/HttpSimpleController.h>
|
||||
using namespace drogon;
|
||||
class JsonCtrl : public drogon::HttpSimpleController<JsonCtrl>
|
||||
{
|
||||
public:
|
||||
void asyncHandleHttpRequest(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback) override;
|
||||
PATH_LIST_BEGIN
|
||||
//list path definitions here;
|
||||
PATH_ADD("/json", Get);
|
||||
PATH_LIST_END
|
||||
};
|
||||
|
||||
/// The source file
|
||||
#include "JsonCtrl.h"
|
||||
void JsonCtrl::asyncHandleHttpRequest(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["message"] = "Hello, World!";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
callback(resp);
|
||||
}
|
||||
```
|
||||
|
||||
讓我們更進一步,透過 HttpController 類別建立一個 RESTful API 的範例,如下所示(省略實作檔案):
|
||||
|
||||
```c++
|
||||
/// The header file
|
||||
#pragma once
|
||||
#include <drogon/HttpController.h>
|
||||
using namespace drogon;
|
||||
namespace api
|
||||
{
|
||||
namespace v1
|
||||
{
|
||||
class User : public drogon::HttpController<User>
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
//use METHOD_ADD to add your custom processing function here;
|
||||
METHOD_ADD(User::getInfo, "/{id}", Get); //path is /api/v1/User/{arg1}
|
||||
METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo
|
||||
METHOD_ADD(User::newUser, "/{name}", Post); //path is /api/v1/User/{arg1}
|
||||
METHOD_LIST_END
|
||||
//your declaration of processing function maybe like this:
|
||||
void getInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
|
||||
void getDetailInfo(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, int userId) const;
|
||||
void newUser(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, std::string &&userName);
|
||||
public:
|
||||
User()
|
||||
{
|
||||
LOG_DEBUG << "User constructor!";
|
||||
}
|
||||
};
|
||||
} // namespace v1
|
||||
} // namespace api
|
||||
```
|
||||
|
||||
如你所見,透過 `HttpController` 類別,使用者可以同時對應路徑和路徑參數,這對 RESTful API 應用來說非常方便。
|
||||
|
||||
另外,你可以發現前面所有的處理函式介面都是非同步的,處理器的回應是透過回呼物件回傳的。這種設計是考慮到效能,因為在非同步模式下,可以使用少量的執行緒(例如和處理器核心數相等的執行緒)處理大量的並行請求。
|
||||
|
||||
編譯上述所有原始檔案後,我們得到了一個非常簡單的網頁應用程式,這是一個不錯的開始。**請瀏覽 GitHub 上的[文件](https://drogonframework.github.io/drogon-docs/#/CHN/CHN-01-%E6%A6%82%E8%BF%B0)**
|
||||
|
||||
## 貢獻方式
|
||||
|
||||
歡迎您的貢獻。請閱讀[貢獻指南](CONTRIBUTING.md)以取得更多資訊。
|
||||
|
||||
<a href="https://github.com/drogonframework/drogon/graphs/contributors"><img src="https://contributors-svg.opencollective.com/drogon/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
||||
|
||||
## QQ 交流群:1137909452
|
||||
|
||||
歡迎交流討論。
|
@ -1 +0,0 @@
|
||||
theme: jekyll-theme-cayman
|
62
build.sh
62
build.sh
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#build drogon
|
||||
function build_drogon() {
|
||||
@ -6,6 +6,9 @@ function build_drogon() {
|
||||
#Update the submodule and initialize
|
||||
git submodule update --init
|
||||
|
||||
#Remove the config.h generated by the old version of drogon.
|
||||
rm -f lib/inc/drogon/config.h
|
||||
|
||||
#Save current directory
|
||||
current_dir="${PWD}"
|
||||
|
||||
@ -24,14 +27,20 @@ function build_drogon() {
|
||||
cd $build_dir
|
||||
|
||||
echo "Start building drogon ..."
|
||||
cmake ..
|
||||
if [ $1 -eq 1 ]; then
|
||||
cmake .. -DBUILD_TESTING=YES $cmake_gen
|
||||
elif [ $1 -eq 2 ]; then
|
||||
cmake .. -DBUILD_TESTING=YES -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=1 $cmake_gen
|
||||
else
|
||||
cmake .. -DCMAKE_BUILD_TYPE=release $cmake_gen
|
||||
fi
|
||||
|
||||
#If errors then exit
|
||||
if [ "$?" != "0" ]; then
|
||||
exit -1
|
||||
fi
|
||||
|
||||
make
|
||||
$make_program $make_flags
|
||||
|
||||
#If errors then exit
|
||||
if [ "$?" != "0" ]; then
|
||||
@ -39,11 +48,54 @@ function build_drogon() {
|
||||
fi
|
||||
|
||||
echo "Installing ..."
|
||||
sudo make install
|
||||
$make_program install
|
||||
|
||||
#Go back to the current directory
|
||||
cd $current_dir
|
||||
#Ok!
|
||||
}
|
||||
|
||||
build_drogon
|
||||
make_program=make
|
||||
make_flags=''
|
||||
cmake_gen=''
|
||||
parallel=1
|
||||
|
||||
case $(uname) in
|
||||
FreeBSD)
|
||||
nproc=$(sysctl -n hw.ncpu)
|
||||
;;
|
||||
Darwin)
|
||||
nproc=$(sysctl -n hw.ncpu) # sysctl -n hw.ncpu is the equivalent to nproc on macOS.
|
||||
;;
|
||||
*)
|
||||
nproc=$(nproc)
|
||||
;;
|
||||
esac
|
||||
|
||||
# simulate ninja's parallelism
|
||||
case nproc in
|
||||
1)
|
||||
parallel=$(( nproc + 1 ))
|
||||
;;
|
||||
2)
|
||||
parallel=$(( nproc + 1 ))
|
||||
;;
|
||||
*)
|
||||
parallel=$(( nproc + 2 ))
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -f /bin/ninja ]; then
|
||||
make_program=ninja
|
||||
cmake_gen='-GNinja'
|
||||
else
|
||||
make_flags="$make_flags -j$parallel"
|
||||
fi
|
||||
|
||||
if [ "X$1" = "X-t" ]; then
|
||||
build_drogon 1
|
||||
elif [ "X$1" = "X-tshared" ]; then
|
||||
build_drogon 2
|
||||
else
|
||||
build_drogon 0
|
||||
fi
|
||||
|
72
cmake/DrogonUtilities.cmake
Normal file
72
cmake/DrogonUtilities.cmake
Normal file
@ -0,0 +1,72 @@
|
||||
# ##############################################################################
|
||||
# function drogon_create_views(target source_path output_path
|
||||
# [TRUE to use_path_as_namespace] [prefixed namespace])
|
||||
# ##############################################################################
|
||||
function(drogon_create_views arg)
|
||||
if(ARGC LESS 3)
|
||||
message(STATUS "arguments error when calling drogon_create_views")
|
||||
return()
|
||||
endif()
|
||||
file(MAKE_DIRECTORY ${ARGV2})
|
||||
file(GLOB_RECURSE SCP_LIST ${ARGV1}/*.csp)
|
||||
foreach(cspFile ${SCP_LIST})
|
||||
file(RELATIVE_PATH
|
||||
inFile
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${cspFile})
|
||||
if(ARGC GREATER 3 AND ARGV3)
|
||||
string(REPLACE "/"
|
||||
"_"
|
||||
f1
|
||||
${inFile})
|
||||
string(REPLACE "\\"
|
||||
"_"
|
||||
f2
|
||||
${f1})
|
||||
string(REPLACE ".csp"
|
||||
""
|
||||
outputFile
|
||||
${f2})
|
||||
set(p2ns "")
|
||||
if("${ARGV3}" STREQUAL "TRUE")
|
||||
set(p2ns "--path-to-namespace")
|
||||
endif()
|
||||
if ( (ARGC EQUAL 5) AND ( NOT "${ARGV4}" STREQUAL "") )
|
||||
string(REPLACE "::" "_" nSpace ${ARGV4})
|
||||
set(outputFile "${nSpace}_${outputFile}")
|
||||
set(ns -n ${ARGV4})
|
||||
else()
|
||||
set(ns "")
|
||||
endif()
|
||||
add_custom_command(OUTPUT ${ARGV2}/${outputFile}.h ${ARGV2}/${outputFile}.cc
|
||||
COMMAND drogon_ctl
|
||||
ARGS
|
||||
create
|
||||
view
|
||||
${inFile}
|
||||
${p2ns}
|
||||
-o
|
||||
${ARGV2}
|
||||
${ns}
|
||||
DEPENDS ${cspFile}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
VERBATIM)
|
||||
set(VIEWSRC ${VIEWSRC} ${ARGV2}/${outputFile}.cc)
|
||||
else()
|
||||
get_filename_component(classname ${cspFile} NAME_WE)
|
||||
add_custom_command(OUTPUT ${ARGV2}/${classname}.h ${ARGV2}/${classname}.cc
|
||||
COMMAND drogon_ctl
|
||||
ARGS
|
||||
create
|
||||
view
|
||||
${inFile}
|
||||
-o
|
||||
${ARGV2}
|
||||
DEPENDS ${cspFile}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
VERBATIM)
|
||||
set(VIEWSRC ${VIEWSRC} ${ARGV2}/${classname}.cc)
|
||||
endif()
|
||||
endforeach()
|
||||
target_sources(${ARGV0} PRIVATE ${VIEWSRC})
|
||||
endfunction(drogon_create_views)
|
37
cmake/Packages.cmake
Normal file
37
cmake/Packages.cmake
Normal file
@ -0,0 +1,37 @@
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_PACKAGE_CONTACT "https://github.com/drogonframework/drogon")
|
||||
|
||||
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
|
||||
set(CPACK_PACKAGE_VERSION "${DROGON_VERSION}")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A C++14/17 based HTTP web application framework running on Linux/macOS/Unix/Windows")
|
||||
|
||||
# DEB
|
||||
# Figure out dependencies automatically.
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
|
||||
# Should be set automatically, but it is not.
|
||||
execute_process(COMMAND dpkg --print-architecture
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
# The default does not produce valid Debian package names.
|
||||
set(CPACK_DEBIAN_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}-0_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb")
|
||||
|
||||
# RPM
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
|
||||
# Figure out dependencies automatically.
|
||||
set(CPACK_RPM_PACKAGE_AUTOREQ ON)
|
||||
|
||||
# Should be set automatically, but it is not.
|
||||
execute_process(COMMAND uname -m
|
||||
OUTPUT_VARIABLE CPACK_RPM_PACKAGE_ARCHITECTURE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
set(CPACK_PACKAGE_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-0.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
include(CPack)
|
79
cmake/ParseAndAddDrogonTests.cmake
Normal file
79
cmake/ParseAndAddDrogonTests.cmake
Normal file
@ -0,0 +1,79 @@
|
||||
#==================================================================================================#
|
||||
# Adapted and re-written from Catch2 to work with Drogon Test #
|
||||
# #
|
||||
# Usage #
|
||||
# 1. make sure this module is in the path or add this otherwise: #
|
||||
# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") #
|
||||
# 2. make sure that you've enabled testing option for the project by the call: #
|
||||
# enable_testing() #
|
||||
# 3. add the lines to the script for testing target (sample CMakeLists.txt): #
|
||||
# project(testing_target) #
|
||||
# enable_testing() #
|
||||
# #
|
||||
# file(GLOB SOURCE_FILES "*.cpp") #
|
||||
# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) #
|
||||
# #
|
||||
# include(ParseAndAddDrogonTests) #
|
||||
# ParseAndAddDrogonTests(${PROJECT_NAME}) #
|
||||
#==================================================================================================#
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.31)
|
||||
|
||||
# This removes the contents between
|
||||
# - block comments (i.e. /* ... */)
|
||||
# - full line comments (i.e. // ... )
|
||||
# contents have been read into '${CppCode}'.
|
||||
# !keep partial line comments
|
||||
function(RemoveComments CppCode)
|
||||
string(ASCII 2 CMakeBeginBlockComment)
|
||||
string(ASCII 3 CMakeEndBlockComment)
|
||||
string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}")
|
||||
string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}")
|
||||
string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}")
|
||||
string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}")
|
||||
|
||||
set(${CppCode} "${${CppCode}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Worker function
|
||||
function(ParseFile SourceFile TestTarget)
|
||||
set(FullSourcePath ${CMAKE_CURRENT_SOURCE_DIR}/${SourceFile})
|
||||
if(NOT EXISTS ${FullSourcePath})
|
||||
return()
|
||||
endif()
|
||||
file(STRINGS ${FullSourcePath} Contents NEWLINE_CONSUME)
|
||||
|
||||
# Remove block and fullline comments
|
||||
RemoveComments(Contents)
|
||||
|
||||
# Find definition of test names
|
||||
string(REGEX MATCHALL "[ \t]*DROGON_TEST[ \t]*\\\([a-zA-Z0-9_]+\\\)" Tests "${Contents}")
|
||||
|
||||
foreach(TestLine ${Tests})
|
||||
# Strip newlines
|
||||
string(REGEX REPLACE "\\\\\n|\n" "" TestLine "${TestLine}")
|
||||
|
||||
# Get the name of the test
|
||||
string(REGEX REPLACE "[ \t]*DROGON_TEST[ \t]*" "" TestLine "${TestLine}")
|
||||
string(REGEX MATCHALL "[a-zA-Z0-9_]+" TestName "${TestLine}")
|
||||
|
||||
# Validate that a test name and tags have been provided
|
||||
list(LENGTH TestName TestNameLength)
|
||||
if(NOT TestNameLength EQUAL 1)
|
||||
message(FATAL_ERROR "${TestName} in ${SourceFile} is not a valid test name."
|
||||
" Either a bug in the Drogon Test CMake parser or a bug in the test itself")
|
||||
endif()
|
||||
|
||||
# Add the test and set its properties
|
||||
add_test(NAME "${TestName}" COMMAND ${TestTarget} -r ${TestName} ${AdditionalCatchParameters})
|
||||
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# entry point
|
||||
function(ParseAndAddDrogonTests TestTarget)
|
||||
get_target_property(SourceFiles ${TestTarget} SOURCES)
|
||||
foreach(SourceFile ${SourceFiles})
|
||||
ParseFile(${SourceFile} ${TestTarget})
|
||||
endforeach()
|
||||
endfunction()
|
62
cmake/templates/DrogonConfig.cmake.in
Normal file
62
cmake/templates/DrogonConfig.cmake.in
Normal file
@ -0,0 +1,62 @@
|
||||
# - Config file for the Drogon package
|
||||
# It defines the following variables
|
||||
# DROGON_INCLUDE_DIRS - include directories for Drogon
|
||||
# DROGON_LIBRARIES - libraries to link against
|
||||
# DROGON_EXECUTABLE - the drogon_ctl executable
|
||||
# Drogon_FOUND
|
||||
# This module defines the following IMPORTED target:
|
||||
# Drogon::Drogon
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
find_dependency(Jsoncpp REQUIRED)
|
||||
find_dependency(Trantor REQUIRED)
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT WIN32)
|
||||
find_dependency(UUID REQUIRED)
|
||||
endif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT WIN32)
|
||||
find_dependency(ZLIB REQUIRED)
|
||||
if(@pg_FOUND@)
|
||||
find_dependency(pg)
|
||||
endif()
|
||||
if(@SQLite3_FOUND@)
|
||||
find_dependency(SQLite3)
|
||||
endif()
|
||||
if(@MySQL_FOUND@)
|
||||
find_dependency(MySQL)
|
||||
endif()
|
||||
if(@Brotli_FOUND@)
|
||||
find_dependency(Brotli)
|
||||
endif()
|
||||
if(@COZ-PROFILER_FOUND@)
|
||||
find_dependency(coz-profiler)
|
||||
endif()
|
||||
if(@Hiredis_FOUND@)
|
||||
find_dependency(Hiredis)
|
||||
endif()
|
||||
if(@yaml-cpp_FOUND@)
|
||||
find_dependency(yaml-cpp)
|
||||
endif()
|
||||
if(@BUILD_SHARED_LIBS@)
|
||||
find_dependency(Threads)
|
||||
endif()
|
||||
if(@HAS_STD_FILESYSTEM_PATH@)
|
||||
find_dependency(Filesystem)
|
||||
find_package(Filesystem COMPONENTS Final REQUIRED)
|
||||
endif()
|
||||
|
||||
|
||||
# Our library dependencies (contains definitions for IMPORTED targets)
|
||||
|
||||
get_filename_component(DROGON_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
if(NOT TARGET Drogon::Drogon)
|
||||
include("${DROGON_CMAKE_DIR}/DrogonTargets.cmake")
|
||||
include("${DROGON_CMAKE_DIR}/DrogonUtilities.cmake")
|
||||
include("${DROGON_CMAKE_DIR}/ParseAndAddDrogonTests.cmake")
|
||||
endif()
|
||||
|
||||
get_target_property(DROGON_INCLUDE_DIRS Drogon::Drogon INTERFACE_INCLUDE_DIRECTORIES)
|
||||
set(DROGON_LIBRARIES Drogon::Drogon)
|
||||
set(DROGON_EXECUTABLE drogon_ctl)
|
@ -4,7 +4,9 @@
|
||||
#cmakedefine01 LIBPQ_SUPPORTS_BATCH_MODE
|
||||
#cmakedefine01 USE_MYSQL
|
||||
#cmakedefine01 USE_SQLITE3
|
||||
#cmakedefine01 HAS_STD_FILESYSTEM_PATH
|
||||
#cmakedefine OpenSSL_FOUND
|
||||
#cmakedefine Boost_FOUND
|
||||
|
||||
#cmakedefine COMPILATION_FLAGS "@COMPILATION_FLAGS@@DROGON_CXX_STANDARD@"
|
||||
#cmakedefine COMPILER_COMMAND "@COMPILER_COMMAND@"
|
7
cmake/templates/version.h.in
Normal file
7
cmake/templates/version.h.in
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define MAJOR @DROGON_MAJOR_VERSION@
|
||||
#define MINOR @DROGON_MINOR_VERSION@
|
||||
#define PATCH @DROGON_PATCH_VERSION@
|
||||
#define DROGON_VERSION "@DROGON_VERSION_STRING@"
|
||||
#define DROGON_VERSION_SHA1 "@GIT_SHA1@"
|
7
cmake/tests/check_has_std_filesystem_path.cc
Executable file
7
cmake/tests/check_has_std_filesystem_path.cc
Executable file
@ -0,0 +1,7 @@
|
||||
#include <filesystem>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::filesystem::path aPath("../");
|
||||
return 0;
|
||||
}
|
7
cmake/tests/normal_uuid_lib_test.cc
Normal file
7
cmake/tests/normal_uuid_lib_test.cc
Normal file
@ -0,0 +1,7 @@
|
||||
#include <uuid.h>
|
||||
int main()
|
||||
{
|
||||
uuid_t uu;
|
||||
uuid_generate(uu);
|
||||
return 0;
|
||||
}
|
8
cmake/tests/ossp_uuid_lib_test.cc
Normal file
8
cmake/tests/ossp_uuid_lib_test.cc
Normal file
@ -0,0 +1,8 @@
|
||||
#include <uuid.h>
|
||||
int main()
|
||||
{
|
||||
uuid_t *uuid;
|
||||
uuid_create(&uuid);
|
||||
uuid_make(uuid, UUID_MAKE_V1);
|
||||
return 0;
|
||||
}
|
@ -2,11 +2,8 @@
|
||||
|
||||
int main()
|
||||
{
|
||||
PQisInBatchMode(NULL);
|
||||
PQbatchIsAborted(NULL);
|
||||
PQqueriesInBatch(NULL);
|
||||
PQbeginBatchMode(NULL);
|
||||
PQendBatchMode(NULL);
|
||||
PQsendEndBatch(NULL);
|
||||
PQgetNextQuery(NULL);
|
||||
PQenterPipelineMode(NULL);
|
||||
PQexitPipelineMode(NULL);
|
||||
PQpipelineSync(NULL);
|
||||
PQpipelineStatus(NULL);
|
||||
}
|
||||
|
50
cmake_modules/FindBrotli.cmake
Normal file
50
cmake_modules/FindBrotli.cmake
Normal file
@ -0,0 +1,50 @@
|
||||
# ***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# This software is licensed as described in the file COPYING, which you should
|
||||
# have received as part of this distribution. The terms are also available at
|
||||
# https://curl.haxx.se/docs/copyright.html.
|
||||
#
|
||||
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
# copies of the Software, and permit persons to whom the Software is furnished
|
||||
# to do so, under the terms of the COPYING file.
|
||||
#
|
||||
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
# KIND, either express or implied.
|
||||
#
|
||||
# ##############################################################################
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_path(BROTLI_INCLUDE_DIR "brotli/decode.h")
|
||||
|
||||
find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon brotlicommon-static)
|
||||
find_library(BROTLIDEC_LIBRARY NAMES brotlidec brotlidec-static)
|
||||
find_library(BROTLIENC_LIBRARY NAMES brotlienc brotlienc-static)
|
||||
|
||||
find_package_handle_standard_args(Brotli
|
||||
REQUIRED_VARS
|
||||
BROTLIDEC_LIBRARY
|
||||
BROTLIENC_LIBRARY
|
||||
BROTLICOMMON_LIBRARY
|
||||
BROTLI_INCLUDE_DIR
|
||||
FAIL_MESSAGE
|
||||
"Could NOT find BROTLI")
|
||||
|
||||
set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})
|
||||
set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY}
|
||||
${BROTLIENC_LIBRARY} ${BROTLICOMMON_LIBRARY})
|
||||
|
||||
if(Brotli_FOUND)
|
||||
add_library(Brotli_lib INTERFACE IMPORTED)
|
||||
set_target_properties(Brotli_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${BROTLI_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${BROTLI_LIBRARIES}")
|
||||
endif(Brotli_FOUND)
|
261
cmake_modules/FindFilesystem.cmake
Normal file
261
cmake_modules/FindFilesystem.cmake
Normal file
@ -0,0 +1,261 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
|
||||
FindFilesystem
|
||||
##############
|
||||
|
||||
This module supports the C++17 standard library's filesystem utilities. Use the
|
||||
:imp-target:`std::filesystem` imported target to
|
||||
|
||||
Options
|
||||
*******
|
||||
|
||||
The ``COMPONENTS`` argument to this module supports the following values:
|
||||
|
||||
.. find-component:: Experimental
|
||||
:name: fs.Experimental
|
||||
|
||||
Allows the module to find the "experimental" Filesystem TS version of the
|
||||
Filesystem library. This is the library that should be used with the
|
||||
``std::experimental::filesystem`` namespace.
|
||||
|
||||
.. find-component:: Final
|
||||
:name: fs.Final
|
||||
|
||||
Finds the final C++17 standard version of the filesystem library.
|
||||
|
||||
If no components are provided, behaves as if the
|
||||
:find-component:`fs.Final` component was specified.
|
||||
|
||||
If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are
|
||||
provided, first looks for ``Final``, and falls back to ``Experimental`` in case
|
||||
of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all
|
||||
:ref:`variables <fs.variables>` will refer to the ``Final`` version.
|
||||
|
||||
|
||||
Imported Targets
|
||||
****************
|
||||
|
||||
.. imp-target:: std::filesystem
|
||||
|
||||
The ``std::filesystem`` imported target is defined when any requested
|
||||
version of the C++ filesystem library has been found, whether it is
|
||||
*Experimental* or *Final*.
|
||||
|
||||
If no version of the filesystem library is available, this target will not
|
||||
be defined.
|
||||
|
||||
.. note::
|
||||
This target has ``cxx_std_17`` as an ``INTERFACE``
|
||||
:ref:`compile language standard feature <req-lang-standards>`. Linking
|
||||
to this target will automatically enable C++17 if no later standard
|
||||
version is already required on the linking target.
|
||||
|
||||
|
||||
.. _fs.variables:
|
||||
|
||||
Variables
|
||||
*********
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL
|
||||
|
||||
Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++
|
||||
filesystem library was found, otherwise ``FALSE``.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_HAVE_FS
|
||||
|
||||
Set to ``TRUE`` when a filesystem header was found.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_HEADER
|
||||
|
||||
Set to either ``filesystem`` or ``experimental/filesystem`` depending on
|
||||
whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was
|
||||
found.
|
||||
|
||||
.. variable:: CXX_FILESYSTEM_NAMESPACE
|
||||
|
||||
Set to either ``std::filesystem`` or ``std::experimental::filesystem``
|
||||
depending on whether :find-component:`fs.Final` or
|
||||
:find-component:`fs.Experimental` was found.
|
||||
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
Using `find_package(Filesystem)` with no component arguments:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(Filesystem REQUIRED)
|
||||
|
||||
add_executable(my-program main.cpp)
|
||||
target_link_libraries(my-program PRIVATE std::filesystem)
|
||||
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
|
||||
if(TARGET std::filesystem)
|
||||
# This module has already been processed. Don't do it again.
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Ignore filesystem check if version too low
|
||||
if(CMAKE_VERSION VERSION_LESS 3.10)
|
||||
set(CXX_FILESYSTEM_HAVE_FS FALSE CACHE BOOL "TRUE if we have the C++ filesystem headers")
|
||||
set(Filesystem_FOUND FALSE CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
include(CMakePushCheckState)
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
# If we're not cross-compiling, try to run test executables.
|
||||
# Otherwise, assume that compile + link is a sufficient check.
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
include(CheckCXXSourceCompiles)
|
||||
macro(_cmcm_check_cxx_source code var)
|
||||
check_cxx_source_compiles("${code}" ${var})
|
||||
endmacro()
|
||||
else()
|
||||
include(CheckCXXSourceRuns)
|
||||
macro(_cmcm_check_cxx_source code var)
|
||||
check_cxx_source_runs("${code}" ${var})
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
cmake_push_check_state()
|
||||
|
||||
set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY})
|
||||
|
||||
# All of our tests required C++17 or later
|
||||
set(BACKUP_CXX_STANDARD "${CMAKE_CXX_STANDARD}")
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Normalize and check the component list we were given
|
||||
set(want_components ${Filesystem_FIND_COMPONENTS})
|
||||
if(Filesystem_FIND_COMPONENTS STREQUAL "")
|
||||
set(want_components Final)
|
||||
endif()
|
||||
|
||||
# Warn on any unrecognized components
|
||||
set(extra_components ${want_components})
|
||||
list(REMOVE_ITEM extra_components Final Experimental)
|
||||
foreach(component IN LISTS extra_components)
|
||||
message(WARNING "Extraneous find_package component for Filesystem: ${component}")
|
||||
endforeach()
|
||||
|
||||
# Detect which of Experimental and Final we should look for
|
||||
set(find_experimental TRUE)
|
||||
set(find_final TRUE)
|
||||
if(NOT "Final" IN_LIST want_components)
|
||||
set(find_final FALSE)
|
||||
endif()
|
||||
if(NOT "Experimental" IN_LIST want_components)
|
||||
set(find_experimental FALSE)
|
||||
endif()
|
||||
|
||||
if(find_final)
|
||||
check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER)
|
||||
mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
if(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
# We found the non-experimental header. Don't bother looking for the
|
||||
# experimental one.
|
||||
set(find_experimental FALSE)
|
||||
endif()
|
||||
else()
|
||||
set(_CXX_FILESYSTEM_HAVE_HEADER FALSE)
|
||||
endif()
|
||||
|
||||
if(find_experimental)
|
||||
check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
else()
|
||||
set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE)
|
||||
endif()
|
||||
|
||||
if(_CXX_FILESYSTEM_HAVE_HEADER)
|
||||
set(_have_fs TRUE)
|
||||
set(_fs_header filesystem)
|
||||
set(_fs_namespace std::filesystem)
|
||||
set(_is_experimental FALSE)
|
||||
elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER)
|
||||
set(_have_fs TRUE)
|
||||
set(_fs_header experimental/filesystem)
|
||||
set(_fs_namespace std::experimental::filesystem)
|
||||
set(_is_experimental TRUE)
|
||||
else()
|
||||
set(_have_fs FALSE)
|
||||
endif()
|
||||
|
||||
set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers")
|
||||
set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs")
|
||||
set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs")
|
||||
set(CXX_FILESYSTEM_IS_EXPERIMENTAL ${_is_experimental} CACHE BOOL "TRUE if the C++ filesystem library is the experimental version")
|
||||
|
||||
set(_found FALSE)
|
||||
|
||||
if(CXX_FILESYSTEM_HAVE_FS)
|
||||
# We have some filesystem library available. Do link checks
|
||||
string(CONFIGURE [[
|
||||
#include <cstdio>
|
||||
#include <@CXX_FILESYSTEM_HEADER@>
|
||||
|
||||
int main() {
|
||||
auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path();
|
||||
printf("%s", cwd.generic_string().c_str());
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
]] code @ONLY)
|
||||
|
||||
# HACK: Needed to compile correctly on Yocto Linux
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
|
||||
OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
set(CMAKE_REQUIRED_FLAGS ${prev_req_flags} -std=c++17)
|
||||
endif ()
|
||||
# Check a simple filesystem program without any linker flags
|
||||
_cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
|
||||
set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})
|
||||
|
||||
if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES})
|
||||
# Add the libstdc++ flag
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs)
|
||||
_cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED})
|
||||
if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
# Try the libc++ flag
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs)
|
||||
_cmcm_check_cxx_source("${code}" CXX_FILESYSTEM_CPPFS_NEEDED)
|
||||
set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(can_link)
|
||||
add_library(std::filesystem INTERFACE IMPORTED)
|
||||
set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17)
|
||||
set(_found TRUE)
|
||||
|
||||
if(CXX_FILESYSTEM_NO_LINK_NEEDED)
|
||||
# Nothing to add...
|
||||
elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED)
|
||||
set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lstdc++fs)
|
||||
elseif(CXX_FILESYSTEM_CPPFS_NEEDED)
|
||||
set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_LINK_LIBRARIES -lc++fs)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cmake_pop_check_state()
|
||||
|
||||
set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE)
|
||||
|
||||
if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND)
|
||||
message(FATAL_ERROR "Cannot run simple program using std::filesystem")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD "${BACKUP_CXX_STANDARD}")
|
41
cmake_modules/FindHiredis.cmake
Normal file
41
cmake_modules/FindHiredis.cmake
Normal file
@ -0,0 +1,41 @@
|
||||
# Try to find hiredis
|
||||
# Once done, this will define
|
||||
#
|
||||
# HIREDIS_FOUND - system has hiredis
|
||||
# HIREDIS_INCLUDE_DIRS - hiredis include directories
|
||||
# HIREDIS_LIBRARIES - libraries need to use hiredis
|
||||
|
||||
if (HIREDIS_INCLUDE_DIRS AND HIREDIS_LIBRARIES)
|
||||
set(HIREDIS_FIND_QUIETLY TRUE)
|
||||
set(Hiredis_FOUND TRUE)
|
||||
else ()
|
||||
find_path(
|
||||
HIREDIS_INCLUDE_DIR
|
||||
NAMES hiredis/hiredis.h
|
||||
HINTS ${HIREDIS_ROOT_DIR}
|
||||
PATH_SUFFIXES include)
|
||||
|
||||
find_library(
|
||||
HIREDIS_LIBRARY
|
||||
NAMES hiredis
|
||||
HINTS ${HIREDIS_ROOT_DIR}
|
||||
PATH_SUFFIXES ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
set(HIREDIS_INCLUDE_DIRS ${HIREDIS_INCLUDE_DIR})
|
||||
set(HIREDIS_LIBRARIES ${HIREDIS_LIBRARY})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
Hiredis DEFAULT_MSG HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(HIREDIS_LIBRARY HIREDIS_INCLUDE_DIR)
|
||||
endif ()
|
||||
|
||||
if(Hiredis_FOUND)
|
||||
add_library(Hiredis_lib INTERFACE IMPORTED)
|
||||
set_target_properties(Hiredis_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${HIREDIS_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${HIREDIS_LIBRARIES}")
|
||||
endif(Hiredis_FOUND)
|
@ -2,62 +2,72 @@
|
||||
#
|
||||
# Find the jsoncpp includes and library
|
||||
#
|
||||
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
|
||||
# if you nee to add a custom library search path, do it via via
|
||||
# CMAKE_PREFIX_PATH
|
||||
#
|
||||
# This module defines
|
||||
# JSONCPP_INCLUDE_DIRS, where to find header, etc.
|
||||
# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp.
|
||||
# JSONCPP_FOUND, If false, do not try to use jsoncpp.
|
||||
# JSONCPP_INCLUDE_PREFIX, include prefix for jsoncpp
|
||||
# This module defines JSONCPP_INCLUDE_DIRS, where to find header, etc.
|
||||
# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp. JSONCPP_FOUND, If
|
||||
# false, do not try to use jsoncpp.
|
||||
# Jsoncpp_lib - The imported target library.
|
||||
|
||||
# only look in default directories
|
||||
find_path(
|
||||
JSONCPP_INCLUDE_DIR
|
||||
NAMES jsoncpp/json/json.h json/json.h
|
||||
DOC "jsoncpp include dir"
|
||||
)
|
||||
find_path(JSONCPP_INCLUDE_DIRS
|
||||
NAMES json/json.h
|
||||
DOC "jsoncpp include dir"
|
||||
PATH_SUFFIXES jsoncpp)
|
||||
|
||||
find_library(
|
||||
JSONCPP_LIBRARY
|
||||
NAMES jsoncpp
|
||||
DOC "jsoncpp library"
|
||||
)
|
||||
find_library(JSONCPP_LIBRARIES NAMES jsoncpp DOC "jsoncpp library")
|
||||
|
||||
set(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR})
|
||||
set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY})
|
||||
# debug library on windows same naming convention as in qt (appending debug
|
||||
# library with d) boost is using the same "hack" as us with "optimized" and
|
||||
# "debug" if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# find_library(JSONCPP_LIBRARIES_DEBUG NAMES jsoncppd DOC "jsoncpp debug
|
||||
# library") if("${JSONCPP_LIBRARIES_DEBUG}" STREQUAL "JSONCPP_LIBRARIES_DEBUG-
|
||||
# NOTFOUND") set(JSONCPP_LIBRARIES_DEBUG ${JSONCPP_LIBRARIES}) endif()
|
||||
|
||||
# debug library on windows
|
||||
# same naming convention as in qt (appending debug library with d)
|
||||
# boost is using the same "hack" as us with "optimized" and "debug"
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
find_library(
|
||||
JSONCPP_LIBRARY_DEBUG
|
||||
NAMES jsoncppd
|
||||
DOC "jsoncpp debug library"
|
||||
)
|
||||
# set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug
|
||||
# ${JSONCPP_LIBRARIES_DEBUG})
|
||||
|
||||
set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug ${JSONCPP_LIBRARY_DEBUG})
|
||||
# endif()
|
||||
|
||||
endif()
|
||||
|
||||
# find JSONCPP_INCLUDE_PREFIX
|
||||
find_path(
|
||||
JSONCPP_INCLUDE_PREFIX
|
||||
NAMES json.h
|
||||
PATH_SUFFIXES jsoncpp/json json
|
||||
)
|
||||
|
||||
if (${JSONCPP_INCLUDE_PREFIX} MATCHES "jsoncpp")
|
||||
set(JSONCPP_INCLUDE_PREFIX "jsoncpp")
|
||||
set(JSONCPP_INCLUDE_DIRS "${JSONCPP_INCLUDE_DIRS}/jsoncpp")
|
||||
else()
|
||||
set(JSONCPP_INCLUDE_PREFIX "")
|
||||
endif()
|
||||
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE
|
||||
# if all listed variables are TRUE, hide their existence from configuration view
|
||||
# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE if all
|
||||
# listed variables are TRUE, hide their existence from configuration view
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(jsoncpp DEFAULT_MSG
|
||||
JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
|
||||
mark_as_advanced (JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
|
||||
find_package_handle_standard_args(Jsoncpp
|
||||
DEFAULT_MSG
|
||||
JSONCPP_INCLUDE_DIRS
|
||||
JSONCPP_LIBRARIES)
|
||||
mark_as_advanced(JSONCPP_INCLUDE_DIRS JSONCPP_LIBRARIES)
|
||||
|
||||
if(Jsoncpp_FOUND)
|
||||
if(NOT EXISTS ${JSONCPP_INCLUDE_DIRS}/json/version.h)
|
||||
message(FATAL_ERROR "Error: jsoncpp lib is too old.....stop")
|
||||
endif()
|
||||
if(NOT WIN32)
|
||||
execute_process(
|
||||
COMMAND cat ${JSONCPP_INCLUDE_DIRS}/json/version.h
|
||||
COMMAND grep JSONCPP_VERSION_STRING
|
||||
COMMAND sed -e "s/.*define/define/"
|
||||
COMMAND awk "{ printf \$3 }"
|
||||
COMMAND sed -e "s/\"//g"
|
||||
OUTPUT_VARIABLE jsoncpp_ver)
|
||||
if(NOT Jsoncpp_FIND_QUIETLY)
|
||||
message(STATUS "jsoncpp version:" ${jsoncpp_ver})
|
||||
endif()
|
||||
if(jsoncpp_ver LESS 1.7)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"jsoncpp lib is too old, please get new version from https://github.com/open-source-parsers/jsoncpp"
|
||||
)
|
||||
endif(jsoncpp_ver LESS 1.7)
|
||||
endif()
|
||||
if (NOT TARGET Jsoncpp_lib)
|
||||
add_library(Jsoncpp_lib INTERFACE IMPORTED)
|
||||
endif()
|
||||
set_target_properties(Jsoncpp_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${JSONCPP_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${JSONCPP_LIBRARIES}")
|
||||
|
||||
endif(Jsoncpp_FOUND)
|
||||
|
@ -1,114 +1,144 @@
|
||||
#--------------------------------------------------------
|
||||
# --------------------------------------------------------
|
||||
# Copyright (C) 1995-2007 MySQL AB
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of version 2 of the GNU General Public License as
|
||||
# published by the Free Software Foundation.
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of version 2 of the GNU General Public License as published by the
|
||||
# Free Software Foundation.
|
||||
#
|
||||
# There are special exceptions to the terms and conditions of the GPL
|
||||
# as it is applied to this software. View the full text of the exception
|
||||
# in file LICENSE.exceptions in the top-level directory of this software
|
||||
# distribution.
|
||||
# There are special exceptions to the terms and conditions of the GPL as it is
|
||||
# applied to this software. View the full text of the exception in file
|
||||
# LICENSE.exceptions in the top-level directory of this software distribution.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||
# Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
#
|
||||
# The MySQL Connector/ODBC is licensed under the terms of the
|
||||
# GPL, like most MySQL Connectors. There are special exceptions
|
||||
# to the terms and conditions of the GPL as it is applied to
|
||||
# this software, see the FLOSS License Exception available on
|
||||
# mysql.com.
|
||||
# The MySQL Connector/ODBC is licensed under the terms of the GPL, like most
|
||||
# MySQL Connectors. There are special exceptions to the terms and conditions of
|
||||
# the GPL as it is applied to this software, see the FLOSS License Exception
|
||||
# available on mysql.com.
|
||||
# MySQL_lib - The imported target library.
|
||||
|
||||
##########################################################################
|
||||
# ##############################################################################
|
||||
|
||||
# -------------- FIND MYSQL_INCLUDE_DIRS ------------------
|
||||
find_path(MARIADB_INCLUDE_DIRS
|
||||
NAMES mysql.h
|
||||
PATH_SUFFIXES mariadb
|
||||
PATHS /usr/include/mysql
|
||||
/usr/local/include/mysql
|
||||
/usr/include/mariadb
|
||||
/usr/local/include/mariadb
|
||||
/opt/mysql/mysql/include
|
||||
/opt/mysql/mysql/include/mysql
|
||||
/opt/mysql/include
|
||||
/opt/local/include/mysql5
|
||||
/usr/local/mysql/include
|
||||
/usr/local/mysql/include/mysql
|
||||
/usr/local/mariadb/include
|
||||
/usr/local/mariadb/include/mariadb
|
||||
/opt/rh/rh-mariadb105/root/usr/include
|
||||
/opt/rh/rh-mariadb105/root/usr/include/mysql
|
||||
$ENV{ProgramFiles}/MySQL/*/include
|
||||
$ENV{SystemDrive}/MySQL/*/include)
|
||||
|
||||
#-------------- FIND MYSQL_INCLUDE_DIR ------------------
|
||||
FIND_PATH(MYSQL_INCLUDE_DIR mysql.h
|
||||
/usr/include/mysql
|
||||
/usr/local/include/mysql
|
||||
/opt/mysql/mysql/include
|
||||
/opt/mysql/mysql/include/mysql
|
||||
/opt/mysql/include
|
||||
/opt/local/include/mysql5
|
||||
/usr/local/mysql/include
|
||||
/usr/local/mysql/include/mysql
|
||||
$ENV{ProgramFiles}/MySQL/*/include
|
||||
$ENV{SystemDrive}/MySQL/*/include)
|
||||
find_path(MYSQL_INCLUDE_DIRS
|
||||
NAMES mysql.h
|
||||
PATH_SUFFIXES mysql
|
||||
PATHS /usr/include/mysql
|
||||
/usr/local/include/mysql
|
||||
/usr/include/mariadb
|
||||
/usr/local/include/mariadb
|
||||
/opt/mysql/mysql/include
|
||||
/opt/mysql/mysql/include/mysql
|
||||
/opt/mysql/include
|
||||
/opt/local/include/mysql5
|
||||
/usr/local/mysql/include
|
||||
/usr/local/mysql/include/mysql
|
||||
/usr/local/mariadb/include
|
||||
/usr/local/mariadb/include/mariadb
|
||||
/opt/rh/rh-mariadb105/root/usr/include
|
||||
/opt/rh/rh-mariadb105/root/usr/include/mysql
|
||||
$ENV{ProgramFiles}/MySQL/*/include
|
||||
$ENV{SystemDrive}/MySQL/*/include)
|
||||
|
||||
#----------------- FIND MYSQL_LIB_DIR -------------------
|
||||
IF (WIN32)
|
||||
# Set lib path suffixes
|
||||
# dist = for mysql binary distributions
|
||||
# build = for custom built tree
|
||||
IF (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
SET(libsuffixDist debug)
|
||||
SET(libsuffixBuild Debug)
|
||||
ELSE (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
SET(libsuffixDist opt)
|
||||
SET(libsuffixBuild Release)
|
||||
ADD_DEFINITIONS(-DDBUG_OFF)
|
||||
ENDIF (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
if(EXISTS "${MARIADB_INCLUDE_DIRS}/mysql.h")
|
||||
set(MYSQL_INCLUDE_DIRS ${MARIADB_INCLUDE_DIRS})
|
||||
elseif(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql.h")
|
||||
|
||||
FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient
|
||||
PATHS
|
||||
$ENV{MYSQL_DIR}/lib/${libsuffixDist}
|
||||
$ENV{MYSQL_DIR}/libmysql
|
||||
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
|
||||
$ENV{MYSQL_DIR}/client/${libsuffixBuild}
|
||||
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
|
||||
$ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist}
|
||||
$ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist})
|
||||
ELSE (WIN32)
|
||||
FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient_r mariadbclient
|
||||
PATHS
|
||||
/usr/lib/mysql
|
||||
/usr/local/lib/mysql
|
||||
/usr/local/mysql/lib
|
||||
/usr/local/mysql/lib/mysql
|
||||
/opt/local/mysql5/lib
|
||||
/opt/local/lib/mysql5/mysql
|
||||
/opt/mysql/mysql/lib/mysql
|
||||
/opt/mysql/lib/mysql)
|
||||
ENDIF (WIN32)
|
||||
elseif(EXISTS "${MYSQL_INCLUDE_DIRS}/mysql/mysql.h")
|
||||
set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDE_DIRS}/mysql)
|
||||
endif()
|
||||
|
||||
IF(MYSQL_LIB)
|
||||
GET_FILENAME_COMPONENT(MYSQL_LIB_DIR ${MYSQL_LIB} PATH)
|
||||
ENDIF(MYSQL_LIB)
|
||||
# ----------------- FIND MYSQL_LIBRARIES_DIR -------------------
|
||||
if(WIN32)
|
||||
# Set lib path suffixes dist = for mysql binary distributions build = for
|
||||
# custom built tree
|
||||
if(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set(libsuffixDist debug)
|
||||
set(libsuffixBuild Debug)
|
||||
else(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
set(libsuffixDist opt)
|
||||
set(libsuffixBuild Release)
|
||||
add_definitions(-DDBUG_OFF)
|
||||
endif(CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
|
||||
set(MYSQL_VERSION_STRING "")
|
||||
find_library(MYSQL_LIBRARIES
|
||||
NAMES mariadbclient
|
||||
PATHS $ENV{MYSQL_DIR}/lib/${libsuffixDist}
|
||||
$ENV{MYSQL_DIR}/libmysql
|
||||
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
|
||||
$ENV{MYSQL_DIR}/client/${libsuffixBuild}
|
||||
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
|
||||
$ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist}
|
||||
$ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist})
|
||||
else(WIN32)
|
||||
find_library(MYSQL_LIBRARIES
|
||||
NAMES mysqlclient_r mariadbclient mariadb
|
||||
PATHS /usr/lib/mysql
|
||||
/usr/lib/mariadb
|
||||
/usr/local/lib/mysql
|
||||
/usr/local/lib/mariadb
|
||||
/usr/local/mysql/lib
|
||||
/usr/local/mysql/lib/mysql
|
||||
/opt/local/mysql5/lib
|
||||
/opt/local/lib/mysql5/mysql
|
||||
/opt/mysql/mysql/lib/mysql
|
||||
/opt/mysql/lib/mysql
|
||||
/opt/rh/rh-mariadb105/root/usr/lib64)
|
||||
endif(WIN32)
|
||||
|
||||
EXEC_PROGRAM (grep ARGS "MARIADB_BASE_VERSION ${MYSQL_INCLUDE_DIR}/*.h|awk '{print $3}'" OUTPUT_VARIABLE MYSQL_VERSION_STRING)
|
||||
if(MYSQL_INCLUDE_DIRS AND MYSQL_LIBRARIES)
|
||||
message(STATUS "MySQL Include dir: ${MYSQL_INCLUDE_DIRS}")
|
||||
message(STATUS "MySQL client libraries: ${MYSQL_LIBRARIES}")
|
||||
elseif(MySQL_FIND_REQUIRED)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIRS} library dir: ${MYSQL_LIBRARIES_DIR}"
|
||||
)
|
||||
endif(MYSQL_INCLUDE_DIRS AND MYSQL_LIBRARIES)
|
||||
|
||||
IF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR)
|
||||
SET(MYSQL_FOUND TRUE)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(MySQL
|
||||
DEFAULT_MSG
|
||||
MYSQL_LIBRARIES
|
||||
MYSQL_INCLUDE_DIRS)
|
||||
# Copy the results to the output variables.
|
||||
if(MySQL_FOUND)
|
||||
add_library(MySQL_lib INTERFACE IMPORTED)
|
||||
set_target_properties(MySQL_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${MYSQL_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${MYSQL_LIBRARIES}")
|
||||
else(MySQL_FOUND)
|
||||
set(MYSQL_LIBRARIES)
|
||||
set(MYSQL_INCLUDE_DIRS)
|
||||
endif(MySQL_FOUND)
|
||||
|
||||
FIND_LIBRARY(MYSQL_ZLIB zlib PATHS ${MYSQL_LIB_DIR})
|
||||
FIND_LIBRARY(MYSQL_TAOCRYPT taocrypt PATHS ${MYSQL_LIB_DIR})
|
||||
IF (MYSQL_LIB)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_LIB})
|
||||
ELSE()
|
||||
SET(MYSQL_CLIENT_LIBS mysqlclient_r)
|
||||
ENDIF()
|
||||
IF (MYSQL_ZLIB)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} zlib)
|
||||
ENDIF (MYSQL_ZLIB)
|
||||
IF (MYSQL_TAOCRYPT)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} taocrypt)
|
||||
ENDIF (MYSQL_TAOCRYPT)
|
||||
# Added needed mysqlclient dependencies on Windows
|
||||
IF (WIN32)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} ws2_32)
|
||||
ENDIF (WIN32)
|
||||
|
||||
MESSAGE(STATUS "MySQL Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}")
|
||||
MESSAGE(STATUS "MySQL client libraries: ${MYSQL_CLIENT_LIBS}")
|
||||
ELSEIF (MySQL_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}")
|
||||
ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR)
|
||||
mark_as_advanced(MYSQL_INCLUDE_DIRS MYSQL_LIBRARIES)
|
||||
|
@ -1,37 +1,43 @@
|
||||
# Copyright (C) 2007-2009 LuaDist.
|
||||
# Created by Peter Kapec <kapecp@gmail.com>
|
||||
# Redistribution and use of this file is allowed according to the terms of the MIT license.
|
||||
# For details see the COPYRIGHT file distributed with LuaDist.
|
||||
# Note:
|
||||
# Searching headers and libraries is very simple and is NOT as powerful as scripts
|
||||
# distributed with CMake, because LuaDist defines directories to search for.
|
||||
# Everyone is encouraged to contact the author with improvements. Maybe this file
|
||||
# becomes part of CMake distribution sometimes.
|
||||
# Copyright (C) 2007-2009 LuaDist. Created by Peter Kapec <kapecp@gmail.com>
|
||||
# Redistribution and use of this file is allowed according to the terms of the
|
||||
# MIT license. For details see the COPYRIGHT file distributed with LuaDist.
|
||||
# Note: Searching headers and libraries is very simple and is NOT as powerful as
|
||||
# scripts distributed with CMake, because LuaDist defines directories to search
|
||||
# for. Everyone is encouraged to contact the author with improvements. Maybe
|
||||
# this file becomes part of CMake distribution sometimes.
|
||||
|
||||
# - Find sqlite3
|
||||
# Find the native SQLITE3 headers and libraries.
|
||||
# * Find sqlite3 Find the native SQLITE3 headers and libraries.
|
||||
#
|
||||
# SQLITE3_INCLUDE_DIRS - where to find sqlite3.h, etc.
|
||||
# SQLITE3_LIBRARIES - List of libraries when using sqlite.
|
||||
# SQLITE3_FOUND - True if sqlite found.
|
||||
# SQLITE3_INCLUDE_DIRS - where to find sqlite3.h, etc.
|
||||
# SQLITE3_LIBRARIES - List of libraries when using sqlite.
|
||||
# SQLite3_FOUND - True if sqlite3 found.
|
||||
# SQLite3_lib - The imported target library.
|
||||
|
||||
# Look for the header file.
|
||||
FIND_PATH(SQLITE3_INCLUDE_DIR NAMES sqlite3.h)
|
||||
find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h)
|
||||
|
||||
# Look for the library.
|
||||
FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3)
|
||||
find_library(SQLITE3_LIBRARIES NAMES sqlite3)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE if all listed variables are TRUE.
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLITE3 DEFAULT_MSG SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR)
|
||||
# Handle the QUIETLY and REQUIRED arguments and set SQLite3_FOUND to TRUE if all
|
||||
# listed variables are TRUE.
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SQLite3
|
||||
DEFAULT_MSG
|
||||
SQLITE3_LIBRARIES
|
||||
SQLITE3_INCLUDE_DIRS)
|
||||
|
||||
# Copy the results to the output variables.
|
||||
IF(SQLITE3_FOUND)
|
||||
SET(SQLITE3_LIBRARIES ${SQLITE3_LIBRARY})
|
||||
SET(SQLITE3_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIR})
|
||||
ELSE(SQLITE3_FOUND)
|
||||
SET(SQLITE3_LIBRARIES)
|
||||
SET(SQLITE3_INCLUDE_DIRS)
|
||||
ENDIF(SQLITE3_FOUND)
|
||||
if(SQLite3_FOUND)
|
||||
add_library(SQLite3_lib INTERFACE IMPORTED)
|
||||
set_target_properties(SQLite3_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${SQLITE3_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${SQLITE3_LIBRARIES}")
|
||||
else(SQLite3_FOUND)
|
||||
set(SQLITE3_LIBRARIES)
|
||||
set(SQLITE3_INCLUDE_DIRS)
|
||||
endif(SQLite3_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)
|
||||
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)
|
||||
|
@ -1,119 +1,118 @@
|
||||
# - Try to find UUID
|
||||
# Once done this will define
|
||||
# * Try to find UUID Once done this will define
|
||||
#
|
||||
# UUID_FOUND - system has UUID
|
||||
# UUID_INCLUDE_DIRS - the UUID include directory
|
||||
# UUID_LIBRARIES - Link these to use UUID
|
||||
# UUID_DEFINITIONS - Compiler switches required for using UUID
|
||||
# UUID_LIBRARIES - Link these to use UUID UUID_DEFINITIONS - Compiler switches
|
||||
# required for using UUID
|
||||
#
|
||||
# Copyright (c) 2006 Andreas Schneider <mail@cynapses.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
# Redistribution and use is allowed according to the terms of the New BSD
|
||||
# license. For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
|
||||
if(UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(UUID_FOUND TRUE)
|
||||
else()
|
||||
find_path(
|
||||
UUID_INCLUDE_DIR
|
||||
NAMES uuid.h
|
||||
PATH_SUFFIXES uuid
|
||||
HINTS ${UUID_DIR}/include
|
||||
$ENV{UUID_DIR}/include
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/inc
|
||||
$ENV{DELTA_ROOT}/ext/inc
|
||||
$ENV{DELTA_ROOT}
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
/usr/include/gdal
|
||||
/sw/include # Fink
|
||||
/opt/local/include # DarwinPorts
|
||||
/opt/csw/include # Blastwave
|
||||
/opt/include
|
||||
[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include
|
||||
/usr/freeware/include)
|
||||
|
||||
if (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(UUID_FOUND TRUE)
|
||||
else (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
|
||||
find_path(UUID_INCLUDE_DIR
|
||||
NAMES
|
||||
uuid.h
|
||||
PATH_SUFFIXES
|
||||
uuid
|
||||
HINTS
|
||||
${UUID_DIR}/include
|
||||
$ENV{UUID_DIR}/include
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/inc
|
||||
$ENV{DELTA_ROOT}/ext/inc
|
||||
$ENV{DELTA_ROOT}
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
/usr/include/gdal
|
||||
/sw/include # Fink
|
||||
/opt/local/include # DarwinPorts
|
||||
/opt/csw/include # Blastwave
|
||||
/opt/include
|
||||
[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include
|
||||
/usr/freeware/include
|
||||
)
|
||||
find_library(UUID_LIBRARY
|
||||
NAMES uuid ossp-uuid
|
||||
HINTS ${UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/lib
|
||||
$ENV{DELTA_ROOT}/ext/lib
|
||||
$ENV{DELTA_ROOT}
|
||||
$ENV{OSG_ROOT}/lib
|
||||
PATHS ~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
/sw/lib
|
||||
/opt/local/lib
|
||||
/opt/csw/lib
|
||||
/opt/lib
|
||||
/usr/freeware/lib64)
|
||||
|
||||
find_library(UUID_LIBRARY
|
||||
NAMES
|
||||
uuid ossp-uuid
|
||||
HINTS
|
||||
${UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/lib
|
||||
$ENV{DELTA_ROOT}/ext/lib
|
||||
$ENV{DELTA_ROOT}
|
||||
$ENV{OSG_ROOT}/lib
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
/sw/lib
|
||||
/opt/local/lib
|
||||
/opt/csw/lib
|
||||
/opt/lib
|
||||
/usr/freeware/lib64
|
||||
)
|
||||
find_library(UUID_LIBRARY_DEBUG
|
||||
NAMES uuidd
|
||||
HINTS ${UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/lib
|
||||
$ENV{DELTA_ROOT}/ext/lib
|
||||
$ENV{DELTA_ROOT}
|
||||
$ENV{OSG_ROOT}/lib
|
||||
PATHS ~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
/sw/lib
|
||||
/opt/local/lib
|
||||
/opt/csw/lib
|
||||
/opt/lib
|
||||
/usr/freeware/lib64)
|
||||
|
||||
find_library(UUID_LIBRARY_DEBUG
|
||||
NAMES
|
||||
uuidd
|
||||
HINTS
|
||||
${UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/lib
|
||||
$ENV{DELTA_ROOT}/ext/lib
|
||||
$ENV{DELTA_ROOT}
|
||||
$ENV{OSG_ROOT}/lib
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
/sw/lib
|
||||
/opt/local/lib
|
||||
/opt/csw/lib
|
||||
/opt/lib
|
||||
/usr/freeware/lib64
|
||||
)
|
||||
if(NOT UUID_LIBRARY AND (BSD OR APPLE))
|
||||
set(UUID_LIBRARY "")
|
||||
endif()
|
||||
|
||||
if (NOT UUID_LIBRARY AND BSD)
|
||||
set(UUID_LIBRARY "")
|
||||
endif(NOT UUID_LIBRARY AND BSD)
|
||||
set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR})
|
||||
set(UUID_LIBRARIES ${UUID_LIBRARY})
|
||||
|
||||
set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR})
|
||||
set(UUID_LIBRARIES ${UUID_LIBRARY})
|
||||
if(UUID_INCLUDE_DIRS)
|
||||
if((BSD OR APPLE) OR UUID_LIBRARIES)
|
||||
set(UUID_FOUND TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (UUID_INCLUDE_DIRS)
|
||||
if (BSD OR UUID_LIBRARIES)
|
||||
set(UUID_FOUND TRUE)
|
||||
endif (BSD OR UUID_LIBRARIES)
|
||||
endif (UUID_INCLUDE_DIRS)
|
||||
if(UUID_FOUND)
|
||||
if(NOT UUID_FIND_QUIETLY)
|
||||
message(STATUS "Found UUID: ${UUID_LIBRARIES}")
|
||||
endif()
|
||||
else()
|
||||
if(UUID_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find UUID")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (UUID_FOUND)
|
||||
if (NOT UUID_FIND_QUIETLY)
|
||||
message(STATUS "Found UUID: ${UUID_LIBRARIES}")
|
||||
endif (NOT UUID_FIND_QUIETLY)
|
||||
else (UUID_FOUND)
|
||||
if (UUID_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find UUID")
|
||||
endif (UUID_FIND_REQUIRED)
|
||||
endif (UUID_FOUND)
|
||||
# show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced
|
||||
# view
|
||||
mark_as_advanced(UUID_INCLUDE_DIRS UUID_LIBRARIES)
|
||||
|
||||
# show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(UUID_INCLUDE_DIRS UUID_LIBRARIES)
|
||||
endif()
|
||||
|
||||
endif (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
|
||||
if(UUID_FOUND)
|
||||
add_library(UUID_lib INTERFACE IMPORTED)
|
||||
set_target_properties(UUID_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${UUID_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${UUID_LIBRARIES}")
|
||||
else()
|
||||
set(UUID_LIBRARIES)
|
||||
set(UUID_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
23
cmake_modules/Findcoz-profiler.cmake
Normal file
23
cmake_modules/Findcoz-profiler.cmake
Normal file
@ -0,0 +1,23 @@
|
||||
find_path(COZ_INCLUDE_DIRS NAMES coz.h)
|
||||
|
||||
find_library(COZ_LIBRARIES NAMES coz PATH_SUFFIXES coz-profiler)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(coz-profiler
|
||||
DEFAULT_MSG
|
||||
COZ_LIBRARIES
|
||||
COZ_INCLUDE_DIRS)
|
||||
|
||||
if(COZ-PROFILER_FOUND)
|
||||
add_library(coz::coz INTERFACE IMPORTED)
|
||||
set_target_properties(coz::coz
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
${COZ_INCLUDE_DIRS}
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
${COZ_LIBRARIES})
|
||||
else(COZ-PROFILER_FOUND)
|
||||
set(COZ_LIBRARIES)
|
||||
set(COZ_INCLUDE_DIRS)
|
||||
endif(COZ-PROFILER_FOUND)
|
||||
|
||||
mark_as_advanced(COZ_INCLUDE_DIRS COZ_LIBRARIES)
|
27
cmake_modules/Findpg.cmake
Normal file
27
cmake_modules/Findpg.cmake
Normal file
@ -0,0 +1,27 @@
|
||||
# Find PostgreSQL
|
||||
#
|
||||
# Find the PostgreSQL includes and library
|
||||
#
|
||||
# This module defines PG_INCLUDE_DIRS, where to find header, etc. PG_LIBRARIES,
|
||||
# the libraries needed to use PostgreSQL. pg_FOUND, If false, do not try to use
|
||||
# PostgreSQL.
|
||||
# pg_lib - The imported target library.
|
||||
|
||||
find_package(PostgreSQL)
|
||||
if(PostgreSQL_FOUND)
|
||||
set(PG_LIBRARIES ${PostgreSQL_LIBRARIES})
|
||||
set(PG_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIRS})
|
||||
message(STATUS "pg inc: " ${PostgreSQL_INCLUDE_DIRS})
|
||||
add_library(pg_lib INTERFACE IMPORTED)
|
||||
set_target_properties(pg_lib
|
||||
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${PostgreSQL_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${PostgreSQL_LIBRARIES}")
|
||||
mark_as_advanced(PG_INCLUDE_DIRS PG_LIBRARIES)
|
||||
endif(PostgreSQL_FOUND)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(pg
|
||||
DEFAULT_MSG
|
||||
PG_LIBRARIES
|
||||
PG_INCLUDE_DIRS)
|
16
conanfile.txt
Normal file
16
conanfile.txt
Normal file
@ -0,0 +1,16 @@
|
||||
[requires]
|
||||
jsoncpp/1.9.4
|
||||
zlib/1.2.11
|
||||
gtest/1.10.0
|
||||
sqlite3/3.40.1
|
||||
#libpq/13.2
|
||||
openssl/1.1.1t
|
||||
hiredis/1.0.0
|
||||
brotli/1.0.9
|
||||
|
||||
[generators]
|
||||
CMakeToolchain
|
||||
|
||||
[options]
|
||||
|
||||
[imports]
|
@ -2,10 +2,15 @@
|
||||
*/
|
||||
{
|
||||
/*
|
||||
//ssl:The global ssl files setting
|
||||
//ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
|
||||
// "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
|
||||
"ssl": {
|
||||
"cert": "../../trantor/trantor/tests/server.pem",
|
||||
"key": "../../trantor/trantor/tests/server.pem"
|
||||
"cert": "../../trantor/trantor/tests/server.crt",
|
||||
"key": "../../trantor/trantor/tests/server.key",
|
||||
"conf": [
|
||||
//["Options", "-SessionTicket"],
|
||||
//["Options", "Compression"]
|
||||
]
|
||||
},
|
||||
"listeners": [
|
||||
{
|
||||
@ -23,13 +28,18 @@
|
||||
//cert,key: Cert file path and key file path, empty by default,
|
||||
//if empty, use the global setting
|
||||
"cert": "",
|
||||
"key": ""
|
||||
"key": "",
|
||||
//use_old_tls: enable the TLS1.0/1.1, false by default
|
||||
"use_old_tls": false,
|
||||
"ssl_conf": [
|
||||
//["MinProtocol", "TLSv1.3"]
|
||||
]
|
||||
}
|
||||
],
|
||||
"db_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
//"name":"",
|
||||
"name": "default",
|
||||
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
|
||||
"rdbms": "postgresql",
|
||||
//filename: Sqlite3 db file name
|
||||
@ -47,24 +57,80 @@
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//connection_number: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//client_encoding: The character set used by the client. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"connection_number": 1
|
||||
"number_of_connections": 1,
|
||||
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
|
||||
//zero or negative value means no timeout.
|
||||
"timeout": -1.0,
|
||||
//auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
|
||||
//the wiki for more details.
|
||||
"auto_batch": false
|
||||
//connect_options: extra options for the connection. Only works for PostgreSQL now.
|
||||
//For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
|
||||
//"connect_options": { "statement_timeout": "1s" }
|
||||
}
|
||||
],
|
||||
"redis_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//host: Server IP, 127.0.0.1 by default
|
||||
"host": "127.0.0.1",
|
||||
//port: Server port, 6379 by default
|
||||
"port": 6379,
|
||||
//username: '' by default which means 'default' in redis ACL
|
||||
"username": "",
|
||||
//passwd: '' by default
|
||||
"passwd": "",
|
||||
//db index: 0 by default
|
||||
"db": 0,
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"number_of_connections": 1,
|
||||
//timeout: -1.0 by default, in seconds, the timeout for executing a command.
|
||||
//zero or negative value means no timeout.
|
||||
"timeout": -1.0
|
||||
}
|
||||
],*/
|
||||
"app": {
|
||||
//threads_num: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//is the number of CPU cores
|
||||
"threads_num": 1,
|
||||
"number_of_threads": 1,
|
||||
//enable_session: False by default
|
||||
"enable_session": true,
|
||||
"session_timeout": 0,
|
||||
//document_root: Root path of HTTP document, defaut path is ./
|
||||
//string value of SameSite attribute of the Set-Cookie HTTP response header
|
||||
//valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
|
||||
"session_same_site": "Null",
|
||||
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default
|
||||
"session_cookie_key": "JSESSIONID",
|
||||
//session_max_age: The max age of the session cookie, -1 by default
|
||||
"session_max_age": -1,
|
||||
//document_root: Root path of HTTP document, default path is ./
|
||||
"document_root": "./",
|
||||
//home_page: Set the HTML file of the home page, the default value is "index.html"
|
||||
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
|
||||
//to the request for "/".
|
||||
"home_page": "index.html",
|
||||
//use_implicit_page: enable implicit pages if true, true by default
|
||||
"use_implicit_page": true,
|
||||
//implicit_page: Set the file which would the server access in a directory that a user accessed.
|
||||
//For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
|
||||
"implicit_page": "index.html",
|
||||
//static_file_headers: Headers for static files
|
||||
/*"static_file_headers": [
|
||||
{
|
||||
"name": "field-name",
|
||||
"value": "field-value"
|
||||
}
|
||||
],*/
|
||||
//upload_path: The path to save the uploaded file. "uploads" by default.
|
||||
//If the path isn't prefixed with /, ./ or ../,
|
||||
//it is relative path of document_root path
|
||||
@ -86,11 +152,42 @@
|
||||
"xap",
|
||||
"apk",
|
||||
"cur",
|
||||
"xml"
|
||||
"xml",
|
||||
"webp",
|
||||
"svg"
|
||||
],
|
||||
//max_connections: maximum connections number,100000 by default
|
||||
// mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
|
||||
// note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
|
||||
"mime": {
|
||||
// "text/markdown": "md",
|
||||
// "text/gemini": ["gmi", "gemini"]
|
||||
},
|
||||
//locations: An array of locations of static files for GET requests.
|
||||
"locations": [
|
||||
{
|
||||
//uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
|
||||
//"uri_prefix": "/.well-known/acme-challenge/",
|
||||
//default_content_type: The default content type of the static files without
|
||||
//an extension. empty string by default.
|
||||
"default_content_type": "text/plain",
|
||||
//alias: The location in file system, if it is prefixed with "/", it
|
||||
//presents an absolute path, otherwise it presents a relative path to
|
||||
//the document_root path.
|
||||
//The default value is "" which means use the document root path as the location base path.
|
||||
"alias": "",
|
||||
//is_case_sensitive: indicates whether the URI prefix is case sensitive.
|
||||
"is_case_sensitive": false,
|
||||
//allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
|
||||
"allow_all": true,
|
||||
//is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
"is_recursive": true,
|
||||
//filters: string array, the filters applied to the location.
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
//max_connections: maximum number of connections, 100000 by default
|
||||
"max_connections": 100000,
|
||||
//max_connections_per_ip: maximum connections number per clinet,0 by default which means no limit
|
||||
//max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
|
||||
"max_connections_per_ip": 0,
|
||||
//Load_dynamic_views: False by default, when set to true, drogon
|
||||
//compiles and loads dynamically "CSP View Files" in directories defined
|
||||
@ -101,8 +198,27 @@
|
||||
"dynamic_views_path": [
|
||||
"./views"
|
||||
],
|
||||
//dynamic_views_output_path: Default by an empty string which means the output path of source
|
||||
//files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
|
||||
//path of the current working directory.
|
||||
"dynamic_views_output_path": "",
|
||||
//json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
|
||||
"json_parser_stack_limit": 1000,
|
||||
//enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
|
||||
"enable_unicode_escaping_in_json": true,
|
||||
//float_precision_in_json: set precision of float number in json.
|
||||
"float_precision_in_json": {
|
||||
//precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
|
||||
"precision": 0,
|
||||
//precision_type: must be "significant" or "decimal", defaults to "significant" that means
|
||||
//setting max number of significant digits in string, "decimal" means setting max number of
|
||||
//digits after "." in string
|
||||
"precision_type": "significant"
|
||||
},
|
||||
//log: Set log output, drogon output logs to stdout by default
|
||||
"log": {
|
||||
//use_spdlog: Use spdlog library to log
|
||||
"use_spdlog": false,
|
||||
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
|
||||
//"log_path": "./",
|
||||
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
|
||||
@ -111,34 +227,45 @@
|
||||
//log_size_limit: 100000000 bytes by default,
|
||||
//When the log file size reaches "log_size_limit", the log file is switched.
|
||||
"log_size_limit": 100000000,
|
||||
//max_files: 0 by default,
|
||||
//When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
|
||||
"max_files": 0,
|
||||
//log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
||||
//The TRACE level is only valid when built in DEBUG mode.
|
||||
"log_level": "DEBUG"
|
||||
"log_level": "DEBUG",
|
||||
//display_local_time: false by default, if true, the log time is displayed in local time
|
||||
"display_local_time": false
|
||||
},
|
||||
//run_as_daemon: False by default
|
||||
"run_as_daemon": false,
|
||||
//handle_sig_term: True by default
|
||||
"handle_sig_term": true,
|
||||
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
|
||||
"relaunch_on_error": false,
|
||||
//use_sendfile: True by default, if ture, the program
|
||||
//use_sendfile: True by default, if true, the program
|
||||
//uses sendfile() system-call to send static files to clients;
|
||||
"use_sendfile": true,
|
||||
//use_gzip: True by default, use gzip to compress the response body's content;
|
||||
"use_gzip": true,
|
||||
//use_brotli: False by default, use brotli to compress the response body's content;
|
||||
"use_brotli": false,
|
||||
//static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
|
||||
//0 means cache forever, the negative value means no cache
|
||||
"static_files_cache_time": 5,
|
||||
//simple_controllers_map: Used to configure mapping from path to simple controller
|
||||
"simple_controllers_map": [{
|
||||
"path": "/path/name",
|
||||
"controller": "controllerClassName",
|
||||
"http_methods": [
|
||||
"get",
|
||||
"post"
|
||||
],
|
||||
"filters": [
|
||||
"FilterClassName"
|
||||
]
|
||||
}],
|
||||
//"simple_controllers_map": [
|
||||
// {
|
||||
// "path": "/path/name",
|
||||
// "controller": "controllerClassName",
|
||||
// "http_methods": [
|
||||
// "get",
|
||||
// "post"
|
||||
// ],
|
||||
// "filters": [
|
||||
// "FilterClassName"
|
||||
// ]
|
||||
// }
|
||||
//],
|
||||
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||
//of the connection without read or write
|
||||
"idle_connection_timeout": 60,
|
||||
@ -163,6 +290,10 @@
|
||||
//file with the extension ".gz" in the same path and send the compressed file to the client.
|
||||
//The default value of gzip_static is true.
|
||||
"gzip_static": true,
|
||||
//br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
//file with the extension ".br" in the same path and send the compressed file to the client.
|
||||
//The default value of br_static is true.
|
||||
"br_static": true,
|
||||
//client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
|
||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
"client_max_body_size": "1M",
|
||||
@ -172,20 +303,57 @@
|
||||
"client_max_memory_body_size": "64K",
|
||||
//client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
|
||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
"client_max_websocket_message_size": "128K"
|
||||
"client_max_websocket_message_size": "128K",
|
||||
//reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
|
||||
"reuse_port": false,
|
||||
// enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
|
||||
// Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
|
||||
// Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
|
||||
// will be rejected.
|
||||
"enabled_compressed_request": false,
|
||||
// enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
|
||||
// See the wiki for more details.
|
||||
"enable_request_stream": false,
|
||||
},
|
||||
//plugins: Define all plugins running in the application
|
||||
"plugins": [{
|
||||
//name: The class name of the plugin
|
||||
//"name": "TestPlugin",
|
||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
"dependencies": [],
|
||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
//It can be commented out
|
||||
"config": {
|
||||
"heartbeat_interval": 2
|
||||
"plugins": [
|
||||
{
|
||||
//name: The class name of the plugin
|
||||
"name": "drogon::plugin::PromExporter",
|
||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
"dependencies": [],
|
||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
//It can be commented out
|
||||
"config": {
|
||||
"path": "/metrics"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "drogon::plugin::AccessLogger",
|
||||
"dependencies": [],
|
||||
"config": {
|
||||
"use_spdlog": false,
|
||||
"log_path": "",
|
||||
"log_format": "",
|
||||
"log_file": "access.log",
|
||||
"log_size_limit": 0,
|
||||
"use_local_time": true,
|
||||
"log_index": 0,
|
||||
// "show_microseconds": true,
|
||||
// "custom_time_format": "",
|
||||
// "use_real_ip": false
|
||||
}
|
||||
}
|
||||
}],
|
||||
],
|
||||
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
"custom_config": {}
|
||||
"custom_config": {
|
||||
"realm": "drogonRealm",
|
||||
"opaque": "drogonOpaque",
|
||||
"credentials": [
|
||||
{
|
||||
"user": "drogon",
|
||||
"password": "dr0g0n"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
318
config.example.yaml
Normal file
318
config.example.yaml
Normal file
@ -0,0 +1,318 @@
|
||||
# This is a YAML format configuration file
|
||||
|
||||
# ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
|
||||
# "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
|
||||
# ssl:
|
||||
# cert: ../../trantor/trantor/tests/server.crt
|
||||
# key: ../../trantor/trantor/tests/server.key
|
||||
# conf: [
|
||||
# # [Options, -SessionTicket],
|
||||
# # [Options, Compression]
|
||||
# ]
|
||||
# listeners:
|
||||
# # address: Ip address,0.0.0.0 by default
|
||||
# - address: 0.0.0.0
|
||||
# # port: Port number
|
||||
# port: 80
|
||||
# # https: If true, use https for security,false by default
|
||||
# https: false
|
||||
# - address: 0.0.0.0
|
||||
# port: 443
|
||||
# https: true
|
||||
# # cert,key: Cert file path and key file path, empty by default,
|
||||
# # if empty, use the global setting
|
||||
# cert: ''
|
||||
# key: ''
|
||||
# # use_old_tls: enable the TLS1.0/1.1, false by default
|
||||
# use_old_tls: false
|
||||
# ssl_conf: [
|
||||
# # [MinProtocol, TLSv1.3]
|
||||
# ]
|
||||
# db_clients:
|
||||
# # name: Name of the client,'default' by default
|
||||
# - name: default
|
||||
# # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
|
||||
# rdbms: postgresql
|
||||
# # filename: Sqlite3 db file name
|
||||
# # filename: ''
|
||||
# # host: Server address,localhost by default
|
||||
# host: 127.0.0.1
|
||||
# # port: Server port, 5432 by default
|
||||
# port: 5432
|
||||
# # dbname: Database name
|
||||
# dbname: test
|
||||
# # user: 'postgres' by default
|
||||
# user: ''
|
||||
# # passwd: '' by default
|
||||
# passwd: ''
|
||||
# # is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
# # any synchronous interface of it.
|
||||
# is_fast: false
|
||||
# # client_encoding: The character set used by the client. it is empty string by default which
|
||||
# # means use the default character set.
|
||||
# # client_encoding: ''
|
||||
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
# # connections per IO thread, otherwise it is the total number of all connections.
|
||||
# number_of_connections: 1
|
||||
# # timeout: -1 by default, in seconds, the timeout for executing a SQL query.
|
||||
# # zero or negative value means no timeout.
|
||||
# timeout: -1
|
||||
# # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
|
||||
# # the wiki for more details.
|
||||
# auto_batch: false
|
||||
# # connect_options: extra options for the connection. Only works for PostgreSQL now.
|
||||
# # For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
|
||||
# # connect_options:
|
||||
# # statement_timeout: '1s'
|
||||
# redis_clients:
|
||||
# # name: Name of the client,'default' by default
|
||||
# - name: default
|
||||
# # host: Server IP, 127.0.0.1 by default
|
||||
# host: 127.0.0.1
|
||||
# # port: Server port, 6379 by default
|
||||
# port: 6379
|
||||
# # username: '' by default which means 'default' in redis ACL
|
||||
# username: ''
|
||||
# # passwd: '' by default
|
||||
# passwd: ''
|
||||
# # db index: 0 by default
|
||||
# db: 0
|
||||
# # is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
# # any synchronous interface of it.
|
||||
# is_fast: false
|
||||
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
# # connections per IO thread, otherwise it is the total number of all connections.
|
||||
# number_of_connections: 1
|
||||
# # timeout: -1.0 by default, in seconds, the timeout for executing a command.
|
||||
# # zero or negative value means no timeout.
|
||||
# timeout: -1
|
||||
app:
|
||||
# number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
# is the number of CPU cores
|
||||
number_of_threads: 1
|
||||
# enable_session: False by default
|
||||
enable_session: true
|
||||
session_timeout: 0
|
||||
# string value of SameSite attribute of the Set-Cookie HTTP response header
|
||||
# valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
|
||||
session_same_site: 'Null'
|
||||
# session_cookie_key: The cookie key of the session, "JSESSIONID" by default
|
||||
session_cookie_key: 'JSESSIONID'
|
||||
# session_max_age: The max age of the session cookie, -1 by default
|
||||
session_max_age: -1
|
||||
# document_root: Root path of HTTP document, default path is ./
|
||||
document_root: ./
|
||||
# home_page: Set the HTML file of the home page, the default value is "index.html"
|
||||
# If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
|
||||
# to the request for "/".
|
||||
home_page: index.html
|
||||
# use_implicit_page: enable implicit pages if true, true by default
|
||||
use_implicit_page: true
|
||||
# implicit_page: Set the file which would the server access in a directory that a user accessed.
|
||||
# For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
|
||||
implicit_page: index.html
|
||||
# static_file_headers: Headers for static files
|
||||
# static_file_headers:
|
||||
# - name: field-name
|
||||
# value: field-value
|
||||
# upload_path: The path to save the uploaded file. "uploads" by default.
|
||||
# If the path isn't prefixed with /, ./ or ../,
|
||||
# it is relative path of document_root path
|
||||
upload_path: uploads
|
||||
# file_types:
|
||||
# HTTP download file types,The file types supported by drogon
|
||||
# by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
|
||||
# "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
|
||||
# "gif", "bmp", "ico", "icns", etc.
|
||||
file_types:
|
||||
- gif
|
||||
- png
|
||||
- jpg
|
||||
- js
|
||||
- css
|
||||
- html
|
||||
- ico
|
||||
- swf
|
||||
- xap
|
||||
- apk
|
||||
- cur
|
||||
- xml
|
||||
# mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
|
||||
# note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
|
||||
mime: {
|
||||
# text/markdown: md
|
||||
# text/gemini:
|
||||
# - gmi
|
||||
# - gemini
|
||||
}
|
||||
# locations: An array of locations of static files for GET requests.
|
||||
locations:
|
||||
# uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
|
||||
- uri_prefix: '' # /.well-known/acme-challenge/
|
||||
# default_content_type: The default content type of the static files without
|
||||
# an extension. empty string by default.
|
||||
default_content_type: text/plain
|
||||
# alias: The location in file system, if it is prefixed with "/", it
|
||||
# presents an absolute path, otherwise it presents a relative path to
|
||||
# the document_root path.
|
||||
# The default value is "" which means use the document root path as the location base path.
|
||||
alias: ''
|
||||
# is_case_sensitive: indicates whether the URI prefix is case sensitive.
|
||||
is_case_sensitive: false
|
||||
# allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
|
||||
allow_all: true
|
||||
# is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
is_recursive: true
|
||||
# filters: string array, the filters applied to the location.
|
||||
filters: []
|
||||
# max_connections: maximum number of connections, 100000 by default
|
||||
max_connections: 100000
|
||||
# max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
|
||||
max_connections_per_ip: 0
|
||||
# Load_dynamic_views: False by default, when set to true, drogon
|
||||
# compiles and loads dynamically "CSP View Files" in directories defined
|
||||
# by "dynamic_views_path"
|
||||
load_dynamic_views: false
|
||||
# dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
|
||||
# it is relative path of document_root path
|
||||
dynamic_views_path:
|
||||
- ./views
|
||||
# dynamic_views_output_path: Default by an empty string which means the output path of source
|
||||
# files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
|
||||
# path of the current working directory.
|
||||
dynamic_views_output_path: ''
|
||||
# json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
|
||||
json_parser_stack_limit: 1000
|
||||
# enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
|
||||
enable_unicode_escaping_in_json: true
|
||||
# float_precision_in_json: set precision of float number in json.
|
||||
float_precision_in_json:
|
||||
# precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
|
||||
precision: 0
|
||||
# precision_type: must be "significant" or "decimal", defaults to "significant" that means
|
||||
# setting max number of significant digits in string, "decimal" means setting max number of
|
||||
# digits after "." in string
|
||||
precision_type: significant
|
||||
# log: Set log output, drogon output logs to stdout by default
|
||||
log:
|
||||
# use_spdlog: Use spdlog library to log
|
||||
use_spdlog: false
|
||||
# log_path: Log file path,empty by default,in which case,logs are output to the stdout
|
||||
# log_path: ./
|
||||
# logfile_base_name: Log file base name,empty by default which means drogon names logfile as
|
||||
# drogon.log ...
|
||||
logfile_base_name: ''
|
||||
# log_size_limit: 100000000 bytes by default,
|
||||
# When the log file size reaches "log_size_limit", the log file is switched.
|
||||
log_size_limit: 100000000
|
||||
# max_files: 0 by default,
|
||||
# When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
|
||||
max_files: 0
|
||||
# log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
||||
# The TRACE level is only valid when built in DEBUG mode.
|
||||
log_level: DEBUG
|
||||
# display_local_time: false by default, if true, the log time is displayed in local time
|
||||
display_local_time: false
|
||||
# run_as_daemon: False by default
|
||||
run_as_daemon: false
|
||||
# handle_sig_term: True by default
|
||||
handle_sig_term: true
|
||||
# relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
|
||||
relaunch_on_error: false
|
||||
# use_sendfile: True by default, if true, the program
|
||||
# uses sendfile() system-call to send static files to clients;
|
||||
use_sendfile: true
|
||||
# use_gzip: True by default, use gzip to compress the response body's content;
|
||||
use_gzip: true
|
||||
# use_brotli: False by default, use brotli to compress the response body's content;
|
||||
use_brotli: false
|
||||
# static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
|
||||
# 0 means cache forever, the negative value means no cache
|
||||
static_files_cache_time: 5
|
||||
# simple_controllers_map: Used to configure mapping from path to simple controller
|
||||
# simple_controllers_map:
|
||||
# - path: /path/name
|
||||
# controller: controllerClassName
|
||||
# http_methods:
|
||||
# - get
|
||||
# - post
|
||||
# filters:
|
||||
# - FilterClassName
|
||||
# idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||
# of the connection without read or write
|
||||
idle_connection_timeout: 60
|
||||
# server_header_field: Set the 'Server' header field in each response sent by drogon,
|
||||
# empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
|
||||
server_header_field: ''
|
||||
# enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
|
||||
# value is true.
|
||||
enable_server_header: true
|
||||
# enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
|
||||
# value is true.
|
||||
enable_date_header: true
|
||||
# keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
|
||||
# After the maximum number of requests are made, the connection is closed.
|
||||
# The default value of 0 means no limit.
|
||||
keepalive_requests: 0
|
||||
# pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
|
||||
# After the maximum number of requests are made, the connection is closed.
|
||||
# The default value of 0 means no limit.
|
||||
pipelining_requests: 0
|
||||
# gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
# file with the extension ".gz" in the same path and send the compressed file to the client.
|
||||
# The default value of gzip_static is true.
|
||||
gzip_static: true
|
||||
# br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
# file with the extension ".br" in the same path and send the compressed file to the client.
|
||||
# The default value of br_static is true.
|
||||
br_static: true
|
||||
# client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
|
||||
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
client_max_body_size: 1M
|
||||
# max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
|
||||
# If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
|
||||
# Setting it to "" means no limit.
|
||||
client_max_memory_body_size: 64K
|
||||
# client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
|
||||
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
client_max_websocket_message_size: 128K
|
||||
# reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
|
||||
reuse_port: false
|
||||
# enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
|
||||
# Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
|
||||
# Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
|
||||
# will be rejected.
|
||||
enabled_compressed_request: false
|
||||
# enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
|
||||
# See the wiki for more details.
|
||||
enable_request_stream: false
|
||||
# plugins: Define all plugins running in the application
|
||||
plugins:
|
||||
# name: The class name of the plugin
|
||||
- name: drogon::plugin::PromExporter
|
||||
# dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
dependencies: []
|
||||
# config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
# It can be commented out
|
||||
config:
|
||||
path: /metrics
|
||||
- name: drogon::plugin::AccessLogger
|
||||
dependencies: []
|
||||
config:
|
||||
use_spdlog: false
|
||||
log_path: ''
|
||||
log_format: ''
|
||||
log_file: access.log
|
||||
log_size_limit: 0
|
||||
use_local_time: true
|
||||
log_index: 0
|
||||
# show_microseconds: true
|
||||
# custom_time_format: ''
|
||||
# use_real_ip: false
|
||||
# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
custom_config:
|
||||
realm: drogonRealm
|
||||
opaque: drogonOpaque
|
||||
credentials:
|
||||
- user: drogon
|
||||
password: dr0g0n
|
42
docker/alpine/Dockerfile
Normal file
42
docker/alpine/Dockerfile
Normal file
@ -0,0 +1,42 @@
|
||||
FROM alpine:3.14
|
||||
|
||||
ARG USER=drogon
|
||||
ARG UID=1000
|
||||
ARG GID=1000
|
||||
ARG USER_HOME=/drogon
|
||||
|
||||
ENV TZ=UTC
|
||||
|
||||
RUN apk update && apk --no-cache --upgrade add tzdata \
|
||||
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
|
||||
&& echo $TZ > /etc/timezone
|
||||
|
||||
RUN apk --no-cache --upgrade add \
|
||||
sudo curl wget cmake make pkgconfig git gcc g++ \
|
||||
openssl libressl-dev jsoncpp-dev util-linux-dev zlib-dev c-ares-dev \
|
||||
postgresql-dev mariadb-dev sqlite-dev hiredis-dev
|
||||
|
||||
RUN addgroup -S -g $GID $USER \
|
||||
&& adduser -D -u $UID -G $USER -h $USER_HOME $USER \
|
||||
&& mkdir -p /etc/sudoers.d \
|
||||
&& echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER \
|
||||
&& chmod 0440 /etc/sudoers.d/$USER
|
||||
|
||||
USER $USER
|
||||
WORKDIR $USER_HOME
|
||||
|
||||
ENV LANG=en_US.UTF-8 \
|
||||
LANGUAGE=en_US:en \
|
||||
LC_ALL=en_US.UTF-8 \
|
||||
CC=gcc \
|
||||
CXX=g++ \
|
||||
AR=gcc-ar \
|
||||
RANLIB=gcc-ranlib \
|
||||
DROGON_INSTALLED_ROOT=$USER_HOME/install
|
||||
|
||||
RUN wget -O $USER_HOME/version.json https://api.github.com/repos/an-tao/drogon/git/refs/heads/master \
|
||||
&& git clone https://github.com/an-tao/drogon $DROGON_INSTALLED_ROOT
|
||||
|
||||
RUN cd $DROGON_INSTALLED_ROOT \
|
||||
&& sed -i 's/bash/sh/' ./build.sh \
|
||||
&& ./build.sh
|
32
docker/alpine/README.md
Normal file
32
docker/alpine/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
## Build Docker Image
|
||||
|
||||
```shell
|
||||
$ cd drogon/docker/alpine # from this repository
|
||||
$ docker build --no-cache --build-arg UID=`id -u` --build-arg GID=`id -g` -t drogon-alpine . # include last dot(.)
|
||||
```
|
||||
|
||||
## Create a Drogon Project
|
||||
|
||||
```shell
|
||||
$ cd ~/drogon_app # example
|
||||
$ docker run --rm -v="$PWD:/drogon/app" -w="/drogon/app" drogon-alpine drogon_ctl create project hello_world
|
||||
```
|
||||
|
||||
## Build the Project
|
||||
|
||||
```shell
|
||||
$ cd hello_world
|
||||
$ docker run --rm --volume="$PWD:/drogon/app" -w="/drogon/app/build" drogon-alpine sh -c "cmake .. && make"
|
||||
```
|
||||
|
||||
## Start Server
|
||||
|
||||
```shell
|
||||
$ docker run --name drogon_test --rm -u 0 -v="$PWD/build:/drogon/app" -w="/drogon/app" -p 8080:80 -d drogon-alpine ./hello_world # expose port 80 to 8080
|
||||
```
|
||||
|
||||
## Stop Server
|
||||
|
||||
```shell
|
||||
$ docker kill drogon_test
|
||||
```
|
21
docker/arch/Dockerfile
Normal file
21
docker/arch/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
FROM archlinux:base-20210307.0.16708
|
||||
|
||||
RUN pacman -Syu --noconfirm && pacman -S wget sudo cmake make git gcc jsoncpp postgresql mariadb-clients hiredis --noconfirm
|
||||
|
||||
ENV LANG=en_US.UTF-8 \
|
||||
LANGUAGE=en_US:en \
|
||||
LC_ALL=en_US.UTF-8 \
|
||||
CC=gcc \
|
||||
CXX=g++ \
|
||||
AR=gcc-ar \
|
||||
RANLIB=gcc-ranlib \
|
||||
IROOT=/install
|
||||
|
||||
ENV DROGON_ROOT="$IROOT/drogon"
|
||||
|
||||
ADD https://api.github.com/repos/an-tao/drogon/git/refs/heads/master $IROOT/version.json
|
||||
RUN git clone https://github.com/an-tao/drogon $DROGON_ROOT
|
||||
|
||||
WORKDIR $DROGON_ROOT
|
||||
|
||||
RUN ./build.sh
|
30
docker/ubuntu/Dockerfile
Normal file
30
docker/ubuntu/Dockerfile
Normal file
@ -0,0 +1,30 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV TZ=UTC
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
RUN apt-get update -yqq \
|
||||
&& apt-get install -yqq --no-install-recommends software-properties-common \
|
||||
sudo curl wget cmake make pkg-config locales git gcc-11 g++-11 \
|
||||
openssl libssl-dev libjsoncpp-dev uuid-dev zlib1g-dev libc-ares-dev\
|
||||
postgresql-server-dev-all libmariadb-dev libsqlite3-dev libhiredis-dev\
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& locale-gen en_US.UTF-8
|
||||
|
||||
ENV LANG=en_US.UTF-8 \
|
||||
LANGUAGE=en_US:en \
|
||||
LC_ALL=en_US.UTF-8 \
|
||||
CC=gcc-11 \
|
||||
CXX=g++-11 \
|
||||
AR=gcc-ar-11 \
|
||||
RANLIB=gcc-ranlib-11 \
|
||||
IROOT=/install
|
||||
|
||||
ENV DROGON_ROOT="$IROOT/drogon"
|
||||
|
||||
ADD https://api.github.com/repos/drogonframework/drogon/git/refs/heads/master $IROOT/version.json
|
||||
RUN git clone https://github.com/drogonframework/drogon $DROGON_ROOT
|
||||
|
||||
WORKDIR $DROGON_ROOT
|
||||
|
||||
RUN ./build.sh
|
@ -1,37 +1,88 @@
|
||||
LINK_LIBRARIES(drogon trantor pthread dl)
|
||||
|
||||
SET(ctl_sources cmd.cc
|
||||
create.cc
|
||||
create_controller.cc
|
||||
create_filter.cc
|
||||
create_model.cc
|
||||
create_plugin.cc
|
||||
create_project.cc
|
||||
create_view.cc
|
||||
help.cc
|
||||
main.cc
|
||||
press.cc
|
||||
version.cc)
|
||||
ADD_EXECUTABLE(_drogon_ctl main.cc cmd.cc create.cc create_view.cc)
|
||||
FILE(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/templates/*.csp)
|
||||
FOREACH(cspFile ${SCP_LIST})
|
||||
MESSAGE(STATUS "cspFile:" ${cspFile})
|
||||
EXEC_PROGRAM(basename ARGS "${cspFile} .csp" OUTPUT_VARIABLE classname)
|
||||
MESSAGE(STATUS "view classname:" ${classname})
|
||||
ADD_CUSTOM_COMMAND(OUTPUT ${classname}.h ${classname}.cc
|
||||
COMMAND _drogon_ctl
|
||||
ARGS create view ${cspFile}
|
||||
DEPENDS ${cspFile}
|
||||
VERBATIM )
|
||||
SET(TEMPL_SRC ${TEMPL_SRC} ${classname}.cc)
|
||||
ENDFOREACH()
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
ADD_EXECUTABLE(drogon_ctl ${ctl_sources} ${TEMPL_SRC})
|
||||
ADD_DEPENDENCIES(drogon_ctl trantor makeVersion _drogon_ctl)
|
||||
INSTALL(TARGETS drogon_ctl DESTINATION bin)
|
||||
INSTALL(PROGRAMS $<TARGET_FILE_DIR:drogon_ctl>/drogon_ctl DESTINATION bin RENAME dg_ctl)
|
||||
SET(ctl_targets _drogon_ctl drogon_ctl)
|
||||
SET_PROPERTY(TARGET ${ctl_targets} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
SET_PROPERTY(TARGET ${ctl_targets} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
SET_PROPERTY(TARGET ${ctl_targets} PROPERTY CXX_EXTENSIONS OFF)
|
||||
|
||||
set(ctl_sources
|
||||
cmd.cc
|
||||
create.cc
|
||||
create_controller.cc
|
||||
create_filter.cc
|
||||
create_model.cc
|
||||
create_plugin.cc
|
||||
create_project.cc
|
||||
create_view.cc
|
||||
help.cc
|
||||
main.cc
|
||||
press.cc
|
||||
version.cc)
|
||||
add_executable(_drogon_ctl
|
||||
main.cc
|
||||
cmd.cc
|
||||
create.cc
|
||||
create_view.cc)
|
||||
target_link_libraries(_drogon_ctl ${PROJECT_NAME})
|
||||
if (WIN32 AND BUILD_SHARED_LIBS)
|
||||
set(DROGON_FILE $<TARGET_FILE:drogon>)
|
||||
if (USE_SUBMODULE)
|
||||
set(TRANTOR_FILE $<TARGET_FILE:trantor>)
|
||||
else()
|
||||
set(TRANTOR_FILE $<TARGET_FILE:Trantor::Trantor>)
|
||||
endif()
|
||||
add_custom_command(TARGET _drogon_ctl POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCTL_FILE=${DROGON_FILE}
|
||||
-DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>
|
||||
-P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
|
||||
add_custom_command(TARGET _drogon_ctl POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCTL_FILE=${TRANTOR_FILE}
|
||||
-DINSTALL_BIN_DIR=$<TARGET_FILE_DIR:_drogon_ctl>
|
||||
-P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
|
||||
endif()
|
||||
file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/templates/*.csp)
|
||||
foreach(cspFile ${SCP_LIST})
|
||||
message(STATUS "cspFile:" ${cspFile})
|
||||
get_filename_component(classname ${cspFile} NAME_WE)
|
||||
message(STATUS "view classname:" ${classname})
|
||||
add_custom_command(OUTPUT ${classname}.h ${classname}.cc
|
||||
COMMAND $<TARGET_FILE:_drogon_ctl>
|
||||
ARGS
|
||||
create
|
||||
view
|
||||
${cspFile}
|
||||
DEPENDS ${cspFile}
|
||||
VERBATIM)
|
||||
set(TEMPL_SRC ${TEMPL_SRC} ${classname}.cc)
|
||||
endforeach()
|
||||
add_executable(drogon_ctl ${ctl_sources} ${TEMPL_SRC})
|
||||
target_link_libraries(drogon_ctl PRIVATE ${PROJECT_NAME})
|
||||
target_include_directories(drogon_ctl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_dependencies(drogon_ctl _drogon_ctl)
|
||||
if(WIN32)
|
||||
target_link_libraries(drogon_ctl PRIVATE ws2_32 rpcrt4 iphlpapi)
|
||||
endif(WIN32)
|
||||
if(APPLE)
|
||||
target_link_libraries(drogon_ctl PRIVATE resolv)
|
||||
endif()
|
||||
message(STATUS "bin:" ${INSTALL_BIN_DIR})
|
||||
install(TARGETS drogon_ctl RUNTIME DESTINATION ${INSTALL_BIN_DIR})
|
||||
if(WIN32)
|
||||
set(CTL_FILE $<TARGET_FILE:drogon_ctl>)
|
||||
add_custom_command(TARGET drogon_ctl POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCTL_FILE=${CTL_FILE}
|
||||
-DINSTALL_BIN_DIR=${INSTALL_BIN_DIR}
|
||||
-DRENAME_EXE=ON
|
||||
-P
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CopyDlls.cmake)
|
||||
else(WIN32)
|
||||
install(CODE "execute_process( \
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink \
|
||||
./drogon_ctl \
|
||||
./dg_ctl \
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dg_ctl"
|
||||
DESTINATION ${INSTALL_BIN_DIR})
|
||||
endif(WIN32)
|
||||
set(ctl_targets _drogon_ctl drogon_ctl)
|
||||
set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
set_property(TARGET ${ctl_targets} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET ${ctl_targets} PROPERTY CXX_EXTENSIONS OFF)
|
||||
|
@ -22,18 +22,22 @@ class CommandHandler : public virtual drogon::DrObjectBase
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) = 0;
|
||||
|
||||
virtual bool isTopCommand()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual std::string script()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual std::string detail()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual ~CommandHandler()
|
||||
{
|
||||
}
|
||||
|
8
drogon_ctl/CopyDlls.cmake
Normal file
8
drogon_ctl/CopyDlls.cmake
Normal file
@ -0,0 +1,8 @@
|
||||
make_directory("${INSTALL_BIN_DIR}")
|
||||
get_filename_component(CTL_PATH ${CTL_FILE} DIRECTORY)
|
||||
file(GLOB DLL_FILES ${CTL_PATH}/*.dll)
|
||||
file(COPY ${DLL_FILES} DESTINATION ${INSTALL_BIN_DIR})
|
||||
file(COPY ${CTL_FILE} DESTINATION ${INSTALL_BIN_DIR})
|
||||
if (RENAME_EXE)
|
||||
file(RENAME ${INSTALL_BIN_DIR}/drogon_ctl.exe ${INSTALL_BIN_DIR}/dg_ctl.exe)
|
||||
endif()
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
*
|
||||
* create.cc
|
||||
* An Tao
|
||||
* @file create.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
@ -18,26 +18,32 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
using namespace drogon_ctl;
|
||||
|
||||
std::string create::detail()
|
||||
{
|
||||
return "Use create command to create some source files of drogon webapp\n\n"
|
||||
"Usage:drogon_ctl create <view|controller|filter|project|model> "
|
||||
"[-options] <object name>\n\n"
|
||||
"drogon_ctl create view <csp file name> //create HttpView source "
|
||||
"files from csp file\n\n"
|
||||
"drogon_ctl create view <csp file name> [-o <output path>] [-n "
|
||||
"<namespace>] [--path-to-namespace] //create HttpView source files "
|
||||
"from csp files, namespace is prefixed of path-to-namespace\n\n"
|
||||
"drogon_ctl create controller [-s] <[namespace::]class_name> //"
|
||||
"create HttpSimpleController source files\n\n"
|
||||
"drogon_ctl create controller -h <[namespace::]class_name> //"
|
||||
"create HttpController source files\n\n"
|
||||
"drogon_ctl create controller -w <[namespace::]class_name> //"
|
||||
"create WebSocketController source files\n\n"
|
||||
"drogon_ctl create controller -r <[namespace::]class_name> "
|
||||
"[--resource=...]//"
|
||||
"create restful controller source files\n\n"
|
||||
"drogon_ctl create filter <[namespace::]class_name> //"
|
||||
"create a filter named class_name\n\n"
|
||||
"drogon_ctl create plugin <[namespace::]class_name> //"
|
||||
"create a plugin named class_name\n\n"
|
||||
"drogon_ctl create project <project_name> //"
|
||||
"create a project named project_name\n\n"
|
||||
"drogon_ctl create model <model_path> //"
|
||||
"drogon_ctl create model <model_path> [-o <output path>] "
|
||||
"[--table=<table_name>] [-f]//"
|
||||
"create model classes in model_path\n";
|
||||
}
|
||||
|
||||
|
@ -17,21 +17,25 @@
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create : public DrObject<create>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create some source files(Use 'drogon_ctl help create' for more "
|
||||
"information)";
|
||||
}
|
||||
virtual bool isTopCommand() override
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual std::string detail() override;
|
||||
|
||||
std::string detail() override;
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "create_controller.h"
|
||||
#include "cmd.h"
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
@ -24,7 +26,7 @@ void create_controller::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
// std::cout<<"create!"<<std::endl;
|
||||
ControllerType type = Simple;
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)
|
||||
{
|
||||
if ((*iter)[0] == '-')
|
||||
{
|
||||
@ -45,6 +47,12 @@ void create_controller::handleCommand(std::vector<std::string> ¶meters)
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
else if (*iter == "-r" || *iter == "--restful")
|
||||
{
|
||||
type = Restful;
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << ARGS_ERROR_STR << std::endl;
|
||||
@ -52,7 +60,34 @@ void create_controller::handleCommand(std::vector<std::string> ¶meters)
|
||||
}
|
||||
}
|
||||
}
|
||||
createController(parameters, type);
|
||||
if (type != Restful)
|
||||
createController(parameters, type);
|
||||
else
|
||||
{
|
||||
std::string resource;
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); ++iter)
|
||||
{
|
||||
if ((*iter).find("--resource=") == 0)
|
||||
{
|
||||
resource = (*iter).substr(strlen("--resource="));
|
||||
parameters.erase(iter);
|
||||
break;
|
||||
}
|
||||
if ((*iter)[0] == '-')
|
||||
{
|
||||
std::cerr << "Error parameter for '" << (*iter) << "'"
|
||||
<< std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (parameters.size() > 1)
|
||||
{
|
||||
std::cerr << "Too many parameters" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
auto className = parameters[0];
|
||||
createARestfulController(className, resource);
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newSimpleControllerHeaderFile(
|
||||
@ -60,67 +95,69 @@ void create_controller::newSimpleControllerHeaderFile(
|
||||
const std::string &className)
|
||||
{
|
||||
file << "#pragma once\n";
|
||||
file << "\n";
|
||||
file << "#include <drogon/HttpSimpleController.h>\n";
|
||||
file << "\n";
|
||||
file << "using namespace drogon;\n";
|
||||
std::string indent = "";
|
||||
file << "\n";
|
||||
std::string class_name = className;
|
||||
std::string namepace_path = "/";
|
||||
auto pos = class_name.find("::");
|
||||
size_t namespaceCount = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
++namespaceCount;
|
||||
auto namespaceName = class_name.substr(0, pos);
|
||||
class_name = class_name.substr(pos + 2);
|
||||
file << indent << "namespace " << namespaceName << "\n";
|
||||
file << "namespace " << namespaceName << "\n";
|
||||
namepace_path.append(namespaceName).append("/");
|
||||
file << indent << "{\n";
|
||||
indent.append(" ");
|
||||
file << "{\n";
|
||||
pos = class_name.find("::");
|
||||
}
|
||||
file << indent << "class " << class_name
|
||||
<< ":public drogon::HttpSimpleController<" << class_name << ">\n";
|
||||
file << indent << "{\n";
|
||||
file << indent << "public:\n";
|
||||
file << indent
|
||||
<< " virtual void asyncHandleHttpRequest(const HttpRequestPtr& "
|
||||
file << "class " << class_name << " : public drogon::HttpSimpleController<"
|
||||
<< class_name << ">\n";
|
||||
file << "{\n";
|
||||
file << " public:\n";
|
||||
file << " void asyncHandleHttpRequest(const HttpRequestPtr& "
|
||||
"req, std::function<void (const HttpResponsePtr &)> &&callback) "
|
||||
"override;\n";
|
||||
|
||||
file << indent << " PATH_LIST_BEGIN\n";
|
||||
file << indent << " //list path definitions here;\n";
|
||||
file << indent
|
||||
<< " "
|
||||
"//PATH_ADD(\"/"
|
||||
"path\",\"filter1\",\"filter2\",HttpMethod1,HttpMethod2...);\n";
|
||||
file << indent << " PATH_LIST_END\n";
|
||||
file << indent << "};\n";
|
||||
if (indent == "")
|
||||
return;
|
||||
do
|
||||
file << " PATH_LIST_BEGIN\n";
|
||||
file << " // list path definitions here;\n";
|
||||
file << " "
|
||||
"// PATH_ADD(\"/"
|
||||
"path\", \"filter1\", \"filter2\", HttpMethod1, HttpMethod2...);\n";
|
||||
file << " PATH_LIST_END\n";
|
||||
file << "};\n";
|
||||
while (namespaceCount > 0)
|
||||
{
|
||||
indent.resize(indent.length() - 4);
|
||||
file << indent << "}\n";
|
||||
} while (indent != "");
|
||||
--namespaceCount;
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newSimpleControllerSourceFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename)
|
||||
{
|
||||
file << "#include \"" << filename << ".h\"\n";
|
||||
file << "\n";
|
||||
auto pos = className.rfind("::");
|
||||
auto class_name = className;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto namespacename = className.substr(0, pos);
|
||||
file << "using namespace " << namespacename << ";\n";
|
||||
file << "\n";
|
||||
class_name = className.substr(pos + 2);
|
||||
}
|
||||
file << "void " << class_name
|
||||
<< "::asyncHandleHttpRequest(const HttpRequestPtr& req, "
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback)\n";
|
||||
file << "{\n";
|
||||
file << " //write your application logic here\n";
|
||||
file << "}";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
void create_controller::newWebsockControllerHeaderFile(
|
||||
@ -128,86 +165,85 @@ void create_controller::newWebsockControllerHeaderFile(
|
||||
const std::string &className)
|
||||
{
|
||||
file << "#pragma once\n";
|
||||
file << "\n";
|
||||
file << "#include <drogon/WebSocketController.h>\n";
|
||||
file << "\n";
|
||||
file << "using namespace drogon;\n";
|
||||
std::string indent = "";
|
||||
file << "\n";
|
||||
std::string class_name = className;
|
||||
std::string namepace_path = "/";
|
||||
auto pos = class_name.find("::");
|
||||
size_t namespaceCount = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
++namespaceCount;
|
||||
auto namespaceName = class_name.substr(0, pos);
|
||||
class_name = class_name.substr(pos + 2);
|
||||
file << indent << "namespace " << namespaceName << "\n";
|
||||
file << "namespace " << namespaceName << "\n";
|
||||
namepace_path.append(namespaceName).append("/");
|
||||
file << indent << "{\n";
|
||||
indent.append(" ");
|
||||
file << "{\n";
|
||||
pos = class_name.find("::");
|
||||
}
|
||||
file << indent << "class " << class_name
|
||||
<< ":public drogon::WebSocketController<" << class_name << ">\n";
|
||||
file << indent << "{\n";
|
||||
file << indent << "public:\n";
|
||||
file
|
||||
<< indent
|
||||
<< " virtual void handleNewMessage(const WebSocketConnectionPtr&,\n";
|
||||
file << indent << " std::string &&,\n";
|
||||
file << indent
|
||||
<< " const WebSocketMessageType &) "
|
||||
file << "class " << class_name << " : public drogon::WebSocketController<"
|
||||
<< class_name << ">\n";
|
||||
file << "{\n";
|
||||
file << " public:\n";
|
||||
file << " void handleNewMessage(const WebSocketConnectionPtr&,\n";
|
||||
file << " std::string &&,\n";
|
||||
file << " const WebSocketMessageType &) "
|
||||
"override;\n";
|
||||
file << indent
|
||||
<< " virtual void handleNewConnection(const HttpRequestPtr &,\n";
|
||||
file << indent
|
||||
<< " const "
|
||||
"WebSocketConnectionPtr&)override;\n";
|
||||
file << indent
|
||||
<< " virtual void handleConnectionClosed(const "
|
||||
"WebSocketConnectionPtr&)override;\n";
|
||||
file << indent << " WS_PATH_LIST_BEGIN\n";
|
||||
file << indent << " //list path definitions here;\n";
|
||||
file << indent
|
||||
<< " //WS_PATH_ADD(\"/path\",\"filter1\",\"filter2\",...);\n";
|
||||
file << indent << " WS_PATH_LIST_END\n";
|
||||
file << indent << "};\n";
|
||||
if (indent == "")
|
||||
return;
|
||||
do
|
||||
file << " void handleNewConnection(const HttpRequestPtr &,\n";
|
||||
file << " const "
|
||||
"WebSocketConnectionPtr&) override;\n";
|
||||
file << " void handleConnectionClosed(const "
|
||||
"WebSocketConnectionPtr&) override;\n";
|
||||
file << " WS_PATH_LIST_BEGIN\n";
|
||||
file << " // list path definitions here;\n";
|
||||
file << " // WS_PATH_ADD(\"/path\", \"filter1\", \"filter2\", ...);\n";
|
||||
file << " WS_PATH_LIST_END\n";
|
||||
file << "};\n";
|
||||
while (namespaceCount > 0)
|
||||
{
|
||||
indent.resize(indent.length() - 4);
|
||||
file << indent << "}\n";
|
||||
} while (indent != "");
|
||||
--namespaceCount;
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newWebsockControllerSourceFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename)
|
||||
{
|
||||
file << "#include \"" << filename << ".h\"\n";
|
||||
file << "\n";
|
||||
auto pos = className.rfind("::");
|
||||
auto class_name = className;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto namespacename = className.substr(0, pos);
|
||||
file << "using namespace " << namespacename << ";\n";
|
||||
file << "\n";
|
||||
class_name = className.substr(pos + 2);
|
||||
}
|
||||
file << "void " << class_name
|
||||
<< "::handleNewMessage(const WebSocketConnectionPtr& wsConnPtr, "
|
||||
"std::string &&message, const WebSocketMessageType &type)\n";
|
||||
file << "{\n";
|
||||
file << " //write your application logic here\n";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
file << "\n";
|
||||
file << "void " << class_name
|
||||
<< "::handleNewConnection(const HttpRequestPtr &req,const "
|
||||
<< "::handleNewConnection(const HttpRequestPtr &req, const "
|
||||
"WebSocketConnectionPtr& wsConnPtr)\n";
|
||||
file << "{\n";
|
||||
file << " //write your application logic here\n";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
file << "\n";
|
||||
file << "void " << class_name
|
||||
<< "::handleConnectionClosed(const WebSocketConnectionPtr& "
|
||||
"wsConnPtr)\n";
|
||||
file << "{\n";
|
||||
file << " //write your application logic here\n";
|
||||
file << " // write your application logic here\n";
|
||||
file << "}\n";
|
||||
}
|
||||
|
||||
@ -216,86 +252,84 @@ void create_controller::newHttpControllerHeaderFile(
|
||||
const std::string &className)
|
||||
{
|
||||
file << "#pragma once\n";
|
||||
file << "\n";
|
||||
file << "#include <drogon/HttpController.h>\n";
|
||||
file << "\n";
|
||||
file << "using namespace drogon;\n";
|
||||
std::string indent = "";
|
||||
file << "\n";
|
||||
std::string class_name = className;
|
||||
std::string namepace_path = "/";
|
||||
auto pos = class_name.find("::");
|
||||
size_t namespaceCount = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
++namespaceCount;
|
||||
auto namespaceName = class_name.substr(0, pos);
|
||||
class_name = class_name.substr(pos + 2);
|
||||
file << indent << "namespace " << namespaceName << "\n";
|
||||
file << "namespace " << namespaceName << "\n";
|
||||
namepace_path.append(namespaceName).append("/");
|
||||
file << indent << "{\n";
|
||||
indent.append(" ");
|
||||
file << "{\n";
|
||||
pos = class_name.find("::");
|
||||
}
|
||||
file << indent << "class " << class_name
|
||||
<< ":public drogon::HttpController<" << class_name << ">\n";
|
||||
file << indent << "{\n";
|
||||
file << indent << "public:\n";
|
||||
indent.append(" ");
|
||||
file << indent << "METHOD_LIST_BEGIN\n";
|
||||
file << indent
|
||||
<< "//use METHOD_ADD to add your custom processing function here;\n";
|
||||
file << indent << "//METHOD_ADD(" << class_name
|
||||
<< "::get,\"/get/{2}/{1}\",Get);"
|
||||
"//path is "
|
||||
<< namepace_path << class_name << "/get/{arg2}/{arg1}\n";
|
||||
file << indent << "//METHOD_ADD(" << class_name
|
||||
<< "::your_method_name,\"/{1}/{2}/list\",Get);"
|
||||
"//path is "
|
||||
file << "class " << class_name << " : public drogon::HttpController<"
|
||||
<< class_name << ">\n";
|
||||
file << "{\n";
|
||||
file << " public:\n";
|
||||
file << " METHOD_LIST_BEGIN\n";
|
||||
file << " // use METHOD_ADD to add your custom processing function "
|
||||
"here;\n";
|
||||
file << " // METHOD_ADD(" << class_name
|
||||
<< "::get, \"/{2}/{1}\", Get);"
|
||||
" // path is "
|
||||
<< namepace_path << class_name << "/{arg2}/{arg1}\n";
|
||||
file << " // METHOD_ADD(" << class_name
|
||||
<< "::your_method_name, \"/{1}/{2}/list\", Get);"
|
||||
" // path is "
|
||||
<< namepace_path << class_name << "/{arg1}/{arg2}/list\n";
|
||||
file << indent << "//ADD_METHOD_TO(" << class_name
|
||||
<< "::your_method_name,\"/absolute/path/{1}/{2}/list\",Get);"
|
||||
"//path is "
|
||||
<< namepace_path << "/absolute/path/{arg1}/{arg2}/list\n";
|
||||
file << indent << "\n";
|
||||
file << indent << "METHOD_LIST_END\n";
|
||||
file << indent
|
||||
<< "//your declaration of processing function maybe like this:\n";
|
||||
file << indent
|
||||
<< "//void get(const HttpRequestPtr& req,"
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback,int "
|
||||
"p1,std::string p2);\n";
|
||||
file << indent
|
||||
<< "//void your_method_name(const HttpRequestPtr& req,"
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback,double "
|
||||
"p1,int p2) const;\n";
|
||||
indent.resize(indent.length() - 4);
|
||||
file << indent << "};\n";
|
||||
if (indent == "")
|
||||
return;
|
||||
do
|
||||
file << " // ADD_METHOD_TO(" << class_name
|
||||
<< "::your_method_name, \"/absolute/path/{1}/{2}/list\", Get);"
|
||||
" // path is /absolute/path/{arg1}/{arg2}/list\n";
|
||||
file << "\n";
|
||||
file << " METHOD_LIST_END\n";
|
||||
file << " // your declaration of processing function maybe like this:\n";
|
||||
file << " // void get(const HttpRequestPtr& req, "
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback, int "
|
||||
"p1, std::string p2);\n";
|
||||
file << " // void your_method_name(const HttpRequestPtr& req, "
|
||||
"std::function<void (const HttpResponsePtr &)> &&callback, double "
|
||||
"p1, int p2) const;\n";
|
||||
file << "};\n";
|
||||
while (namespaceCount > 0)
|
||||
{
|
||||
indent.resize(indent.length() - 4);
|
||||
file << indent << "}\n";
|
||||
} while (indent != "");
|
||||
--namespaceCount;
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::newHttpControllerSourceFile(
|
||||
std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &filename)
|
||||
{
|
||||
file << "#include \"" << filename << ".h\"\n";
|
||||
file << "\n";
|
||||
auto pos = className.rfind("::");
|
||||
auto class_name = className;
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto namespacename = className.substr(0, pos);
|
||||
file << "using namespace " << namespacename << ";\n";
|
||||
file << "\n";
|
||||
class_name = className.substr(pos + 2);
|
||||
}
|
||||
|
||||
file << "//add definition of your processing function here\n";
|
||||
file << "// Add definition of your processing function here\n";
|
||||
}
|
||||
|
||||
void create_controller::createController(std::vector<std::string> &httpClasses,
|
||||
ControllerType type)
|
||||
{
|
||||
for (auto iter = httpClasses.begin(); iter != httpClasses.end(); iter++)
|
||||
for (auto iter = httpClasses.begin(); iter != httpClasses.end(); ++iter)
|
||||
{
|
||||
if ((*iter)[0] == '-')
|
||||
{
|
||||
@ -345,21 +379,93 @@ void create_controller::createController(const std::string &className,
|
||||
}
|
||||
if (type == Http)
|
||||
{
|
||||
std::cout << "create a http controller:" << className << std::endl;
|
||||
std::cout << "Create a http controller: " << className << std::endl;
|
||||
newHttpControllerHeaderFile(oHeadFile, className);
|
||||
newHttpControllerSourceFile(oSourceFile, className, ctlName);
|
||||
}
|
||||
else if (type == Simple)
|
||||
{
|
||||
std::cout << "create a http simple controller:" << className
|
||||
std::cout << "Create a http simple controller: " << className
|
||||
<< std::endl;
|
||||
newSimpleControllerHeaderFile(oHeadFile, className);
|
||||
newSimpleControllerSourceFile(oSourceFile, className, ctlName);
|
||||
}
|
||||
else if (type == WebSocket)
|
||||
{
|
||||
std::cout << "create a websocket controller:" << className << std::endl;
|
||||
std::cout << "Create a websocket controller: " << className
|
||||
<< std::endl;
|
||||
newWebsockControllerHeaderFile(oHeadFile, className);
|
||||
newWebsockControllerSourceFile(oSourceFile, className, ctlName);
|
||||
}
|
||||
}
|
||||
|
||||
void create_controller::createARestfulController(const std::string &className,
|
||||
const std::string &resource)
|
||||
{
|
||||
std::regex regex("::");
|
||||
std::string ctlName =
|
||||
std::regex_replace(className, regex, std::string("_"));
|
||||
|
||||
std::string headFileName = ctlName + ".h";
|
||||
std::string sourceFilename = ctlName + ".cc";
|
||||
{
|
||||
std::ifstream iHeadFile(headFileName.c_str(), std::ifstream::in);
|
||||
std::ifstream iSourceFile(sourceFilename.c_str(), std::ifstream::in);
|
||||
|
||||
if (iHeadFile || iSourceFile)
|
||||
{
|
||||
std::cout << "The file you want to create already exists, "
|
||||
"overwrite it(y/n)?"
|
||||
<< std::endl;
|
||||
auto in = getchar();
|
||||
(void)getchar(); // get the return key
|
||||
if (in != 'Y' && in != 'y')
|
||||
{
|
||||
std::cout << "Abort!" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
||||
std::ofstream oSourceFile(sourceFilename.c_str(), std::ofstream::out);
|
||||
if (!oHeadFile || !oSourceFile)
|
||||
{
|
||||
perror("");
|
||||
exit(1);
|
||||
}
|
||||
auto v = utils::splitString(className, "::");
|
||||
drogon::DrTemplateData data;
|
||||
data.insert("className", v[v.size() - 1]);
|
||||
v.pop_back();
|
||||
data.insert("namespaceVector", v);
|
||||
data.insert("resource", resource);
|
||||
data.insert("fileName", ctlName);
|
||||
if (resource.empty())
|
||||
{
|
||||
data.insert("ctlCommand",
|
||||
std::string("drogon_ctl create controller -r ") +
|
||||
className);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.insert("ctlCommand",
|
||||
std::string("drogon_ctl create controller -r ") +
|
||||
className + " --resource=" + resource);
|
||||
}
|
||||
try
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("restful_controller_h.csp");
|
||||
oHeadFile << templ->genText(data);
|
||||
templ = DrTemplateBase::newTemplate("restful_controller_cc.csp");
|
||||
oSourceFile << templ->genText(data);
|
||||
}
|
||||
catch (const std::exception &err)
|
||||
{
|
||||
std::cerr << err.what() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::cout << "Create a http restful API controller: " << className
|
||||
<< std::endl;
|
||||
std::cout << "File name: " << ctlName << ".h and " << ctlName << ".cc"
|
||||
<< std::endl;
|
||||
}
|
||||
|
@ -15,16 +15,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/DrObject.h>
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_controller : public DrObject<create_controller>,
|
||||
public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create controller files";
|
||||
}
|
||||
@ -34,12 +37,15 @@ class create_controller : public DrObject<create_controller>,
|
||||
{
|
||||
Simple = 0,
|
||||
Http,
|
||||
WebSocket
|
||||
WebSocket,
|
||||
Restful
|
||||
};
|
||||
|
||||
void createController(std::vector<std::string> &httpClasses,
|
||||
ControllerType type);
|
||||
void createController(const std::string &className, ControllerType type);
|
||||
void createARestfulController(const std::string &className,
|
||||
const std::string &resource);
|
||||
|
||||
void newSimpleControllerHeaderFile(std::ofstream &file,
|
||||
const std::string &className);
|
||||
|
@ -18,7 +18,9 @@
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
@ -67,6 +69,7 @@ static void createFilterSourceFile(std::ofstream &file,
|
||||
data.insert("filename", fileName);
|
||||
file << templ->genText(data);
|
||||
}
|
||||
|
||||
void create_filter::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.size() < 1)
|
||||
|
@ -17,18 +17,20 @@
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_filter : public DrObject<create_filter>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create filter class files";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string _outputPath = ".";
|
||||
std::string outputPath_{"."};
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,12 +15,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/config.h>
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <json/json.h>
|
||||
#include <drogon/orm/DbClient.h>
|
||||
using namespace drogon::orm;
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
@ -28,52 +30,405 @@ namespace drogon_ctl
|
||||
{
|
||||
struct ColumnInfo
|
||||
{
|
||||
std::string _colName;
|
||||
std::string _colValName;
|
||||
std::string _colTypeName;
|
||||
std::string _colType;
|
||||
std::string _colDatabaseType;
|
||||
std::string _dbType;
|
||||
ssize_t _colLength = 0;
|
||||
size_t _index = 0;
|
||||
bool _isAutoVal = false;
|
||||
bool _isPrimaryKey = false;
|
||||
bool _notNull = false;
|
||||
bool _hasDefaultVal = false;
|
||||
std::string colName_;
|
||||
std::string colValName_;
|
||||
std::string colTypeName_;
|
||||
std::string colType_;
|
||||
std::string colDatabaseType_;
|
||||
std::string dbType_;
|
||||
ssize_t colLength_{0};
|
||||
size_t index_{0};
|
||||
bool isAutoVal_{false};
|
||||
bool isPrimaryKey_{false};
|
||||
bool notNull_{false};
|
||||
bool hasDefaultVal_{false};
|
||||
};
|
||||
|
||||
inline std::string nameTransform(const std::string &origName, bool isType)
|
||||
{
|
||||
auto str = origName;
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) {
|
||||
return tolower(c);
|
||||
});
|
||||
std::string::size_type startPos = 0;
|
||||
std::string::size_type pos;
|
||||
std::string ret;
|
||||
do
|
||||
{
|
||||
pos = str.find("_", startPos);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos = str.find(".", startPos);
|
||||
}
|
||||
if (pos != std::string::npos)
|
||||
ret += str.substr(startPos, pos - startPos);
|
||||
else
|
||||
{
|
||||
ret += str.substr(startPos);
|
||||
break;
|
||||
}
|
||||
while (str[pos] == '_' || str[pos] == '.')
|
||||
++pos;
|
||||
if (str[pos] >= 'a' && str[pos] <= 'z')
|
||||
str[pos] += ('A' - 'a');
|
||||
startPos = pos;
|
||||
} while (1);
|
||||
if (isType && ret[0] >= 'a' && ret[0] <= 'z')
|
||||
ret[0] += ('A' - 'a');
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string escapeIdentifier(const std::string &identifier,
|
||||
const std::string &rdbms);
|
||||
|
||||
class PivotTable
|
||||
{
|
||||
public:
|
||||
PivotTable() = default;
|
||||
|
||||
PivotTable(const Json::Value &json)
|
||||
: tableName_(json.get("table_name", "").asString())
|
||||
{
|
||||
if (tableName_.empty())
|
||||
{
|
||||
throw std::runtime_error("table_name can't be empty");
|
||||
}
|
||||
originalKey_ = json.get("original_key", "").asString();
|
||||
if (originalKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("original_key can't be empty");
|
||||
}
|
||||
targetKey_ = json.get("target_key", "").asString();
|
||||
if (targetKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("target_key can't be empty");
|
||||
}
|
||||
}
|
||||
|
||||
PivotTable reverse() const
|
||||
{
|
||||
PivotTable pivot;
|
||||
pivot.tableName_ = tableName_;
|
||||
pivot.originalKey_ = targetKey_;
|
||||
pivot.targetKey_ = originalKey_;
|
||||
return pivot;
|
||||
}
|
||||
|
||||
const std::string &tableName() const
|
||||
{
|
||||
return tableName_;
|
||||
}
|
||||
|
||||
const std::string &originalKey() const
|
||||
{
|
||||
return originalKey_;
|
||||
}
|
||||
|
||||
const std::string &targetKey() const
|
||||
{
|
||||
return targetKey_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string tableName_;
|
||||
std::string originalKey_;
|
||||
std::string targetKey_;
|
||||
};
|
||||
|
||||
class ConvertMethod
|
||||
{
|
||||
public:
|
||||
ConvertMethod(const Json::Value &convert)
|
||||
{
|
||||
tableName_ = convert.get("table", "*").asString();
|
||||
colName_ = convert.get("column", "*").asString();
|
||||
|
||||
auto method = convert["method"];
|
||||
if (method.isNull())
|
||||
{
|
||||
throw std::runtime_error("method - object is missing.");
|
||||
} // endif
|
||||
if (!method.isObject())
|
||||
{
|
||||
throw std::runtime_error("method is not an object.");
|
||||
} // endif
|
||||
methodBeforeDbWrite_ = method.get("before_db_write", "").asString();
|
||||
methodAfterDbRead_ = method.get("after_db_read", "").asString();
|
||||
|
||||
auto includeFiles = convert["includes"];
|
||||
if (includeFiles.isNull())
|
||||
{
|
||||
return;
|
||||
} // endif
|
||||
if (!includeFiles.isArray())
|
||||
{
|
||||
throw std::runtime_error("includes must be an array");
|
||||
} // endif
|
||||
for (auto &i : includeFiles)
|
||||
{
|
||||
includeFiles_.push_back(i.asString());
|
||||
} // for
|
||||
}
|
||||
|
||||
ConvertMethod() = default;
|
||||
|
||||
bool shouldConvert(const std::string &tableName,
|
||||
const std::string &colName) const;
|
||||
|
||||
const std::string &tableName() const
|
||||
{
|
||||
return tableName_;
|
||||
}
|
||||
|
||||
const std::string &colName() const
|
||||
{
|
||||
return colName_;
|
||||
}
|
||||
|
||||
const std::string &methodBeforeDbWrite() const
|
||||
{
|
||||
return methodBeforeDbWrite_;
|
||||
}
|
||||
|
||||
const std::string &methodAfterDbRead() const
|
||||
{
|
||||
return methodAfterDbRead_;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &includeFiles() const
|
||||
{
|
||||
return includeFiles_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string tableName_{"*"};
|
||||
std::string colName_{"*"};
|
||||
std::string methodBeforeDbWrite_;
|
||||
std::string methodAfterDbRead_;
|
||||
std::vector<std::string> includeFiles_;
|
||||
};
|
||||
|
||||
class Relationship
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
HasOne,
|
||||
HasMany,
|
||||
ManyToMany
|
||||
};
|
||||
|
||||
Relationship(const Json::Value &relationship)
|
||||
{
|
||||
auto type = relationship.get("type", "has one").asString();
|
||||
if (type == "has one")
|
||||
{
|
||||
type_ = Relationship::Type::HasOne;
|
||||
}
|
||||
else if (type == "has many")
|
||||
{
|
||||
type_ = Relationship::Type::HasMany;
|
||||
}
|
||||
else if (type == "many to many")
|
||||
{
|
||||
type_ = Relationship::Type::ManyToMany;
|
||||
}
|
||||
else
|
||||
{
|
||||
char message[128];
|
||||
snprintf(message,
|
||||
sizeof(message),
|
||||
"Invalid relationship type: %s",
|
||||
type.data());
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
originalTableName_ =
|
||||
relationship.get("original_table_name", "").asString();
|
||||
if (originalTableName_.empty())
|
||||
{
|
||||
throw std::runtime_error("original_table_name can't be empty");
|
||||
}
|
||||
originalKey_ = relationship.get("original_key", "").asString();
|
||||
if (originalKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("original_key can't be empty");
|
||||
}
|
||||
originalTableAlias_ =
|
||||
relationship.get("original_table_alias", "").asString();
|
||||
targetTableName_ = relationship.get("target_table_name", "").asString();
|
||||
if (targetTableName_.empty())
|
||||
{
|
||||
throw std::runtime_error("target_table_name can't be empty");
|
||||
}
|
||||
targetKey_ = relationship.get("target_key", "").asString();
|
||||
if (targetKey_.empty())
|
||||
{
|
||||
throw std::runtime_error("target_key can't be empty");
|
||||
}
|
||||
targetTableAlias_ =
|
||||
relationship.get("target_table_alias", "").asString();
|
||||
enableReverse_ = relationship.get("enable_reverse", false).asBool();
|
||||
if (type_ == Type::ManyToMany)
|
||||
{
|
||||
auto &pivot = relationship["pivot_table"];
|
||||
if (pivot.isNull())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"ManyToMany relationship needs a pivot table");
|
||||
}
|
||||
pivotTable_ = PivotTable(pivot);
|
||||
}
|
||||
}
|
||||
|
||||
Relationship() = default;
|
||||
|
||||
Relationship reverse() const
|
||||
{
|
||||
Relationship r;
|
||||
if (type_ == Type::HasMany)
|
||||
{
|
||||
r.type_ = Type::HasOne;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.type_ = type_;
|
||||
}
|
||||
r.originalTableName_ = targetTableName_;
|
||||
r.originalTableAlias_ = targetTableAlias_;
|
||||
r.originalKey_ = targetKey_;
|
||||
r.targetTableName_ = originalTableName_;
|
||||
r.targetTableAlias_ = originalTableAlias_;
|
||||
r.targetKey_ = originalKey_;
|
||||
r.enableReverse_ = enableReverse_;
|
||||
r.pivotTable_ = pivotTable_.reverse();
|
||||
return r;
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
bool enableReverse() const
|
||||
{
|
||||
return enableReverse_;
|
||||
}
|
||||
|
||||
const std::string &originalTableName() const
|
||||
{
|
||||
return originalTableName_;
|
||||
}
|
||||
|
||||
const std::string &originalTableAlias() const
|
||||
{
|
||||
return originalTableAlias_;
|
||||
}
|
||||
|
||||
const std::string &originalKey() const
|
||||
{
|
||||
return originalKey_;
|
||||
}
|
||||
|
||||
const std::string &targetTableName() const
|
||||
{
|
||||
return targetTableName_;
|
||||
}
|
||||
|
||||
const std::string &targetTableAlias() const
|
||||
{
|
||||
return targetTableAlias_;
|
||||
}
|
||||
|
||||
const std::string &targetKey() const
|
||||
{
|
||||
return targetKey_;
|
||||
}
|
||||
|
||||
const PivotTable &pivotTable() const
|
||||
{
|
||||
return pivotTable_;
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_{Type::HasOne};
|
||||
std::string originalTableName_;
|
||||
std::string originalTableAlias_;
|
||||
std::string targetTableName_;
|
||||
std::string targetTableAlias_;
|
||||
std::string originalKey_;
|
||||
std::string targetKey_;
|
||||
bool enableReverse_{false};
|
||||
PivotTable pivotTable_;
|
||||
};
|
||||
|
||||
class create_model : public DrObject<create_model>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create Model classes files";
|
||||
}
|
||||
|
||||
protected:
|
||||
void createModel(const std::string &path);
|
||||
void createModel(const std::string &path, const Json::Value &config);
|
||||
void createModel(const std::string &path,
|
||||
const std::string &singleModelName);
|
||||
void createModel(const std::string &path,
|
||||
const Json::Value &config,
|
||||
const std::string &singleModelName);
|
||||
#if USE_POSTGRESQL
|
||||
void createModelClassFromPG(const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName);
|
||||
void createModelFromPG(const std::string &path, const DbClientPtr &client);
|
||||
void createModelClassFromPG(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName,
|
||||
const std::string &schema,
|
||||
const Json::Value &restfulApiConfig,
|
||||
const std::vector<Relationship> &relationships,
|
||||
const std::vector<ConvertMethod> &convertMethods);
|
||||
|
||||
void createModelFromPG(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &schema,
|
||||
const Json::Value &restfulApiConfig,
|
||||
std::map<std::string, std::vector<Relationship>> &relationships,
|
||||
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
|
||||
#endif
|
||||
#if USE_MYSQL
|
||||
void createModelClassFromMysql(const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName);
|
||||
void createModelFromMysql(const std::string &path,
|
||||
const DbClientPtr &client);
|
||||
void createModelClassFromMysql(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName,
|
||||
const Json::Value &restfulApiConfig,
|
||||
const std::vector<Relationship> &relationships,
|
||||
const std::vector<ConvertMethod> &convertMethods);
|
||||
void createModelFromMysql(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const Json::Value &restfulApiConfig,
|
||||
std::map<std::string, std::vector<Relationship>> &relationships,
|
||||
std::map<std::string, std::vector<ConvertMethod>> &convertMethods);
|
||||
#endif
|
||||
#if USE_SQLITE3
|
||||
void createModelClassFromSqlite3(const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName);
|
||||
void createModelFromSqlite3(const std::string &path,
|
||||
const DbClientPtr &client);
|
||||
void createModelClassFromSqlite3(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const std::string &tableName,
|
||||
const Json::Value &restfulApiConfig,
|
||||
const std::vector<Relationship> &relationships,
|
||||
const std::vector<ConvertMethod> &convertMethod);
|
||||
void createModelFromSqlite3(
|
||||
const std::string &path,
|
||||
const DbClientPtr &client,
|
||||
const Json::Value &restfulApiConfig,
|
||||
std::map<std::string, std::vector<Relationship>> &relationships,
|
||||
std::map<std::string, std::vector<ConvertMethod>> &convertMethod);
|
||||
#endif
|
||||
std::string _dbname;
|
||||
void createRestfulAPIController(const DrTemplateData &tableInfo,
|
||||
const Json::Value &restfulApiConfig);
|
||||
std::string dbname_;
|
||||
bool forceOverwrite_{false};
|
||||
std::string outputPath_;
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
|
@ -18,7 +18,9 @@
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
@ -67,6 +69,7 @@ static void createPluginSourceFile(std::ofstream &file,
|
||||
data.insert("filename", fileName);
|
||||
file << templ->genText(data);
|
||||
}
|
||||
|
||||
void create_plugin::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.size() < 1)
|
||||
|
@ -17,18 +17,20 @@
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_plugin : public DrObject<create_plugin>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create plugin class files";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string _outputPath = ".";
|
||||
std::string outputPath_{"."};
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
|
@ -14,10 +14,16 @@
|
||||
|
||||
#include "create_project.h"
|
||||
#include <drogon/DrTemplateBase.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#endif
|
||||
#include <fstream>
|
||||
|
||||
using namespace drogon_ctl;
|
||||
@ -32,6 +38,7 @@ void create_project::handleCommand(std::vector<std::string> ¶meters)
|
||||
auto pName = parameters[0];
|
||||
createProject(pName);
|
||||
}
|
||||
|
||||
static void newCmakeFile(std::ofstream &cmakeFile,
|
||||
const std::string &projectName)
|
||||
{
|
||||
@ -40,54 +47,59 @@ static void newCmakeFile(std::ofstream &cmakeFile,
|
||||
auto templ = DrTemplateBase::newTemplate("cmake.csp");
|
||||
cmakeFile << templ->genText(data);
|
||||
}
|
||||
|
||||
static void newMainFile(std::ofstream &mainFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("demoMain");
|
||||
mainFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newGitIgFile(std::ofstream &gitFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("gitignore.csp");
|
||||
gitFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newUuidFindFile(std::ofstream &uuidFile)
|
||||
static void newConfigJsonFile(std::ofstream &configJsonFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("FindUUID.csp");
|
||||
uuidFile << templ->genText();
|
||||
auto templ = DrTemplateBase::newTemplate("config_json");
|
||||
configJsonFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newJsonFindFile(std::ofstream &jsonFile)
|
||||
static void newConfigYamlFile(std::ofstream &configYamlFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("FindJsoncpp.csp");
|
||||
jsonFile << templ->genText();
|
||||
auto templ = DrTemplateBase::newTemplate("config_yaml");
|
||||
configYamlFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newMySQLFindFile(std::ofstream &mysqlFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("FindMySQL.csp");
|
||||
mysqlFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newSQLite3FindFile(std::ofstream &sqlite3File)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("FindSQLite3.csp");
|
||||
sqlite3File << templ->genText();
|
||||
}
|
||||
|
||||
static void newConfigFile(std::ofstream &configFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("config");
|
||||
configFile << templ->genText();
|
||||
}
|
||||
static void newModelConfigFile(std::ofstream &configFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("model_json");
|
||||
configFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newTestMainFile(std::ofstream &mainFile)
|
||||
{
|
||||
auto templ = DrTemplateBase::newTemplate("test_main");
|
||||
mainFile << templ->genText();
|
||||
}
|
||||
|
||||
static void newTestCmakeFile(std::ofstream &testCmakeFile,
|
||||
const std::string &projectName)
|
||||
{
|
||||
HttpViewData data;
|
||||
data.insert("ProjectName", projectName);
|
||||
auto templ = DrTemplateBase::newTemplate("test_cmake");
|
||||
testCmakeFile << templ->genText(data);
|
||||
}
|
||||
|
||||
void create_project::createProject(const std::string &projectName)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (_access(projectName.data(), 0) == 0)
|
||||
#else
|
||||
if (access(projectName.data(), 0) == 0)
|
||||
#endif
|
||||
{
|
||||
std::cerr
|
||||
<< "The directory already exists, please use another project name!"
|
||||
@ -95,37 +107,37 @@ void create_project::createProject(const std::string &projectName)
|
||||
exit(1);
|
||||
}
|
||||
std::cout << "create a project named " << projectName << std::endl;
|
||||
mkdir(projectName.data(), 0755);
|
||||
// 1.create CMakeLists.txt
|
||||
|
||||
drogon::utils::createPath(projectName);
|
||||
// 1.create CMakeLists.txt
|
||||
#ifdef _WIN32
|
||||
auto r = _chdir(projectName.data());
|
||||
#else
|
||||
auto r = chdir(projectName.data());
|
||||
#endif
|
||||
(void)(r);
|
||||
std::ofstream cmakeFile("CMakeLists.txt", std::ofstream::out);
|
||||
newCmakeFile(cmakeFile, projectName);
|
||||
std::ofstream mainFile("main.cc", std::ofstream::out);
|
||||
newMainFile(mainFile);
|
||||
mkdir("views", 0755);
|
||||
mkdir("controllers", 0755);
|
||||
mkdir("filters", 0755);
|
||||
mkdir("plugins", 0755);
|
||||
mkdir("build", 0755);
|
||||
mkdir("models", 0755);
|
||||
mkdir("cmake_modules", 0755);
|
||||
std::ofstream jsonFile("cmake_modules/FindJsoncpp.cmake",
|
||||
std::ofstream::out);
|
||||
newJsonFindFile(jsonFile);
|
||||
std::ofstream uuidFile("cmake_modules/FindUUID.cmake", std::ofstream::out);
|
||||
newUuidFindFile(uuidFile);
|
||||
std::ofstream mysqlFile("cmake_modules/FindMySQL.cmake",
|
||||
std::ofstream::out);
|
||||
newMySQLFindFile(mysqlFile);
|
||||
std::ofstream sqlite3File("cmake_modules/FindSQLite3.cmake",
|
||||
std::ofstream::out);
|
||||
newSQLite3FindFile(sqlite3File);
|
||||
drogon::utils::createPath("views");
|
||||
drogon::utils::createPath("controllers");
|
||||
drogon::utils::createPath("filters");
|
||||
drogon::utils::createPath("plugins");
|
||||
drogon::utils::createPath("build");
|
||||
drogon::utils::createPath("models");
|
||||
drogon::utils::createPath("test");
|
||||
|
||||
std::ofstream gitFile(".gitignore", std::ofstream::out);
|
||||
newGitIgFile(gitFile);
|
||||
std::ofstream configFile("config.json", std::ofstream::out);
|
||||
newConfigFile(configFile);
|
||||
std::ofstream configJsonFile("config.json", std::ofstream::out);
|
||||
newConfigJsonFile(configJsonFile);
|
||||
std::ofstream configYamlFile("config.yaml", std::ofstream::out);
|
||||
newConfigYamlFile(configYamlFile);
|
||||
std::ofstream modelConfigFile("models/model.json", std::ofstream::out);
|
||||
newModelConfigFile(modelConfigFile);
|
||||
std::ofstream testMainFile("test/test_main.cc", std::ofstream::out);
|
||||
newTestMainFile(testMainFile);
|
||||
std::ofstream testCmakeFile("test/CMakeLists.txt", std::ofstream::out);
|
||||
newTestCmakeFile(testCmakeFile, projectName);
|
||||
}
|
||||
|
@ -16,19 +16,21 @@
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_project : public DrObject<create_project>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create a project";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string _outputPath = ".";
|
||||
std::string outputPath_{"."};
|
||||
void createProject(const std::string &projectName);
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
*
|
||||
* create_view.cc
|
||||
* An Tao
|
||||
* @file create_view.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
#include "create_view.h"
|
||||
#include "cmd.h"
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
@ -45,13 +46,14 @@ static std::string &replace_all(std::string &str,
|
||||
{
|
||||
str = str.replace(pos, old_value.length(), new_value);
|
||||
pos += new_value.length() - old_value.length();
|
||||
pos++;
|
||||
++pos;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static void parseCxxLine(std::ofstream &oSrcFile,
|
||||
const std::string &line,
|
||||
const std::string &streamName,
|
||||
@ -65,6 +67,7 @@ static void parseCxxLine(std::ofstream &oSrcFile,
|
||||
oSrcFile << tmp << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static void outputVal(std::ofstream &oSrcFile,
|
||||
const std::string &streamName,
|
||||
const std::string &viewDataName,
|
||||
@ -75,12 +78,12 @@ static void outputVal(std::ofstream &oSrcFile,
|
||||
<< "\"];\n";
|
||||
oSrcFile << " if(val.type()==typeid(const char *)){\n";
|
||||
oSrcFile << " " << streamName
|
||||
<< "<<*any_cast<const char *>(&val);\n";
|
||||
<< "<<*(std::any_cast<const char *>(&val));\n";
|
||||
oSrcFile << " }else "
|
||||
"if(val.type()==typeid(std::string)||val.type()==typeid(const "
|
||||
"std::string)){\n";
|
||||
oSrcFile << " " << streamName
|
||||
<< "<<*any_cast<const std::string>(&val);\n";
|
||||
<< "<<*(std::any_cast<const std::string>(&val));\n";
|
||||
oSrcFile << " }\n";
|
||||
oSrcFile << "}\n";
|
||||
}
|
||||
@ -109,11 +112,15 @@ static void parseLine(std::ofstream &oSrcFile,
|
||||
{
|
||||
std::string::size_type pos(0);
|
||||
// std::cout<<line<<"("<<line.length()<<")\n";
|
||||
if (line.length() > 0 && line[line.length() - 1] == '\r')
|
||||
{
|
||||
line.resize(line.length() - 1);
|
||||
}
|
||||
if (line.length() == 0)
|
||||
{
|
||||
// std::cout<<"blank line!"<<std::endl;
|
||||
// std::cout<<streamName<<"<<\"\\n\";\n";
|
||||
if (returnFlag)
|
||||
if (returnFlag && !cxx_flag)
|
||||
oSrcFile << streamName << "<<\"\\n\";\n";
|
||||
return;
|
||||
}
|
||||
@ -149,10 +156,10 @@ static void parseLine(std::ofstream &oSrcFile,
|
||||
std::string keyName = newLine.substr(0, pos);
|
||||
auto iter = keyName.begin();
|
||||
while (iter != keyName.end() && *iter == ' ')
|
||||
iter++;
|
||||
++iter;
|
||||
auto iterEnd = iter;
|
||||
while (iterEnd != keyName.end() && *iterEnd != ' ')
|
||||
iterEnd++;
|
||||
++iterEnd;
|
||||
keyName = std::string(iter, iterEnd);
|
||||
outputVal(oSrcFile, streamName, viewDataName, keyName);
|
||||
std::string tailLine =
|
||||
@ -182,10 +189,10 @@ static void parseLine(std::ofstream &oSrcFile,
|
||||
std::string keyName = newLine.substr(0, pos);
|
||||
auto iter = keyName.begin();
|
||||
while (iter != keyName.end() && *iter == ' ')
|
||||
iter++;
|
||||
++iter;
|
||||
auto iterEnd = iter;
|
||||
while (iterEnd != keyName.end() && *iterEnd != ' ')
|
||||
iterEnd++;
|
||||
++iterEnd;
|
||||
keyName = std::string(iter, iterEnd);
|
||||
outputSubView(oSrcFile, streamName, viewDataName, keyName);
|
||||
std::string tailLine =
|
||||
@ -243,61 +250,123 @@ static void parseLine(std::ofstream &oSrcFile,
|
||||
|
||||
void create_view::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
|
||||
for (auto iter = parameters.begin(); iter != parameters.end();)
|
||||
{
|
||||
auto file = *iter;
|
||||
auto &file = *iter;
|
||||
if (file == "-o" || file == "--output")
|
||||
{
|
||||
iter = parameters.erase(iter);
|
||||
if (iter != parameters.end())
|
||||
{
|
||||
_outputPath = *iter;
|
||||
outputPath_ = *iter;
|
||||
iter = parameters.erase(iter);
|
||||
}
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
else if (file == "-n" || file == "--namespace")
|
||||
{
|
||||
iter = parameters.erase(iter);
|
||||
if (iter != parameters.end())
|
||||
{
|
||||
namespaces_ = utils::splitString(*iter, "::");
|
||||
iter = parameters.erase(iter);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (file == "--path-to-namespace")
|
||||
{
|
||||
iter = parameters.erase(iter);
|
||||
pathToNamespaceFlag_ = true;
|
||||
continue;
|
||||
}
|
||||
else if (file[0] == '-')
|
||||
{
|
||||
std::cout << ARGS_ERROR_STR << std::endl;
|
||||
return;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
createViewFiles(parameters);
|
||||
}
|
||||
|
||||
void create_view::createViewFiles(std::vector<std::string> &cspFileNames)
|
||||
{
|
||||
for (auto const &file : cspFileNames)
|
||||
{
|
||||
std::cout << "create view:" << file << std::endl;
|
||||
createViewFile(file);
|
||||
if (createViewFile(file) != 0)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int create_view::createViewFile(const std::string &script_filename)
|
||||
{
|
||||
std::cout << "create HttpView Class file by " << script_filename
|
||||
<< std::endl;
|
||||
if (pathToNamespaceFlag_)
|
||||
{
|
||||
std::string::size_type pos1 = 0, pos2 = 0;
|
||||
if (script_filename.length() >= 2 && script_filename[0] == '.' &&
|
||||
(script_filename[1] == '/' || script_filename[1] == '\\'))
|
||||
{
|
||||
pos1 = pos2 = 2;
|
||||
}
|
||||
else if (script_filename.length() >= 1 &&
|
||||
(script_filename[0] == '/' || script_filename[0] == '\\'))
|
||||
{
|
||||
pos1 = pos2 = 1;
|
||||
}
|
||||
while (pos2 < script_filename.length() - 1)
|
||||
{
|
||||
if (script_filename[pos2] == '/' || script_filename[pos2] == '\\')
|
||||
{
|
||||
if (pos2 > pos1)
|
||||
{
|
||||
namespaces_.push_back(
|
||||
script_filename.substr(pos1, pos2 - pos1));
|
||||
}
|
||||
pos1 = ++pos2;
|
||||
}
|
||||
else
|
||||
{
|
||||
++pos2;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string npPrefix;
|
||||
for (auto &np : namespaces_)
|
||||
{
|
||||
npPrefix += np;
|
||||
npPrefix += "_";
|
||||
}
|
||||
std::ifstream infile(script_filename.c_str(), std::ifstream::in);
|
||||
if (infile)
|
||||
{
|
||||
std::string::size_type pos = script_filename.rfind(".");
|
||||
std::string::size_type pos = script_filename.rfind('.');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string className = script_filename.substr(0, pos);
|
||||
if ((pos = className.rfind("/")) != std::string::npos)
|
||||
if ((pos = className.rfind('/')) != std::string::npos)
|
||||
{
|
||||
className = className.substr(pos + 1);
|
||||
}
|
||||
std::cout << "className=" << className << std::endl;
|
||||
std::string headFileName = _outputPath + "/" + className + ".h";
|
||||
std::string sourceFilename = _outputPath + "/" + className + ".cc";
|
||||
std::string headFileName =
|
||||
outputPath_ + "/" + npPrefix + className + ".h";
|
||||
std::string sourceFilename =
|
||||
outputPath_ + "/" + npPrefix + className + ".cc";
|
||||
std::ofstream oHeadFile(headFileName.c_str(), std::ofstream::out);
|
||||
std::ofstream oSourceFile(sourceFilename.c_str(),
|
||||
std::ofstream::out);
|
||||
if (!oHeadFile || !oSourceFile)
|
||||
{
|
||||
std::cerr << "Can't open " << headFileName << " or "
|
||||
<< sourceFilename << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
newViewHeaderFile(oHeadFile, className);
|
||||
newViewSourceFile(oSourceFile, className, infile);
|
||||
newViewSourceFile(oSourceFile, className, npPrefix, infile);
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
@ -309,29 +378,41 @@ int create_view::createViewFile(const std::string &script_filename)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void create_view::newViewHeaderFile(std::ofstream &file,
|
||||
const std::string &className)
|
||||
{
|
||||
file << "//this file is generated by program automatically,don't modify "
|
||||
"it!\n";
|
||||
file << "#include <drogon/DrTemplate.h>\n";
|
||||
file << "using namespace drogon;\n";
|
||||
file << "class " << className << ":public DrTemplate<" << className
|
||||
for (auto &np : namespaces_)
|
||||
{
|
||||
file << "namespace " << np << "\n";
|
||||
file << "{\n";
|
||||
}
|
||||
file << "class " << className << ":public drogon::DrTemplate<" << className
|
||||
<< ">\n";
|
||||
file << "{\npublic:\n\t" << className << "(){};\n\tvirtual ~" << className
|
||||
<< "(){};\n\t"
|
||||
"virtual std::string genText(const DrTemplateData &) override;\n};";
|
||||
"virtual std::string genText(const drogon::DrTemplateData &) "
|
||||
"override;\n};\n";
|
||||
for (std::size_t i = 0; i < namespaces_.size(); ++i)
|
||||
{
|
||||
file << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
void create_view::newViewSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &namespacePrefix,
|
||||
std::ifstream &infile)
|
||||
{
|
||||
file << "//this file is generated by program(drogon_ctl) "
|
||||
"automatically,don't modify it!\n";
|
||||
file << "#include \"" << className << ".h\"\n";
|
||||
file << "#include \"" << namespacePrefix << className << ".h\"\n";
|
||||
file << "#include <drogon/utils/OStringStream.h>\n";
|
||||
file << "#include <drogon/utils/Utilities.h>\n";
|
||||
file << "#include <string>\n";
|
||||
file << "#include <sstream>\n";
|
||||
file << "#include <map>\n";
|
||||
file << "#include <vector>\n";
|
||||
file << "#include <set>\n";
|
||||
@ -343,19 +424,26 @@ void create_view::newViewSourceFile(std::ofstream &file,
|
||||
file << "#include <deque>\n";
|
||||
file << "#include <queue>\n";
|
||||
|
||||
// file << "using namespace std;\n";
|
||||
// file <<"void __attribute__((constructor)) startup()\n";
|
||||
// file <<"{std::cout<<\"dynamic lib start to load!\"<<std::endl;}\n";
|
||||
// file <<"void __attribute__((destructor)) shutdown()\n";
|
||||
// file <<"{std::cout<<\"dynamic lib start to unload!\"<<std::endl;}\n";
|
||||
std::string buffer;
|
||||
char line[8192];
|
||||
int import_flag = 0;
|
||||
|
||||
while (infile.getline(line, sizeof(line)))
|
||||
// Find layout tag
|
||||
std::string layoutName;
|
||||
std::regex layoutReg("<%layout[ \\t]+(((?!%\\}).)*[^ \\t])[ \\t]*%>");
|
||||
for (std::string buffer; std::getline(infile, buffer);)
|
||||
{
|
||||
std::smatch results;
|
||||
if (std::regex_search(buffer, results, layoutReg))
|
||||
{
|
||||
if (results.size() > 1)
|
||||
{
|
||||
layoutName = results[1].str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
infile.clear();
|
||||
infile.seekg(0, std::ifstream::beg);
|
||||
bool import_flag{false};
|
||||
for (std::string buffer; std::getline(infile, buffer);)
|
||||
{
|
||||
buffer = line;
|
||||
|
||||
std::string::size_type pos(0);
|
||||
|
||||
if (!import_flag)
|
||||
@ -364,12 +452,12 @@ void create_view::newViewSourceFile(std::ofstream &file,
|
||||
std::transform(lowerBuffer.begin(),
|
||||
lowerBuffer.end(),
|
||||
lowerBuffer.begin(),
|
||||
::tolower);
|
||||
[](unsigned char c) { return tolower(c); });
|
||||
if ((pos = lowerBuffer.find(cxx_include)) != std::string::npos)
|
||||
{
|
||||
// std::cout<<"haha find it!"<<endl;
|
||||
std::string newLine = buffer.substr(pos + cxx_include.length());
|
||||
import_flag = 1;
|
||||
import_flag = true;
|
||||
if ((pos = newLine.find(cxx_end)) != std::string::npos)
|
||||
{
|
||||
newLine = newLine.substr(0, pos);
|
||||
@ -389,7 +477,6 @@ void create_view::newViewSourceFile(std::ofstream &file,
|
||||
{
|
||||
std::string newLine = buffer.substr(0, pos);
|
||||
file << newLine << "\n";
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
@ -400,14 +487,29 @@ void create_view::newViewSourceFile(std::ofstream &file,
|
||||
}
|
||||
}
|
||||
// std::cout<<"import_flag="<<import_flag<<std::endl;
|
||||
if (import_flag == 0)
|
||||
if (!import_flag)
|
||||
{
|
||||
infile.clear();
|
||||
infile.seekg(0, std::ifstream::beg);
|
||||
}
|
||||
|
||||
// std::cout<<"file pos:"<<infile.tellg()<<std::endl;
|
||||
|
||||
if (!namespaces_.empty())
|
||||
{
|
||||
file << "using namespace ";
|
||||
for (std::size_t i = 0; i < namespaces_.size(); ++i)
|
||||
{
|
||||
if (i != namespaces_.size() - 1)
|
||||
{
|
||||
file << namespaces_[i] << "::";
|
||||
}
|
||||
else
|
||||
{
|
||||
file << namespaces_[i] << ";";
|
||||
}
|
||||
}
|
||||
file << "\n";
|
||||
}
|
||||
file << "using namespace drogon;\n";
|
||||
std::string viewDataName = className + "_view_data";
|
||||
// virtual std::string genText(const DrTemplateData &)
|
||||
file << "std::string " << className << "::genText(const DrTemplateData& "
|
||||
@ -416,18 +518,37 @@ void create_view::newViewSourceFile(std::ofstream &file,
|
||||
std::string streamName = className + "_tmp_stream";
|
||||
|
||||
// oSrcFile <<"\tstd::string "<<bodyName<<";\n";
|
||||
file << "\tstd::stringstream " << streamName << ";\n";
|
||||
file << "\tdrogon::OStringStream " << streamName << ";\n";
|
||||
file << "\tstd::string layoutName{\"" << layoutName << "\"};\n";
|
||||
int cxx_flag = 0;
|
||||
while (infile.getline(line, sizeof(line)))
|
||||
for (std::string buffer; std::getline(infile, buffer);)
|
||||
{
|
||||
buffer = line;
|
||||
if (buffer.length() > 0)
|
||||
{
|
||||
std::regex re("\\{%[ \\t]*([^ \\t%]*)[^%]*%\\}");
|
||||
std::smatch results;
|
||||
if (std::regex_search(buffer, results, layoutReg))
|
||||
{
|
||||
if (results.size() > 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::regex re("\\{%[ \\t]*(((?!%\\}).)*[^ \\t])[ \\t]*%\\}");
|
||||
buffer = std::regex_replace(buffer, re, "<%c++$$$$<<$1;%>");
|
||||
}
|
||||
parseLine(file, buffer, streamName, viewDataName, cxx_flag);
|
||||
}
|
||||
|
||||
file << "return " << streamName << ".str();\n}\n";
|
||||
file << "if(layoutName.empty())\n{\n";
|
||||
file << "std::string ret{std::move(" << streamName << ".str())};\n";
|
||||
file << "return ret;\n}else\n{\n";
|
||||
file << "auto templ = DrTemplateBase::newTemplate(layoutName);\n";
|
||||
file << "if(!templ) return \"\";\n";
|
||||
file << "HttpViewData data = " << viewDataName << ";\n";
|
||||
file << "auto str = std::move(" << streamName << ".str());\n";
|
||||
file << "if(!str.empty() && str[str.length()-1] == '\\n') "
|
||||
"str.resize(str.length()-1);\n";
|
||||
file << "data[\"\"] = std::move(str);\n";
|
||||
file << "return templ->genText(data);\n";
|
||||
file << "}\n}\n";
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
*
|
||||
* create_view.h
|
||||
* An Tao
|
||||
* @file create_view.h
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
@ -17,24 +17,29 @@
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class create_view : public DrObject<create_view>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "create view class files";
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string _outputPath = ".";
|
||||
std::string outputPath_{"."};
|
||||
std::vector<std::string> namespaces_;
|
||||
bool pathToNamespaceFlag_{false};
|
||||
void createViewFiles(std::vector<std::string> &cspFileNames);
|
||||
int createViewFile(const std::string &script_filename);
|
||||
void newViewHeaderFile(std::ofstream &file, const std::string &className);
|
||||
void newViewSourceFile(std::ofstream &file,
|
||||
const std::string &className,
|
||||
const std::string &namespacePrefix,
|
||||
std::ifstream &infile);
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
using namespace drogon_ctl;
|
||||
|
||||
void help::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (parameters.size() == 0)
|
||||
|
@ -17,17 +17,20 @@
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class help : public DrObject<help>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "display this message";
|
||||
}
|
||||
virtual bool isTopCommand() override
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
*
|
||||
* main.cc
|
||||
* An Tao
|
||||
* @file main.cc
|
||||
* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
@ -13,21 +13,20 @@
|
||||
*/
|
||||
|
||||
#include "cmd.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
if (argc < 2)
|
||||
{
|
||||
std::vector<std::string> args = {"help"};
|
||||
args = {"help"};
|
||||
exeCommand(args);
|
||||
return 0;
|
||||
}
|
||||
std::vector<std::string> args;
|
||||
for (int i = 1; i < argc; i++)
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
|
@ -18,10 +18,17 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <iomanip>
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
#include <json/json.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace drogon_ctl;
|
||||
|
||||
std::string press::detail()
|
||||
{
|
||||
return "Use press command to do stress testing\n"
|
||||
@ -29,17 +36,19 @@ std::string press::detail()
|
||||
" -n num number of requests(default : 1)\n"
|
||||
" -t num number of threads(default : 1)\n"
|
||||
" -c num concurrent connections(default : 1)\n"
|
||||
// " -k keep alive(default: no)\n"
|
||||
" -q no progress indication(default: no)\n\n"
|
||||
" -k disable SSL certificate validation(default: enable)\n"
|
||||
" -f customize http request json file(default: disenable)\n"
|
||||
" -q no progress indication(default: show)\n\n"
|
||||
"example: drogon_ctl press -n 10000 -c 100 -t 4 -q "
|
||||
"http://localhost:8080/index.html\n";
|
||||
"http://localhost:8080/index.html -f ./http_request.json\n";
|
||||
}
|
||||
|
||||
void outputErrorAndExit(const string_view &err)
|
||||
void outputErrorAndExit(const std::string_view &err)
|
||||
{
|
||||
std::cout << err << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
for (auto iter = parameters.begin(); iter != parameters.end(); iter++)
|
||||
@ -49,7 +58,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (param == "-n")
|
||||
{
|
||||
iter++;
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No number of requests!");
|
||||
@ -57,7 +66,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
auto &num = *iter;
|
||||
try
|
||||
{
|
||||
_numOfRequests = std::stoll(num);
|
||||
numOfRequests_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -70,7 +79,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
auto num = param.substr(2);
|
||||
try
|
||||
{
|
||||
_numOfRequests = std::stoll(num);
|
||||
numOfRequests_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -83,7 +92,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (param == "-t")
|
||||
{
|
||||
iter++;
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No number of threads!");
|
||||
@ -91,7 +100,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
auto &num = *iter;
|
||||
try
|
||||
{
|
||||
_numOfThreads = std::stoll(num);
|
||||
numOfThreads_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -104,7 +113,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
auto num = param.substr(2);
|
||||
try
|
||||
{
|
||||
_numOfThreads = std::stoll(num);
|
||||
numOfThreads_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -117,7 +126,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
if (param == "-c")
|
||||
{
|
||||
iter++;
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No number of connections!");
|
||||
@ -125,7 +134,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
auto &num = *iter;
|
||||
try
|
||||
{
|
||||
_numOfConnections = std::stoll(num);
|
||||
numOfConnections_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -138,7 +147,7 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
auto num = param.substr(2);
|
||||
try
|
||||
{
|
||||
_numOfConnections = std::stoll(num);
|
||||
numOfConnections_ = std::stoll(num);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -147,88 +156,230 @@ void press::handleCommand(std::vector<std::string> ¶meters)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// else if (param == "-k")
|
||||
// {
|
||||
// _keepAlive = true;
|
||||
// continue;
|
||||
// }
|
||||
else if (param.find("-f") == 0)
|
||||
{
|
||||
if (param == "-f")
|
||||
{
|
||||
++iter;
|
||||
if (iter == parameters.end())
|
||||
{
|
||||
outputErrorAndExit("No http request json file!");
|
||||
}
|
||||
httpRequestJsonFile_ = *iter;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpRequestJsonFile_ = param.substr(2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (param == "-k")
|
||||
{
|
||||
certValidation_ = false;
|
||||
continue;
|
||||
}
|
||||
else if (param == "-q")
|
||||
{
|
||||
_processIndication = false;
|
||||
processIndication_ = false;
|
||||
}
|
||||
else if (param[0] != '-')
|
||||
{
|
||||
_url = param;
|
||||
url_ = param;
|
||||
}
|
||||
}
|
||||
// std::cout << "n=" << _numOfRequests << std::endl;
|
||||
// std::cout << "t=" << _numOfThreads << std::endl;
|
||||
// std::cout << "c=" << _numOfConnections << std::endl;
|
||||
// std::cout << "q=" << _processIndication << std::endl;
|
||||
// std::cout << "url=" << _url << std::endl;
|
||||
if (_url.empty() || _url.find("http") != 0 ||
|
||||
_url.find("://") == std::string::npos)
|
||||
// std::cout << "n=" << numOfRequests_ << std::endl;
|
||||
// std::cout << "t=" << numOfThreads_ << std::endl;
|
||||
// std::cout << "c=" << numOfConnections_ << std::endl;
|
||||
// std::cout << "q=" << processIndication_ << std::endl;
|
||||
// std::cout << "url=" << url_ << std::endl;
|
||||
if (url_.empty() || url_.compare(0, 4, "http") != 0 ||
|
||||
(url_.compare(4, 3, "://") != 0 && url_.compare(4, 4, "s://") != 0))
|
||||
{
|
||||
outputErrorAndExit("Invalid URL");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pos = _url.find("://");
|
||||
auto posOfPath = _url.find("/", pos + 3);
|
||||
auto pos = url_.find("://");
|
||||
auto posOfPath = url_.find('/', pos + 3);
|
||||
if (posOfPath == std::string::npos)
|
||||
{
|
||||
_host = _url;
|
||||
_path = "/";
|
||||
host_ = url_;
|
||||
path_ = "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
_host = _url.substr(0, posOfPath);
|
||||
_path = _url.substr(posOfPath);
|
||||
host_ = url_.substr(0, posOfPath);
|
||||
path_ = url_.substr(posOfPath);
|
||||
}
|
||||
}
|
||||
// std::cout << "host=" << _host << std::endl;
|
||||
// std::cout << "path=" << _path << std::endl;
|
||||
|
||||
/*
|
||||
http_request.json
|
||||
{
|
||||
"method": "POST",
|
||||
"header": {
|
||||
"token": "e2e9d0fe-dd14-4eaf-8ac1-0997730a805d"
|
||||
},
|
||||
"body": {
|
||||
"passwd": "123456",
|
||||
"account": "10001"
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (!httpRequestJsonFile_.empty())
|
||||
{
|
||||
Json::Value httpRequestJson;
|
||||
std::ifstream httpRequestFile(httpRequestJsonFile_,
|
||||
std::ifstream::binary);
|
||||
if (!httpRequestFile.is_open())
|
||||
{
|
||||
outputErrorAndExit(std::string{"No "} + httpRequestJsonFile_);
|
||||
}
|
||||
httpRequestFile >> httpRequestJson;
|
||||
|
||||
if (!httpRequestJson.isMember("method"))
|
||||
{
|
||||
outputErrorAndExit("No contain method");
|
||||
}
|
||||
|
||||
auto methodStr = httpRequestJson["method"].asString();
|
||||
std::transform(methodStr.begin(),
|
||||
methodStr.end(),
|
||||
methodStr.begin(),
|
||||
::toupper);
|
||||
|
||||
auto toHttpMethod = [&]() -> drogon::HttpMethod {
|
||||
if (methodStr == "GET")
|
||||
{
|
||||
return drogon::HttpMethod::Get;
|
||||
}
|
||||
else if (methodStr == "POST")
|
||||
{
|
||||
return drogon::HttpMethod::Post;
|
||||
}
|
||||
else if (methodStr == "HEAD")
|
||||
{
|
||||
return drogon::HttpMethod::Head;
|
||||
}
|
||||
else if (methodStr == "PUT")
|
||||
{
|
||||
return drogon::HttpMethod::Put;
|
||||
}
|
||||
else if (methodStr == "DELETE")
|
||||
{
|
||||
return drogon::HttpMethod::Delete;
|
||||
}
|
||||
else if (methodStr == "OPTIONS")
|
||||
{
|
||||
return drogon::HttpMethod::Options;
|
||||
}
|
||||
else if (methodStr == "PATCH")
|
||||
{
|
||||
return drogon::HttpMethod::Patch;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputErrorAndExit("invalid method");
|
||||
}
|
||||
return drogon::HttpMethod::Get;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::string> header;
|
||||
if (httpRequestJson.isMember("header"))
|
||||
{
|
||||
auto &jsonValue = httpRequestJson["header"];
|
||||
for (const auto &key : jsonValue.getMemberNames())
|
||||
{
|
||||
if (jsonValue[key].isString())
|
||||
{
|
||||
header[key] = jsonValue[key].asString();
|
||||
}
|
||||
else
|
||||
{
|
||||
header[key] = jsonValue[key].toStyledString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string body;
|
||||
if (httpRequestJson.isMember("body"))
|
||||
{
|
||||
Json::FastWriter fastWriter;
|
||||
body = fastWriter.write(httpRequestJson["body"]);
|
||||
}
|
||||
|
||||
createHttpRequestFunc_ = [this,
|
||||
method = toHttpMethod(),
|
||||
body = std::move(body),
|
||||
header =
|
||||
std::move(header)]() -> HttpRequestPtr {
|
||||
auto request = HttpRequest::newHttpRequest();
|
||||
request->setPath(path_);
|
||||
request->setMethod(method);
|
||||
for (const auto &[field, val] : header)
|
||||
request->addHeader(field, val);
|
||||
if (!body.empty())
|
||||
request->setBody(body);
|
||||
return request;
|
||||
};
|
||||
}
|
||||
|
||||
// std::cout << "host=" << host_ << std::endl;
|
||||
// std::cout << "path=" << path_ << std::endl;
|
||||
doTesting();
|
||||
}
|
||||
|
||||
void press::doTesting()
|
||||
{
|
||||
createRequestAndClients();
|
||||
if (_clients.empty())
|
||||
if (clients_.empty())
|
||||
{
|
||||
outputErrorAndExit("No connection!");
|
||||
}
|
||||
_stat._startDate = trantor::Date::now();
|
||||
for (auto &client : _clients)
|
||||
statistics_.startDate_ = trantor::Date::now();
|
||||
for (auto &client : clients_)
|
||||
{
|
||||
sendRequest(client);
|
||||
}
|
||||
_loopPool->wait();
|
||||
loopPool_->wait();
|
||||
}
|
||||
|
||||
void press::createRequestAndClients()
|
||||
{
|
||||
_loopPool = std::make_unique<trantor::EventLoopThreadPool>(_numOfThreads);
|
||||
_loopPool->start();
|
||||
for (size_t i = 0; i < _numOfConnections; i++)
|
||||
loopPool_ = std::make_unique<trantor::EventLoopThreadPool>(numOfThreads_);
|
||||
loopPool_->start();
|
||||
for (size_t i = 0; i < numOfConnections_; ++i)
|
||||
{
|
||||
auto client =
|
||||
HttpClient::newHttpClient(_host, _loopPool->getNextLoop());
|
||||
auto client = HttpClient::newHttpClient(host_,
|
||||
loopPool_->getNextLoop(),
|
||||
false,
|
||||
certValidation_);
|
||||
client->enableCookies();
|
||||
_clients.push_back(client);
|
||||
clients_.push_back(client);
|
||||
}
|
||||
}
|
||||
|
||||
void press::sendRequest(const HttpClientPtr &client)
|
||||
{
|
||||
auto numOfRequest = _stat._numOfRequestsSent++;
|
||||
if (numOfRequest >= _numOfRequests)
|
||||
auto numOfRequest = statistics_.numOfRequestsSent_++;
|
||||
if (numOfRequest >= numOfRequests_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto request = HttpRequest::newHttpRequest();
|
||||
request->setPath(_path);
|
||||
request->setMethod(Get);
|
||||
|
||||
HttpRequestPtr request;
|
||||
if (createHttpRequestFunc_)
|
||||
{
|
||||
request = createHttpRequestFunc_();
|
||||
}
|
||||
else
|
||||
{
|
||||
request = HttpRequest::newHttpRequest();
|
||||
request->setPath(path_);
|
||||
request->setMethod(Get);
|
||||
}
|
||||
|
||||
// std::cout << "send!" << std::endl;
|
||||
client->sendRequest(
|
||||
request,
|
||||
@ -237,23 +388,23 @@ void press::sendRequest(const HttpClientPtr &client)
|
||||
if (r == ReqResult::Ok)
|
||||
{
|
||||
// std::cout << "OK" << std::endl;
|
||||
goodNum = ++_stat._numOfGoodResponse;
|
||||
badNum = _stat._numOfBadResponse;
|
||||
_stat._bytesRecieved += resp->body().length();
|
||||
goodNum = ++statistics_.numOfGoodResponse_;
|
||||
badNum = statistics_.numOfBadResponse_;
|
||||
statistics_.bytesRecieved_ += resp->body().length();
|
||||
auto delay = trantor::Date::now().microSecondsSinceEpoch() -
|
||||
request->creationDate().microSecondsSinceEpoch();
|
||||
_stat._totalDelay += delay;
|
||||
statistics_.totalDelay_ += delay;
|
||||
}
|
||||
else
|
||||
{
|
||||
goodNum = _stat._numOfGoodResponse;
|
||||
badNum = ++_stat._numOfBadResponse;
|
||||
if (badNum > _numOfRequests / 10)
|
||||
goodNum = statistics_.numOfGoodResponse_;
|
||||
badNum = ++statistics_.numOfBadResponse_;
|
||||
if (badNum > numOfRequests_ / 10)
|
||||
{
|
||||
outputErrorAndExit("Too many errors");
|
||||
}
|
||||
}
|
||||
if (goodNum + badNum >= _numOfRequests)
|
||||
if (goodNum + badNum >= numOfRequests_)
|
||||
{
|
||||
outputResults();
|
||||
}
|
||||
@ -266,7 +417,7 @@ void press::sendRequest(const HttpClientPtr &client)
|
||||
});
|
||||
}
|
||||
|
||||
if (_processIndication)
|
||||
if (processIndication_)
|
||||
{
|
||||
auto rec = goodNum + badNum;
|
||||
if (rec % 100000 == 0)
|
||||
@ -280,34 +431,37 @@ void press::sendRequest(const HttpClientPtr &client)
|
||||
|
||||
void press::outputResults()
|
||||
{
|
||||
static std::mutex mtx;
|
||||
size_t totalSent = 0;
|
||||
size_t totalRecv = 0;
|
||||
for (auto &client : _clients)
|
||||
for (auto &client : clients_)
|
||||
{
|
||||
totalSent += client->bytesSent();
|
||||
totalRecv += client->bytesReceived();
|
||||
}
|
||||
auto now = trantor::Date::now();
|
||||
auto microSecs = now.microSecondsSinceEpoch() -
|
||||
_stat._startDate.microSecondsSinceEpoch();
|
||||
statistics_.startDate_.microSecondsSinceEpoch();
|
||||
double seconds = (double)microSecs / 1000000.0;
|
||||
size_t rps = _stat._numOfGoodResponse / seconds;
|
||||
auto rps = static_cast<size_t>(statistics_.numOfGoodResponse_ / seconds);
|
||||
std::cout << std::endl;
|
||||
std::cout << "TOTALS: " << _numOfConnections << " connect, "
|
||||
<< _numOfRequests << " requests, " << _stat._numOfGoodResponse
|
||||
<< " success, " << _stat._numOfBadResponse << " fail"
|
||||
<< std::endl;
|
||||
std::cout << "TOTALS: " << numOfConnections_ << " connect, "
|
||||
<< numOfRequests_ << " requests, "
|
||||
<< statistics_.numOfGoodResponse_ << " success, "
|
||||
<< statistics_.numOfBadResponse_ << " fail" << std::endl;
|
||||
|
||||
std::cout << "TRAFFIC: " << _stat._bytesRecieved / _stat._numOfGoodResponse
|
||||
std::cout << "TRAFFIC: "
|
||||
<< statistics_.bytesRecieved_ / statistics_.numOfGoodResponse_
|
||||
<< " avg bytes, "
|
||||
<< (totalRecv - _stat._bytesRecieved) / _stat._numOfGoodResponse
|
||||
<< " avg overhead, " << _stat._bytesRecieved << " bytes, "
|
||||
<< totalRecv - _stat._bytesRecieved << " overhead" << std::endl;
|
||||
<< (totalRecv - statistics_.bytesRecieved_) /
|
||||
statistics_.numOfGoodResponse_
|
||||
<< " avg overhead, " << statistics_.bytesRecieved_ << " bytes, "
|
||||
<< totalRecv - statistics_.bytesRecieved_ << " overhead"
|
||||
<< std::endl;
|
||||
|
||||
std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(3)
|
||||
<< "TIMING: " << seconds << " seconds, " << rps << " rps, "
|
||||
<< (double)(_stat._totalDelay) / _stat._numOfGoodResponse / 1000
|
||||
<< (double)(statistics_.totalDelay_) /
|
||||
statistics_.numOfGoodResponse_ / 1000
|
||||
<< " ms avg req time" << std::endl;
|
||||
|
||||
std::cout << "SPEED: download " << totalRecv / seconds / 1000
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <drogon/HttpClient.h>
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/net/EventLoopThreadPool.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@ -31,44 +32,50 @@ namespace drogon_ctl
|
||||
{
|
||||
struct Statistics
|
||||
{
|
||||
std::atomic_size_t _numOfRequestsSent = ATOMIC_VAR_INIT(0);
|
||||
std::atomic_size_t _bytesRecieved = ATOMIC_VAR_INIT(0);
|
||||
std::atomic_size_t _numOfGoodResponse = ATOMIC_VAR_INIT(0);
|
||||
std::atomic_size_t _numOfBadResponse = ATOMIC_VAR_INIT(0);
|
||||
std::atomic_size_t _totalDelay = ATOMIC_VAR_INIT(0);
|
||||
trantor::Date _startDate;
|
||||
trantor::Date _endDate;
|
||||
std::atomic_size_t numOfRequestsSent_{0};
|
||||
std::atomic_size_t bytesRecieved_{0};
|
||||
std::atomic_size_t numOfGoodResponse_{0};
|
||||
std::atomic_size_t numOfBadResponse_{0};
|
||||
std::atomic_size_t totalDelay_{0};
|
||||
trantor::Date startDate_;
|
||||
trantor::Date endDate_;
|
||||
};
|
||||
|
||||
class press : public DrObject<press>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "Do stress testing(Use 'drogon_ctl help press' for more "
|
||||
"information)";
|
||||
}
|
||||
virtual bool isTopCommand() override
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual std::string detail() override;
|
||||
|
||||
std::string detail() override;
|
||||
|
||||
private:
|
||||
size_t _numOfThreads = 1;
|
||||
size_t _numOfRequests = 1;
|
||||
size_t _numOfConnections = 1;
|
||||
// bool _keepAlive = false;
|
||||
bool _processIndication = true;
|
||||
std::string _url;
|
||||
std::string _host;
|
||||
std::string _path;
|
||||
size_t numOfThreads_{1};
|
||||
size_t numOfRequests_{1};
|
||||
size_t numOfConnections_{1};
|
||||
std::string httpRequestJsonFile_;
|
||||
std::function<HttpRequestPtr()> createHttpRequestFunc_;
|
||||
bool certValidation_{true};
|
||||
bool processIndication_{true};
|
||||
std::string url_;
|
||||
std::string host_;
|
||||
std::string path_;
|
||||
void doTesting();
|
||||
void createRequestAndClients();
|
||||
void sendRequest(const HttpClientPtr &client);
|
||||
void outputResults();
|
||||
std::unique_ptr<trantor::EventLoopThreadPool> _loopPool;
|
||||
std::vector<HttpClientPtr> _clients;
|
||||
Statistics _stat;
|
||||
std::unique_ptr<trantor::EventLoopThreadPool> loopPool_;
|
||||
std::vector<HttpClientPtr> clients_;
|
||||
Statistics statistics_;
|
||||
};
|
||||
} // namespace drogon_ctl
|
||||
|
@ -1,63 +0,0 @@
|
||||
# Find jsoncpp
|
||||
#
|
||||
# Find the jsoncpp includes and library
|
||||
#
|
||||
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
|
||||
#
|
||||
# This module defines
|
||||
# JSONCPP_INCLUDE_DIRS, where to find header, etc.
|
||||
# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp.
|
||||
# JSONCPP_FOUND, If false, do not try to use jsoncpp.
|
||||
# JSONCPP_INCLUDE_PREFIX, include prefix for jsoncpp
|
||||
|
||||
# only look in default directories
|
||||
find_path(
|
||||
JSONCPP_INCLUDE_DIR
|
||||
NAMES jsoncpp/json/json.h json/json.h
|
||||
DOC "jsoncpp include dir"
|
||||
)
|
||||
|
||||
find_library(
|
||||
JSONCPP_LIBRARY
|
||||
NAMES jsoncpp
|
||||
DOC "jsoncpp library"
|
||||
)
|
||||
|
||||
set(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR})
|
||||
set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY})
|
||||
|
||||
# debug library on windows
|
||||
# same naming convention as in qt (appending debug library with d)
|
||||
# boost is using the same "hack" as us with "optimized" and "debug"
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
find_library(
|
||||
JSONCPP_LIBRARY_DEBUG
|
||||
NAMES jsoncppd
|
||||
DOC "jsoncpp debug library"
|
||||
)
|
||||
|
||||
set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug ${JSONCPP_LIBRARY_DEBUG})
|
||||
|
||||
endif()
|
||||
|
||||
# find JSONCPP_INCLUDE_PREFIX
|
||||
find_path(
|
||||
JSONCPP_INCLUDE_PREFIX
|
||||
NAMES json.h
|
||||
PATH_SUFFIXES jsoncpp/json json
|
||||
)
|
||||
|
||||
if (${JSONCPP_INCLUDE_PREFIX} MATCHES "jsoncpp")
|
||||
set(JSONCPP_INCLUDE_PREFIX "jsoncpp")
|
||||
set(JSONCPP_INCLUDE_DIRS "${JSONCPP_INCLUDE_DIRS}/jsoncpp")
|
||||
else()
|
||||
set(JSONCPP_INCLUDE_PREFIX "")
|
||||
endif()
|
||||
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE
|
||||
# if all listed variables are TRUE, hide their existence from configuration view
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(jsoncpp DEFAULT_MSG
|
||||
JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
|
||||
mark_as_advanced (JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
|
@ -1,114 +0,0 @@
|
||||
#--------------------------------------------------------
|
||||
# Copyright (C) 1995-2007 MySQL AB
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of version 2 of the GNU General Public License as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# There are special exceptions to the terms and conditions of the GPL
|
||||
# as it is applied to this software. View the full text of the exception
|
||||
# in file LICENSE.exceptions in the top-level directory of this software
|
||||
# distribution.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
#
|
||||
# The MySQL Connector/ODBC is licensed under the terms of the
|
||||
# GPL, like most MySQL Connectors. There are special exceptions
|
||||
# to the terms and conditions of the GPL as it is applied to
|
||||
# this software, see the FLOSS License Exception available on
|
||||
# mysql.com.
|
||||
|
||||
##########################################################################
|
||||
|
||||
|
||||
#-------------- FIND MYSQL_INCLUDE_DIR ------------------
|
||||
FIND_PATH(MYSQL_INCLUDE_DIR mysql.h
|
||||
/usr/include/mysql
|
||||
/usr/local/include/mysql
|
||||
/opt/mysql/mysql/include
|
||||
/opt/mysql/mysql/include/mysql
|
||||
/opt/mysql/include
|
||||
/opt/local/include/mysql5
|
||||
/usr/local/mysql/include
|
||||
/usr/local/mysql/include/mysql
|
||||
$ENV{ProgramFiles}/MySQL/*/include
|
||||
$ENV{SystemDrive}/MySQL/*/include)
|
||||
|
||||
#----------------- FIND MYSQL_LIB_DIR -------------------
|
||||
IF (WIN32)
|
||||
# Set lib path suffixes
|
||||
# dist = for mysql binary distributions
|
||||
# build = for custom built tree
|
||||
IF (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
SET(libsuffixDist debug)
|
||||
SET(libsuffixBuild Debug)
|
||||
ELSE (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
SET(libsuffixDist opt)
|
||||
SET(libsuffixBuild Release)
|
||||
ADD_DEFINITIONS(-DDBUG_OFF)
|
||||
ENDIF (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||
|
||||
FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient
|
||||
PATHS
|
||||
$ENV{MYSQL_DIR}/lib/${libsuffixDist}
|
||||
$ENV{MYSQL_DIR}/libmysql
|
||||
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
|
||||
$ENV{MYSQL_DIR}/client/${libsuffixBuild}
|
||||
$ENV{MYSQL_DIR}/libmysql/${libsuffixBuild}
|
||||
$ENV{ProgramFiles}/MySQL/*/lib/${libsuffixDist}
|
||||
$ENV{SystemDrive}/MySQL/*/lib/${libsuffixDist})
|
||||
ELSE (WIN32)
|
||||
FIND_LIBRARY(MYSQL_LIB NAMES mysqlclient_r mariadbclient
|
||||
PATHS
|
||||
/usr/lib/mysql
|
||||
/usr/local/lib/mysql
|
||||
/usr/local/mysql/lib
|
||||
/usr/local/mysql/lib/mysql
|
||||
/opt/local/mysql5/lib
|
||||
/opt/local/lib/mysql5/mysql
|
||||
/opt/mysql/mysql/lib/mysql
|
||||
/opt/mysql/lib/mysql)
|
||||
ENDIF (WIN32)
|
||||
|
||||
IF(MYSQL_LIB)
|
||||
GET_FILENAME_COMPONENT(MYSQL_LIB_DIR ${MYSQL_LIB} PATH)
|
||||
ENDIF(MYSQL_LIB)
|
||||
|
||||
set(MYSQL_VERSION_STRING "")
|
||||
|
||||
EXEC_PROGRAM (grep ARGS "MARIADB_BASE_VERSION ${MYSQL_INCLUDE_DIR}/*.h|awk '{print $3}'" OUTPUT_VARIABLE MYSQL_VERSION_STRING)
|
||||
|
||||
IF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR)
|
||||
SET(MYSQL_FOUND TRUE)
|
||||
|
||||
FIND_LIBRARY(MYSQL_ZLIB zlib PATHS ${MYSQL_LIB_DIR})
|
||||
FIND_LIBRARY(MYSQL_TAOCRYPT taocrypt PATHS ${MYSQL_LIB_DIR})
|
||||
IF (MYSQL_LIB)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_LIB})
|
||||
ELSE()
|
||||
SET(MYSQL_CLIENT_LIBS mysqlclient_r)
|
||||
ENDIF()
|
||||
IF (MYSQL_ZLIB)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} zlib)
|
||||
ENDIF (MYSQL_ZLIB)
|
||||
IF (MYSQL_TAOCRYPT)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} taocrypt)
|
||||
ENDIF (MYSQL_TAOCRYPT)
|
||||
# Added needed mysqlclient dependencies on Windows
|
||||
IF (WIN32)
|
||||
SET(MYSQL_CLIENT_LIBS ${MYSQL_CLIENT_LIBS} ws2_32)
|
||||
ENDIF (WIN32)
|
||||
|
||||
MESSAGE(STATUS "MySQL Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}")
|
||||
MESSAGE(STATUS "MySQL client libraries: ${MYSQL_CLIENT_LIBS}")
|
||||
ELSEIF (MySQL_FIND_REQUIRED)
|
||||
MESSAGE(FATAL_ERROR "Cannot find MySQL. Include dir: ${MYSQL_INCLUDE_DIR} library dir: ${MYSQL_LIB_DIR}")
|
||||
ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIB_DIR)
|
@ -1,37 +0,0 @@
|
||||
# Copyright (C) 2007-2009 LuaDist.
|
||||
# Created by Peter Kapec <kapecp@gmail.com>
|
||||
# Redistribution and use of this file is allowed according to the terms of the MIT license.
|
||||
# For details see the COPYRIGHT file distributed with LuaDist.
|
||||
# Note:
|
||||
# Searching headers and libraries is very simple and is NOT as powerful as scripts
|
||||
# distributed with CMake, because LuaDist defines directories to search for.
|
||||
# Everyone is encouraged to contact the author with improvements. Maybe this file
|
||||
# becomes part of CMake distribution sometimes.
|
||||
|
||||
# - Find sqlite3
|
||||
# Find the native SQLITE3 headers and libraries.
|
||||
#
|
||||
# SQLITE3_INCLUDE_DIRS - where to find sqlite3.h, etc.
|
||||
# SQLITE3_LIBRARIES - List of libraries when using sqlite.
|
||||
# SQLITE3_FOUND - True if sqlite found.
|
||||
|
||||
# Look for the header file.
|
||||
FIND_PATH(SQLITE3_INCLUDE_DIR NAMES sqlite3.h)
|
||||
|
||||
# Look for the library.
|
||||
FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE if all listed variables are TRUE.
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLITE3 DEFAULT_MSG SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR)
|
||||
|
||||
# Copy the results to the output variables.
|
||||
IF(SQLITE3_FOUND)
|
||||
SET(SQLITE3_LIBRARIES ${SQLITE3_LIBRARY})
|
||||
SET(SQLITE3_INCLUDE_DIRS ${SQLITE3_INCLUDE_DIR})
|
||||
ELSE(SQLITE3_FOUND)
|
||||
SET(SQLITE3_LIBRARIES)
|
||||
SET(SQLITE3_INCLUDE_DIRS)
|
||||
ENDIF(SQLITE3_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)
|
@ -1,119 +0,0 @@
|
||||
# - Try to find UUID
|
||||
# Once done this will define
|
||||
#
|
||||
# UUID_FOUND - system has UUID
|
||||
# UUID_INCLUDE_DIRS - the UUID include directory
|
||||
# UUID_LIBRARIES - Link these to use UUID
|
||||
# UUID_DEFINITIONS - Compiler switches required for using UUID
|
||||
#
|
||||
# Copyright (c) 2006 Andreas Schneider <mail@cynapses.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
|
||||
|
||||
if (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(UUID_FOUND TRUE)
|
||||
else (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
|
||||
find_path(UUID_INCLUDE_DIR
|
||||
NAMES
|
||||
uuid.h
|
||||
PATH_SUFFIXES
|
||||
uuid
|
||||
HINTS
|
||||
${UUID_DIR}/include
|
||||
$ENV{UUID_DIR}/include
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/inc
|
||||
$ENV{DELTA_ROOT}/ext/inc
|
||||
$ENV{DELTA_ROOT}
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
/usr/include/gdal
|
||||
/sw/include # Fink
|
||||
/opt/local/include # DarwinPorts
|
||||
/opt/csw/include # Blastwave
|
||||
/opt/include
|
||||
[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include
|
||||
/usr/freeware/include
|
||||
)
|
||||
|
||||
find_library(UUID_LIBRARY
|
||||
NAMES
|
||||
uuid ossp-uuid
|
||||
HINTS
|
||||
${UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/lib
|
||||
$ENV{DELTA_ROOT}/ext/lib
|
||||
$ENV{DELTA_ROOT}
|
||||
$ENV{OSG_ROOT}/lib
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
/sw/lib
|
||||
/opt/local/lib
|
||||
/opt/csw/lib
|
||||
/opt/lib
|
||||
/usr/freeware/lib64
|
||||
)
|
||||
|
||||
find_library(UUID_LIBRARY_DEBUG
|
||||
NAMES
|
||||
uuidd
|
||||
HINTS
|
||||
${UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}/lib
|
||||
$ENV{UUID_DIR}
|
||||
${DELTA3D_EXT_DIR}/lib
|
||||
$ENV{DELTA_ROOT}/ext/lib
|
||||
$ENV{DELTA_ROOT}
|
||||
$ENV{OSG_ROOT}/lib
|
||||
PATHS
|
||||
~/Library/Frameworks
|
||||
/Library/Frameworks
|
||||
/usr/local/lib
|
||||
/usr/lib
|
||||
/sw/lib
|
||||
/opt/local/lib
|
||||
/opt/csw/lib
|
||||
/opt/lib
|
||||
/usr/freeware/lib64
|
||||
)
|
||||
|
||||
if (NOT UUID_LIBRARY AND BSD)
|
||||
set(UUID_LIBRARY "")
|
||||
endif(NOT UUID_LIBRARY AND BSD)
|
||||
|
||||
set(UUID_INCLUDE_DIRS ${UUID_INCLUDE_DIR})
|
||||
set(UUID_LIBRARIES ${UUID_LIBRARY})
|
||||
|
||||
if (UUID_INCLUDE_DIRS)
|
||||
if (BSD OR UUID_LIBRARIES)
|
||||
set(UUID_FOUND TRUE)
|
||||
endif (BSD OR UUID_LIBRARIES)
|
||||
endif (UUID_INCLUDE_DIRS)
|
||||
|
||||
if (UUID_FOUND)
|
||||
if (NOT UUID_FIND_QUIETLY)
|
||||
message(STATUS "Found UUID: ${UUID_LIBRARIES}")
|
||||
endif (NOT UUID_FIND_QUIETLY)
|
||||
else (UUID_FOUND)
|
||||
if (UUID_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find UUID")
|
||||
endif (UUID_FIND_REQUIRED)
|
||||
endif (UUID_FOUND)
|
||||
|
||||
# show the UUID_INCLUDE_DIRS and UUID_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(UUID_INCLUDE_DIRS UUID_LIBRARIES)
|
||||
|
||||
endif (UUID_LIBRARIES AND UUID_INCLUDE_DIRS)
|
@ -1,106 +1,75 @@
|
||||
cmake_minimum_required (VERSION 3.2)
|
||||
PROJECT([[ProjectName]])
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project([[ProjectName]] CXX)
|
||||
|
||||
LINK_DIRECTORIES(/usr/local/lib)
|
||||
LINK_LIBRARIES(drogon trantor pthread dl)
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
INCLUDE(CheckIncludeFileCXX)
|
||||
check_include_file_cxx(any HAS_ANY)
|
||||
check_include_file_cxx(string_view HAS_STRING_VIEW)
|
||||
check_include_file_cxx(coroutine HAS_COROUTINE)
|
||||
if (NOT "${CMAKE_CXX_STANDARD}" STREQUAL "")
|
||||
# Do nothing
|
||||
elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
elseif (HAS_ANY AND HAS_STRING_VIEW)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
else ()
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif ()
|
||||
|
||||
CHECK_INCLUDE_FILE_CXX(any HAS_ANY)
|
||||
CHECK_INCLUDE_FILE_CXX(string_view HAS_STRING_VIEW)
|
||||
IF(HAS_ANY AND HAS_STRING_VIEW)
|
||||
SET(CMAKE_CXX_STANDARD 17)
|
||||
ELSE()
|
||||
SET(CMAKE_CXX_STANDARD 14)
|
||||
ENDIF()
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
SET(CMAKE_CXX_EXTENSIONS OFF)
|
||||
add_executable(${PROJECT_NAME} main.cc)
|
||||
|
||||
IF(CMAKE_CXX_STANDARD LESS 17)
|
||||
#With C++14, use boost to support any and string_view
|
||||
MESSAGE(STATUS "use c++14")
|
||||
FIND_PACKAGE(Boost REQUIRED)
|
||||
IF(Boost_FOUND)
|
||||
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
|
||||
ENDIF()
|
||||
ELSE()
|
||||
MESSAGE(STATUS "use c++17")
|
||||
ENDIF()
|
||||
# ##############################################################################
|
||||
# If you include the drogon source code locally in your project, use this method
|
||||
# to add drogon
|
||||
# add_subdirectory(drogon)
|
||||
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
||||
#
|
||||
# and comment out the following lines
|
||||
find_package(Drogon CONFIG REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
|
||||
|
||||
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules/)
|
||||
# ##############################################################################
|
||||
|
||||
#jsoncpp
|
||||
FIND_PACKAGE (Jsoncpp REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${JSONCPP_INCLUDE_DIRS})
|
||||
LINK_LIBRARIES(${JSONCPP_LIBRARIES})
|
||||
if (CMAKE_CXX_STANDARD LESS 17)
|
||||
message(FATAL_ERROR "c++17 or higher is required")
|
||||
elseif (CMAKE_CXX_STANDARD LESS 20)
|
||||
message(STATUS "use c++17")
|
||||
else ()
|
||||
message(STATUS "use c++20")
|
||||
endif ()
|
||||
|
||||
#uuid
|
||||
FIND_PACKAGE (UUID REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${UUID_INCLUDE_DIR})
|
||||
LINK_LIBRARIES(${UUID_LIBRARIES})
|
||||
aux_source_directory(controllers CTL_SRC)
|
||||
aux_source_directory(filters FILTER_SRC)
|
||||
aux_source_directory(plugins PLUGIN_SRC)
|
||||
aux_source_directory(models MODEL_SRC)
|
||||
|
||||
#OpenSSL
|
||||
FIND_PACKAGE (OpenSSL)
|
||||
IF(OpenSSL_FOUND)
|
||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||
LINK_LIBRARIES(${OPENSSL_LIBRARIES})
|
||||
ENDIF()
|
||||
drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
# use the following line to create views with namespaces.
|
||||
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
# ${CMAKE_CURRENT_BINARY_DIR} TRUE)
|
||||
# use the following line to create views with namespace CHANGE_ME prefixed
|
||||
# and path namespaces.
|
||||
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
# ${CMAKE_CURRENT_BINARY_DIR} TRUE CHANGE_ME)
|
||||
|
||||
#zlib
|
||||
FIND_PACKAGE(ZLIB REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
|
||||
LINK_LIBRARIES(${ZLIB_LIBRARIES})
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/models)
|
||||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${SRC_DIR}
|
||||
${CTL_SRC}
|
||||
${FILTER_SRC}
|
||||
${PLUGIN_SRC}
|
||||
${MODEL_SRC})
|
||||
# ##############################################################################
|
||||
# uncomment the following line for dynamically loading views
|
||||
# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)
|
||||
|
||||
#find postgres
|
||||
FIND_PACKAGE(PostgreSQL)
|
||||
IF(PostgreSQL_FOUND)
|
||||
INCLUDE_DIRECTORIES(${PostgreSQL_INCLUDE_DIR})
|
||||
LINK_LIBRARIES(${PostgreSQL_LIBRARIES})
|
||||
ENDIF()
|
||||
# ##############################################################################
|
||||
|
||||
#Find mysql, only mariadb client liberary is supported
|
||||
FIND_PACKAGE(MySQL)
|
||||
IF(MYSQL_FOUND)
|
||||
MESSAGE(STATUS "inc:" ${MYSQL_INCLUDE_DIR})
|
||||
MESSAGE(STATUS "libs:" ${MYSQL_CLIENT_LIBS})
|
||||
MESSAGE(STATUS "version:" ${MYSQL_VERSION_STRING})
|
||||
IF(MYSQL_VERSION_STRING STREQUAL "")
|
||||
MESSAGE(STATUS "The mysql in your system is not the mariadb, so we can't use it in drogon")
|
||||
ELSE()
|
||||
MESSAGE(STATUS "Ok! We find the mariadb!")
|
||||
INCLUDE_DIRECTORIES(${MYSQL_INCLUDE_DIR})
|
||||
LINK_LIBRARIES(${MYSQL_CLIENT_LIBS})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
#Find sqlite3.
|
||||
FIND_PACKAGE (SQLite3)
|
||||
if (SQLITE3_FOUND)
|
||||
INCLUDE_DIRECTORIES(${SQLITE3_INCLUDE_DIRS})
|
||||
LINK_LIBRARIES(${SQLITE3_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
AUX_SOURCE_DIRECTORY(./ SRC_DIR)
|
||||
AUX_SOURCE_DIRECTORY(controllers CTL_SRC)
|
||||
AUX_SOURCE_DIRECTORY(filters FILTER_SRC)
|
||||
AUX_SOURCE_DIRECTORY(plugins PLUGIN_SRC)
|
||||
AUX_SOURCE_DIRECTORY(models MODEL_SRC)
|
||||
|
||||
INCLUDE_DIRECTORIES(/usr/local/include)
|
||||
|
||||
FILE(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/views/*.csp)
|
||||
FOREACH(cspFile ${SCP_LIST})
|
||||
MESSAGE(STATUS "cspFile:" ${cspFile})
|
||||
EXEC_PROGRAM(basename ARGS "${cspFile} .csp" OUTPUT_VARIABLE classname)
|
||||
MESSAGE(STATUS "view classname:" ${classname})
|
||||
ADD_CUSTOM_COMMAND(OUTPUT ${classname}.h ${classname}.cc
|
||||
COMMAND drogon_ctl
|
||||
ARGS create view ${cspFile}
|
||||
DEPENDS ${cspFile}
|
||||
VERBATIM )
|
||||
SET(VIEWSRC ${VIEWSRC} ${classname}.cc)
|
||||
ENDFOREACH()
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
ADD_EXECUTABLE([[ProjectName]] ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC} ${PLUGIN_SRC} ${MODEL_SRC})
|
||||
add_subdirectory(test)
|
||||
|
350
drogon_ctl/templates/config_json.csp
Normal file
350
drogon_ctl/templates/config_json.csp
Normal file
@ -0,0 +1,350 @@
|
||||
/* This is a JSON format configuration file
|
||||
*/
|
||||
{
|
||||
/*
|
||||
//ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
|
||||
// "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
|
||||
"ssl": {
|
||||
"cert": "../../trantor/trantor/tests/server.crt",
|
||||
"key": "../../trantor/trantor/tests/server.key",
|
||||
"conf": [
|
||||
//["Options", "-SessionTicket"],
|
||||
//["Options", "Compression"]
|
||||
]
|
||||
},
|
||||
"listeners": [
|
||||
{
|
||||
//address: Ip address,0.0.0.0 by default
|
||||
"address": "0.0.0.0",
|
||||
//port: Port number
|
||||
"port": 80,
|
||||
//https: If true, use https for security,false by default
|
||||
"https": false
|
||||
},
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"port": 443,
|
||||
"https": true,
|
||||
//cert,key: Cert file path and key file path, empty by default,
|
||||
//if empty, use the global setting
|
||||
"cert": "",
|
||||
"key": "",
|
||||
//use_old_tls: enable the TLS1.0/1.1, false by default
|
||||
"use_old_tls": false,
|
||||
"ssl_conf": [
|
||||
//["MinProtocol", "TLSv1.3"]
|
||||
]
|
||||
}
|
||||
],
|
||||
"db_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
|
||||
"rdbms": "postgresql",
|
||||
//filename: Sqlite3 db file name
|
||||
//"filename":"",
|
||||
//host: Server address,localhost by default
|
||||
"host": "127.0.0.1",
|
||||
//port: Server port, 5432 by default
|
||||
"port": 5432,
|
||||
//dbname: Database name
|
||||
"dbname": "test",
|
||||
//user: 'postgres' by default
|
||||
"user": "",
|
||||
//passwd: '' by default
|
||||
"passwd": "",
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//client_encoding: The character set used by the client. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"number_of_connections": 1,
|
||||
//timeout: -1.0 by default, in seconds, the timeout for executing a SQL query.
|
||||
//zero or negative value means no timeout.
|
||||
"timeout": -1.0,
|
||||
//auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
|
||||
//the wiki for more details.
|
||||
"auto_batch": false
|
||||
//connect_options: extra options for the connection. Only works for PostgreSQL now.
|
||||
//For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
|
||||
//"connect_options": { "statement_timeout": "1s" }
|
||||
}
|
||||
],
|
||||
"redis_clients": [
|
||||
{
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//host: Server IP, 127.0.0.1 by default
|
||||
"host": "127.0.0.1",
|
||||
//port: Server port, 6379 by default
|
||||
"port": 6379,
|
||||
//username: '' by default which means 'default' in redis ACL
|
||||
"username": "",
|
||||
//passwd: '' by default
|
||||
"passwd": "",
|
||||
//db index: 0 by default
|
||||
"db": 0,
|
||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"number_of_connections": 1,
|
||||
//timeout: -1.0 by default, in seconds, the timeout for executing a command.
|
||||
//zero or negative value means no timeout.
|
||||
"timeout": -1.0
|
||||
}
|
||||
],*/
|
||||
"app": {
|
||||
//number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
//is the number of CPU cores
|
||||
"number_of_threads": 1,
|
||||
//enable_session: False by default
|
||||
"enable_session": false,
|
||||
"session_timeout": 0,
|
||||
//string value of SameSite attribute of the Set-Cookie HTTP response header
|
||||
//valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
|
||||
"session_same_site" : "Null",
|
||||
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default
|
||||
"session_cookie_key": "JSESSIONID",
|
||||
//session_max_age: The max age of the session cookie, -1 by default
|
||||
"session_max_age": -1,
|
||||
//document_root: Root path of HTTP document, default path is ./
|
||||
"document_root": "./",
|
||||
//home_page: Set the HTML file of the home page, the default value is "index.html"
|
||||
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
|
||||
//to the request for "/".
|
||||
"home_page": "index.html",
|
||||
//use_implicit_page: enable implicit pages if true, true by default
|
||||
"use_implicit_page": true,
|
||||
//implicit_page: Set the file which would the server access in a directory that a user accessed.
|
||||
//For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
|
||||
"implicit_page": "index.html",
|
||||
//static_file_headers: Headers for static files
|
||||
/*"static_file_headers": [
|
||||
{
|
||||
"name": "field-name",
|
||||
"value": "field-value"
|
||||
}
|
||||
],*/
|
||||
//upload_path: The path to save the uploaded file. "uploads" by default.
|
||||
//If the path isn't prefixed with /, ./ or ../,
|
||||
//it is relative path of document_root path
|
||||
"upload_path": "uploads",
|
||||
/* file_types:
|
||||
* HTTP download file types,The file types supported by drogon
|
||||
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
|
||||
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
|
||||
* "gif", "bmp", "ico", "icns", etc. */
|
||||
"file_types": [
|
||||
"gif",
|
||||
"png",
|
||||
"jpg",
|
||||
"js",
|
||||
"css",
|
||||
"html",
|
||||
"ico",
|
||||
"swf",
|
||||
"xap",
|
||||
"apk",
|
||||
"cur",
|
||||
"xml",
|
||||
"webp",
|
||||
"svg"
|
||||
],
|
||||
// mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
|
||||
// note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
|
||||
"mime": {
|
||||
// "text/markdown": "md",
|
||||
// "text/gemini": ["gmi", "gemini"]
|
||||
},
|
||||
//locations: An array of locations of static files for GET requests.
|
||||
"locations": [
|
||||
{
|
||||
//uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
|
||||
//"uri_prefix": "/.well-known/acme-challenge/",
|
||||
//default_content_type: The default content type of the static files without
|
||||
//an extension. empty string by default.
|
||||
"default_content_type": "text/plain",
|
||||
//alias: The location in file system, if it is prefixed with "/", it
|
||||
//presents an absolute path, otherwise it presents a relative path to
|
||||
//the document_root path.
|
||||
//The default value is "" which means use the document root path as the location base path.
|
||||
"alias": "",
|
||||
//is_case_sensitive: indicates whether the URI prefix is case sensitive.
|
||||
"is_case_sensitive": false,
|
||||
//allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
|
||||
"allow_all": true,
|
||||
//is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
"is_recursive": true,
|
||||
//filters: string array, the filters applied to the location.
|
||||
"filters": []
|
||||
}
|
||||
],
|
||||
//max_connections: maximum number of connections, 100000 by default
|
||||
"max_connections": 100000,
|
||||
//max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
|
||||
"max_connections_per_ip": 0,
|
||||
//Load_dynamic_views: False by default, when set to true, drogon
|
||||
//compiles and loads dynamically "CSP View Files" in directories defined
|
||||
//by "dynamic_views_path"
|
||||
"load_dynamic_views": false,
|
||||
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
|
||||
//it is relative path of document_root path
|
||||
"dynamic_views_path": [
|
||||
"./views"
|
||||
],
|
||||
//dynamic_views_output_path: Default by an empty string which means the output path of source
|
||||
//files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
|
||||
//path of the current working directory.
|
||||
"dynamic_views_output_path": "",
|
||||
//json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
|
||||
"json_parser_stack_limit": 1000,
|
||||
//enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
|
||||
"enable_unicode_escaping_in_json": true,
|
||||
//float_precision_in_json: set precision of float number in json.
|
||||
"float_precision_in_json": {
|
||||
//precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
|
||||
"precision": 0,
|
||||
//precision_type: must be "significant" or "decimal", defaults to "significant" that means
|
||||
//setting max number of significant digits in string, "decimal" means setting max number of
|
||||
//digits after "." in string
|
||||
"precision_type": "significant"
|
||||
},
|
||||
//log: Set log output, drogon output logs to stdout by default
|
||||
"log": {
|
||||
//use_spdlog: Use spdlog library to log
|
||||
"use_spdlog": false,
|
||||
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
|
||||
//"log_path": "./",
|
||||
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
|
||||
//drogon.log ...
|
||||
"logfile_base_name": "",
|
||||
//log_size_limit: 100000000 bytes by default,
|
||||
//When the log file size reaches "log_size_limit", the log file is switched.
|
||||
"log_size_limit": 100000000,
|
||||
//max_files: 0 by default,
|
||||
//When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
|
||||
"max_files": 0,
|
||||
//log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
||||
//The TRACE level is only valid when built in DEBUG mode.
|
||||
"log_level": "DEBUG",
|
||||
//display_local_time: false by default, if true, the log time is displayed in local time
|
||||
"display_local_time": false
|
||||
},
|
||||
//run_as_daemon: False by default
|
||||
"run_as_daemon": false,
|
||||
//handle_sig_term: True by default
|
||||
"handle_sig_term": true,
|
||||
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
|
||||
"relaunch_on_error": false,
|
||||
//use_sendfile: True by default, if true, the program
|
||||
//uses sendfile() system-call to send static files to clients;
|
||||
"use_sendfile": true,
|
||||
//use_gzip: True by default, use gzip to compress the response body's content;
|
||||
"use_gzip": true,
|
||||
//use_brotli: False by default, use brotli to compress the response body's content;
|
||||
"use_brotli": false,
|
||||
//static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
|
||||
//0 means cache forever, the negative value means no cache
|
||||
"static_files_cache_time": 5,
|
||||
//simple_controllers_map: Used to configure mapping from path to simple controller
|
||||
//"simple_controllers_map": [
|
||||
// {
|
||||
// "path": "/path/name",
|
||||
// "controller": "controllerClassName",
|
||||
// "http_methods": [
|
||||
// "get",
|
||||
// "post"
|
||||
// ],
|
||||
// "filters": [
|
||||
// "FilterClassName"
|
||||
// ]
|
||||
// }
|
||||
//],
|
||||
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||
//of the connection without read or write
|
||||
"idle_connection_timeout": 60,
|
||||
//server_header_field: Set the 'Server' header field in each response sent by drogon,
|
||||
//empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
|
||||
"server_header_field": "",
|
||||
//enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
|
||||
//value is true.
|
||||
"enable_server_header": true,
|
||||
//enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
|
||||
//value is true.
|
||||
"enable_date_header": true,
|
||||
//keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
|
||||
//After the maximum number of requests are made, the connection is closed.
|
||||
//The default value of 0 means no limit.
|
||||
"keepalive_requests": 0,
|
||||
//pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
|
||||
//After the maximum number of requests are made, the connection is closed.
|
||||
//The default value of 0 means no limit.
|
||||
"pipelining_requests": 0,
|
||||
//gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
//file with the extension ".gz" in the same path and send the compressed file to the client.
|
||||
//The default value of gzip_static is true.
|
||||
"gzip_static": true,
|
||||
//br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
//file with the extension ".br" in the same path and send the compressed file to the client.
|
||||
//The default value of br_static is true.
|
||||
"br_static": true,
|
||||
//client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
|
||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
"client_max_body_size": "1M",
|
||||
//max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
|
||||
//If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
|
||||
//Setting it to "" means no limit.
|
||||
"client_max_memory_body_size": "64K",
|
||||
//client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
|
||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
"client_max_websocket_message_size": "128K",
|
||||
//reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
|
||||
"reuse_port": false,
|
||||
// enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
|
||||
// Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
|
||||
// Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
|
||||
// will be rejected.
|
||||
"enabled_compressed_request": false,
|
||||
// enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
|
||||
// See the wiki for more details.
|
||||
"enable_request_stream": false,
|
||||
},
|
||||
//plugins: Define all plugins running in the application
|
||||
"plugins": [
|
||||
{
|
||||
//name: The class name of the plugin
|
||||
"name": "drogon::plugin::PromExporter",
|
||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
"dependencies": [],
|
||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
//It can be commented out
|
||||
"config": {
|
||||
"path": "/metrics"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "drogon::plugin::AccessLogger",
|
||||
"dependencies": [],
|
||||
"config": {
|
||||
"use_spdlog": false,
|
||||
"log_path": "",
|
||||
"log_format": "",
|
||||
"log_file": "access.log",
|
||||
"log_size_limit": 0,
|
||||
"use_local_time": true,
|
||||
"log_index": 0,
|
||||
// "show_microseconds": true,
|
||||
// "custom_time_format": "",
|
||||
// "use_real_ip": false
|
||||
}
|
||||
}
|
||||
],
|
||||
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
"custom_config": {}
|
||||
}
|
313
drogon_ctl/templates/config_yaml.csp
Normal file
313
drogon_ctl/templates/config_yaml.csp
Normal file
@ -0,0 +1,313 @@
|
||||
# This is a YAML format configuration file
|
||||
|
||||
# ssl:The global SSL settings. "key" and "cert" are the path to the SSL key and certificate. While
|
||||
# "conf" is an array of 1 or 2-element tuples that supplies file style options for `SSL_CONF_cmd`.
|
||||
# ssl:
|
||||
# cert: ../../trantor/trantor/tests/server.crt
|
||||
# key: ../../trantor/trantor/tests/server.key
|
||||
# conf: [
|
||||
# # [Options, -SessionTicket],
|
||||
# # [Options, Compression]
|
||||
# ]
|
||||
# listeners:
|
||||
# # address: Ip address,0.0.0.0 by default
|
||||
# - address: 0.0.0.0
|
||||
# # port: Port number
|
||||
# port: 80
|
||||
# # https: If true, use https for security,false by default
|
||||
# https: false
|
||||
# - address: 0.0.0.0
|
||||
# port: 443
|
||||
# https: true
|
||||
# # cert,key: Cert file path and key file path, empty by default,
|
||||
# # if empty, use the global setting
|
||||
# cert: ''
|
||||
# key: ''
|
||||
# # use_old_tls: enable the TLS1.0/1.1, false by default
|
||||
# use_old_tls: false
|
||||
# ssl_conf: [
|
||||
# # [MinProtocol, TLSv1.3]
|
||||
# ]
|
||||
# db_clients:
|
||||
# # name: Name of the client,'default' by default
|
||||
# - name: default
|
||||
# # rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
|
||||
# rdbms: postgresql
|
||||
# # filename: Sqlite3 db file name
|
||||
# # filename: ''
|
||||
# # host: Server address,localhost by default
|
||||
# host: 127.0.0.1
|
||||
# # port: Server port, 5432 by default
|
||||
# port: 5432
|
||||
# # dbname: Database name
|
||||
# dbname: test
|
||||
# # user: 'postgres' by default
|
||||
# user: ''
|
||||
# # passwd: '' by default
|
||||
# passwd: ''
|
||||
# # is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
# # any synchronous interface of it.
|
||||
# is_fast: false
|
||||
# # client_encoding: The character set used by the client. it is empty string by default which
|
||||
# # means use the default character set.
|
||||
# # client_encoding: ''
|
||||
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
# # connections per IO thread, otherwise it is the total number of all connections.
|
||||
# number_of_connections: 1
|
||||
# # timeout: -1 by default, in seconds, the timeout for executing a SQL query.
|
||||
# # zero or negative value means no timeout.
|
||||
# timeout: -1
|
||||
# # auto_batch: this feature is only available for the PostgreSQL driver(version >= 14.0), see
|
||||
# # the wiki for more details.
|
||||
# auto_batch: false
|
||||
# # connect_options: extra options for the connection. Only works for PostgreSQL now.
|
||||
# # For more information, see https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-OPTIONS
|
||||
# # connect_options:
|
||||
# # statement_timeout: '1s'
|
||||
# redis_clients:
|
||||
# # name: Name of the client,'default' by default
|
||||
# - name: default
|
||||
# # host: Server IP, 127.0.0.1 by default
|
||||
# host: 127.0.0.1
|
||||
# # port: Server port, 6379 by default
|
||||
# port: 6379
|
||||
# # username: '' by default which means 'default' in redis ACL
|
||||
# username: ''
|
||||
# # passwd: '' by default
|
||||
# passwd: ''
|
||||
# # db index: 0 by default
|
||||
# db: 0
|
||||
# # is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
# # any synchronous interface of it.
|
||||
# is_fast: false
|
||||
# # number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
# # connections per IO thread, otherwise it is the total number of all connections.
|
||||
# number_of_connections: 1
|
||||
# # timeout: -1.0 by default, in seconds, the timeout for executing a command.
|
||||
# # zero or negative value means no timeout.
|
||||
# timeout: -1
|
||||
app:
|
||||
# number_of_threads: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
||||
# is the number of CPU cores
|
||||
number_of_threads: 1
|
||||
# enable_session: False by default
|
||||
enable_session: false
|
||||
session_timeout: 0
|
||||
# string value of SameSite attribute of the Set-Cookie HTTP response header
|
||||
# valid value is either 'Null' (default), 'Lax', 'Strict' or 'None'
|
||||
session_same_site: 'Null'
|
||||
# session_cookie_key: The cookie key of the session, "JSESSIONID" by default
|
||||
session_cookie_key: 'JSESSIONID'
|
||||
# session_max_age: The max age of the session cookie, -1 by default
|
||||
session_max_age: -1
|
||||
# document_root: Root path of HTTP document, default path is ./
|
||||
document_root: ./
|
||||
# home_page: Set the HTML file of the home page, the default value is "index.html"
|
||||
# If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
|
||||
# to the request for "/".
|
||||
home_page: index.html
|
||||
# use_implicit_page: enable implicit pages if true, true by default
|
||||
use_implicit_page: true
|
||||
# implicit_page: Set the file which would the server access in a directory that a user accessed.
|
||||
# For example, by default, http://localhost/a-directory resolves to http://localhost/a-directory/index.html.
|
||||
implicit_page: index.html
|
||||
# static_file_headers: Headers for static files
|
||||
# static_file_headers:
|
||||
# - name: field-name
|
||||
# value: field-value
|
||||
# upload_path: The path to save the uploaded file. "uploads" by default.
|
||||
# If the path isn't prefixed with /, ./ or ../,
|
||||
# it is relative path of document_root path
|
||||
upload_path: uploads
|
||||
# file_types:
|
||||
# HTTP download file types,The file types supported by drogon
|
||||
# by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
|
||||
# "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
|
||||
# "gif", "bmp", "ico", "icns", etc.
|
||||
file_types:
|
||||
- gif
|
||||
- png
|
||||
- jpg
|
||||
- js
|
||||
- css
|
||||
- html
|
||||
- ico
|
||||
- swf
|
||||
- xap
|
||||
- apk
|
||||
- cur
|
||||
- xml
|
||||
# mime: A dictionary that extends the internal MIME type support. Maps extensions into new MIME types
|
||||
# note: This option only adds MIME to the sever. `file_types` above have to be set for the server to serve them.
|
||||
mime: {
|
||||
# text/markdown: md
|
||||
# text/gemini:
|
||||
# - gmi
|
||||
# - gemini
|
||||
}
|
||||
# locations: An array of locations of static files for GET requests.
|
||||
locations:
|
||||
# uri_prefix: The URI prefix of the location prefixed with "/", the default value is "" that disables the location.
|
||||
- uri_prefix: '' # /.well-known/acme-challenge/
|
||||
# default_content_type: The default content type of the static files without
|
||||
# an extension. empty string by default.
|
||||
default_content_type: text/plain
|
||||
# alias: The location in file system, if it is prefixed with "/", it
|
||||
# presents an absolute path, otherwise it presents a relative path to
|
||||
# the document_root path.
|
||||
# The default value is "" which means use the document root path as the location base path.
|
||||
alias: ''
|
||||
# is_case_sensitive: indicates whether the URI prefix is case sensitive.
|
||||
is_case_sensitive: false
|
||||
# allow_all: true by default. If it is set to false, only static files with a valid extension can be accessed.
|
||||
allow_all: true
|
||||
# is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
is_recursive: true
|
||||
# filters: string array, the filters applied to the location.
|
||||
filters: []
|
||||
# max_connections: maximum number of connections, 100000 by default
|
||||
max_connections: 100000
|
||||
# max_connections_per_ip: maximum number of connections per client, 0 by default which means no limit
|
||||
max_connections_per_ip: 0
|
||||
# Load_dynamic_views: False by default, when set to true, drogon
|
||||
# compiles and loads dynamically "CSP View Files" in directories defined
|
||||
# by "dynamic_views_path"
|
||||
load_dynamic_views: false
|
||||
# dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
|
||||
# it is relative path of document_root path
|
||||
dynamic_views_path:
|
||||
- ./views
|
||||
# dynamic_views_output_path: Default by an empty string which means the output path of source
|
||||
# files is the path where the csp files locate. If the path isn't prefixed with /, it is relative
|
||||
# path of the current working directory.
|
||||
dynamic_views_output_path: ''
|
||||
# json_parser_stack_limit: 1000 by default, the maximum number of stack depth when reading a json string by the jsoncpp library.
|
||||
json_parser_stack_limit: 1000
|
||||
# enable_unicode_escaping_in_json: true by default, enable unicode escaping in json.
|
||||
enable_unicode_escaping_in_json: true
|
||||
# float_precision_in_json: set precision of float number in json.
|
||||
float_precision_in_json:
|
||||
# precision: 0 by default, 0 means use the default precision of the jsoncpp lib.
|
||||
precision: 0
|
||||
# precision_type: must be "significant" or "decimal", defaults to "significant" that means
|
||||
# setting max number of significant digits in string, "decimal" means setting max number of
|
||||
# digits after "." in string
|
||||
precision_type: significant
|
||||
# log: Set log output, drogon output logs to stdout by default
|
||||
log:
|
||||
# use_spdlog: Use spdlog library to log
|
||||
use_spdlog: false
|
||||
# log_path: Log file path,empty by default,in which case,logs are output to the stdout
|
||||
# log_path: ./
|
||||
# logfile_base_name: Log file base name,empty by default which means drogon names logfile as
|
||||
# drogon.log ...
|
||||
logfile_base_name: ''
|
||||
# log_size_limit: 100000000 bytes by default,
|
||||
# When the log file size reaches "log_size_limit", the log file is switched.
|
||||
log_size_limit: 100000000
|
||||
# max_files: 0 by default,
|
||||
# When the number of old log files exceeds "max_files", the oldest file will be deleted. 0 means never delete.
|
||||
max_files: 0
|
||||
# log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
||||
# The TRACE level is only valid when built in DEBUG mode.
|
||||
log_level: DEBUG
|
||||
# display_local_time: false by default, if true, the log time is displayed in local time
|
||||
display_local_time: false
|
||||
# run_as_daemon: False by default
|
||||
run_as_daemon: false
|
||||
# handle_sig_term: True by default
|
||||
handle_sig_term: true
|
||||
# relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
|
||||
relaunch_on_error: false
|
||||
# use_sendfile: True by default, if true, the program
|
||||
# uses sendfile() system-call to send static files to clients;
|
||||
use_sendfile: true
|
||||
# use_gzip: True by default, use gzip to compress the response body's content;
|
||||
use_gzip: true
|
||||
# use_brotli: False by default, use brotli to compress the response body's content;
|
||||
use_brotli: false
|
||||
# static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
|
||||
# 0 means cache forever, the negative value means no cache
|
||||
static_files_cache_time: 5
|
||||
# simple_controllers_map: Used to configure mapping from path to simple controller
|
||||
# simple_controllers_map:
|
||||
# - path: /path/name
|
||||
# controller: controllerClassName
|
||||
# http_methods:
|
||||
# - get
|
||||
# - post
|
||||
# filters:
|
||||
# - FilterClassName
|
||||
# idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||
# of the connection without read or write
|
||||
idle_connection_timeout: 60
|
||||
# server_header_field: Set the 'Server' header field in each response sent by drogon,
|
||||
# empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
|
||||
server_header_field: ''
|
||||
# enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
|
||||
# value is true.
|
||||
enable_server_header: true
|
||||
# enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
|
||||
# value is true.
|
||||
enable_date_header: true
|
||||
# keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
|
||||
# After the maximum number of requests are made, the connection is closed.
|
||||
# The default value of 0 means no limit.
|
||||
keepalive_requests: 0
|
||||
# pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
|
||||
# After the maximum number of requests are made, the connection is closed.
|
||||
# The default value of 0 means no limit.
|
||||
pipelining_requests: 0
|
||||
# gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
# file with the extension ".gz" in the same path and send the compressed file to the client.
|
||||
# The default value of gzip_static is true.
|
||||
gzip_static: true
|
||||
# br_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
||||
# file with the extension ".br" in the same path and send the compressed file to the client.
|
||||
# The default value of br_static is true.
|
||||
br_static: true
|
||||
# client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
|
||||
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
client_max_body_size: 1M
|
||||
# max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
|
||||
# If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
|
||||
# Setting it to "" means no limit.
|
||||
client_max_memory_body_size: 64K
|
||||
# client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
|
||||
# One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
||||
client_max_websocket_message_size: 128K
|
||||
# reuse_port: Defaults to false, users can run multiple processes listening on the same port at the same time.
|
||||
reuse_port: false
|
||||
# enabled_compressed_request: Defaults to false. If true the server will automatically decompress compressed request bodies.
|
||||
# Currently only gzip and br are supported. Note: max_memory_body_size and max_body_size applies twice for compressed requests.
|
||||
# Once when receiving and once when decompressing. i.e. if the decompressed body is larger than max_body_size, the request
|
||||
# will be rejected.
|
||||
enabled_compressed_request: false
|
||||
# enable_request_stream: Defaults to false. If true the server will enable stream mode for http requests.
|
||||
# See the wiki for more details.
|
||||
enable_request_stream: false
|
||||
# plugins: Define all plugins running in the application
|
||||
plugins:
|
||||
# name: The class name of the plugin
|
||||
- name: drogon::plugin::PromExporter
|
||||
# dependencies: Plugins that the plugin depends on. It can be commented out
|
||||
dependencies: []
|
||||
# config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
# It can be commented out
|
||||
config:
|
||||
path: /metrics
|
||||
- name: drogon::plugin::AccessLogger
|
||||
dependencies: []
|
||||
config:
|
||||
use_spdlog: false
|
||||
log_path: ''
|
||||
log_format: ''
|
||||
log_file: access.log
|
||||
log_size_limit: 0
|
||||
use_local_time: true
|
||||
log_index: 0
|
||||
# show_microseconds: true
|
||||
# custom_time_format: ''
|
||||
# use_real_ip: false
|
||||
# custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
custom_config: {}
|
@ -1,9 +1,10 @@
|
||||
#include <drogon/drogon.h>
|
||||
int main() {
|
||||
//Set HTTP listener address and port
|
||||
drogon::app().addListener("0.0.0.0",80);
|
||||
drogon::app().addListener("0.0.0.0", 5555);
|
||||
//Load config file
|
||||
//drogon::app().loadConfigFile("../config.json");
|
||||
//drogon::app().loadConfigFile("../config.yaml");
|
||||
//Run HTTP framework,the method will block in the internal event loop
|
||||
drogon::app().run();
|
||||
return 0;
|
||||
|
@ -22,9 +22,9 @@ class [[className]] : public HttpFilter<[[className]]>
|
||||
{
|
||||
public:
|
||||
[[className]]() {}
|
||||
virtual void doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb) override;
|
||||
void doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb) override;
|
||||
};
|
||||
|
||||
<%c++for(size_t i=0;i<namespaceVector.size();i++){%>
|
||||
|
@ -1,20 +1,73 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,visualstudio,visualstudiocode,cmake,c,c++
|
||||
|
||||
### C ###
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
### C++ ###
|
||||
# Prerequisites
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
|
||||
# Precompiled Headers
|
||||
|
||||
# Linker files
|
||||
|
||||
# Debugger Files
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
@ -22,15 +75,487 @@
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
build
|
||||
cmake-build-debug
|
||||
.idea
|
||||
### CMake ###
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
CMakeUserPresets.json
|
||||
|
||||
### CMake Patch ###
|
||||
# External projects
|
||||
*-prefix/
|
||||
|
||||
### Intellij+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij+all Patch ###
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
|
||||
.idea/
|
||||
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### VisualStudio ###
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.meta
|
||||
*.iobj
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*[.json, .xml, .info]
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
### VisualStudio Patch ###
|
||||
# Additional files built by Visual Studio
|
||||
*.tlog
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/intellij+all,visualstudio,visualstudiocode,cmake,c,c++
|
File diff suppressed because it is too large
Load Diff
@ -14,21 +14,63 @@ using namespace drogon_ctl;
|
||||
#include <drogon/orm/Field.h>
|
||||
#include <drogon/orm/SqlBinder.h>
|
||||
#include <drogon/orm/Mapper.h>
|
||||
#include <drogon/orm/BaseBuilder.h>
|
||||
#ifdef __cpp_impl_coroutine
|
||||
#include <drogon/orm/CoroMapper.h>
|
||||
#endif
|
||||
#include <trantor/utils/Date.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <json/json.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace drogon::orm;
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
namespace orm
|
||||
{
|
||||
class DbClient;
|
||||
using DbClientPtr = std::shared_ptr<DbClient>;
|
||||
}
|
||||
}
|
||||
namespace drogon_model
|
||||
{
|
||||
namespace [[dbName]]
|
||||
{
|
||||
<%c++
|
||||
auto &schema=@@.get<std::string>("schema");
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"namespace "<<schema<<"\n";
|
||||
$$<<"{\n";
|
||||
}
|
||||
std::vector<std::string> relationshipClassNames;
|
||||
auto &relationships=@@.get<std::vector<Relationship>>("relationships");
|
||||
for(auto &relationship : relationships)
|
||||
{
|
||||
auto &name = relationship.targetTableName();
|
||||
auto relationshipClassName = nameTransform(name, true);
|
||||
relationshipClassNames.push_back(relationshipClassName);
|
||||
if(relationship.type() == Relationship::Type::ManyToMany)
|
||||
{
|
||||
auto &pivotTableName = relationship.pivotTable().tableName();
|
||||
auto pivotTableClassName = nameTransform(pivotTableName, true);
|
||||
relationshipClassNames.push_back(pivotTableClassName);
|
||||
}
|
||||
}
|
||||
std::sort(relationshipClassNames.begin(), relationshipClassNames.end());
|
||||
relationshipClassNames.erase(std::unique(relationshipClassNames.begin(), relationshipClassNames.end()), relationshipClassNames.end());
|
||||
for(std::string &relationshipClassName : relationshipClassNames)
|
||||
{
|
||||
%>
|
||||
class {%relationshipClassName%};
|
||||
<%c++
|
||||
}
|
||||
%>
|
||||
|
||||
class [[className]]
|
||||
{
|
||||
@ -39,21 +81,22 @@ class [[className]]
|
||||
auto cols=@@.get<std::vector<ColumnInfo>>("columns");
|
||||
for(size_t i=0;i<cols.size();i++)
|
||||
{
|
||||
$$<<" static const std::string "<<cols[i]._colName<<";\n";
|
||||
$$<<" static const std::string _"<<cols[i].colName_<<";\n";
|
||||
}
|
||||
%>
|
||||
};
|
||||
|
||||
const static int primaryKeyNumber;
|
||||
const static std::string tableName;
|
||||
const static bool hasPrimaryKey;
|
||||
static const int primaryKeyNumber;
|
||||
static const std::string tableName;
|
||||
static const bool hasPrimaryKey;
|
||||
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
|
||||
const static std::string primaryKeyName;
|
||||
static const std::string primaryKeyName;
|
||||
<%c++if(!@@.get<std::string>("primaryKeyType").empty()){%>
|
||||
typedef [[primaryKeyType]] PrimaryKeyType;
|
||||
const PrimaryKeyType & getPrimaryKey() const;
|
||||
using PrimaryKeyType = [[primaryKeyType]];
|
||||
const PrimaryKeyType &getPrimaryKey() const;
|
||||
<%c++}else{%>
|
||||
typedef void PrimaryKeyType;
|
||||
using PrimaryKeyType = void;
|
||||
int getPrimaryKey() const { assert(false); return 0; }
|
||||
<%c++}%>
|
||||
<%c++}else{
|
||||
auto pkTypes=@@.get<std::vector<std::string>>("primaryKeyType");
|
||||
@ -65,8 +108,8 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
|
||||
typelist += ",";
|
||||
}
|
||||
%>
|
||||
const static std::vector<std::string> primaryKeyName;
|
||||
typedef std::tuple<{%typelist%}> PrimaryKeyType;//<%c++
|
||||
static const std::vector<std::string> primaryKeyName;
|
||||
using PrimaryKeyType = std::tuple<{%typelist%}>;//<%c++
|
||||
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
|
||||
for(size_t i=0;i<pkName.size();i++)
|
||||
{
|
||||
@ -78,38 +121,81 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
|
||||
|
||||
PrimaryKeyType getPrimaryKey() const;
|
||||
<%c++}%>
|
||||
explicit [[className]](const Row &r) noexcept;
|
||||
|
||||
/**
|
||||
* @brief constructor
|
||||
* @param r One row of records in the SQL query result.
|
||||
* @param indexOffset Set the offset to -1 to access all columns by column names,
|
||||
* otherwise access all columns by offsets.
|
||||
* @note If the SQL is not a style of 'select * from table_name ...' (select all
|
||||
* columns by an asterisk), please set the offset to -1.
|
||||
*/
|
||||
explicit [[className]](const drogon::orm::Row &r, const ssize_t indexOffset = 0) noexcept;
|
||||
|
||||
/**
|
||||
* @brief constructor
|
||||
* @param pJson The json object to construct a new instance.
|
||||
*/
|
||||
explicit [[className]](const Json::Value &pJson) noexcept(false);
|
||||
|
||||
/**
|
||||
* @brief constructor
|
||||
* @param pJson The json object to construct a new instance.
|
||||
* @param pMasqueradingVector The aliases of table columns.
|
||||
*/
|
||||
[[className]](const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false);
|
||||
|
||||
[[className]]() = default;
|
||||
|
||||
void updateByJson(const Json::Value &pJson) noexcept(false);
|
||||
void updateByMasqueradedJson(const Json::Value &pJson,
|
||||
const std::vector<std::string> &pMasqueradingVector) noexcept(false);
|
||||
static bool validateJsonForCreation(const Json::Value &pJson, std::string &err);
|
||||
static bool validateMasqueradedJsonForCreation(const Json::Value &,
|
||||
const std::vector<std::string> &pMasqueradingVector,
|
||||
std::string &err);
|
||||
static bool validateJsonForUpdate(const Json::Value &pJson, std::string &err);
|
||||
static bool validateMasqueradedJsonForUpdate(const Json::Value &,
|
||||
const std::vector<std::string> &pMasqueradingVector,
|
||||
std::string &err);
|
||||
static bool validJsonOfField(size_t index,
|
||||
const std::string &fieldName,
|
||||
const Json::Value &pJson,
|
||||
std::string &err,
|
||||
bool isForCreation);
|
||||
|
||||
<%c++
|
||||
for(auto col:cols)
|
||||
for(const auto &col:cols)
|
||||
{
|
||||
$$<<" /** For column "<<col._colName<<" */\n";
|
||||
if(!col._colType.empty())
|
||||
$$<<" /** For column "<<col.colName_<<" */\n";
|
||||
if(!col.colType_.empty())
|
||||
{
|
||||
$$<<" ///Get the value of the column "<<col._colName<<", returns the default value if the column is null\n";
|
||||
$$<<" const "<<col._colType<<" &getValueOf"<<col._colTypeName<<"(const "<<col._colType<<" &defaultValue="<<col._colType<<"()) const noexcept;\n";
|
||||
if(col._colType=="std::vector<char>")
|
||||
$$<<" ///Get the value of the column "<<col.colName_<<", returns the default value if the column is null\n";
|
||||
$$<<" const "<<col.colType_<<" &getValueOf"<<col.colTypeName_<<"() const noexcept;\n";
|
||||
if(col.colType_=="std::vector<char>")
|
||||
{
|
||||
$$<<" ///Return the column value by std::string with binary data\n";
|
||||
$$<<" std::string getValueOf"<<col._colTypeName<<"AsString(const std::string &defaultValue=\"\") const noexcept;\n";
|
||||
$$<<" std::string getValueOf"<<col.colTypeName_<<"AsString() const noexcept;\n";
|
||||
}
|
||||
$$<<" ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null\n";
|
||||
$$<<" std::shared_ptr<const "<<col._colType<<"> get"<<col._colTypeName<<"() const noexcept;\n";
|
||||
if(!col._isAutoVal)
|
||||
{
|
||||
$$<<" ///Set the value of the column "<<col._colName<<"\n";
|
||||
$$<<" void set"<<col._colTypeName<<"(const "<<col._colType<<" &"<<col._colValName<<") noexcept;\n";
|
||||
if(col._colType=="std::string")
|
||||
$$<<" void set"<<col._colTypeName<<"("<<col._colType<<" &&"<<col._colValName<<") noexcept;\n";
|
||||
if(col._colType=="std::vector<char>")
|
||||
$$<<" const std::shared_ptr<"<<col.colType_<<"> &get"<<col.colTypeName_<<"() const noexcept;\n";
|
||||
|
||||
$$<<" ///Set the value of the column "<<col.colName_<<"\n";
|
||||
$$<<" void set"<<col.colTypeName_<<"(const "<<col.colType_<<" &p"<<col.colTypeName_<<") noexcept;\n";
|
||||
if(col.colType_=="std::string")
|
||||
$$<<" void set"<<col.colTypeName_<<"("<<col.colType_<<" &&p"<<col.colTypeName_<<") noexcept;\n";
|
||||
if(col.colType_=="std::vector<char>")
|
||||
{
|
||||
$$<<" void set"<<col._colTypeName<<"(const std::string &"<<col._colValName<<") noexcept;\n";
|
||||
$$<<" void set"<<col.colTypeName_<<"(const std::string &p"<<col.colTypeName_<<") noexcept;\n";
|
||||
}
|
||||
}
|
||||
if(!col.notNull_)
|
||||
{
|
||||
$$<<" void set"<<col.colTypeName_<<"ToNull() noexcept;\n";
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
$$<<" //FIXME!!"<<" getValueOf"<<col._colTypeName<<"() const noexcept;\n";
|
||||
$$<<" //FIXME!!"<<" getValueOf"<<col.colTypeName_<<"() const noexcept;\n";
|
||||
$$<<"\n";
|
||||
}
|
||||
%>
|
||||
@ -118,9 +204,114 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
|
||||
static const std::string &getColumnName(size_t index) noexcept(false);
|
||||
|
||||
Json::Value toJson() const;
|
||||
|
||||
Json::Value toMasqueradedJson(const std::vector<std::string> &pMasqueradingVector) const;
|
||||
/// Relationship interfaces
|
||||
<%c++
|
||||
for(auto &relationship : relationships)
|
||||
{
|
||||
if(relationship.targetKey().empty() || relationship.originalKey().empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto &name=relationship.targetTableName();
|
||||
auto relationshipClassName=nameTransform(name, true);
|
||||
auto relationshipValName=nameTransform(name, false);
|
||||
auto alias=relationship.targetTableAlias();
|
||||
auto aliasValName=nameTransform(alias, false);
|
||||
if(relationship.type() == Relationship::Type::HasOne)
|
||||
{
|
||||
if(!alias.empty())
|
||||
{
|
||||
if(alias[0] <= 'z' && alias[0] >= 'a')
|
||||
{
|
||||
alias[0] += ('A' - 'a');
|
||||
}
|
||||
std::string alind(alias.length(), ' ');
|
||||
%>
|
||||
{%relationshipClassName%} get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%alind%} const std::function<void({%relationshipClassName%})> &rcb,
|
||||
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string relationshipClassInde(relationshipClassName.length(), ' ');
|
||||
%>
|
||||
{%relationshipClassName%} get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%relationshipClassInde%} const std::function<void({%relationshipClassName%})> &rcb,
|
||||
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
else if(relationship.type() == Relationship::Type::HasMany)
|
||||
{
|
||||
if(!alias.empty())
|
||||
{
|
||||
if(alias[0] <= 'z' && alias[0] >= 'a')
|
||||
{
|
||||
alias[0] += ('A' - 'a');
|
||||
}
|
||||
std::string alind(alias.length(), ' ');
|
||||
%>
|
||||
std::vector<{%relationshipClassName%}> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%alind%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
|
||||
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string relationshipClassInde(relationshipClassName.length(), ' ');
|
||||
%>
|
||||
std::vector<{%relationshipClassName%}> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%relationshipClassInde%} const std::function<void(std::vector<{%relationshipClassName%}>)> &rcb,
|
||||
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
else if(relationship.type() == Relationship::Type::ManyToMany)
|
||||
{
|
||||
auto &pivotTableName=relationship.pivotTable().tableName();
|
||||
auto pivotTableClassName=nameTransform(pivotTableName, true);
|
||||
if(!alias.empty())
|
||||
{
|
||||
if(alias[0] <= 'z' && alias[0] >= 'a')
|
||||
{
|
||||
alias[0] += ('A' - 'a');
|
||||
}
|
||||
std::string alind(alias.length(), ' ');
|
||||
%>
|
||||
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%alias%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%alias%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%alind%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
|
||||
{%alind%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string relationshipClassInde(relationshipClassName.length(), ' ');
|
||||
%>
|
||||
std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>> get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr) const;
|
||||
void get{%relationshipClassName%}(const drogon::orm::DbClientPtr &clientPtr,
|
||||
{%relationshipClassInde%} const std::function<void(std::vector<std::pair<{%relationshipClassName%},{%pivotTableClassName%}>>)> &rcb,
|
||||
{%relationshipClassInde%} const drogon::orm::ExceptionCallback &ecb) const;
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
}
|
||||
%>
|
||||
private:
|
||||
friend Mapper<[[className]]>;
|
||||
friend drogon::orm::Mapper<[[className]]>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], true, true>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], true, false>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], false, true>;
|
||||
friend drogon::orm::BaseBuilder<[[className]], false, false>;
|
||||
#ifdef __cpp_impl_coroutine
|
||||
friend drogon::orm::CoroMapper<[[className]]>;
|
||||
#endif
|
||||
static const std::vector<std::string> &insertColumns() noexcept;
|
||||
void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
|
||||
const std::vector<std::string> updateColumns() const;
|
||||
@ -130,23 +321,249 @@ auto cols=@@.get<std::vector<ColumnInfo>>("columns");
|
||||
<%c++
|
||||
for(auto col:cols)
|
||||
{
|
||||
if(!col._colType.empty())
|
||||
$$<<" std::shared_ptr<"<<col._colType<<"> _"<<col._colValName<<";\n";
|
||||
if(!col.colType_.empty())
|
||||
$$<<" std::shared_ptr<"<<col.colType_<<"> "<<col.colValName_<<"_;\n";
|
||||
}
|
||||
%>
|
||||
struct MetaData
|
||||
{
|
||||
const std::string _colName;
|
||||
const std::string _colType;
|
||||
const std::string _colDatabaseType;
|
||||
const ssize_t _colLength;
|
||||
const bool _isAutoVal;
|
||||
const bool _isPrimaryKey;
|
||||
const bool _notNull;
|
||||
const std::string colName_;
|
||||
const std::string colType_;
|
||||
const std::string colDatabaseType_;
|
||||
const ssize_t colLength_;
|
||||
const bool isAutoVal_;
|
||||
const bool isPrimaryKey_;
|
||||
const bool notNull_;
|
||||
};
|
||||
static const std::vector<MetaData> _metaData;
|
||||
bool _dirtyFlag[{%cols.size()%}]={ false };
|
||||
};
|
||||
static const std::vector<MetaData> metaData_;
|
||||
bool dirtyFlag_[{%cols.size()%}]={ false };
|
||||
public:
|
||||
static const std::string &sqlForFindingByPrimaryKey()
|
||||
{
|
||||
<%c++
|
||||
auto rdbms=@@.get<std::string>("rdbms");
|
||||
if(@@.get<int>("hasPrimaryKey")<=1){
|
||||
if(!@@.get<std::string>("primaryKeyType").empty()){%>
|
||||
static const std::string sql="select * from " + tableName + " where [[primaryKeyName]] = {%(rdbms=="postgresql"?"$1":"?")%}";
|
||||
<%c++}else{%>
|
||||
static const std::string sql="";
|
||||
<%c++}%>
|
||||
<%c++}else{
|
||||
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
|
||||
%>
|
||||
static const std::string sql="select * from " + tableName + " where <%c++
|
||||
for(size_t i=0;i<pkName.size();i++)
|
||||
{
|
||||
if(rdbms=="postgresql")
|
||||
{
|
||||
$$<<pkName[i]<<" = $"<<i+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<pkName[i]<<" = ?";
|
||||
}
|
||||
if(i<(pkName.size()-1))
|
||||
$$<<" and ";
|
||||
}
|
||||
$$<<"\";\n";
|
||||
}
|
||||
%>
|
||||
return sql;
|
||||
}
|
||||
|
||||
static const std::string &sqlForDeletingByPrimaryKey()
|
||||
{
|
||||
<%c++
|
||||
if(@@.get<int>("hasPrimaryKey")<=1){
|
||||
if(!@@.get<std::string>("primaryKeyType").empty()){%>
|
||||
static const std::string sql="delete from " + tableName + " where [[primaryKeyName]] = {%(rdbms=="postgresql"?"$1":"?")%}";
|
||||
<%c++}else{%>
|
||||
static const std::string sql="";
|
||||
<%c++}%>
|
||||
<%c++}else{
|
||||
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
|
||||
%>
|
||||
static const std::string sql="delete from " + tableName + " where <%c++
|
||||
for(size_t i=0;i<pkName.size();i++)
|
||||
{
|
||||
if(rdbms=="postgresql")
|
||||
{
|
||||
$$<<pkName[i]<<" = $"<<i+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<pkName[i]<<" = ?";
|
||||
}
|
||||
if(i<(pkName.size()-1))
|
||||
$$<<" and ";
|
||||
}
|
||||
$$<<"\";\n";
|
||||
}
|
||||
%>
|
||||
return sql;
|
||||
}
|
||||
std::string sqlForInserting(bool &needSelection) const
|
||||
{
|
||||
std::string sql="insert into " + tableName + " (";
|
||||
size_t parametersCount = 0;
|
||||
needSelection = false;
|
||||
<%c++
|
||||
bool selFlag=false;
|
||||
for(size_t i=0;i<cols.size();i++)
|
||||
{
|
||||
if(cols[i].isAutoVal_)
|
||||
{
|
||||
if(@@.get<std::string>("rdbms")=="sqlite3")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(@@.get<int>("hasPrimaryKey")>0)
|
||||
{
|
||||
selFlag = true;
|
||||
}
|
||||
$$<<" sql += \""<<cols[i].colName_<<",\";\n";
|
||||
$$<<" ++parametersCount;\n";
|
||||
continue;
|
||||
}
|
||||
if(cols[i].colType_.empty())
|
||||
continue;
|
||||
if(cols[i].hasDefaultVal_)
|
||||
{
|
||||
if(@@.get<std::string>("rdbms")!="sqlite3")
|
||||
{
|
||||
$$<<" sql += \""<<cols[i].colName_<<",\";\n";
|
||||
$$<<" ++parametersCount;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
%>
|
||||
if(dirtyFlag_[{%i%}])
|
||||
{
|
||||
sql += "{%cols[i].colName_%},";
|
||||
++parametersCount;
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
if(@@.get<int>("hasPrimaryKey")>0||@@.get<std::string>("rdbms")=="postgresql")
|
||||
{
|
||||
%>
|
||||
if(!dirtyFlag_[{%i%}])
|
||||
{
|
||||
needSelection=true;
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
%>
|
||||
if(dirtyFlag_[{%i%}])
|
||||
{
|
||||
sql += "{%cols[i].colName_%},";
|
||||
++parametersCount;
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
if(selFlag)
|
||||
{
|
||||
$$<<" needSelection=true;\n";
|
||||
}
|
||||
%>
|
||||
if(parametersCount > 0)
|
||||
{
|
||||
sql[sql.length()-1]=')';
|
||||
sql += " values (";
|
||||
}
|
||||
else
|
||||
sql += ") values (";
|
||||
|
||||
<%c++
|
||||
if(@@.get<std::string>("rdbms")=="postgresql")
|
||||
{
|
||||
%>
|
||||
int placeholder=1;
|
||||
char placeholderStr[64];
|
||||
size_t n=0;
|
||||
<%c++
|
||||
}
|
||||
for(size_t i=0;i<cols.size();i++)
|
||||
{
|
||||
if(cols[i].isAutoVal_)
|
||||
{
|
||||
if(@@.get<std::string>("rdbms")!="sqlite3")
|
||||
{
|
||||
%>
|
||||
sql +="default,";
|
||||
<%c++
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(cols[i].colType_.empty())
|
||||
continue;
|
||||
%>
|
||||
if(dirtyFlag_[{%i%}])
|
||||
{
|
||||
<%c++
|
||||
if(@@.get<std::string>("rdbms")=="postgresql")
|
||||
{
|
||||
%>
|
||||
n = snprintf(placeholderStr,sizeof(placeholderStr),"$%d,",placeholder++);
|
||||
sql.append(placeholderStr, n);
|
||||
<%c++
|
||||
}else
|
||||
{
|
||||
%>
|
||||
sql.append("?,");
|
||||
|
||||
<%c++
|
||||
}
|
||||
%>
|
||||
}
|
||||
<%c++
|
||||
if(cols[i].hasDefaultVal_&&@@.get<std::string>("rdbms")!="sqlite3")
|
||||
{
|
||||
%>
|
||||
else
|
||||
{
|
||||
sql +="default,";
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
%>
|
||||
if(parametersCount > 0)
|
||||
{
|
||||
sql.resize(sql.length() - 1);
|
||||
}
|
||||
<%c++
|
||||
if(rdbms=="postgresql")
|
||||
{
|
||||
%>
|
||||
if(needSelection)
|
||||
{
|
||||
sql.append(") returning *");
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.append(1, ')');
|
||||
}
|
||||
<%c++
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<" sql.append(1, ')');\n";
|
||||
}
|
||||
%>
|
||||
LOG_TRACE << sql;
|
||||
return sql;
|
||||
}
|
||||
};
|
||||
<%c++
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"} // namespace "<<schema<<"\n";
|
||||
}
|
||||
%>
|
||||
} // namespace [[dbName]]
|
||||
} // namespace drogon_model
|
||||
|
@ -1,15 +1,104 @@
|
||||
{
|
||||
//rdbms:server type, postgresql,mysql or sqlite3
|
||||
"rdbms":"postgresql",
|
||||
//filename:sqlite3 db file name
|
||||
//rdbms: server type, postgresql,mysql or sqlite3
|
||||
"rdbms": "postgresql",
|
||||
//filename: sqlite3 db file name
|
||||
//"filename":"",
|
||||
//host:server address,localhost by default;
|
||||
"host":"127.0.0.1",
|
||||
//port:server port, 5432 by default;
|
||||
"port":5432,
|
||||
//dbname:Database name;
|
||||
"dbname":"",
|
||||
"user":"",
|
||||
"passwd":"",
|
||||
"tables":[]
|
||||
//host: server address,localhost by default;
|
||||
"host": "127.0.0.1",
|
||||
//port: server port, 5432 by default;
|
||||
"port": 5432,
|
||||
//dbname: Database name;
|
||||
"dbname": "",
|
||||
//schema: valid for postgreSQL, "public" by default;
|
||||
"schema": "public",
|
||||
//user: User name
|
||||
"user": "",
|
||||
//password or passwd: Password
|
||||
"password": "",
|
||||
//client_encoding: The character set used by drogon_ctl. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.
|
||||
"tables": [],
|
||||
//convert: the value can be changed by a function call before it is stored into database or
|
||||
//after it is read from database
|
||||
"convert": {
|
||||
"enabled": false,
|
||||
"items":[{
|
||||
"table": "user",
|
||||
"column": "password",
|
||||
"method": {
|
||||
//after_db_read: name of the method which is called after reading from database, signature: void([const] std::shared_ptr [&])
|
||||
"after_db_read": "decrypt_password",
|
||||
//before_db_write: name of the method which is called before writing to database, signature: void([const] std::shared_ptr [&])
|
||||
"before_db_write": "encrypt_password"
|
||||
},
|
||||
"includes": [
|
||||
"\"file_local_search_path.h\"","<file_in_global_search_path.h>"
|
||||
]
|
||||
}]
|
||||
},
|
||||
"relationships": {
|
||||
"enabled": false,
|
||||
"items": [{
|
||||
"type": "has one",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "product",
|
||||
"original_key": "id",
|
||||
"target_table_name": "skus",
|
||||
"target_table_alias": "SKU",
|
||||
"target_key": "product_id",
|
||||
"enable_reverse": true
|
||||
},
|
||||
{
|
||||
"type": "has many",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "product",
|
||||
"original_key": "id",
|
||||
"target_table_name": "reviews",
|
||||
"target_table_alias": "",
|
||||
"target_key": "product_id",
|
||||
"enable_reverse": true
|
||||
},
|
||||
{
|
||||
"type": "many to many",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "",
|
||||
"original_key": "id",
|
||||
"pivot_table": {
|
||||
"table_name": "carts_products",
|
||||
"original_key": "product_id",
|
||||
"target_key": "cart_id"
|
||||
},
|
||||
"target_table_name": "carts",
|
||||
"target_table_alias": "",
|
||||
"target_key": "id",
|
||||
"enable_reverse": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"restful_api_controllers": {
|
||||
"enabled": false,
|
||||
// resource_uri: The URI to access the resource, the default value
|
||||
// is '/*' in which the asterisk represents the table name.
|
||||
// If this option is set to a empty string, the URI is composed of the namespaces and the class name.
|
||||
"resource_uri": "/*",
|
||||
// class_name: "Restful*Ctrl" by default, the asterisk represents the table name.
|
||||
// This option can contain namespaces.
|
||||
"class_name": "Restful*Ctrl",
|
||||
// filters: an array of filter names.
|
||||
"filters": [],
|
||||
// db_client: the database client used by the controller. this option must be consistent with
|
||||
// the configuration of the application.
|
||||
"db_client": {
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//is_fast:
|
||||
"is_fast": false
|
||||
},
|
||||
// directory: The directory where the controller source files are stored.
|
||||
"directory": "controllers",
|
||||
// generate_base_only: false by default. Set to true to avoid overwriting custom subclasses.
|
||||
"generate_base_only": false
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,11 @@ class [[className]] : public drogon::Plugin<[[className]]>
|
||||
[[className]]() {}
|
||||
/// This method must be called by drogon to initialize and start the plugin.
|
||||
/// It must be implemented by the user.
|
||||
virtual void initAndStart(const Json::Value &config) override;
|
||||
void initAndStart(const Json::Value &config) override;
|
||||
|
||||
/// This method must be called by drogon to shutdown the plugin.
|
||||
/// It must be implemented by the user.
|
||||
virtual void shutdown() override;
|
||||
void shutdown() override;
|
||||
};
|
||||
|
||||
<%c++for(size_t i=0;i<namespaceVector.size();i++){%>
|
||||
|
494
drogon_ctl/templates/restful_controller_base_cc.csp
Normal file
494
drogon_ctl/templates/restful_controller_base_cc.csp
Normal file
@ -0,0 +1,494 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
|
||||
/**
|
||||
*
|
||||
* [[fileName]]Base.cc
|
||||
* DO NOT EDIT. This file is generated by drogon_ctl automatically.
|
||||
* Users should implement business logic in the derived class.
|
||||
*/
|
||||
|
||||
#include "[[fileName]]Base.h"
|
||||
#include <string>
|
||||
|
||||
<%c++
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
std::string namespaceStr;
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
namespaceStr.append(name);
|
||||
namespaceStr.append("::");
|
||||
}
|
||||
if(!namespaceStr.empty())
|
||||
{
|
||||
namespaceStr.resize(namespaceStr.length()-2);
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
}
|
||||
std::string indentStr(@@.get<std::string>("className").length(), ' ');
|
||||
%>
|
||||
<%c++
|
||||
if(hasPrimaryKey)
|
||||
{%>
|
||||
void [[className]]Base::getOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
mapper.findByPrimaryKey(
|
||||
id,
|
||||
[req, callbackPtr, this]({%modelName%} r) {
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(makeJson(req, r)));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
const drogon::orm::UnexpectedRows *s=dynamic_cast<const drogon::orm::UnexpectedRows *>(&e.base());
|
||||
if(s)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k404NotFound);
|
||||
(*callbackPtr)(resp);
|
||||
return;
|
||||
}
|
||||
LOG_ERROR<<e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void [[className]]Base::updateOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
auto jsonPtr=req->jsonObject();
|
||||
if(!jsonPtr)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="No json object is found in the request";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
{%modelName%} object;
|
||||
std::string err;
|
||||
if(!doCustomValidations(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if(isMasquerading())
|
||||
{
|
||||
if(!{%modelName%}::validateMasqueradedJsonForUpdate(*jsonPtr, masqueradingVector(), err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
object.updateByMasqueradedJson(*jsonPtr, masqueradingVector());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!{%modelName%}::validateJsonForUpdate(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
object.updateByJson(*jsonPtr);
|
||||
}
|
||||
}
|
||||
catch(const Json::Exception &e)
|
||||
{
|
||||
LOG_ERROR << e.what();
|
||||
Json::Value ret;
|
||||
ret["error"]="Field type error";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
if(object.getPrimaryKey() != id)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="Bad primary key";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
|
||||
mapper.update(
|
||||
object,
|
||||
[callbackPtr](const size_t count)
|
||||
{
|
||||
if(count == 1)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k202Accepted);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else if(count == 0)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="No resources are updated";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k404NotFound);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL << "More than one resource is updated: " << count;
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void [[className]]Base::deleteOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
mapper.deleteByPrimaryKey(
|
||||
id,
|
||||
[callbackPtr](const size_t count) {
|
||||
if(count == 1)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k204NoContent);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else if(count == 0)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = "No resources deleted";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k404NotFound);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL << "Delete more than one records: " << count;
|
||||
Json::Value ret;
|
||||
ret["error"] = "Database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
}
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
<%c++}%>
|
||||
|
||||
void [[className]]Base::get(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
auto dbClientPtr = getDbClient();
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
auto ¶meters = req->parameters();
|
||||
auto iter = parameters.find("sort");
|
||||
if(iter != parameters.end())
|
||||
{
|
||||
auto sortFields = drogon::utils::splitString(iter->second, ",");
|
||||
for(auto &field : sortFields)
|
||||
{
|
||||
if(field.empty())
|
||||
continue;
|
||||
if(field[0] == '+')
|
||||
{
|
||||
field = field.substr(1);
|
||||
mapper.orderBy(field, SortOrder::ASC);
|
||||
}
|
||||
else if(field[0] == '-')
|
||||
{
|
||||
field = field.substr(1);
|
||||
mapper.orderBy(field, SortOrder::DESC);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapper.orderBy(field, SortOrder::ASC);
|
||||
}
|
||||
}
|
||||
}
|
||||
iter = parameters.find("offset");
|
||||
if(iter != parameters.end())
|
||||
{
|
||||
try{
|
||||
auto offset = std::stoll(iter->second);
|
||||
mapper.offset(offset);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
iter = parameters.find("limit");
|
||||
if(iter != parameters.end())
|
||||
{
|
||||
try{
|
||||
auto limit = std::stoll(iter->second);
|
||||
mapper.limit(limit);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
auto jsonPtr = req->jsonObject();
|
||||
if(jsonPtr && jsonPtr->isMember("filter"))
|
||||
{
|
||||
try{
|
||||
auto criteria = makeCriteria((*jsonPtr)["filter"]);
|
||||
mapper.findBy(criteria,
|
||||
[req, callbackPtr, this](const std::vector<{%modelName%}> &v) {
|
||||
Json::Value ret;
|
||||
ret.resize(0);
|
||||
for (auto &obj : v)
|
||||
{
|
||||
ret.append(makeJson(req, obj));
|
||||
}
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(ret));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
LOG_ERROR << e.what();
|
||||
Json::Value ret;
|
||||
ret["error"] = e.what();
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
(*callbackPtr)(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mapper.findAll([req, callbackPtr, this](const std::vector<{%modelName%}> &v) {
|
||||
Json::Value ret;
|
||||
ret.resize(0);
|
||||
for (auto &obj : v)
|
||||
{
|
||||
ret.append(makeJson(req, obj));
|
||||
}
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(ret));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e) {
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void [[className]]Base::create(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
auto jsonPtr=req->jsonObject();
|
||||
if(!jsonPtr)
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"]="No json object is found in the request";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
std::string err;
|
||||
if(!doCustomValidations(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
if(isMasquerading())
|
||||
{
|
||||
if(!{%modelName%}::validateMasqueradedJsonForCreation(*jsonPtr, masqueradingVector(), err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!{%modelName%}::validateJsonForCreation(*jsonPtr, err))
|
||||
{
|
||||
Json::Value ret;
|
||||
ret["error"] = err;
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
{%modelName%} object =
|
||||
(isMasquerading()?
|
||||
{%modelName%}(*jsonPtr, masqueradingVector()) :
|
||||
{%modelName%}(*jsonPtr));
|
||||
auto dbClientPtr = getDbClient();
|
||||
auto callbackPtr =
|
||||
std::make_shared<std::function<void(const HttpResponsePtr &)>>(
|
||||
std::move(callback));
|
||||
drogon::orm::Mapper<{%modelName%}> mapper(dbClientPtr);
|
||||
mapper.insert(
|
||||
object,
|
||||
[req, callbackPtr, this]({%modelName%} newObject){
|
||||
(*callbackPtr)(HttpResponse::newHttpJsonResponse(
|
||||
makeJson(req, newObject)));
|
||||
},
|
||||
[callbackPtr](const DrogonDbException &e){
|
||||
LOG_ERROR << e.base().what();
|
||||
Json::Value ret;
|
||||
ret["error"] = "database error";
|
||||
auto resp = HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
(*callbackPtr)(resp);
|
||||
});
|
||||
}
|
||||
catch(const Json::Exception &e)
|
||||
{
|
||||
LOG_ERROR << e.what();
|
||||
Json::Value ret;
|
||||
ret["error"]="Field type error";
|
||||
auto resp= HttpResponse::newHttpJsonResponse(ret);
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void [[className]]Base::update(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
[[className]]Base::[[className]]Base()
|
||||
: RestfulController({
|
||||
<%c++
|
||||
tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
const auto &cols=tableInfo.get<std::vector<ColumnInfo>>("columns");
|
||||
for(size_t i=0; i<cols.size(); ++i)
|
||||
{
|
||||
auto &col = cols[i];
|
||||
if(i < (cols.size()-1))
|
||||
{
|
||||
%>
|
||||
"{%col.colName_%}",
|
||||
<%c++
|
||||
}else{
|
||||
%>
|
||||
"{%col.colName_%}"
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
%>
|
||||
})
|
||||
{
|
||||
/**
|
||||
* The items in the vector are aliases of column names in the table.
|
||||
* if one item is set to an empty string, the related column is not sent
|
||||
* to clients.
|
||||
*/
|
||||
enableMasquerading({
|
||||
<%c++
|
||||
for(size_t i=0; i<cols.size(); ++i)
|
||||
{
|
||||
auto &col = cols[i];
|
||||
if(i < (cols.size()-1))
|
||||
{
|
||||
%>
|
||||
"{%col.colName_%}", // the alias for the {%col.colName_%} column.
|
||||
<%c++
|
||||
}else{
|
||||
%>
|
||||
"{%col.colName_%}" // the alias for the {%col.colName_%} column.
|
||||
<%c++
|
||||
}
|
||||
}
|
||||
%>
|
||||
});
|
||||
}
|
87
drogon_ctl/templates/restful_controller_base_h.csp
Normal file
87
drogon_ctl/templates/restful_controller_base_h.csp
Normal file
@ -0,0 +1,87 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
/**
|
||||
*
|
||||
* [[fileName]]Base.h
|
||||
* DO NOT EDIT. This file is generated by drogon_ctl automatically.
|
||||
* Users should implement business logic in the derived class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
#include <drogon/orm/RestfulController.h>
|
||||
|
||||
<%c++
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
$$<<"#include \""<<modelName<<".h\"\n";
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
$$<<"using namespace drogon;\n";
|
||||
$$<<"using namespace drogon::orm;\n";
|
||||
|
||||
$$<<"using namespace drogon_model::"<<tableInfo.get<std::string>("dbName");
|
||||
auto &schema=tableInfo.get<std::string>("schema");
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"::"<<schema<<";\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<";\n";
|
||||
}
|
||||
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
%>
|
||||
namespace {%name%}
|
||||
{
|
||||
<%c++}%>
|
||||
/**
|
||||
* @brief this class is created by the drogon_ctl command.
|
||||
* this class is a restful API controller for reading and writing the [[tableName]] table.
|
||||
*/
|
||||
|
||||
class [[className]]Base : public RestfulController
|
||||
{
|
||||
public:
|
||||
<%c++if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
void getOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void updateOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void deleteOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
<%c++}
|
||||
%>
|
||||
void get(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
void create(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
|
||||
// void update(const HttpRequestPtr &req,
|
||||
// std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
orm::DbClientPtr getDbClient()
|
||||
{
|
||||
return drogon::app().get{%(@@.get<bool>("isFastDbClient")?"Fast":"")%}DbClient(dbClientName_);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Ensure that subclasses inherited from this class are instantiated.
|
||||
[[className]]Base();
|
||||
const std::string dbClientName_{"[[dbClientName]]"};
|
||||
};
|
||||
<%c++ for(size_t i=0;i<namespaceVector.size();++i)
|
||||
{
|
||||
$$<<"}\n";
|
||||
}
|
||||
%>
|
58
drogon_ctl/templates/restful_controller_cc.csp
Normal file
58
drogon_ctl/templates/restful_controller_cc.csp
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
*
|
||||
* [[fileName]].cc
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#include "[[fileName]].h"
|
||||
#include <string>
|
||||
|
||||
<%c++
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
std::string namespaceStr;
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
namespaceStr.append(name);
|
||||
namespaceStr.append("::");
|
||||
}
|
||||
if(!namespaceStr.empty())
|
||||
{
|
||||
namespaceStr.resize(namespaceStr.length()-2);
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
}
|
||||
std::string indentStr(@@.get<std::string>("className").length(), ' ');
|
||||
%>
|
||||
|
||||
void [[className]]::getOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} std::string &&id)
|
||||
{
|
||||
}
|
||||
|
||||
void [[className]]::get(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
}
|
||||
void [[className]]::create(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
}
|
||||
void [[className]]::updateOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} std::string &&id)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
void [[className]]::update(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
void [[className]]::deleteOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} std::string &&id)
|
||||
{
|
||||
}
|
70
drogon_ctl/templates/restful_controller_custom_cc.csp
Normal file
70
drogon_ctl/templates/restful_controller_custom_cc.csp
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
*
|
||||
* [[fileName]].cc
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#include "[[fileName]].h"
|
||||
#include <string>
|
||||
|
||||
<%c++
|
||||
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
|
||||
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
std::string namespaceStr;
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
namespaceStr.append(name);
|
||||
namespaceStr.append("::");
|
||||
}
|
||||
if(!namespaceStr.empty())
|
||||
{
|
||||
namespaceStr.resize(namespaceStr.length()-2);
|
||||
$$<<"using namespace "<<namespaceStr<<";\n";
|
||||
}
|
||||
std::string indentStr(@@.get<std::string>("className").length(), ' ');
|
||||
%>
|
||||
|
||||
<%c++
|
||||
if(hasPrimaryKey)
|
||||
{%>
|
||||
void [[className]]::getOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
[[className]]Base::getOne(req, std::move(callback), std::move(id));
|
||||
}
|
||||
|
||||
|
||||
void [[className]]::updateOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
[[className]]Base::updateOne(req, std::move(callback), std::move(id));
|
||||
}
|
||||
|
||||
|
||||
void [[className]]::deleteOne(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%indentStr%} {%modelName%}::PrimaryKeyType &&id)
|
||||
{
|
||||
[[className]]Base::deleteOne(req, std::move(callback), std::move(id));
|
||||
}
|
||||
<%c++}%>
|
||||
|
||||
void [[className]]::get(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
[[className]]Base::get(req, std::move(callback));
|
||||
}
|
||||
|
||||
void [[className]]::create(const HttpRequestPtr &req,
|
||||
{%indentStr%} std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
[[className]]Base::create(req, std::move(callback));
|
||||
}
|
104
drogon_ctl/templates/restful_controller_custom_h.csp
Normal file
104
drogon_ctl/templates/restful_controller_custom_h.csp
Normal file
@ -0,0 +1,104 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
/**
|
||||
*
|
||||
* [[fileName]].h
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
#include "[[className]]Base.h"
|
||||
|
||||
<%c++
|
||||
auto tableInfo = @@.get<DrTemplateData>("tableInfo");
|
||||
auto modelName = tableInfo.get<std::string>("className");
|
||||
$$<<"#include \""<<modelName<<".h\"\n";
|
||||
bool hasPrimaryKey = (tableInfo.get<int>("hasPrimaryKey")==1);
|
||||
$$<<"using namespace drogon;\n";
|
||||
|
||||
$$<<"using namespace drogon_model::"<<tableInfo.get<std::string>("dbName");
|
||||
auto &schema=tableInfo.get<std::string>("schema");
|
||||
if(!schema.empty())
|
||||
{
|
||||
$$<<"::"<<schema<<";\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$$<<";\n";
|
||||
}
|
||||
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
%>
|
||||
namespace {%name%}
|
||||
{
|
||||
<%c++}%>
|
||||
/**
|
||||
* @brief this class is created by the drogon_ctl command.
|
||||
* this class is a restful API controller for reading and writing the [[tableName]] table.
|
||||
*/
|
||||
|
||||
class [[className]]: public drogon::HttpController<[[className]]>, public [[className]]Base
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
<%c++
|
||||
auto resource=@@.get<std::string>("resource");
|
||||
if(resource.empty())
|
||||
{
|
||||
if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
METHOD_ADD([[className]]::getOne,"/{1}",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::updateOne,"/{1}",Put,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::deleteOne,"/{1}",Delete,Options[[filters]]);
|
||||
<%c++}%>
|
||||
METHOD_ADD([[className]]::get,"",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::create,"",Post,Options[[filters]]);
|
||||
//METHOD_ADD([[className]]::update,"",Put,Options[[filters]]);
|
||||
<%c++
|
||||
}else
|
||||
{
|
||||
if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
ADD_METHOD_TO([[className]]::getOne,"{%resource%}/{1}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::updateOne,"{%resource%}/{1}",Put,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::deleteOne,"{%resource%}/{1}",Delete,Options[[filters]]);
|
||||
<%c++}%>
|
||||
ADD_METHOD_TO([[className]]::get,"{%resource%}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::create,"{%resource%}",Post,Options[[filters]]);
|
||||
//ADD_METHOD_TO([[className]]::update,"{%resource%}",Put,Options[[filters]]);
|
||||
<%c++}%>
|
||||
METHOD_LIST_END
|
||||
|
||||
<%c++if(hasPrimaryKey)
|
||||
{
|
||||
%>
|
||||
void getOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void updateOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
void deleteOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
{%modelName%}::PrimaryKeyType &&id);
|
||||
<%c++}
|
||||
%>
|
||||
void get(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
void create(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
};
|
||||
<%c++ for(size_t i=0;i<namespaceVector.size();++i)
|
||||
{
|
||||
$$<<"}\n";
|
||||
}
|
||||
%>
|
80
drogon_ctl/templates/restful_controller_h.csp
Normal file
80
drogon_ctl/templates/restful_controller_h.csp
Normal file
@ -0,0 +1,80 @@
|
||||
<%inc#include "create_model.h"
|
||||
using namespace drogon_ctl;
|
||||
%>
|
||||
/**
|
||||
*
|
||||
* [[fileName]].h
|
||||
* This file is generated by drogon_ctl
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <drogon/HttpController.h>
|
||||
<%c++
|
||||
$$<<"using namespace drogon;\n";
|
||||
auto namespaceVector=@@.get<std::vector<std::string>>("namespaceVector");
|
||||
for(auto &name:namespaceVector)
|
||||
{
|
||||
%>
|
||||
namespace {%name%}
|
||||
{
|
||||
<%c++
|
||||
}
|
||||
%>
|
||||
/**
|
||||
* @brief this class is created by the drogon_ctl command ([[ctlCommand]]).
|
||||
* this class is a restful API controller.
|
||||
*/
|
||||
class [[className]]: public drogon::HttpController<[[className]]>
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
// use METHOD_ADD to add your custom processing function here;
|
||||
<%c++
|
||||
auto resource=@@.get<std::string>("resource");
|
||||
if(resource.empty())
|
||||
{
|
||||
%>
|
||||
METHOD_ADD([[className]]::getOne,"/{1}",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::get,"",Get,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::create,"",Post,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::updateOne,"/{1}",Put,Options[[filters]]);
|
||||
//METHOD_ADD([[className]]::update,"",Put,Options[[filters]]);
|
||||
METHOD_ADD([[className]]::deleteOne,"/{1}",Delete,Options[[filters]]);
|
||||
<%c++
|
||||
}else
|
||||
{
|
||||
%>
|
||||
ADD_METHOD_TO([[className]]::getOne,"{%resource%}/{1}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::updateOne,"{%resource%}/{1}",Put,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::deleteOne,"{%resource%}/{1}",Delete,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::get,"{%resource%}",Get,Options[[filters]]);
|
||||
ADD_METHOD_TO([[className]]::create,"{%resource%}",Post,Options[[filters]]);
|
||||
//ADD_METHOD_TO([[className]]::update,"{%resource%}",Put,Options[[filters]]);
|
||||
<%c++}%>
|
||||
METHOD_LIST_END
|
||||
|
||||
void getOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string &&id);
|
||||
void updateOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string &&id);
|
||||
void deleteOne(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
std::string &&id);
|
||||
void get(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
void create(const HttpRequestPtr &req,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
// void update(const HttpRequestPtr &req,
|
||||
// std::function<void(const HttpResponsePtr &)> &&callback);
|
||||
|
||||
};
|
||||
<%c++ for(size_t i=0;i<namespaceVector.size();++i)
|
||||
{
|
||||
$$<<"}\n";
|
||||
}
|
||||
%>
|
14
drogon_ctl/templates/test_cmake.csp
Normal file
14
drogon_ctl/templates/test_cmake.csp
Normal file
@ -0,0 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project([[ProjectName]]_test CXX)
|
||||
|
||||
add_executable(${PROJECT_NAME} test_main.cc)
|
||||
|
||||
# ##############################################################################
|
||||
# If you include the drogon source code locally in your project, use this method
|
||||
# to add drogon
|
||||
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
||||
#
|
||||
# and comment out the following lines
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
|
||||
|
||||
ParseAndAddDrogonTests(${PROJECT_NAME})
|
32
drogon_ctl/templates/test_main.csp
Normal file
32
drogon_ctl/templates/test_main.csp
Normal file
@ -0,0 +1,32 @@
|
||||
#define DROGON_TEST_MAIN
|
||||
#include <drogon/drogon_test.h>
|
||||
#include <drogon/drogon.h>
|
||||
|
||||
DROGON_TEST(BasicTest)
|
||||
{
|
||||
// Add your tests here
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace drogon;
|
||||
|
||||
std::promise<void> p1;
|
||||
std::future<void> f1 = p1.get_future();
|
||||
|
||||
// Start the main loop on another thread
|
||||
std::thread thr([&]() {
|
||||
// Queues the promise to be fulfilled after starting the loop
|
||||
app().getLoop()->queueInLoop([&p1]() { p1.set_value(); });
|
||||
app().run();
|
||||
});
|
||||
|
||||
// The future is only satisfied after the event loop started
|
||||
f1.get();
|
||||
int status = test::run(argc, argv);
|
||||
|
||||
// Ask the event loop to shutdown and wait
|
||||
app().getLoop()->queueInLoop([]() { app().quit(); });
|
||||
thr.join();
|
||||
return status;
|
||||
}
|
@ -15,23 +15,54 @@
|
||||
#include "version.h"
|
||||
#include <drogon/config.h>
|
||||
#include <drogon/version.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include <trantor/net/Resolver.h>
|
||||
#include <trantor/utils/Utilities.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace drogon_ctl;
|
||||
static const char banner[] =
|
||||
" _ \n"
|
||||
" __| |_ __ ___ __ _ ___ _ __ \n"
|
||||
" / _` | '__/ _ \\ / _` |/ _ \\| '_ \\ \n"
|
||||
"| (_| | | | (_) | (_| | (_) | | | |\n"
|
||||
" \\__,_|_| \\___/ \\__, |\\___/|_| |_|\n"
|
||||
" |___/ \n";
|
||||
R"( _
|
||||
__| |_ __ ___ __ _ ___ _ __
|
||||
/ _` | '__/ _ \ / _` |/ _ \| '_ \
|
||||
| (_| | | | (_) | (_| | (_) | | | |
|
||||
\__,_|_| \___/ \__, |\___/|_| |_|
|
||||
|___/
|
||||
)";
|
||||
|
||||
void version::handleCommand(std::vector<std::string> ¶meters)
|
||||
{
|
||||
const auto tlsBackend = trantor::utils::tlsBackend();
|
||||
const bool tlsSupported = drogon::utils::supportsTls();
|
||||
std::cout << banner << std::endl;
|
||||
std::cout << "A utility for drogon" << std::endl;
|
||||
std::cout << "Version:" << VERSION << std::endl;
|
||||
std::cout << "Git commit:" << VERSION_MD5 << std::endl;
|
||||
std::cout << "Compile config:" << COMPILATION_FLAGS << " " << INCLUDING_DIRS
|
||||
<< std::endl;
|
||||
std::cout << "Version: " << DROGON_VERSION << std::endl;
|
||||
std::cout << "Git commit: " << DROGON_VERSION_SHA1 << std::endl;
|
||||
std::cout << "Compilation: \n Compiler: " << COMPILER_COMMAND
|
||||
<< "\n Compiler ID: " << COMPILER_ID
|
||||
<< "\n Compilation flags: " << COMPILATION_FLAGS
|
||||
<< INCLUDING_DIRS << std::endl;
|
||||
std::cout << "Libraries: \n postgresql: "
|
||||
<< (USE_POSTGRESQL ? "yes" : "no") << " (pipeline mode: "
|
||||
<< (LIBPQ_SUPPORTS_BATCH_MODE ? "yes)\n" : "no)\n")
|
||||
<< " mariadb: " << (USE_MYSQL ? "yes\n" : "no\n")
|
||||
<< " sqlite3: " << (USE_SQLITE3 ? "yes\n" : "no\n");
|
||||
std::cout << " ssl/tls backend: " << tlsBackend << "\n";
|
||||
#ifdef USE_BROTLI
|
||||
std::cout << " brotli: yes\n";
|
||||
#else
|
||||
std::cout << " brotli: no\n";
|
||||
#endif
|
||||
#ifdef USE_REDIS
|
||||
std::cout << " hiredis: yes\n";
|
||||
#else
|
||||
std::cout << " hiredis: no\n";
|
||||
#endif
|
||||
std::cout << " c-ares: "
|
||||
<< (trantor::Resolver::isCAresUsed() ? "yes\n" : "no\n");
|
||||
#ifdef HAS_YAML_CPP
|
||||
std::cout << " yaml-cpp: yes\n";
|
||||
#else
|
||||
std::cout << " yaml-cpp: no\n";
|
||||
#endif
|
||||
}
|
||||
|
@ -17,20 +17,24 @@
|
||||
#include <drogon/DrObject.h>
|
||||
#include "CommandHandler.h"
|
||||
using namespace drogon;
|
||||
|
||||
namespace drogon_ctl
|
||||
{
|
||||
class version : public DrObject<version>, public CommandHandler
|
||||
{
|
||||
public:
|
||||
virtual void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
virtual std::string script() override
|
||||
void handleCommand(std::vector<std::string> ¶meters) override;
|
||||
|
||||
std::string script() override
|
||||
{
|
||||
return "display version of this tool";
|
||||
}
|
||||
virtual bool isTopCommand() override
|
||||
|
||||
bool isTopCommand() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
version()
|
||||
{
|
||||
}
|
||||
|
@ -1,62 +1,64 @@
|
||||
LINK_LIBRARIES(drogon trantor pthread dl)
|
||||
link_libraries(${PROJECT_NAME})
|
||||
|
||||
FILE(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/simple_example/*.csp)
|
||||
FOREACH(cspFile ${SCP_LIST})
|
||||
MESSAGE(STATUS "cspFile:" ${cspFile})
|
||||
EXEC_PROGRAM(basename ARGS "${cspFile} .csp" OUTPUT_VARIABLE classname)
|
||||
MESSAGE(STATUS "view classname:" ${classname})
|
||||
ADD_CUSTOM_COMMAND(OUTPUT ${classname}.h ${classname}.cc
|
||||
COMMAND drogon_ctl
|
||||
ARGS create view ${cspFile}
|
||||
DEPENDS ${cspFile}
|
||||
VERBATIM )
|
||||
SET(VIEWSRC ${VIEWSRC} ${classname}.cc)
|
||||
ENDFOREACH()
|
||||
set(benchmark_sources benchmark/BenchmarkCtrl.cc benchmark/JsonCtrl.cc
|
||||
benchmark/main.cc)
|
||||
|
||||
add_executable(client client_example/main.cc)
|
||||
add_executable(websocket_client websocket_client/WebSocketClient.cc)
|
||||
add_executable(websocket_server websocket_server/WebSocketServer.cc)
|
||||
add_executable(benchmark ${benchmark_sources})
|
||||
add_executable(helloworld helloworld/main.cc
|
||||
helloworld/HelloController.cc
|
||||
helloworld/HelloViewController.cc)
|
||||
drogon_create_views(helloworld
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/helloworld
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_executable(file_upload file_upload/file_upload.cc)
|
||||
drogon_create_views(file_upload
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/file_upload
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_executable(login_session login_session/main.cc)
|
||||
drogon_create_views(login_session
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/login_session
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
SET(simple_example_sources simple_example/CustomCtrl.cc
|
||||
simple_example/CustomHeaderFilter.cc
|
||||
simple_example/DoNothingPlugin.cc
|
||||
simple_example/ForwardCtrl.cc
|
||||
simple_example/JsonTestController.cc
|
||||
simple_example/ListParaCtl.cc
|
||||
simple_example/PipeliningTest.cc
|
||||
simple_example/TestController.cc
|
||||
simple_example/TestPlugin.cc
|
||||
simple_example/TestViewCtl.cc
|
||||
simple_example/WebSocketTest.cc
|
||||
simple_example/api_Attachment.cc
|
||||
simple_example/api_v1_ApiTest.cc
|
||||
simple_example/TimeFilter.cc
|
||||
simple_example/main.cc)
|
||||
add_executable(jsonstore jsonstore/main.cc)
|
||||
|
||||
ADD_EXECUTABLE(webapp ${simple_example_sources} ${VIEWSRC})
|
||||
ADD_DEPENDENCIES(webapp drogon_ctl)
|
||||
add_executable(redis_simple redis/main.cc
|
||||
redis/controllers/Client.cc
|
||||
redis/controllers/WsClient.cc)
|
||||
|
||||
SET(client_example_sources client_example/main.cc)
|
||||
SET(benchmark_sources benchmark/BenchmarkCtrl.cc
|
||||
benchmark/JsonCtrl.cc
|
||||
benchmark/main.cc)
|
||||
#AUX_SOURCE_DIRECTORY(simple_example_test DIR_TEST)
|
||||
add_executable(redis_chat redis_chat/main.cc
|
||||
redis_chat/controllers/Chat.cc)
|
||||
|
||||
ADD_EXECUTABLE(client ${client_example_sources})
|
||||
ADD_EXECUTABLE(benchmark ${benchmark_sources})
|
||||
ADD_EXECUTABLE(webapp_test simple_example_test/main.cc)
|
||||
ADD_EXECUTABLE(pipelining_test simple_example_test/HttpPipeliningTest.cc)
|
||||
ADD_EXECUTABLE(websocket_test simple_example_test/WebSocketTest.cc)
|
||||
add_executable(async_stream async_stream/main.cc
|
||||
async_stream/RequestStreamExampleCtrl.cc)
|
||||
|
||||
ADD_CUSTOM_COMMAND(TARGET webapp POST_BUILD
|
||||
COMMAND gzip
|
||||
ARGS -c ${CMAKE_CURRENT_SOURCE_DIR}/simple_example/index.html > ${CMAKE_CURRENT_BINARY_DIR}/index.html.gz
|
||||
VERBATIM)
|
||||
ADD_CUSTOM_COMMAND(TARGET webapp POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${PROJECT_SOURCE_DIR}/config.example.json ${PROJECT_SOURCE_DIR}/drogon.jpg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/simple_example/index.html
|
||||
${PROJECT_SOURCE_DIR}/trantor/trantor/tests/server.pem $<TARGET_FILE_DIR:webapp>)
|
||||
add_executable(cors cors/main.cc)
|
||||
|
||||
SET(example_targets webapp webapp_test client benchmark pipelining_test websocket_test)
|
||||
set(example_targets
|
||||
benchmark
|
||||
client
|
||||
websocket_client
|
||||
websocket_server
|
||||
helloworld
|
||||
file_upload
|
||||
login_session
|
||||
jsonstore
|
||||
redis_simple
|
||||
redis_chat
|
||||
async_stream
|
||||
cors)
|
||||
|
||||
SET_PROPERTY(TARGET ${example_targets} PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
SET_PROPERTY(TARGET ${example_targets} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
SET_PROPERTY(TARGET ${example_targets} PROPERTY CXX_EXTENSIONS OFF)
|
||||
# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear
|
||||
# when the templated functions are instantiated at their point of use.
|
||||
if(NOT ${CMAKE_PLATFORM_NAME} STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
foreach(target ${example_targets})
|
||||
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${example_targets}
|
||||
PROPERTY CXX_STANDARD ${DROGON_CXX_STANDARD})
|
||||
set_property(TARGET ${example_targets} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET ${example_targets} PROPERTY CXX_EXTENSIONS OFF)
|
||||
|
@ -2,11 +2,26 @@
|
||||
|
||||
The following examples can help you understand how to use Drogon:
|
||||
|
||||
1. [benchmark](https://github.com/an-tao/drogon/tree/master/examples/benchmark) - Basic benchmark example. see [wiki benchmarks](https://github.com/an-tao/drogon/wiki/13-Benchmarks)
|
||||
2. [client_example](https://github.com/an-tao/drogon/tree/master/examples/client_example/main.cc) - A client example.
|
||||
3. [simple_example](https://github.com/an-tao/drogon/tree/master/examples/simple_example) - A simple example showing how to create a web application using Drogon.
|
||||
4. [simple_example_test](https://github.com/an-tao/drogon/tree/master/examples/simple_example_test) - Some tests for the `simple_example`.
|
||||
1. [helloworld](https://github.com/drogonframework/drogon/tree/master/examples/helloworld) - The multiple ways of "Hello, World!"
|
||||
2. [client_example](https://github.com/drogonframework/drogon/tree/master/examples/client_example/main.cc) - A client example
|
||||
3. [websocket_client](https://github.com/drogonframework/drogon/tree/master/examples/websocket_client/WebSocketClient.cc) - An example on how to use the WebSocket client
|
||||
4. [login_session](https://github.com/drogonframework/drogon/tree/master/examples/login_session) - How to use the built-in session system to handle login and out
|
||||
5. [file_upload](https://github.com/drogonframework/drogon/tree/master/examples/file_upload) - How to handle file uploads in Drogon
|
||||
6. [simple_reverse_proxy](https://github.com/drogonframework/drogon/tree/master/examples/simple_reverse_proxy) - An example showing how to use Drogon as a HTTP reverse
|
||||
proxy with a simple round robin
|
||||
7. [benchmark](https://github.com/drogonframework/drogon/tree/master/examples/benchmark) - Basic benchmark(https://github.com/drogonframework/drogon/wiki/13-Benchmarks) example
|
||||
8. [jsonstore](https://github.com/drogonframework/drogon/tree/master/examples/jsonstore) - Implementation of a [jsonstore](https://github.com/bluzi/jsonstore)-like storage service that is concurrent and stores in memory. Serving as a showcase on how to build a minimally useful RESTful APIs in Drogon
|
||||
9. [redis](https://github.com/drogonframework/drogon/tree/master/examples/redis) - A simple example of Redis
|
||||
10. [websocket_server](https://github.com/drogonframework/drogon/tree/master/examples/websocket_server) - A example websocket chat room server
|
||||
11. [redis_cache](https://github.com/drogonframework/drogon/tree/master/examples/redis_cache) - An example for using coroutines of Redis clients
|
||||
12. [redis_chat](https://github.com/drogonframework/drogon/tree/master/examples/redis_chat) - A chatroom server built with websocket and Redis pub/sub service
|
||||
13. [prometheus_example](https://github.com/drogonframework/drogon/tree/master/examples/prometheus_example) - An example of how to use the Prometheus exporter in Drogon
|
||||
14. [cors](https://github.com/drogonframework/drogon/tree/master/examples/cors) - An example demonstrating how to implement CORS (Cross-Origin Resource Sharing) support in Drogon
|
||||
|
||||
### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite
|
||||
|
||||
I created a benchmark suite for the `tfb`, see [here](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/C%2B%2B/drogon) for details.
|
||||
|
||||
### Another test suite
|
||||
|
||||
I also created a test suite for another web frameworks benchmark repository, see [here](https://github.com/the-benchmarker/web-frameworks/tree/master/cpp/drogon). In this project, Drogon is used as a sub-module (locally include in the project).
|
||||
|
167
examples/async_stream/RequestStreamExampleCtrl.cc
Normal file
167
examples/async_stream/RequestStreamExampleCtrl.cc
Normal file
@ -0,0 +1,167 @@
|
||||
#include <drogon/drogon.h>
|
||||
#include <drogon/HttpController.h>
|
||||
#include <drogon/HttpRequest.h>
|
||||
#include <fstream>
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
class StreamEchoReader : public RequestStreamReader
|
||||
{
|
||||
public:
|
||||
StreamEchoReader(ResponseStreamPtr respStream)
|
||||
: respStream_(std::move(respStream))
|
||||
{
|
||||
}
|
||||
|
||||
void onStreamData(const char *data, size_t length) override
|
||||
{
|
||||
LOG_INFO << "onStreamData[" << length << "]";
|
||||
respStream_->send({data, length});
|
||||
}
|
||||
|
||||
void onStreamFinish(std::exception_ptr ptr) override
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(ptr);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR << "onStreamError: " << e.what();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO << "onStreamFinish";
|
||||
}
|
||||
respStream_->close();
|
||||
}
|
||||
|
||||
private:
|
||||
ResponseStreamPtr respStream_;
|
||||
};
|
||||
|
||||
class RequestStreamExampleCtrl : public HttpController<RequestStreamExampleCtrl>
|
||||
{
|
||||
public:
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(RequestStreamExampleCtrl::stream_echo, "/stream_echo", Post);
|
||||
ADD_METHOD_TO(RequestStreamExampleCtrl::stream_upload,
|
||||
"/stream_upload",
|
||||
Post);
|
||||
METHOD_LIST_END
|
||||
|
||||
void stream_echo(
|
||||
const HttpRequestPtr &,
|
||||
RequestStreamPtr &&stream,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback) const
|
||||
{
|
||||
auto resp = drogon::HttpResponse::newAsyncStreamResponse(
|
||||
[stream](ResponseStreamPtr respStream) {
|
||||
stream->setStreamReader(
|
||||
std::make_shared<StreamEchoReader>(std::move(respStream)));
|
||||
});
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
void stream_upload(
|
||||
const HttpRequestPtr &req,
|
||||
RequestStreamPtr &&stream,
|
||||
std::function<void(const HttpResponsePtr &)> &&callback) const
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
MultipartHeader header;
|
||||
std::string tmpName;
|
||||
std::ofstream file;
|
||||
};
|
||||
|
||||
auto files = std::make_shared<std::vector<Entry>>();
|
||||
auto reader = RequestStreamReader::newMultipartReader(
|
||||
req,
|
||||
[files](MultipartHeader &&header) {
|
||||
LOG_INFO << "Multipart name: " << header.name
|
||||
<< ", filename:" << header.filename
|
||||
<< ", contentType:" << header.contentType;
|
||||
|
||||
files->push_back({std::move(header)});
|
||||
auto tmpName = drogon::utils::genRandomString(40);
|
||||
if (!files->back().header.filename.empty())
|
||||
{
|
||||
files->back().tmpName = tmpName;
|
||||
files->back().file.open("uploads/" + tmpName,
|
||||
std::ios::trunc);
|
||||
}
|
||||
},
|
||||
[files](const char *data, size_t length) {
|
||||
if (files->back().tmpName.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto ¤tFile = files->back().file;
|
||||
if (length == 0)
|
||||
{
|
||||
LOG_INFO << "file finish";
|
||||
if (currentFile.is_open())
|
||||
{
|
||||
currentFile.flush();
|
||||
currentFile.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
LOG_INFO << "data[" << length << "]: ";
|
||||
if (currentFile.is_open())
|
||||
{
|
||||
LOG_INFO << "write file";
|
||||
currentFile.write(data, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR << "file not open";
|
||||
}
|
||||
},
|
||||
[files, callback = std::move(callback)](std::exception_ptr ex) {
|
||||
if (ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(std::move(ex));
|
||||
}
|
||||
catch (const StreamError &e)
|
||||
{
|
||||
LOG_ERROR << "stream error: " << e.what();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOG_ERROR << "multipart error: " << e.what();
|
||||
}
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
resp->setBody("error\n");
|
||||
callback(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO << "stream finish, received " << files->size()
|
||||
<< " files";
|
||||
Json::Value respJson;
|
||||
for (const auto &item : *files)
|
||||
{
|
||||
if (item.tmpName.empty())
|
||||
continue;
|
||||
Json::Value entry;
|
||||
entry["name"] = item.header.name;
|
||||
entry["filename"] = item.header.filename;
|
||||
entry["tmpName"] = item.tmpName;
|
||||
respJson.append(entry);
|
||||
}
|
||||
auto resp = HttpResponse::newHttpJsonResponse(respJson);
|
||||
callback(resp);
|
||||
}
|
||||
});
|
||||
|
||||
stream->setStreamReader(std::move(reader));
|
||||
}
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user