mirror of
https://github.com/v2board/v2board.git
synced 2025-08-02 21:38:49 +08:00
Compare commits
694 Commits
Author | SHA1 | Date | |
---|---|---|---|
95e698dbc8 | |||
c3e924036a | |||
8dc19ee67b | |||
d020ddf926 | |||
334f70f19e | |||
49e155797a | |||
53e1e41902 | |||
afc9b462e5 | |||
5c2c7e502c | |||
c3eac30c66 | |||
f667f5ee41 | |||
8838381432 | |||
a6bcc3f153 | |||
593066f9de | |||
73cbfba19b | |||
17e3905f18 | |||
b7e8db727c | |||
7464466b85 | |||
417d5255e7 | |||
9885661795 | |||
bade7e2cf3 | |||
88c0e75937 | |||
7eb5532c30 | |||
eb49e29cd0 | |||
6a5c3f6206 | |||
ccd52546c8 | |||
d9b4a872ff | |||
6c3148bdb9 | |||
afcd0d9c10 | |||
b851f1207a | |||
2e13bab3d8 | |||
bd1b339db8 | |||
0a32b0b085 | |||
c90aa538bc | |||
506d662ae9 | |||
dab9afca53 | |||
6e509dab73 | |||
9ea13bb00f | |||
59672a6f2c | |||
71c42765fc | |||
c5b56da958 | |||
83592d2f3f | |||
941289c641 | |||
e3c5466c0a | |||
c5b5abab1a | |||
03eb8d0724 | |||
1ed5a278da | |||
108d54f3cb | |||
c648308634 | |||
12db88b998 | |||
11ca911d02 | |||
de77170bdc | |||
f030023ec0 | |||
fddd816129 | |||
fa6aea6e2d | |||
ce19ebc97f | |||
ca650dd067 | |||
1acfd84d4b | |||
29d7228861 | |||
422b18ca66 | |||
871291e02d | |||
f26d9495e3 | |||
a7d6b615ed | |||
8afa3c8f09 | |||
503ac97a8c | |||
ade3770d50 | |||
83cbe86192 | |||
22643f04b1 | |||
af71ab8e27 | |||
c29bd836eb | |||
0c2090cb3c | |||
f7959dcd93 | |||
9158697546 | |||
c6cfa2d31c | |||
aaa04f12a3 | |||
a4df1416de | |||
d1a2e7a29e | |||
a3b400ed32 | |||
0d6d10421b | |||
deb12ae707 | |||
bf3b7bb66f | |||
15a28e7bd3 | |||
c46b8b1b40 | |||
98b9ca62f6 | |||
6857967eec | |||
9fed8b8f9d | |||
c5d74f8b38 | |||
3167306bc8 | |||
51fee8892e | |||
25d4c5b31d | |||
aaad8a7f7e | |||
5630066aa4 | |||
998ac1d500 | |||
890626d892 | |||
dae5d2a2a3 | |||
c7a45c9d3d | |||
71a8daf271 | |||
8fd0592139 | |||
153a0e9fdf | |||
9f3aaac614 | |||
ba1c4ffa00 | |||
99117cca58 | |||
42ad99065e | |||
1da49f7f9f | |||
16bdafc952 | |||
ecca13911c | |||
8a0ac687cf | |||
2e4de78923 | |||
867f1760d3 | |||
e2a3a1e72d | |||
c17b614e13 | |||
45a76b25ef | |||
3f2c8266de | |||
94158cb6e3 | |||
467f33c71d | |||
2076dded41 | |||
9e07861c9b | |||
6e2379cb6b | |||
295b4552d7 | |||
4ccd41e197 | |||
3b486e4693 | |||
50b5ed6b8e | |||
39ae037080 | |||
89b6fe119f | |||
bb56b581be | |||
1cc0dea454 | |||
4301d7e4ab | |||
2523253637 | |||
1b3833173d | |||
54a8542e0f | |||
3e550142cd | |||
b020f2c196 | |||
e1b16ef7e6 | |||
887aad7737 | |||
a1f2290ff2 | |||
d23daf4a68 | |||
c4868a9c48 | |||
6050e6e4a9 | |||
7c69e19304 | |||
fd42a855cf | |||
e73cbe9597 | |||
276b040581 | |||
495a5f89c5 | |||
7c3309164b | |||
674b31675a | |||
b2c33cd31b | |||
4240e8355a | |||
7770bf6b99 | |||
55118d7706 | |||
2b34f5ec82 | |||
7dff7ddfc7 | |||
ed3e468a0a | |||
901d89b5d7 | |||
402b9e0c3f | |||
ae543d1c2c | |||
5d9b98f383 | |||
a2183b7143 | |||
a2278487ee | |||
075f7b39a8 | |||
3db93b4739 | |||
1fc9f94dad | |||
bdfa1ff0d5 | |||
98e4aca61f | |||
139aeb3f48 | |||
dfdf995ddb | |||
4b4d777a4e | |||
42607a789d | |||
79f53f2836 | |||
d1bf743316 | |||
39fadd8a63 | |||
4831c9f194 | |||
ee80e0f2ff | |||
1be7151b6c | |||
bb1ad02cf8 | |||
64f379d99d | |||
c271647ecc | |||
e8b6f1b481 | |||
1c6907fe33 | |||
68f7cdeed8 | |||
40e5ee62b1 | |||
9f9bb14e9d | |||
bcc80581d3 | |||
288f4aba18 | |||
cc673cdbd1 | |||
0781a0740b | |||
8d56db2bf6 | |||
56fd3f5b99 | |||
5a84e412c4 | |||
3216a90235 | |||
59fa3a3316 | |||
06465b3eb3 | |||
cc12605984 | |||
8fa9d60c4d | |||
1c3eff241a | |||
79aa942d5b | |||
f4688b6d50 | |||
d9c0d18689 | |||
7e88a39249 | |||
064834001d | |||
49d5b407bc | |||
1291bf47be | |||
e15d5961f0 | |||
04c6b865b4 | |||
5b33bf7a0b | |||
2235a5e7c5 | |||
6fba0b6dab | |||
3075f0d411 | |||
111d2720bd | |||
03e0b5d087 | |||
96f562a9e7 | |||
7bb4852cca | |||
8986ba1d42 | |||
f193f35642 | |||
2a92ee8b41 | |||
56cabbdc00 | |||
5f4d02dde3 | |||
00c2dee361 | |||
35917ad199 | |||
e4cb6458c0 | |||
93c1031078 | |||
26252aee02 | |||
8d10b52a35 | |||
6e7da97fcd | |||
13dbb143f8 | |||
01da63f82e | |||
d2a0422f64 | |||
c81cb8acca | |||
6bf0d2d94e | |||
260c1d7361 | |||
6a6de2dc22 | |||
ba9ec7006b | |||
f17b5d04a8 | |||
cccd8f36ee | |||
4d7ebe4aea | |||
8ac8427c2f | |||
97056be8c3 | |||
68d44e7657 | |||
b07511f01b | |||
a13809ac02 | |||
5b317478c6 | |||
57fd282024 | |||
27ccf9869d | |||
5e5e3fbb08 | |||
f5132abad1 | |||
4f709bf1f6 | |||
e62ef5cb0a | |||
e5c207ccff | |||
6b7cea671d | |||
dbf73f3a38 | |||
a3b7130857 | |||
c0bae87556 | |||
f18715ed09 | |||
7f3e0d9fb9 | |||
e5c8e18206 | |||
796a5ba55e | |||
9bf6f64f71 | |||
66957eff6d | |||
4e03662e8f | |||
fc48f5553f | |||
c4ddde1c94 | |||
92f44d7a2e | |||
3bfd64d8ca | |||
cb2efac160 | |||
c3898ec795 | |||
6dc5dd0edb | |||
e97f6c64a0 | |||
89630d889d | |||
3becb71a5a | |||
d361ff80ed | |||
3fe442313d | |||
f76609a38d | |||
51f4ad417e | |||
516b2626ae | |||
b9312f362c | |||
102f18bef1 | |||
1fc36d8e29 | |||
a3837f83cb | |||
47f27f4852 | |||
e73eaea66c | |||
bc5ddbf40a | |||
a2c674f00c | |||
218338960b | |||
d9dcea4e4f | |||
fad12a62ce | |||
1ee104ec88 | |||
19dcd0ffba | |||
ab2e39f7dd | |||
8f4ebe1764 | |||
3763320261 | |||
376c79aa91 | |||
47686d50c5 | |||
c8cb1f8e83 | |||
620ed8e04c | |||
c28a6ec1d3 | |||
3c78bed800 | |||
72fb0d131d | |||
378c96d4c7 | |||
ca2a744b10 | |||
0f72e9a091 | |||
bb49fb15d1 | |||
47d8dfd7c8 | |||
60b197410c | |||
e2b73094c0 | |||
7d8b8ad8ac | |||
1992b0a9e9 | |||
b4bede869d | |||
a11e65f84a | |||
e1a8ffe7c4 | |||
2217286d03 | |||
dd924d95c6 | |||
53710f2e01 | |||
f03c53815c | |||
6e98e42f51 | |||
9932e34634 | |||
6ba4702539 | |||
b29ac8ac2c | |||
866fda84ed | |||
95c7c48cc7 | |||
010f0cbc03 | |||
0d2b8bc976 | |||
3b9e9cbe7f | |||
a7b47d8f77 | |||
dea26121fc | |||
10e4de39de | |||
e7fd81bf4c | |||
0f9cb9696d | |||
a7b3d6e778 | |||
637bacd62f | |||
2171ef59d7 | |||
0a92308ad2 | |||
8ff53673d7 | |||
07772ceb66 | |||
fc8333d757 | |||
64dbd11e62 | |||
bfeab8eae2 | |||
5ee6fc2996 | |||
018e4aa810 | |||
17d6d04cc6 | |||
e174ef193b | |||
1887259554 | |||
8b804913f5 | |||
a2ea88beb5 | |||
d969220654 | |||
5154993887 | |||
61b31c1925 | |||
a0454577cb | |||
f7fc6b9dfc | |||
1b7fc9529f | |||
a56faa7e8e | |||
0a2c626f89 | |||
19df032b57 | |||
de8f95ee3d | |||
568648dc2f | |||
9f5be4e83a | |||
14d0de18ec | |||
64ae39aa2d | |||
fb09baa3c1 | |||
153bdcaad1 | |||
f3ac8a37be | |||
9925ab6b47 | |||
84ff8d3ab3 | |||
fcec3af75f | |||
2a9a8805a8 | |||
72eafb9698 | |||
e837e774ef | |||
00fcff5f0d | |||
6b43597eaf | |||
c576299a60 | |||
4926264160 | |||
0c1a53faab | |||
0f0b092f86 | |||
3beb938cbc | |||
c45578417f | |||
088dc2cfa6 | |||
0b4c884824 | |||
7b9390a6cb | |||
2b69e83132 | |||
56820346d0 | |||
1ea307854f | |||
290ef77c03 | |||
172a639f59 | |||
bff92180b0 | |||
e094a2f4d1 | |||
b852180b4d | |||
a0a5327f42 | |||
874648a4c0 | |||
15cd13c26c | |||
76c50e01bf | |||
9ae3994705 | |||
cab7cc9d19 | |||
dbac9d9dac | |||
2e18b1b108 | |||
c1583a1014 | |||
01c5901c71 | |||
0446e7d99f | |||
557f6bc0c2 | |||
e49dc87fda | |||
edf88cbccf | |||
19940f599d | |||
ace06630d8 | |||
50582137e4 | |||
ab8da6bb02 | |||
0a58930619 | |||
9d9866689d | |||
2d21fa3fb7 | |||
5cd020ea7a | |||
43e7bb6c9f | |||
2b8456fdf5 | |||
b34fa70165 | |||
d845439f14 | |||
d356722482 | |||
72ac20d387 | |||
878a7da62c | |||
4fd0c9ce51 | |||
18ed6947b3 | |||
b8b1e1043b | |||
705ceb80b2 | |||
47be10f274 | |||
540b331f16 | |||
e9ba149c0f | |||
895e0f177b | |||
9dc64114f5 | |||
168b0ad72e | |||
c82acafdf8 | |||
e14489cf66 | |||
96a36b1afb | |||
df9d99c271 | |||
79bd46c0da | |||
e638660bf7 | |||
880b8e8ad9 | |||
ba40e3a1bf | |||
8fafdc9bb5 | |||
e07b97b14e | |||
c817710556 | |||
19fd19fbcf | |||
26e63cbe1c | |||
5bd524fbf1 | |||
3a7a0c6e62 | |||
23bfb20307 | |||
7f9ad68e1b | |||
cb8cdb2e0e | |||
5db26d862b | |||
13bc683faf | |||
e9229781bc | |||
6b9e424b74 | |||
078e1e7bd6 | |||
d417e70475 | |||
214fb2a2bf | |||
f61e37aeaf | |||
98e23be297 | |||
5bc4bff8f9 | |||
ab97db552d | |||
2e2a0bf273 | |||
1f471545d4 | |||
ffe05a5467 | |||
ec73224671 | |||
3f4e3d46fb | |||
6920352c0e | |||
e57a70970f | |||
406f1ee80d | |||
f7558fecc7 | |||
43d98a485d | |||
be2caad604 | |||
984abba50d | |||
62f7d5b3e8 | |||
37844c1bb4 | |||
87e0d34961 | |||
ce6bd80702 | |||
79bb987c37 | |||
ec8357aa3e | |||
24f5324c2a | |||
0299cd63ad | |||
bb566f01d9 | |||
c03bd0098d | |||
073f7611e1 | |||
517f7d0122 | |||
32d6a983a3 | |||
a8db24492b | |||
1b23998248 | |||
d744fba683 | |||
3f6799aa4b | |||
1d704a4424 | |||
ab5aeb545e | |||
5e7e42e52c | |||
241cbd3016 | |||
9ff815f853 | |||
b61e641dad | |||
a9eff7bea9 | |||
fc3c4b3b73 | |||
b75104475c | |||
6c606527b1 | |||
34892d9c34 | |||
51bb2779b9 | |||
45a257778c | |||
4286bbb988 | |||
2f8f3afebf | |||
15fc60d3c7 | |||
b8218f2c51 | |||
9080de4d0a | |||
50ffdd9db9 | |||
6cf3b73da2 | |||
e963a61910 | |||
038effdd42 | |||
5a4b208aab | |||
af400b3a0b | |||
9a4b2027f6 | |||
9ae2fc3f1d | |||
ba9d00f088 | |||
7fc34fa2b8 | |||
7cb5eb03b9 | |||
d7063191f4 | |||
deb7e4ed04 | |||
602e12d745 | |||
f2ffb184d4 | |||
6dd562df8c | |||
5fae1f2bd8 | |||
a60d68a13f | |||
dad9e43114 | |||
aac4ce8098 | |||
52b9ed47b0 | |||
6d76e30299 | |||
0675268a16 | |||
aaf94e61a2 | |||
47d5031fe6 | |||
8d4575ede9 | |||
e9a69cd0f6 | |||
625bcc59e8 | |||
34533ae10c | |||
b4fc90c133 | |||
12278e143e | |||
10510164e4 | |||
75e91b281c | |||
c027c1127b | |||
0dbdb55ed2 | |||
66042904e8 | |||
508961c4fb | |||
f03dbd1dcb | |||
e576d82955 | |||
88d7e2d35e | |||
9b6919e9c9 | |||
3e86cdcbbf | |||
9fc4a6129c | |||
db9743a67f | |||
0f5942bc03 | |||
28cc21e09b | |||
fc7f73b558 | |||
fb0ebea836 | |||
7d7cef5f37 | |||
abd488d247 | |||
e3916aaa06 | |||
32795cf541 | |||
415850184e | |||
afa3c0aa52 | |||
82875ae6f0 | |||
bcd85ef71a | |||
8f35924dee | |||
4a01b8f11e | |||
fc18168aa2 | |||
abdb6f80af | |||
fbf3a83104 | |||
515e23762f | |||
f7fdfadfb0 | |||
35f954cd84 | |||
ed0fe84687 | |||
a96ca5a363 | |||
6a051f469e | |||
5e9bc09396 | |||
1ef8eab552 | |||
4d66941ef6 | |||
2e505a9759 | |||
85e0eb2760 | |||
c68440ce32 | |||
1cd0dbdd31 | |||
d268f6ddec | |||
b75c33b1d9 | |||
0ee67ef270 | |||
44e57b7073 | |||
0de4a137bf | |||
392c849241 | |||
2f3f457ad9 | |||
776e866b3c | |||
9541bc8cd0 | |||
dc52c3191c | |||
85a29d3a8b | |||
7a0c9ce4c4 | |||
b4bb93e23e | |||
11d9654010 | |||
52163329da | |||
45b03b4fba | |||
0749372f34 | |||
588577d513 | |||
e3431c6ae7 | |||
2346b1a2dc | |||
c61f64d623 | |||
25fbdf0013 | |||
3a9b3ab32d | |||
a524fb6f76 | |||
2172f088a2 | |||
93fa074358 | |||
27e417a5f2 | |||
97e1d9b3bd | |||
1a0d8e9c55 | |||
b49b941e50 | |||
b9cee36641 | |||
2ecdc27921 | |||
2d58744de4 | |||
869a6a920b | |||
c82a189d76 | |||
7543819ef2 | |||
4ceca1957f | |||
44bd189259 | |||
b6752e3952 | |||
45ba9b0c15 | |||
52e7925cac | |||
54273a8f16 | |||
c6461f0bdf | |||
145f55ae29 | |||
a201b19940 | |||
f4166bed45 | |||
8f8be2ea33 | |||
df02973756 | |||
9e55cb2f5d | |||
5699fe09e9 | |||
34fa75b4cc | |||
96d3a27a5b | |||
b8c8335542 | |||
aceff450ec | |||
5d7b5eb8f6 | |||
afc9d64aab | |||
09e31dc70b | |||
d653541ef3 | |||
dc37499df9 | |||
2911680eaf | |||
6ff24f77b8 | |||
9f4c19bcab | |||
453a078cd5 | |||
20be5c3182 | |||
7639f07b83 | |||
2a693e4911 | |||
89d67279c6 | |||
8bc5004654 | |||
ec4c0ba339 | |||
b4e5eb26e3 | |||
1bf03b28c7 | |||
aa8f5bfa79 | |||
495aa04273 | |||
202a21c17a | |||
9eefb32a4c | |||
2eb594a4fa | |||
34e71ff049 | |||
63c1faba5e | |||
5b2e18f702 | |||
784b53c9f3 | |||
01d0f6b29d | |||
2d7d5a564e | |||
70c1d5c874 | |||
10029c8362 | |||
9cd377f8ed | |||
f5db443668 | |||
e1fce3ae37 | |||
fae5ef21e6 | |||
4c976b1cbe | |||
6de46f5c36 | |||
4992ea635c | |||
928e59a977 | |||
2be5b8e357 | |||
76ce89c17f | |||
8c5b32de90 | |||
0b89446b63 | |||
0d4a86c9e9 | |||
aa1d54137c | |||
eee6351e35 | |||
4b43bd2b9e | |||
ff6aeb92ec | |||
a9abdcd9d3 | |||
7965a020b6 | |||
17e626493a | |||
0de055a36b | |||
5070c851ed | |||
157a97b35d | |||
8e6ee35efb | |||
ec284241ee | |||
a3f59aac0a | |||
35c71be4a9 | |||
abf6dbf73c | |||
037ecc0a2a | |||
5a87d59d30 | |||
1351ec583e | |||
5b29257227 | |||
551073cb83 | |||
a1b466e2c2 | |||
0b3cf4a2a4 | |||
0dccfd9f09 | |||
988f088a58 |
10
.env.example
10
.env.example
@ -7,16 +7,16 @@ APP_URL=http://localhost
|
||||
LOG_CHANNEL=stack
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=db
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=123456
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
CACHE_DRIVER=redis
|
||||
QUEUE_CONNECTION=redis
|
||||
SESSION_DRIVER=redis
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
@ -31,6 +31,8 @@ MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_NAME=null
|
||||
MAILGUN_DOMAIN=
|
||||
MAILGUN_SECRET=
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
/node_modules
|
||||
/config/v2panel.php
|
||||
/config/v2board.php
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/env.example.js
|
||||
@ -9,6 +9,7 @@
|
||||
.env.backup
|
||||
.phpunit.result.cache
|
||||
.idea
|
||||
.lock
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2019 Bruskyii Panda
|
||||
Copyright (c) 2019 Tokumeikoi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -38,6 +38,24 @@ class CheckCommission extends Command
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->autoCheck();
|
||||
$this->autoPayCommission();
|
||||
}
|
||||
|
||||
public function autoCheck()
|
||||
{
|
||||
if ((int)config('v2board.commission_auto_check_enable', 1)) {
|
||||
Order::where('commission_status', 0)
|
||||
->where('status', 3)
|
||||
->where('updated_at', '<=', strtotime('-3 day', time()))
|
||||
->update([
|
||||
'commission_status' => 1
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function autoPayCommission()
|
||||
{
|
||||
$order = Order::where('commission_status', 1)
|
||||
->where('status', 3)
|
||||
|
@ -2,11 +2,14 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\OrderService;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Utils\Helper;
|
||||
use App\Models\Coupon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CheckOrder extends Command
|
||||
{
|
||||
@ -41,14 +44,14 @@ class CheckOrder extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$order = Order::get();
|
||||
foreach ($order as $item) {
|
||||
$orders = Order::get();
|
||||
foreach ($orders as $item) {
|
||||
switch ($item->status) {
|
||||
// cancel
|
||||
case 0:
|
||||
if (strtotime($item->created_at) <= (time() - 1800)) {
|
||||
$item->status = 2;
|
||||
$item->save();
|
||||
$orderService = new OrderService($item);
|
||||
$orderService->cancel();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
@ -59,33 +62,94 @@ class CheckOrder extends Command
|
||||
}
|
||||
}
|
||||
|
||||
private function orderHandle ($order) {
|
||||
private function orderHandle(Order $order)
|
||||
{
|
||||
$user = User::find($order->user_id);
|
||||
return $this->buy($order, $user);
|
||||
$plan = Plan::find($order->plan_id);
|
||||
|
||||
if ($order->refund_amount) {
|
||||
$user->balance = $user->balance + $order->refund_amount;
|
||||
}
|
||||
DB::beginTransaction();
|
||||
if ($order->surplus_order_ids) {
|
||||
try {
|
||||
Order::whereIn('id', json_decode($order->surplus_order_ids))->update([
|
||||
'status' => 4
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
abort(500, '开通失败');
|
||||
}
|
||||
}
|
||||
switch ((string)$order->cycle) {
|
||||
case 'onetime_price':
|
||||
$this->buyByOneTime($order, $user, $plan);
|
||||
break;
|
||||
case 'reset_price':
|
||||
$this->buyReset($user);
|
||||
break;
|
||||
default:
|
||||
$this->buyByCycle($order, $user, $plan);
|
||||
}
|
||||
if (!$user->save()) {
|
||||
DB::rollBack();
|
||||
abort(500, '开通失败');
|
||||
}
|
||||
$order->status = 3;
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
abort(500, '开通失败');
|
||||
}
|
||||
|
||||
private function buy ($order, $user) {
|
||||
$plan = Plan::find($order->plan_id);
|
||||
DB::commit();
|
||||
}
|
||||
|
||||
private function buyReset(User $user)
|
||||
{
|
||||
$user->u = 0;
|
||||
$user->d = 0;
|
||||
}
|
||||
|
||||
private function buyByCycle(Order $order, User $user, Plan $plan)
|
||||
{
|
||||
// change plan process
|
||||
if ((int)$order->type === 3) {
|
||||
$user->expired_at = time();
|
||||
}
|
||||
$user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$user->enable = 1;
|
||||
if ((int)config('v2board.renew_reset_traffic_enable', 1)) {
|
||||
$user->u = 0;
|
||||
$user->d = 0;
|
||||
}
|
||||
$user->plan_id = $plan->id;
|
||||
$user->group_id = $plan->group_id;
|
||||
$user->expired_at = $this->getTime($order->cycle, $user->expired_at);
|
||||
if ($user->save()) {
|
||||
$order->status = 3;
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function getTime ($str, $timestamp) {
|
||||
private function buyByOneTime(Order $order, User $user, Plan $plan)
|
||||
{
|
||||
$user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$user->u = 0;
|
||||
$user->d = 0;
|
||||
$user->plan_id = $plan->id;
|
||||
$user->group_id = $plan->group_id;
|
||||
$user->expired_at = NULL;
|
||||
}
|
||||
|
||||
private function getTime($str, $timestamp)
|
||||
{
|
||||
if ($timestamp < time()) {
|
||||
$timestamp = time();
|
||||
}
|
||||
switch ($str) {
|
||||
case 'month_price': return strtotime('+1 month', $timestamp);
|
||||
case 'quarter_price': return strtotime('+3 month', $timestamp);
|
||||
case 'half_year_price': return strtotime('+6 month', $timestamp);
|
||||
case 'year_price': return strtotime('+12 month', $timestamp);
|
||||
case 'month_price':
|
||||
return strtotime('+1 month', $timestamp);
|
||||
case 'quarter_price':
|
||||
return strtotime('+3 month', $timestamp);
|
||||
case 'half_year_price':
|
||||
return strtotime('+6 month', $timestamp);
|
||||
case 'year_price':
|
||||
return strtotime('+12 month', $timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\User;
|
||||
|
||||
class ResetTraffic extends Command
|
||||
{
|
||||
@ -38,7 +38,47 @@ class ResetTraffic extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
DB::table('v2_user')->update([
|
||||
$user = User::where('expired_at', '!=', NULL)
|
||||
->where('expired_at', '>', time());
|
||||
$resetTrafficMethod = config('v2board.reset_traffic_method', 0);
|
||||
switch ((int)$resetTrafficMethod) {
|
||||
// 1 a month
|
||||
case 0:
|
||||
$this->resetByMonthFirstDay($user);
|
||||
break;
|
||||
// expire day
|
||||
case 1:
|
||||
$this->resetByExpireDay($user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function resetByMonthFirstDay($user):void
|
||||
{
|
||||
if ((string)date('d') === '01') {
|
||||
$user->update([
|
||||
'u' => 0,
|
||||
'd' => 0
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetByExpireDay($user):void
|
||||
{
|
||||
$lastDay = date('d', strtotime('last day of +0 months'));
|
||||
$users = [];
|
||||
foreach ($user->get() as $item) {
|
||||
$expireDay = date('d', $item->expired_at);
|
||||
$today = date('d');
|
||||
if ($expireDay === $today) {
|
||||
array_push($users, $item->id);
|
||||
}
|
||||
|
||||
if (($today === $lastDay) && $expireDay >= $lastDay) {
|
||||
array_push($users, $item->id);
|
||||
}
|
||||
}
|
||||
User::whereIn('id', $users)->update([
|
||||
'u' => 0,
|
||||
'd' => 0
|
||||
]);
|
||||
|
91
app/Console/Commands/SendRemindMail.php
Normal file
91
app/Console/Commands/SendRemindMail.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\MailLog;
|
||||
use App\Jobs\SendEmailJob;
|
||||
|
||||
class SendRemindMail extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'send:remindMail';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '发送提醒邮件';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$users = User::all();
|
||||
foreach ($users as $user) {
|
||||
if ($user->remind_expire) $this->remindExpire($user);
|
||||
if ($user->remind_traffic) $this->remindTraffic($user);
|
||||
}
|
||||
}
|
||||
|
||||
private function remindExpire($user)
|
||||
{
|
||||
if ($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time()) {
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => '在' . config('v2board.app_name', 'V2board') . '的服务即将到期',
|
||||
'template_name' => 'remindExpire',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function remindTraffic($user)
|
||||
{
|
||||
if ($this->remindTrafficIsWarnValue(($user->u + $user->d), $user->transfer_enable)) {
|
||||
$sendCount = MailLog::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('template_name', 'like', '%remindTraffic%')
|
||||
->count();
|
||||
if ($sendCount > 0) return;
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => '在' . config('v2board.app_name', 'V2board') . '的流量使用已达到80%',
|
||||
'template_name' => 'remindTraffic',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function remindTrafficIsWarnValue($ud, $transfer_enable)
|
||||
{
|
||||
if ($ud <= 0) return false;
|
||||
if (($ud / $transfer_enable * 100) < 80) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\Order;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
|
||||
class SystemCache extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'system:cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '系统缓存任务';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->setMonthIncome();
|
||||
$this->setMonthRegisterTotal();
|
||||
}
|
||||
|
||||
private function setMonthIncome() {
|
||||
Redis::set(
|
||||
'month_income',
|
||||
Order::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->where('status', '3')
|
||||
->sum('total_amount')
|
||||
);
|
||||
}
|
||||
|
||||
private function setMonthRegisterTotal() {
|
||||
Redis::set(
|
||||
'month_register_total',
|
||||
User::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->count()
|
||||
);
|
||||
}
|
||||
}
|
@ -3,24 +3,22 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
|
||||
class CheckExpire extends Command
|
||||
class Test extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'check:expire';
|
||||
protected $signature = 'test';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '过期检查';
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@ -39,15 +37,5 @@ class CheckExpire extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$user = User::all();
|
||||
foreach ($user as $item) {
|
||||
if ($item->expired_at < time() || $item->u + $item->d >= $item->transfer_enable) {
|
||||
$item->enable = 0;
|
||||
} else {
|
||||
$item->enable = 1;
|
||||
}
|
||||
$item->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,23 +4,27 @@ namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\Order;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ImportReset extends Command
|
||||
class V2boardCache extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'import:reset';
|
||||
protected $signature = 'v2board:cache';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '为导入用户重置所有uuid及token';
|
||||
protected $description = '缓存任务';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@ -39,11 +43,5 @@ class ImportReset extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$user = User::all();
|
||||
foreach ($user as $item) {
|
||||
$item->v2ray_uuid = Helper::guid(true);
|
||||
$item->token = Helper::guid();
|
||||
$item->save();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use App\Models\User;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@ -40,14 +41,35 @@ class V2boardInstall extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$this->info("__ ______ ____ _ ");
|
||||
$this->info("\ \ / /___ \| __ ) ___ __ _ _ __ __| | ");
|
||||
$this->info(" \ \ / / __) | _ \ / _ \ / _` | '__/ _` | ");
|
||||
$this->info(" \ V / / __/| |_) | (_) | (_| | | | (_| | ");
|
||||
$this->info(" \_/ |_____|____/ \___/ \__,_|_| \__,_| ");
|
||||
if (\File::exists(base_path() . '/.lock')) {
|
||||
abort(500, 'V2board 已安装,如需重新安装请删除目录下.lock文件');
|
||||
}
|
||||
\Artisan::call('key:generate');
|
||||
sleep(2);
|
||||
if (!\File::exists(base_path() . '/.env')) {
|
||||
if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
|
||||
abort(500, '复制环境文件失败,请检查目录权限');
|
||||
}
|
||||
}
|
||||
$this->saveToEnv([
|
||||
'APP_KEY' => 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')),
|
||||
'DB_HOST' => $this->ask('请输入数据库地址(默认:localhost)', 'localhost'),
|
||||
'DB_DATABASE' => $this->ask('请输入数据库名'),
|
||||
'DB_USERNAME' => $this->ask('请输入数据库用户名'),
|
||||
'DB_PASSWORD' => $this->ask('请输入数据库密码')
|
||||
]);
|
||||
\Artisan::call('config:clear');
|
||||
\Artisan::call('config:cache');
|
||||
try {
|
||||
DB::connection()->getPdo();
|
||||
$file = \File::get(base_path() . '/install.sql');
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '数据库连接失败');
|
||||
}
|
||||
$file = \File::get(base_path() . '/database/install.sql');
|
||||
if (!$file) {
|
||||
abort(500, '数据库文件不存在');
|
||||
}
|
||||
@ -57,11 +79,13 @@ class V2boardInstall extends Command
|
||||
abort(500, '数据库文件格式有误');
|
||||
}
|
||||
$this->info('正在导入数据库请稍等...');
|
||||
foreach($sql as $item) {
|
||||
foreach ($sql as $item) {
|
||||
try {
|
||||
DB::select(DB::raw($item));
|
||||
} catch (\Exception $e) {}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
$this->info('数据库导入完成');
|
||||
$email = '';
|
||||
while (!$email) {
|
||||
$email = $this->ask('请输入管理员邮箱?');
|
||||
@ -75,16 +99,56 @@ class V2boardInstall extends Command
|
||||
}
|
||||
|
||||
$this->info('一切就绪');
|
||||
$this->info('访问 http(s)://你的站点/admin 进入管理面板');
|
||||
\File::put(base_path() . '/.lock', time());
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function registerAdmin ($email, $password) {
|
||||
private function registerAdmin($email, $password)
|
||||
{
|
||||
$user = new User();
|
||||
$user->email = $email;
|
||||
if (strlen($password) < 8) {
|
||||
abort(500, '管理员密码长度最小为8位字符');
|
||||
}
|
||||
$user->password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$user->v2ray_uuid = Helper::guid(true);
|
||||
$user->token = Helper::guid();
|
||||
$user->is_admin = 1;
|
||||
return $user->save();
|
||||
}
|
||||
|
||||
private function saveToEnv($data = [])
|
||||
{
|
||||
function set_env_var($key, $value)
|
||||
{
|
||||
if (! is_bool(strpos($value, ' '))) {
|
||||
$value = '"' . $value . '"';
|
||||
}
|
||||
$key = strtoupper($key);
|
||||
|
||||
$envPath = app()->environmentFilePath();
|
||||
$contents = file_get_contents($envPath);
|
||||
|
||||
preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches);
|
||||
|
||||
$oldValue = count($matches) ? $matches[0] : '';
|
||||
|
||||
if ($oldValue) {
|
||||
$contents = str_replace("{$oldValue}", "{$key}={$value}", $contents);
|
||||
} else {
|
||||
$contents = $contents . "\n{$key}={$value}\n";
|
||||
}
|
||||
|
||||
$file = fopen($envPath, 'w');
|
||||
fwrite($file, $contents);
|
||||
return fclose($file);
|
||||
}
|
||||
foreach($data as $key => $value) {
|
||||
set_env_var($key, $value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class V2boardUpdate extends Command
|
||||
{
|
||||
\Artisan::call('config:cache');
|
||||
DB::connection()->getPdo();
|
||||
$file = \File::get(base_path() . '/update.sql');
|
||||
$file = \File::get(base_path() . '/database/update.sql');
|
||||
if (!$file) {
|
||||
abort(500, '数据库文件不存在');
|
||||
}
|
||||
@ -50,10 +50,11 @@ class V2boardUpdate extends Command
|
||||
abort(500, '数据库文件格式有误');
|
||||
}
|
||||
$this->info('正在导入数据库请稍等...');
|
||||
foreach($sql as $item) {
|
||||
foreach ($sql as $item) {
|
||||
try {
|
||||
DB::select(DB::raw($item));
|
||||
} catch (\Exception $e) {}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
$this->info('更新完毕');
|
||||
}
|
||||
|
@ -24,17 +24,16 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// check order
|
||||
// v2board
|
||||
$schedule->command('v2board:cache')->hourly();
|
||||
// check
|
||||
$schedule->command('check:order')->everyMinute();
|
||||
// check expire
|
||||
$schedule->command('check:expire')->everyMinute();
|
||||
// check commission
|
||||
$schedule->command('check:commission')->everyMinute();
|
||||
// system cache
|
||||
$schedule->command('system:cache')->hourly();
|
||||
// reset
|
||||
$schedule->command('reset:traffic')->monthlyOn(1, '00:00');
|
||||
$schedule->command('reset:serverLog')->monthlyOn(1, '00:00');
|
||||
$schedule->command('reset:traffic')->daily();
|
||||
$schedule->command('reset:serverLog')->monthly();
|
||||
// send
|
||||
$schedule->command('send:remindMail')->dailyAt('11:30');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,7 +43,7 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
$this->load(__DIR__ . '/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
|
@ -46,6 +46,9 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function render($request, Exception $exception)
|
||||
{
|
||||
if($exception instanceof \Illuminate\Http\Exceptions\ThrottleRequestsException) {
|
||||
abort(429, '请求频繁,请稍后再试');
|
||||
}
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
|
@ -3,35 +3,69 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\ConfigSave;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Utils\Dict;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
|
||||
class ConfigController extends Controller
|
||||
{
|
||||
public function init () {
|
||||
|
||||
public function getEmailTemplate()
|
||||
{
|
||||
$path = resource_path('views/mail/');
|
||||
$files = array_map(function ($item) use ($path) {
|
||||
return str_replace($path, '', $item);
|
||||
}, glob($path . '*'));
|
||||
return response([
|
||||
'data' => $files
|
||||
]);
|
||||
}
|
||||
|
||||
public function fetch () {
|
||||
public function setTelegramWebhook(Request $request)
|
||||
{
|
||||
$telegramService = new TelegramService($request->input('telegram_bot_token'));
|
||||
$telegramService->getMe();
|
||||
$telegramService->setWebhook(
|
||||
url(
|
||||
'/api/v1/guest/telegram/webhook?access_token=' . md5(config('v2board.telegram_bot_token', $request->input('telegram_bot_token')))
|
||||
)
|
||||
);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function fetch()
|
||||
{
|
||||
// TODO: default should be in Dict
|
||||
return response([
|
||||
'data' => [
|
||||
'invite' => [
|
||||
'invite_force' => (int)config('v2board.invite_force', 0),
|
||||
'invite_commission' => config('v2board.invite_commission', 10),
|
||||
'invite_gen_limit' => config('v2board.invite_gen_limit', 5),
|
||||
'invite_never_expire' => config('v2board.invite_never_expire', 0)
|
||||
'invite_never_expire' => config('v2board.invite_never_expire', 0),
|
||||
'commission_first_time_enable' => config('v2board.commission_first_time_enable', 1),
|
||||
'commission_auto_check_enable' => config('v2board.commission_auto_check_enable', 1)
|
||||
],
|
||||
'site' => [
|
||||
'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),
|
||||
'stop_register' => (int)config('v2board.stop_register', 0),
|
||||
'email_verify' => (int)config('v2board.email_verify', 0),
|
||||
'app_name' => config('v2board.app_name', 'V2Board'),
|
||||
'app_description' => config('v2board.app_description', 'V2Board is best!'),
|
||||
'app_url' => config('v2board.app_url'),
|
||||
'subscribe_url' => config('v2board.subscribe_url'),
|
||||
'plan_update_fee' => config('v2board.plan_update_fee', 0.5),
|
||||
'plan_is_update' => (int)config('v2board.plan_is_update', 1)
|
||||
'try_out_plan_id' => (int)config('v2board.try_out_plan_id', 0),
|
||||
'try_out_hour' => (int)config('v2board.try_out_hour', 1),
|
||||
'email_whitelist_enable' => (int)config('v2board.email_whitelist_enable', 0),
|
||||
'email_whitelist_suffix' => config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT),
|
||||
'email_gmail_limit_enable' => config('v2board.email_gmail_limit_enable', 0)
|
||||
],
|
||||
'subscribe' => [
|
||||
'plan_change_enable' => (int)config('v2board.plan_change_enable', 1),
|
||||
'reset_traffic_method' => (int)config('v2board.reset_traffic_method', 0),
|
||||
'renew_reset_traffic_enable' => (int)config('v2board.renew_reset_traffic_enable', 1)
|
||||
],
|
||||
'pay' => [
|
||||
// alipay
|
||||
@ -40,39 +74,65 @@ class ConfigController extends Controller
|
||||
'alipay_pubkey' => config('v2board.alipay_pubkey'),
|
||||
'alipay_privkey' => config('v2board.alipay_privkey'),
|
||||
// stripe
|
||||
'stripe_alipay_enable' => (int)config('v2board.stripe_alipay_enable', 0),
|
||||
'stripe_wepay_enable' => (int)config('v2board.stripe_wepay_enable', 0),
|
||||
'stripe_sk_live' => config('v2board.stripe_sk_live'),
|
||||
'stripe_pk_live' => config('v2board.stripe_pk_live'),
|
||||
'stripe_alipay_enable' => (int)config('v2board.stripe_alipay_enable'),
|
||||
'stripe_wepay_enable' => (int)config('v2board.stripe_wepay_enable'),
|
||||
'stripe_webhook_key' => config('v2board.stripe_webhook_key'),
|
||||
'stripe_currency' => config('v2board.stripe_currency', 'hkd'),
|
||||
// bitpayx
|
||||
'bitpayx_enable' => config('v2board.bitpayx_enable'),
|
||||
'bitpayx_appsecret' => config('v2board.bitpayx_appsecret')
|
||||
'bitpayx_name' => config('v2board.bitpayx_name', '聚合支付'),
|
||||
'bitpayx_enable' => (int)config('v2board.bitpayx_enable', 0),
|
||||
'bitpayx_appsecret' => config('v2board.bitpayx_appsecret'),
|
||||
// paytaro
|
||||
'paytaro_name' => config('v2board.paytaro_name', '聚合支付'),
|
||||
'paytaro_enable' => (int)config('v2board.paytaro_enable', 0),
|
||||
'paytaro_app_id' => config('v2board.paytaro_app_id'),
|
||||
'paytaro_app_secret' => config('v2board.paytaro_app_secret')
|
||||
],
|
||||
'frontend' => [
|
||||
'frontend_theme_sidebar' => config('v2board.frontend_theme_sidebar', 'light'),
|
||||
'frontend_theme_header' => config('v2board.frontend_theme_header', 'dark'),
|
||||
'frontend_theme_color' => config('v2board.frontend_theme_color', 'default'),
|
||||
'frontend_background_url' => config('v2board.frontend_background_url')
|
||||
],
|
||||
'server' => [
|
||||
'server_token' => config('v2board.server_token')
|
||||
'server_token' => config('v2board.server_token'),
|
||||
'server_license' => config('v2board.server_license'),
|
||||
'server_log_level' => config('v2board.server_log_level', 'none')
|
||||
],
|
||||
'tutorial' => [
|
||||
'apple_id' => config('v2board.apple_id')
|
||||
],
|
||||
'email' => [
|
||||
'email_template' => config('v2board.email_template', 'default')
|
||||
],
|
||||
'telegram' => [
|
||||
'telegram_bot_enable' => config('v2board.telegram_bot_enable', 0),
|
||||
'telegram_bot_token' => config('v2board.telegram_bot_token')
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (ConfigSave $request) {
|
||||
public function save(ConfigSave $request)
|
||||
{
|
||||
$data = $request->input();
|
||||
$array = \Config::get('v2board');
|
||||
foreach ($data as $k => $v) {
|
||||
if (!in_array($k, ConfigSave::filter())) {
|
||||
abort(500, '禁止修改');
|
||||
if (!in_array($k, array_keys(ConfigSave::RULES))) {
|
||||
abort(500, '参数' . $k . '不在规则内,禁止修改');
|
||||
}
|
||||
$array[$k] = $v;
|
||||
}
|
||||
$data = var_export($array, 1);
|
||||
if(!\File::put(base_path() . '/config/v2board.php', "<?php\n return $data ;")) {
|
||||
if (!\File::put(base_path() . '/config/v2board.php', "<?php\n return $data ;")) {
|
||||
abort(500, '修改失败');
|
||||
}
|
||||
\Artisan::call('config:cache');
|
||||
if (function_exists('opcache')) {
|
||||
opcache_reset();
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
|
66
app/Http/Controllers/Admin/CouponController.php
Normal file
66
app/Http/Controllers/Admin/CouponController.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\CouponSave;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Coupon;
|
||||
use App\Utils\Helper;
|
||||
|
||||
class CouponController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
return response([
|
||||
'data' => Coupon::all()
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(CouponSave $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'name',
|
||||
'type',
|
||||
'value',
|
||||
'started_at',
|
||||
'ended_at',
|
||||
'limit_use'
|
||||
]);
|
||||
|
||||
if (!$request->input('id')) {
|
||||
$params['code'] = Helper::randomChar(8);
|
||||
if (!Coupon::create($params)) {
|
||||
abort(500, '创建失败');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Coupon::find($request->input('id'))->update($params);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数有误');
|
||||
}
|
||||
$coupon = Coupon::find($request->input('id'));
|
||||
if (!$coupon) {
|
||||
abort(500, '优惠券不存在');
|
||||
}
|
||||
if (!$coupon->delete()) {
|
||||
abort(500, '删除失败');
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
47
app/Http/Controllers/Admin/MailController.php
Normal file
47
app/Http/Controllers/Admin/MailController.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\MailSend;
|
||||
use App\Services\UserService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\SendEmailJob;
|
||||
|
||||
class MailController extends Controller
|
||||
{
|
||||
public function send(MailSend $request)
|
||||
{
|
||||
$userService = new UserService();
|
||||
$users = [];
|
||||
switch ($request->input('type')) {
|
||||
case 1: $users = $userService->getAllUsers();
|
||||
break;
|
||||
case 2: $users = $userService->getUsersByIds($request->input('receiver'));
|
||||
break;
|
||||
// available users
|
||||
case 3: $users = $userService->getAvailableUsers();
|
||||
break;
|
||||
// un available users
|
||||
case 4: $users = $userService->getUnAvailbaleUsers();
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($users as $user) {
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => $request->input('subject'),
|
||||
'template_name' => 'notify',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'url' => config('v2board.app_url'),
|
||||
'content' => $request->input('content')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
@ -6,45 +6,42 @@ use App\Http\Requests\Admin\NoticeSave;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Notice;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class NoticeController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
return response([
|
||||
'data' => Notice::orderBy('id', 'DESC')->get()
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (NoticeSave $request) {
|
||||
public function save(NoticeSave $request)
|
||||
{
|
||||
$data = $request->only([
|
||||
'title',
|
||||
'content',
|
||||
'img_url'
|
||||
]);
|
||||
if (!$request->input('id')) {
|
||||
if (!Notice::create($data)) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (NoticeSave $request) {
|
||||
$data = $request->only([
|
||||
'title',
|
||||
'content',
|
||||
'img_url'
|
||||
]);
|
||||
if (!Notice::where('id', $request->input('id'))->update($data)) {
|
||||
} else {
|
||||
try {
|
||||
Notice::find($request->input('id'))->update($data);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop (Request $request) {
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
|
@ -2,16 +2,21 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\OrderAssign;
|
||||
use App\Http\Requests\Admin\OrderUpdate;
|
||||
use App\Services\OrderService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$orderModel = Order::orderBy('created_at', 'DESC');
|
||||
@ -21,10 +26,14 @@ class OrderController extends Controller
|
||||
if ($request->input('is_commission')) {
|
||||
$orderModel->where('invite_user_id', '!=', NULL);
|
||||
$orderModel->where('status', 3);
|
||||
$orderModel->where('commission_balance', '>', 0);
|
||||
}
|
||||
if ($request->input('id')) {
|
||||
$orderModel->where('id', $request->input('id'));
|
||||
}
|
||||
if ($request->input('user_id')) {
|
||||
$orderModel->where('user_id', $request->input('user_id'));
|
||||
}
|
||||
$total = $orderModel->count();
|
||||
$res = $orderModel->forPage($current, $pageSize)
|
||||
->get();
|
||||
@ -42,8 +51,9 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (OrderUpdate $request) {
|
||||
$updateData = $request->only([
|
||||
public function update(OrderUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'status',
|
||||
'commission_status'
|
||||
]);
|
||||
@ -54,7 +64,19 @@ class OrderController extends Controller
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
|
||||
if (!$order->update($updateData)) {
|
||||
if (isset($params['status']) && (int)$params['status'] === 2) {
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->cancel()) {
|
||||
abort(500, '更新失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$order->update($params);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '更新失败');
|
||||
}
|
||||
|
||||
@ -63,7 +85,8 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function repair (Request $request) {
|
||||
public function repair(Request $request)
|
||||
{
|
||||
if (empty($request->input('trade_no'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -81,4 +104,50 @@ class OrderController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function assign(OrderAssign $request)
|
||||
{
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
|
||||
if (!$user) {
|
||||
abort(500, '该用户不存在');
|
||||
}
|
||||
|
||||
if (!$plan) {
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
$order = new Order();
|
||||
$orderService = new OrderService($order);
|
||||
$order->user_id = $user->id;
|
||||
$order->plan_id = $plan->id;
|
||||
$order->cycle = $request->input('cycle');
|
||||
$order->trade_no = Helper::guid();
|
||||
$order->total_amount = $request->input('total_amount');
|
||||
|
||||
if ($order->cycle === 'reset_price') {
|
||||
$order->type = 4;
|
||||
} else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
|
||||
$order->type = 3;
|
||||
} else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) {
|
||||
$order->type = 2;
|
||||
} else {
|
||||
$order->type = 1;
|
||||
}
|
||||
|
||||
$orderService->setInvite($user);
|
||||
|
||||
if (!$order->save()) {
|
||||
DB::rollback();
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response([
|
||||
'data' => $order->trade_no
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,48 +3,59 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\PlanSave;
|
||||
use App\Http\Requests\Admin\PlanSort;
|
||||
use App\Http\Requests\Admin\PlanUpdate;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class PlanController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
return response([
|
||||
'data' => Plan::get()
|
||||
'data' => Plan::orderBy('sort', 'ASC')->get()
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (PlanSave $request) {
|
||||
public function save(PlanSave $request)
|
||||
{
|
||||
$params = $request->only(array_keys(PlanSave::RULES));
|
||||
if ($request->input('id')) {
|
||||
$plan = Plan::find($request->input('id'));
|
||||
if (!$plan) {
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
} else {
|
||||
$plan = new Plan();
|
||||
DB::beginTransaction();
|
||||
// update user group id and transfer
|
||||
try {
|
||||
User::where('plan_id', $plan->id)->update([
|
||||
'group_id' => $plan->group_id,
|
||||
'transfer_enable' => $plan->transfer_enable * 1073741824
|
||||
]);
|
||||
$plan->update($params);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
$plan->name = $request->input('name');
|
||||
$plan->content = $request->input('content');
|
||||
if ($plan->content) {
|
||||
$plan->content = str_replace(PHP_EOL, '', $plan->content);
|
||||
}
|
||||
$plan->transfer_enable = $request->input('transfer_enable');
|
||||
$plan->group_id = $request->input('group_id');
|
||||
$plan->month_price = $request->input('month_price');
|
||||
$plan->quarter_price = $request->input('quarter_price');
|
||||
$plan->half_year_price = $request->input('half_year_price');
|
||||
$plan->year_price = $request->input('year_price');
|
||||
|
||||
DB::commit();
|
||||
return response([
|
||||
'data' => $plan->save()
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
if (!Plan::create($params)) {
|
||||
abort(500, '创建失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop (Request $request) {
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (Order::where('plan_id', $request->input('id'))->first()) {
|
||||
abort(500, '该订阅下存在订单无法删除');
|
||||
}
|
||||
@ -62,7 +73,8 @@ class PlanController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (PlanUpdate $request) {
|
||||
public function update(PlanUpdate $request)
|
||||
{
|
||||
$updateData = $request->only([
|
||||
'show',
|
||||
'renew'
|
||||
@ -72,7 +84,10 @@ class PlanController extends Controller
|
||||
if (!$plan) {
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
if (!$plan->update($updateData)) {
|
||||
|
||||
try {
|
||||
$plan->update($updateData);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
|
||||
@ -80,4 +95,19 @@ class PlanController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function sort(PlanSort $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
foreach ($request->input('plan_ids') as $k => $v) {
|
||||
if (!Plan::find($v)->update(['sort' => $k + 1])) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,61 +3,97 @@
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\ServerSave;
|
||||
use App\Http\Requests\Admin\ServerSort;
|
||||
use App\Http\Requests\Admin\ServerUpdate;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\Server;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
$server = Server::get();
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$server = Server::orderBy('sort', 'ASC')->get();
|
||||
for ($i = 0; $i < count($server); $i++) {
|
||||
if (!empty($server[$i]['tags'])) {
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
$server[$i]['last_check_at'] = Redis::get('server_last_check_at_' . $server[$i]['id']);
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['parent_id']);
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['id']);
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $server
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (ServerSave $request) {
|
||||
public function save(ServerSave $request)
|
||||
{
|
||||
$params = $request->only(array_keys(ServerSave::RULES));
|
||||
$params['group_id'] = json_encode($params['group_id']);
|
||||
if (isset($params['tags'])) {
|
||||
$params['tags'] = json_encode($params['tags']);
|
||||
}
|
||||
|
||||
if (isset($params['dnsSettings'])) {
|
||||
if (!is_object(json_decode($params['dnsSettings']))) {
|
||||
abort(500, 'DNS规则配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['ruleSettings'])) {
|
||||
if (!is_object(json_decode($params['ruleSettings']))) {
|
||||
abort(500, '审计规则配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['networkSettings'])) {
|
||||
if (!is_object(json_decode($params['networkSettings']))) {
|
||||
abort(500, '传输协议配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['tlsSettings'])) {
|
||||
if (!is_object(json_decode($params['tlsSettings']))) {
|
||||
abort(500, 'TLS配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->input('id')) {
|
||||
$server = Server::find($request->input('id'));
|
||||
if (!$server) {
|
||||
abort(500, '服务器不存在');
|
||||
}
|
||||
} else {
|
||||
$server = new Server();
|
||||
}
|
||||
$server->group_id = json_encode($request->input('group_id'));
|
||||
$server->name = $request->input('name');
|
||||
$server->host = $request->input('host');
|
||||
$server->port = $request->input('port');
|
||||
$server->server_port = $request->input('server_port');
|
||||
$server->tls = $request->input('tls');
|
||||
$server->tags = $request->input('tags') ? json_encode($request->input('tags')) : NULL;
|
||||
$server->rate = $request->input('rate');
|
||||
$server->network = $request->input('network');
|
||||
if ($request->input('settings')) {
|
||||
if (!is_object(json_decode($request->input('settings')))) {
|
||||
abort(500, '传输协议配置格式不正确');
|
||||
}
|
||||
$server->settings = $request->input('settings');
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
return response([
|
||||
'data' => $server->save()
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function groupFetch (Request $request) {
|
||||
if (!Server::create($params)) {
|
||||
abort(500, '创建失败');
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function groupFetch(Request $request)
|
||||
{
|
||||
if ($request->input('group_id')) {
|
||||
return response([
|
||||
'data' => [ServerGroup::find($request->input('group_id'))]
|
||||
@ -68,7 +104,8 @@ class ServerController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function groupSave (Request $request) {
|
||||
public function groupSave(Request $request)
|
||||
{
|
||||
if (empty($request->input('name'))) {
|
||||
abort(500, '组名不能为空');
|
||||
}
|
||||
@ -85,7 +122,8 @@ class ServerController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function groupDrop (Request $request) {
|
||||
public function groupDrop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$serverGroup = ServerGroup::find($request->input('id'));
|
||||
if (!$serverGroup) {
|
||||
@ -112,7 +150,8 @@ class ServerController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop (Request $request) {
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$server = Server::find($request->input('id'));
|
||||
if (!$server) {
|
||||
@ -124,16 +163,20 @@ class ServerController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (ServerUpdate $request) {
|
||||
$updateData = $request->only([
|
||||
public function update(ServerUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'show',
|
||||
]);
|
||||
|
||||
$server = Server::find($request->input('id'));
|
||||
|
||||
if (!$server) {
|
||||
abort(500, '该服务器不存在');
|
||||
}
|
||||
if (!$server->update($updateData)) {
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
|
||||
@ -141,4 +184,43 @@ class ServerController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = Server::find($request->input('id'));
|
||||
if (!$server) {
|
||||
abort(500, '服务器不存在');
|
||||
}
|
||||
if (!Server::create($server->toArray())) {
|
||||
abort(500, '复制失败');
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function viewConfig(Request $request)
|
||||
{
|
||||
$serverService = new ServerService();
|
||||
$config = $serverService->getConfig($request->input('node_id'), 23333);
|
||||
return response([
|
||||
'data' => $config
|
||||
]);
|
||||
}
|
||||
|
||||
public function sort(ServerSort $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
foreach ($request->input('server_ids') as $k => $v) {
|
||||
if (!Server::find($v)->update(['sort' => $k + 1])) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -10,22 +10,36 @@ use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class StatController extends Controller
|
||||
{
|
||||
public function getOverride (Request $request) {
|
||||
public function getOverride(Request $request)
|
||||
{
|
||||
return response([
|
||||
'data' => [
|
||||
'month_income' => Redis::get('month_income'),
|
||||
'month_register_total' => Redis::get('month_register_total'),
|
||||
'month_income' => Order::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->whereIn('status', [3, 4])
|
||||
->sum('total_amount'),
|
||||
'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->count(),
|
||||
'ticket_pendding_total' => Ticket::where('status', 0)
|
||||
->count(),
|
||||
'commission_pendding_total' => Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->where('status', 3)
|
||||
->where('commission_balance', '>', 0)
|
||||
->count(),
|
||||
|
||||
'day_income' => Order::where('created_at', '>=', strtotime(date('Y-m-d')))
|
||||
->where('created_at', '<', time())
|
||||
->where('status', 3)
|
||||
->sum('total_amount'),
|
||||
'last_month_income' => Order::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
|
||||
->where('created_at', '<', strtotime(date('Y-m-1')))
|
||||
->where('status', 3)
|
||||
->sum('total_amount')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
@ -2,15 +2,19 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Jobs\SendEmailJob;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use App\Models\TicketMessage;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->first();
|
||||
@ -29,21 +33,30 @@ class TicketController extends Controller
|
||||
'data' => $ticket
|
||||
]);
|
||||
}
|
||||
$ticket = Ticket::orderBy('created_at', 'DESC')
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$model = Ticket::orderBy('created_at', 'DESC');
|
||||
if ($request->input('status') !== NULL) {
|
||||
$model->where('status', $request->input('status'));
|
||||
}
|
||||
$total = $model->count();
|
||||
$res = $model->forPage($current, $pageSize)
|
||||
->get();
|
||||
for ($i = 0; $i < count($ticket); $i++) {
|
||||
if ($ticket[$i]['last_reply_user_id'] == $request->session()->get('id')) {
|
||||
$ticket[$i]['reply_status'] = 0;
|
||||
for ($i = 0; $i < count($res); $i++) {
|
||||
if ($res[$i]['last_reply_user_id'] == $request->session()->get('id')) {
|
||||
$res[$i]['reply_status'] = 0;
|
||||
} else {
|
||||
$ticket[$i]['reply_status'] = 1;
|
||||
$res[$i]['reply_status'] = 1;
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $ticket
|
||||
'data' => $res,
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
|
||||
public function reply (Request $request) {
|
||||
public function reply(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -70,12 +83,14 @@ class TicketController extends Controller
|
||||
abort(500, '工单回复失败');
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendEmailNotify($ticket, $ticketMessage);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function close (Request $request) {
|
||||
public function close(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -92,4 +107,24 @@ class TicketController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
// 半小时内不再重复通知
|
||||
private function sendEmailNotify(Ticket $ticket, TicketMessage $ticketMessage)
|
||||
{
|
||||
$user = User::find($ticket->user_id);
|
||||
$cacheKey = 'ticket_sendEmailNotify_' . $ticket->user_id;
|
||||
if (!Cache::get($cacheKey)) {
|
||||
Cache::put($cacheKey, 1, 1800);
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => '您在' . config('v2board.app_name', 'V2Board') . '的工单得到了回复',
|
||||
'template_name' => 'notify',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'url' => config('v2board.app_url'),
|
||||
'content' => "主题:{$ticket->subject}\r\n回复内容:{$ticketMessage->message}"
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
93
app/Http/Controllers/Admin/TutorialController.php
Normal file
93
app/Http/Controllers/Admin/TutorialController.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\TutorialSave;
|
||||
use App\Http\Requests\Admin\TutorialSort;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Tutorial;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TutorialController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
return response([
|
||||
'data' => Tutorial::orderBy('sort', 'ASC')->get()
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(TutorialSave $request)
|
||||
{
|
||||
$params = $request->only(array_keys(TutorialSave::RULES));
|
||||
|
||||
if (!$request->input('id')) {
|
||||
if (!Tutorial::create($params)) {
|
||||
abort(500, '创建失败');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Tutorial::find($request->input('id'))->update($params);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数有误');
|
||||
}
|
||||
$tutorial = Tutorial::find($request->input('id'));
|
||||
if (!$tutorial) {
|
||||
abort(500, '教程不存在');
|
||||
}
|
||||
$tutorial->show = $tutorial->show ? 0 : 1;
|
||||
if (!$tutorial->save()) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function sort(TutorialSort $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
foreach ($request->input('tutorial_ids') as $k => $v) {
|
||||
if (!Tutorial::find($v)->update(['sort' => $k + 1])) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数有误');
|
||||
}
|
||||
$tutorial = Tutorial::find($request->input('id'));
|
||||
if (!$tutorial) {
|
||||
abort(500, '教程不存在');
|
||||
}
|
||||
if (!$tutorial->delete()) {
|
||||
abort(500, '删除失败');
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
@ -11,13 +11,19 @@ use App\Models\Plan;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$userModel = User::orderBy('created_at', 'DESC');
|
||||
$sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
|
||||
$sort = $request->input('sort') ? $request->input('sort') : 'created_at';
|
||||
$userModel = User::orderBy($sort, $sortType);
|
||||
if ($request->input('email')) {
|
||||
$userModel->where('email', $request->input('email'));
|
||||
}
|
||||
if ($request->input('invite_user_id')) {
|
||||
$userModel->where('invite_user_id', $request->input('invite_user_id'));
|
||||
}
|
||||
$total = $userModel->count();
|
||||
$res = $userModel->forPage($current, $pageSize)
|
||||
->get();
|
||||
@ -35,53 +41,43 @@ class UserController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function id2UserInfo ($id) {
|
||||
if (empty($id)) {
|
||||
public function getUserInfoById(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
return response([
|
||||
'data' => User::select([
|
||||
'email',
|
||||
'u',
|
||||
'd',
|
||||
'transfer_enable',
|
||||
'expired_at'
|
||||
])->find($id)
|
||||
'data' => User::find($request->input('id'))
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (UserUpdate $request) {
|
||||
$updateData = $request->only([
|
||||
'email',
|
||||
'password',
|
||||
'transfer_enable',
|
||||
'expired_at',
|
||||
'banned',
|
||||
'plan_id',
|
||||
'commission_rate',
|
||||
'is_admin'
|
||||
]);
|
||||
public function update(UserUpdate $request)
|
||||
{
|
||||
$params = $request->only(array_keys(UserUpdate::RULES));
|
||||
$user = User::find($request->input('id'));
|
||||
if (!$user) {
|
||||
abort(500, '用户不存在');
|
||||
}
|
||||
if (User::where('email', $updateData['email'])->first() && $user->email !== $updateData['email']) {
|
||||
if (User::where('email', $params['email'])->first() && $user->email !== $params['email']) {
|
||||
abort(500, '邮箱已被使用');
|
||||
}
|
||||
if (isset($updateData['password'])) {
|
||||
$updateData['password'] = password_hash($updateData['password'], PASSWORD_DEFAULT);
|
||||
if (isset($params['password'])) {
|
||||
$params['password'] = password_hash($params['password'], PASSWORD_DEFAULT);
|
||||
$params['password_algo'] = NULL;
|
||||
} else {
|
||||
unset($updateData['password']);
|
||||
unset($params['password']);
|
||||
}
|
||||
$updateData['transfer_enable'] = $updateData['transfer_enable'] * 1073741824;
|
||||
if (isset($updateData['plan_id'])) {
|
||||
$plan = Plan::find($updateData['plan_id']);
|
||||
if (isset($params['plan_id'])) {
|
||||
$plan = Plan::find($params['plan_id']);
|
||||
if (!$plan) {
|
||||
abort(500, '订阅计划不存在');
|
||||
}
|
||||
$updateData['group_id'] = $plan->group_id;
|
||||
$params['group_id'] = $plan->group_id;
|
||||
}
|
||||
if (!$user->update($updateData)) {
|
||||
|
||||
try {
|
||||
$user->update($params);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
return response([
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
@ -12,11 +12,13 @@ use App\Utils\Helper;
|
||||
|
||||
class AppController extends Controller
|
||||
{
|
||||
CONST CLIENT_CONFIG = '{"policy":{"levels":{"0":{"uplinkOnly":0}}},"dns":{"servers":["114.114.114.114","8.8.8.8"]},"outboundDetour":[{"protocol":"freedom","tag":"direct","settings":{}}],"inbound":{"listen":"0.0.0.0","port":31211,"protocol":"socks","settings":{"auth":"noauth","udp":true,"ip":"127.0.0.1"}},"inboundDetour":[{"listen":"0.0.0.0","allocate":{"strategy":"always","refresh":5,"concurrency":3},"port":31210,"protocol":"http","tag":"httpDetour","domainOverride":["http","tls"],"streamSettings":{},"settings":{"timeout":0}}],"routing":{"strategy":"rules","settings":{"domainStrategy":"IPIfNonMatch","rules":[{"port":"1-52","type":"field","outboundTag":"direct"},{"port":"54-79","type":"field","outboundTag":"direct"},{"port":"81-442","type":"field","outboundTag":"direct"},{"port":"444-65535","type":"field","outboundTag":"direct"},{"type":"field","ip":["0.0.0.0/8","10.0.0.0/8","100.64.0.0/10","127.0.0.0/8","169.254.0.0/16","172.16.0.0/12","192.0.0.0/24","192.0.2.0/24","192.168.0.0/16","198.18.0.0/15","198.51.100.0/24","203.0.113.0/24","::1/128","fc00::/7","fe80::/10"],"outboundTag":"direct"},{"type":"field","ip":["geoip:cn"],"outboundTag":"direct"}]}},"outbound":{"tag":"proxy","sendThrough":"0.0.0.0","mux":{"enabled":false,"concurrency":8},"protocol":"vmess","settings":{"vnext":[{"address":"server","port":443,"users":[{"id":"uuid","alterId":2,"security":"auto","level":0}],"remark":"remark"}]},"streamSettings":{"network":"tcp","tcpSettings":{"header":{"type":"none"}},"security":"none","tlsSettings":{"allowInsecure":true,"allowInsecureCiphers":true},"kcpSettings":{"header":{"type":"none"},"mtu":1350,"congestion":false,"tti":20,"uplinkCapacity":5,"writeBufferSize":1,"readBufferSize":1,"downlinkCapacity":20},"wsSettings":{"path":"","headers":{"Host":"server.cc"}}}}}';
|
||||
CONST CLIENT_CONFIG = '{"policy":{"levels":{"0":{"uplinkOnly":0}}},"dns":{"servers":["114.114.114.114","8.8.8.8"]},"outboundDetour":[{"protocol":"freedom","tag":"direct","settings":{}}],"inbound":{"listen":"0.0.0.0","port":31211,"protocol":"socks","settings":{"auth":"noauth","udp":true,"ip":"127.0.0.1"}},"inboundDetour":[{"listen":"0.0.0.0","allocate":{"strategy":"always","refresh":5,"concurrency":3},"port":31210,"protocol":"http","tag":"httpDetour","domainOverride":["http","tls"],"streamSettings":{},"settings":{"timeout":0}}],"routing":{"strategy":"rules","settings":{"domainStrategy":"IPIfNonMatch","rules":[{"type":"field","ip":["geoip:cn"],"outboundTag":"direct"},{"type":"field","ip":["0.0.0.0/8","10.0.0.0/8","100.64.0.0/10","127.0.0.0/8","169.254.0.0/16","172.16.0.0/12","192.0.0.0/24","192.0.2.0/24","192.168.0.0/16","198.18.0.0/15","198.51.100.0/24","203.0.113.0/24","::1/128","fc00::/7","fe80::/10"],"outboundTag":"direct"}]}},"outbound":{"tag":"proxy","sendThrough":"0.0.0.0","mux":{"enabled":false,"concurrency":8},"protocol":"vmess","settings":{"vnext":[{"address":"server","port":443,"users":[{"id":"uuid","alterId":2,"security":"auto","level":0}],"remark":"remark"}]},"streamSettings":{"network":"tcp","tcpSettings":{"header":{"type":"none"}},"security":"none","tlsSettings":{"allowInsecure":true,"allowInsecureCiphers":true},"kcpSettings":{"header":{"type":"none"},"mtu":1350,"congestion":false,"tti":20,"uplinkCapacity":5,"writeBufferSize":1,"readBufferSize":1,"downlinkCapacity":20},"wsSettings":{"path":"","headers":{"Host":"server.cc"}}}}}';
|
||||
CONST SOCKS_PORT = 10010;
|
||||
CONST HTTP_PORT = 10011;
|
||||
|
||||
public function data (Request $request) {
|
||||
// TODO: 1.1.1 abolish
|
||||
public function data(Request $request)
|
||||
{
|
||||
$user = $request->user;
|
||||
$nodes = [];
|
||||
if ($user->plan_id) {
|
||||
@ -49,12 +51,13 @@ class AppController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function config (Request $request) {
|
||||
public function config(Request $request)
|
||||
{
|
||||
if (empty($request->input('server_id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$user = $request->user;
|
||||
if ($user->expired_at < time()) {
|
||||
if ($user->expired_at < time() && $user->expired_at !== NULL) {
|
||||
abort(500, '订阅计划已过期');
|
||||
}
|
||||
$server = Server::where('show', 1)
|
||||
@ -75,24 +78,30 @@ class AppController extends Controller
|
||||
$json->outbound->settings->vnext[0]->users[0]->alterId = (int)$user->v2ray_alter_id;
|
||||
$json->outbound->settings->vnext[0]->remark = (string)$server->name;
|
||||
$json->outbound->streamSettings->network = $server->network;
|
||||
if ($server->settings) {
|
||||
if ($server->networkSettings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp': $json->outbound->streamSettings->tcpSettings = json_decode($server->settings);
|
||||
case 'tcp':
|
||||
$json->outbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'kcp': $json->outbound->streamSettings->kcpSettings = json_decode($server->settings);
|
||||
case 'kcp':
|
||||
$json->outbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'ws': $json->outbound->streamSettings->wsSettings = json_decode($server->settings);
|
||||
case 'ws':
|
||||
$json->outbound->streamSettings->wsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'http': $json->outbound->streamSettings->httpSettings = json_decode($server->settings);
|
||||
case 'http':
|
||||
$json->outbound->streamSettings->httpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'domainsocket': $json->outbound->streamSettings->dsSettings = json_decode($server->settings);
|
||||
case 'domainsocket':
|
||||
$json->outbound->streamSettings->dsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'quic': $json->outbound->streamSettings->quicSettings = json_decode($server->settings);
|
||||
case 'quic':
|
||||
$json->outbound->streamSettings->quicSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($request->input('is_global')) {
|
||||
$json->routing->settings->rules[5]->outboundTag = 'proxy';
|
||||
$json->routing->settings->rules[0]->outboundTag = 'proxy';
|
||||
}
|
||||
if ($server->tls) {
|
||||
$json->outbound->streamSettings->security = "tls";
|
262
app/Http/Controllers/Client/ClientController.php
Normal file
262
app/Http/Controllers/Client/ClientController.php
Normal file
@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Server;
|
||||
use App\Utils\Helper;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use App\Services\UserService;
|
||||
|
||||
class ClientController extends Controller
|
||||
{
|
||||
public function subscribe(Request $request)
|
||||
{
|
||||
$user = $request->user;
|
||||
$server = [];
|
||||
// account not expired and is not banned.
|
||||
$userService = new UserService();
|
||||
if ($userService->isAvailable($user)) {
|
||||
$servers = Server::where('show', 1)
|
||||
->orderBy('sort', 'ASC')
|
||||
->get();
|
||||
foreach ($servers as $item) {
|
||||
$groupId = json_decode($item['group_id']);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
array_push($server, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($_SERVER['HTTP_USER_AGENT'])) {
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Quantumult%20X') !== false) {
|
||||
die($this->quantumultX($user, $server));
|
||||
}
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Quantumult') !== false) {
|
||||
die($this->quantumult($user, $server));
|
||||
}
|
||||
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'clash') !== false) {
|
||||
die($this->clash($user, $server));
|
||||
}
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Surfboard') !== false) {
|
||||
die($this->surfboard($user, $server));
|
||||
}
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Surge') !== false) {
|
||||
die($this->surge($user, $server));
|
||||
}
|
||||
}
|
||||
die($this->origin($user, $server));
|
||||
}
|
||||
|
||||
private function quantumultX($user, $server)
|
||||
{
|
||||
$uri = '';
|
||||
foreach ($server as $item) {
|
||||
$uri .= "vmess=" . $item->host . ":" . $item->port . ", method=none, password=" . $user->v2ray_uuid . ", fast-open=false, udp-relay=false, tag=" . $item->name;
|
||||
if ($item->tls) {
|
||||
$tlsSettings = json_decode($item->tlsSettings);
|
||||
if ($item->network === 'tcp') $uri .= ', obfs=over-tls';
|
||||
if (isset($tlsSettings->allowInsecure)) {
|
||||
// Default: tls-verification=true
|
||||
$uri .= ', tls-verification=' . ($tlsSettings->allowInsecure ? "false" : "true");
|
||||
}
|
||||
if (isset($tlsSettings->serverName)) {
|
||||
$uri .= ', obfs-host=' . $tlsSettings->serverName;
|
||||
}
|
||||
}
|
||||
if ($item->network === 'ws') {
|
||||
$uri .= ', obfs=' . ($item->tls ? 'wss' : 'ws');
|
||||
if ($item->networkSettings) {
|
||||
$wsSettings = json_decode($item->networkSettings);
|
||||
if (isset($wsSettings->path)) $uri .= ', obfs-uri=' . $wsSettings->path;
|
||||
if (isset($wsSettings->headers->Host)) $uri .= ', obfs-host=' . $wsSettings->headers->Host;
|
||||
}
|
||||
}
|
||||
$uri .= "\r\n";
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function quantumult($user, $server)
|
||||
{
|
||||
$uri = '';
|
||||
header('subscription-userinfo: upload=' . $user->u . '; download=' . $user->d . ';total=' . $user->transfer_enable);
|
||||
foreach ($server as $item) {
|
||||
$str = '';
|
||||
$str .= $item->name . '= vmess, ' . $item->host . ', ' . $item->port . ', chacha20-ietf-poly1305, "' . $user->v2ray_uuid . '", over-tls=' . ($item->tls ? "true" : "false") . ', certificate=0, group=' . config('v2board.app_name', 'V2Board');
|
||||
if ($item->network === 'ws') {
|
||||
$str .= ', obfs=ws';
|
||||
if ($item->networkSettings) {
|
||||
$wsSettings = json_decode($item->networkSettings);
|
||||
if (isset($wsSettings->path)) $str .= ', obfs-path="' . $wsSettings->path . '"';
|
||||
if (isset($wsSettings->headers->Host)) $str .= ', obfs-header="Host:' . $wsSettings->headers->Host . '"';
|
||||
}
|
||||
}
|
||||
$uri .= "vmess://" . base64_encode($str) . "\r\n";
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function origin($user, $server)
|
||||
{
|
||||
$uri = '';
|
||||
foreach ($server as $item) {
|
||||
$uri .= Helper::buildVmessLink($item, $user);
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function surge($user, $server)
|
||||
{
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
foreach ($server as $item) {
|
||||
// [Proxy]
|
||||
$proxies .= $item->name . ' = vmess, ' . $item->host . ', ' . $item->port . ', username=' . $user->v2ray_uuid . ', tfo=true';
|
||||
if ($item->tls) {
|
||||
$tlsSettings = json_decode($item->tlsSettings);
|
||||
$proxies .= ', tls=' . ($item->tls ? "true" : "false");
|
||||
if (isset($tlsSettings->allowInsecure)) {
|
||||
$proxies .= ', skip-cert-verify=' . ($tlsSettings->allowInsecure ? "true" : "false");
|
||||
}
|
||||
}
|
||||
if ($item->network == 'ws') {
|
||||
$proxies .= ', ws=true';
|
||||
if ($item->networkSettings) {
|
||||
$wsSettings = json_decode($item->networkSettings);
|
||||
if (isset($wsSettings->path)) $proxies .= ', ws-path=' . $wsSettings->path;
|
||||
if (isset($wsSettings->headers->Host)) $proxies .= ', ws-headers=host:' . $wsSettings->headers->Host;
|
||||
}
|
||||
}
|
||||
$proxies .= "\r\n";
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item->name . ', ';
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surge.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surge.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = 'http';
|
||||
if (isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on') {
|
||||
$subsURL .= 's';
|
||||
}
|
||||
$subsURL .= '://';
|
||||
if ($_SERVER['SERVER_PORT'] != ('80' || '443')) {
|
||||
$subsURL .= $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
|
||||
} else {
|
||||
$subsURL .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
|
||||
$config = str_replace('$subs_link',$subsURL,$config);
|
||||
$config = str_replace('$proxies',$proxies,$config);
|
||||
$config = str_replace('$proxy_group',rtrim($proxyGroup, ', '),$config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function surfboard($user, $server)
|
||||
{
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
foreach ($server as $item) {
|
||||
// [Proxy]
|
||||
$proxies .= $item->name . ' = vmess, ' . $item->host . ', ' . $item->port . ', username=' . $user->v2ray_uuid;
|
||||
if ($item->tls) {
|
||||
$tlsSettings = json_decode($item->tlsSettings);
|
||||
$proxies .= ', tls=' . ($item->tls ? "true" : "false");
|
||||
if (isset($tlsSettings->allowInsecure)) {
|
||||
$proxies .= ', skip-cert-verify=' . ($tlsSettings->allowInsecure ? "true" : "false");
|
||||
}
|
||||
}
|
||||
if ($item->network == 'ws') {
|
||||
$proxies .= ', ws=true';
|
||||
if ($item->networkSettings) {
|
||||
$wsSettings = json_decode($item->networkSettings);
|
||||
if (isset($wsSettings->path)) $proxies .= ', ws-path=' . $wsSettings->path;
|
||||
if (isset($wsSettings->headers->Host)) $proxies .= ', ws-headers=host:' . $wsSettings->headers->Host;
|
||||
}
|
||||
}
|
||||
$proxies .= "\r\n";
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item->name . ', ';
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surfboard.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surfboard.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = 'http';
|
||||
if (isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on') {
|
||||
$subsURL .= 's';
|
||||
}
|
||||
$subsURL .= '://';
|
||||
if ($_SERVER['SERVER_PORT'] != ('80' || '443')) {
|
||||
$subsURL .= $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
|
||||
} else {
|
||||
$subsURL .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
|
||||
$config = str_replace('$subs_link',$subsURL,$config);
|
||||
$config = str_replace('$proxies',$proxies,$config);
|
||||
$config = str_replace('$proxy_group',rtrim($proxyGroup, ', '),$config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function clash($user, $server)
|
||||
{
|
||||
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
|
||||
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = Yaml::parseFile($customConfig);
|
||||
} else {
|
||||
$config = Yaml::parseFile($defaultConfig);
|
||||
}
|
||||
$proxy = [];
|
||||
$proxies = [];
|
||||
foreach ($server as $item) {
|
||||
$array = [];
|
||||
$array['name'] = $item->name;
|
||||
$array['type'] = 'vmess';
|
||||
$array['server'] = $item->host;
|
||||
$array['port'] = $item->port;
|
||||
$array['uuid'] = $user->v2ray_uuid;
|
||||
$array['alterId'] = $user->v2ray_alter_id;
|
||||
$array['cipher'] = 'auto';
|
||||
if ($item->tls) {
|
||||
$tlsSettings = json_decode($item->tlsSettings);
|
||||
$array['tls'] = true;
|
||||
if (isset($tlsSettings->allowInsecure)) $array['skip-cert-verify'] = ($tlsSettings->allowInsecure ? true : false );
|
||||
}
|
||||
if ($item->network == 'ws') {
|
||||
$array['network'] = $item->network;
|
||||
if ($item->networkSettings) {
|
||||
$wsSettings = json_decode($item->networkSettings);
|
||||
if (isset($wsSettings->path)) $array['ws-path'] = $wsSettings->path;
|
||||
if (isset($wsSettings->headers->Host)) $array['ws-headers'] = [
|
||||
'Host' => $wsSettings->headers->Host
|
||||
];
|
||||
}
|
||||
}
|
||||
array_push($proxy, $array);
|
||||
array_push($proxies, $item->name);
|
||||
}
|
||||
|
||||
$config['Proxy'] = array_merge($config['Proxy'] ? $config['Proxy'] : [], $proxy);
|
||||
foreach ($config['Proxy Group'] as $k => $v) {
|
||||
$config['Proxy Group'][$k]['proxies'] = array_merge($config['Proxy Group'][$k]['proxies'], $proxies);
|
||||
}
|
||||
$yaml = Yaml::dump($config);
|
||||
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
|
||||
return $yaml;
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Utils\Helper;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ClientController extends Controller
|
||||
{
|
||||
public function subscribe (Request $request) {
|
||||
$user = $request->user;
|
||||
$server = [];
|
||||
if ($user->expired_at > time()) {
|
||||
$servers = Server::where('show', 1)
|
||||
->orderBy('name')
|
||||
->get();
|
||||
foreach ($servers as $item) {
|
||||
$groupId = json_decode($item['group_id']);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
array_push($server, $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(isset($_SERVER['HTTP_USER_AGENT'])) {
|
||||
if(strpos($_SERVER['HTTP_USER_AGENT'], 'Quantumult%20X') !== false) {
|
||||
die($this->quantumultX($user, $server));
|
||||
}
|
||||
if(strpos($_SERVER['HTTP_USER_AGENT'], 'Quantumult') !== false) {
|
||||
die($this->quantumult($user, $server));
|
||||
}
|
||||
if(strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'clash') !== false) {
|
||||
die($this->clash($user, $server));
|
||||
}
|
||||
}
|
||||
die($this->origin($user, $server));
|
||||
}
|
||||
|
||||
private function quantumultX ($user, $server) {
|
||||
$uri = '';
|
||||
foreach($server as $item) {
|
||||
$uri .= "vmess=".$item->host.":".$item->port.", method=none, password=".$user->v2ray_uuid.", fast-open=false, udp-relay=false, tag=".$item->name;
|
||||
if ($item->network == 'ws') {
|
||||
$uri .= ', obfs=ws';
|
||||
if ($item->settings) {
|
||||
$wsSettings = json_decode($item->settings);
|
||||
if ($wsSettings->path) $uri .= ', obfs-uri='.$wsSettings->path;
|
||||
}
|
||||
}
|
||||
$uri .= "\r\n";
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function quantumult ($user, $server) {
|
||||
$uri = '';
|
||||
header('subscription-userinfo: upload='.$user->u.'; download='.$user->d.';total='.$user->transfer_enable);
|
||||
foreach($server as $item) {
|
||||
$str = '';
|
||||
$str .= $item->name.'= vmess, '.$item->host.', '.$item->port.', chacha20-ietf-poly1305, "'.$user->v2ray_uuid.'", over-tls='.($item->tls?"true":"false").', certificate=0, group='.config('v2board.app_name', 'V2Board');
|
||||
if ($item->network === 'ws') {
|
||||
$str .= ', obfs=ws';
|
||||
if ($item->settings) {
|
||||
$wsSettings = json_decode($item->settings);
|
||||
if ($wsSettings->path) $str .= ', obfs-path="'.$wsSettings->path.'"';
|
||||
if ($wsSettings->headers->Host) $str .= ', obfs-header="Host:'.$wsSettings->headers->Host.'"';
|
||||
}
|
||||
}
|
||||
$uri .= "vmess://".base64_encode($str)."\r\n";
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function origin ($user, $server) {
|
||||
$uri = '';
|
||||
foreach($server as $item) {
|
||||
$uri .= Helper::buildVmessLink($item, $user);
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function clash ($user, $server) {
|
||||
$proxy = [];
|
||||
$proxyGroup = [];
|
||||
$proxies = [];
|
||||
foreach ($server as $item) {
|
||||
$array = [];
|
||||
$array['name'] = $item->name;
|
||||
$array['type'] = 'vmess';
|
||||
$array['server'] = $item->host;
|
||||
$array['port'] = $item->port;
|
||||
$array['uuid'] = $user->v2ray_uuid;
|
||||
$array['alterId'] = $user->v2ray_alter_id;
|
||||
$array['cipher'] = 'auto';
|
||||
if ($item->tls) {
|
||||
$array['tls'] = true;
|
||||
}
|
||||
if ($item->network == 'ws') {
|
||||
$array['network'] = $item->network;
|
||||
if ($item->settings) {
|
||||
$wsSettings = json_decode($item->settings);
|
||||
if ($wsSettings->path) $array['ws-path'] = $wsSettings->path;
|
||||
if ($wsSettings->headers->Host) $array['ws-headers'] = [
|
||||
'Host' => $wsSettings->headers->Host
|
||||
];
|
||||
}
|
||||
}
|
||||
array_push($proxy, $array);
|
||||
array_push($proxies, $item->name);
|
||||
}
|
||||
array_push($proxyGroup, [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'type' => 'select',
|
||||
'proxies' => $proxies
|
||||
]);
|
||||
|
||||
$config = [
|
||||
'port' => 7890,
|
||||
'socks-port' => 0,
|
||||
'allow-lan' => false,
|
||||
'mode' => 'Rule',
|
||||
'log-level' => 'info',
|
||||
'external-controller' => '0.0.0.0:9090',
|
||||
'secret' => '',
|
||||
'Proxy' => $proxy,
|
||||
'Proxy Group' => $proxyGroup,
|
||||
'Rule' => [
|
||||
'DOMAIN-SUFFIX,google.com,'.config('v2board.app_name', 'V2Board'),
|
||||
'DOMAIN-KEYWORD,google,'.config('v2board.app_name', 'V2Board'),
|
||||
'DOMAIN,google.com,'.config('v2board.app_name', 'V2Board'),
|
||||
'DOMAIN-SUFFIX,ad.com,REJECT',
|
||||
'IP-CIDR,127.0.0.0/8,DIRECT',
|
||||
'GEOIP,CN,DIRECT',
|
||||
'MATCH,'.config('v2board.app_name', 'V2Board')
|
||||
]
|
||||
];
|
||||
return Yaml::dump($config);
|
||||
}
|
||||
}
|
@ -7,13 +7,15 @@ use App\Http\Controllers\Controller;
|
||||
use App\Models\Order;
|
||||
use Omnipay\Omnipay;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Library\BitpayX;
|
||||
use Library\PayTaro;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function alipayNotify (Request $request) {
|
||||
Log::info('alipayNotifyData: ' . json_encode($_POST));
|
||||
public function alipayNotify(Request $request)
|
||||
{
|
||||
// Log::info('alipayNotifyData: ' . json_encode($_POST));
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId(config('v2board.alipay_appid'));
|
||||
@ -25,7 +27,7 @@ class OrderController extends Controller
|
||||
/** @var \Omnipay\Alipay\Responses\AopCompletePurchaseResponse $response */
|
||||
$response = $request->send();
|
||||
|
||||
if($response->isPaid()){
|
||||
if ($response->isPaid()) {
|
||||
/**
|
||||
* Payment is successful
|
||||
*/
|
||||
@ -34,7 +36,7 @@ class OrderController extends Controller
|
||||
}
|
||||
|
||||
die('success'); //The response should be 'success' only
|
||||
}else{
|
||||
} else {
|
||||
/**
|
||||
* Payment is not successful
|
||||
*/
|
||||
@ -48,8 +50,9 @@ class OrderController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function stripeNotify (Request $request) {
|
||||
Log::info('stripeNotifyData: ' . json_encode($request->input()));
|
||||
public function stripeNotify(Request $request)
|
||||
{
|
||||
// Log::info('stripeNotifyData: ' . json_encode($request->input()));
|
||||
|
||||
\Stripe\Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
try {
|
||||
@ -68,16 +71,17 @@ class OrderController extends Controller
|
||||
'amount' => $source['amount'],
|
||||
'currency' => $source['currency'],
|
||||
'source' => $source['id'],
|
||||
'description' => config('v2board.app_name', 'V2Board') . $source['metadata']['invoice_id'],
|
||||
]);
|
||||
if ($charge['status'] == 'succeeded') {
|
||||
$trade_no = Redis::get($source['id']);
|
||||
$trade_no = Cache::get($source['id']);
|
||||
if (!$trade_no) {
|
||||
abort(500, 'redis is not found trade no by stripe source id');
|
||||
}
|
||||
if (!$this->handle($trade_no, $source['id'])) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
Redis::del($source['id']);
|
||||
Cache::forget($source['id']);
|
||||
die('success');
|
||||
}
|
||||
break;
|
||||
@ -86,9 +90,10 @@ class OrderController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function bitpayXNotify (Request $request) {
|
||||
public function bitpayXNotify(Request $request)
|
||||
{
|
||||
$inputString = file_get_contents('php://input', 'r');
|
||||
Log::info('bitpayXNotifyData: ' . $inputString);
|
||||
// Log::info('bitpayXNotifyData: ' . $inputString);
|
||||
$inputStripped = str_replace(array("\r", "\n", "\t", "\v"), '', $inputString);
|
||||
$inputJSON = json_decode($inputStripped, true); //convert JSON into array
|
||||
|
||||
@ -105,35 +110,41 @@ class OrderController extends Controller
|
||||
];
|
||||
$strToSign = $bitpayX->prepareSignId($inputJSON['merchant_order_id']);
|
||||
if (!$bitpayX->verify($strToSign, $inputJSON['token'])) {
|
||||
die([
|
||||
'status' => 400,
|
||||
'error' => 'sign error'
|
||||
]);
|
||||
abort(500, 'sign error');
|
||||
}
|
||||
if ($params['status'] !== 'PAID') {
|
||||
die([
|
||||
'status' => 400,
|
||||
'error' => 'order is not paid'
|
||||
]);
|
||||
abort(500, 'order is not paid');
|
||||
}
|
||||
if (!$this->handle($params['merchant_order_id'], $params['order_id'])) {
|
||||
die([
|
||||
'status' => 400,
|
||||
'error' => 'order process fail'
|
||||
]);
|
||||
abort(500, 'order process fail');
|
||||
}
|
||||
die([
|
||||
die(json_encode([
|
||||
'status' => 200
|
||||
]);
|
||||
]));
|
||||
}
|
||||
|
||||
private function handle ($tradeNo, $callbackNo) {
|
||||
public function payTaroNotify(Request $request)
|
||||
{
|
||||
// Log::info('payTaroNotify: ' . json_encode($request->input()));
|
||||
|
||||
$payTaro = new PayTaro(config('v2board.paytaro_app_id'), config('v2board.paytaro_app_secret'));
|
||||
if (!$payTaro->verify($request->input())) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
if (!$this->handle($request->input('out_trade_no'), $request->input('trade_no'))) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
die('success');
|
||||
}
|
||||
|
||||
private function handle($tradeNo, $callbackNo)
|
||||
{
|
||||
$order = Order::where('trade_no', $tradeNo)->first();
|
||||
if (!$order) {
|
||||
abort(500, 'order is not found');
|
||||
}
|
||||
if ($order->status !== 0) {
|
||||
abort(500, 'order is paid');
|
||||
return true;
|
||||
}
|
||||
$order->status = 1;
|
||||
$order->callback_no = $callbackNo;
|
||||
|
@ -8,7 +8,8 @@ use App\Models\Plan;
|
||||
|
||||
class PlanController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$plan = Plan::where('show', 1)->get();
|
||||
return response([
|
||||
'data' => $plan
|
||||
|
111
app/Http/Controllers/Guest/TelegramController.php
Normal file
111
app/Http/Controllers/Guest/TelegramController.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Guest;
|
||||
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Utils\Helper;
|
||||
|
||||
class TelegramController extends Controller
|
||||
{
|
||||
protected $msg;
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
if ($request->input('access_token') !== md5(config('v2board.telegram_bot_token'))) {
|
||||
abort(500, 'authentication failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function webhook(Request $request)
|
||||
{
|
||||
$this->msg = $this->getMessage($request->input());
|
||||
if (!$this->msg) return;
|
||||
try {
|
||||
switch($this->msg->command) {
|
||||
case '/bind': $this->bind();
|
||||
break;
|
||||
case '/traffic': $this->traffic();
|
||||
break;
|
||||
default: $this->help();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$telegramService = new TelegramService();
|
||||
$telegramService->sendMessage($this->msg->chat_id, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function getMessage(array $data)
|
||||
{
|
||||
if (!isset($data['message'])) return false;
|
||||
$obj = new \StdClass();
|
||||
$obj->is_private = $data['message']['chat']['type'] === 'private' ? true : false;
|
||||
if (!isset($data['message']['text'])) return false;
|
||||
$text = explode(' ', $data['message']['text']);
|
||||
$obj->command = $text[0];
|
||||
$obj->args = array_slice($text, 1);
|
||||
$obj->chat_id = $data['message']['chat']['id'];
|
||||
$obj->message_id = $data['message']['message_id'];
|
||||
return $obj;
|
||||
}
|
||||
|
||||
private function bind()
|
||||
{
|
||||
$msg = $this->msg;
|
||||
if (!$msg->is_private) return;
|
||||
if (!isset($msg->args[0])) {
|
||||
abort(500, '参数有误,请携带订阅地址发送');
|
||||
}
|
||||
$subscribeUrl = $msg->args[0];
|
||||
$subscribeUrl = parse_url($subscribeUrl);
|
||||
parse_str($subscribeUrl['query'], $query);
|
||||
$token = $query['token'];
|
||||
if (!$token) {
|
||||
abort(500, '订阅地址无效');
|
||||
}
|
||||
$user = User::where('token', $token)->first();
|
||||
if (!$user) {
|
||||
abort(500, '用户不存在');
|
||||
}
|
||||
$user->telegram_id = $msg->chat_id;
|
||||
if (!$user->save()) {
|
||||
abort(500, '设置失败');
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
$telegramService->sendMessage($msg->chat_id, '绑定成功');
|
||||
}
|
||||
|
||||
private function help()
|
||||
{
|
||||
$msg = $this->msg;
|
||||
if (!$msg->is_private) return;
|
||||
$telegramService = new TelegramService();
|
||||
$commands = [
|
||||
'/bind 订阅地址 - 绑定你的' . config('v2board.app_name', 'V2Board') . '账号',
|
||||
'/traffic - 查询流量信息'
|
||||
];
|
||||
$text = implode(PHP_EOL, $commands);
|
||||
$telegramService->sendMessage($msg->chat_id, "你可以使用以下命令进行操作:\n\n$text", 'markdown');
|
||||
}
|
||||
|
||||
private function traffic()
|
||||
{
|
||||
$msg = $this->msg;
|
||||
if (!$msg->is_private) return;
|
||||
$user = User::where('telegram_id', $msg->chat_id)->first();
|
||||
$telegramService = new TelegramService();
|
||||
if (!$user) {
|
||||
$this->help();
|
||||
$telegramService->sendMessage($msg->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
|
||||
return;
|
||||
}
|
||||
$transferEnable = Helper::trafficConvert($user->transfer_enable);
|
||||
$up = Helper::trafficConvert($user->u);
|
||||
$down = Helper::trafficConvert($user->d);
|
||||
$remaining = Helper::trafficConvert($user->transfer_enable - ($user->u + $user->d));
|
||||
$text = "🚥流量查询\n———————————————\n计划流量:`{$transferEnable}`\n已用上行:`{$up}`\n已用下行:`{$down}`\n剩余流量:`{$remaining}`";
|
||||
$telegramService->sendMessage($msg->chat_id, $text, 'markdown');
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Notice;
|
||||
use App\Utils\Helper;
|
||||
|
||||
class NoticeController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
return response([
|
||||
'data' => Notice::orderBy('created_at', 'DESC')->first()
|
||||
]);
|
||||
}
|
||||
}
|
216
app/Http/Controllers/Passport/AuthController.php
Normal file
216
app/Http/Controllers/Passport/AuthController.php
Normal file
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Passport;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Passport\AuthRegister;
|
||||
use App\Http\Requests\Passport\AuthForget;
|
||||
use App\Http\Requests\Passport\AuthLogin;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Models\InviteCode;
|
||||
use App\Utils\Helper;
|
||||
use App\Utils\Dict;
|
||||
use App\Utils\CacheKey;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
public function register(AuthRegister $request)
|
||||
{
|
||||
if ((int)config('v2board.email_whitelist_enable', 0)) {
|
||||
if (!Helper::emailSuffixVerify(
|
||||
$request->input('email'),
|
||||
config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT))
|
||||
) {
|
||||
abort(500, '邮箱后缀不处于白名单中');
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_gmail_limit_enable', 0)) {
|
||||
$prefix = explode('@', $request->input('email'))[0];
|
||||
if (strpos($prefix, '.') !== false || strpos($prefix, '+') !== false) {
|
||||
abort(500, '不支持Gmail别名邮箱');
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.stop_register', 0)) {
|
||||
abort(500, '本站已关闭注册');
|
||||
}
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
if (empty($request->input('invite_code'))) {
|
||||
abort(500, '必须使用邀请码才可以注册');
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
if (empty($request->input('email_code'))) {
|
||||
abort(500, '邮箱验证码不能为空');
|
||||
}
|
||||
if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
}
|
||||
}
|
||||
$email = $request->input('email');
|
||||
$password = $request->input('password');
|
||||
$exist = User::where('email', $email)->first();
|
||||
if ($exist) {
|
||||
abort(500, '邮箱已存在系统中');
|
||||
}
|
||||
$user = new User();
|
||||
$user->email = $email;
|
||||
$user->password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$user->v2ray_uuid = Helper::guid(true);
|
||||
$user->token = Helper::guid();
|
||||
if ($request->input('invite_code')) {
|
||||
$inviteCode = InviteCode::where('code', $request->input('invite_code'))
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$inviteCode) {
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
abort(500, '邀请码无效');
|
||||
}
|
||||
} else {
|
||||
$user->invite_user_id = $inviteCode->user_id ? $inviteCode->user_id : null;
|
||||
if (!(int)config('v2board.invite_never_expire', 0)) {
|
||||
$inviteCode->status = 1;
|
||||
$inviteCode->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try out
|
||||
if ((int)config('v2board.try_out_plan_id', 0)) {
|
||||
$plan = Plan::find(config('v2board.try_out_plan_id'));
|
||||
if ($plan) {
|
||||
$user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$user->plan_id = $plan->id;
|
||||
$user->group_id = $plan->group_id;
|
||||
$user->expired_at = time() + (config('v2board.try_out_hour', 1) * 3600);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$user->save()) {
|
||||
abort(500, '注册失败');
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
|
||||
}
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
return response()->json([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function login(AuthLogin $request)
|
||||
{
|
||||
$email = $request->input('email');
|
||||
$password = $request->input('password');
|
||||
|
||||
$user = User::where('email', $email)->first();
|
||||
if (!$user) {
|
||||
abort(500, '用户名或密码错误');
|
||||
}
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$password,
|
||||
$user->password)
|
||||
) {
|
||||
abort(500, '用户名或密码错误');
|
||||
}
|
||||
|
||||
if ($user->banned) {
|
||||
abort(500, '该账户已被停止使用');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'token' => $user->token
|
||||
];
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
if ($user->is_admin) {
|
||||
$request->session()->put('is_admin', true);
|
||||
$data['is_admin'] = true;
|
||||
}
|
||||
return response([
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function token2Login(Request $request)
|
||||
{
|
||||
if ($request->input('token')) {
|
||||
$user = User::where('token', $request->input('token'))->first();
|
||||
if (!$user) {
|
||||
return header('Location:' . config('v2board.app_url'));
|
||||
}
|
||||
$code = Helper::guid();
|
||||
$key = 'token2Login_' . $code;
|
||||
Cache::put($key, $user->id, 600);
|
||||
$redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
|
||||
if (config('v2board.app_url')) {
|
||||
$location = config('v2board.app_url') . $redirect;
|
||||
} else {
|
||||
$location = url($redirect);
|
||||
}
|
||||
return header('Location:' . $location);
|
||||
}
|
||||
|
||||
if ($request->input('verify')) {
|
||||
$key = 'token2Login_' . $request->input('verify');
|
||||
$userId = Cache::get($key);
|
||||
if (!$userId) {
|
||||
abort(500, '令牌有误');
|
||||
}
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
abort(500, '用户不存在');
|
||||
}
|
||||
if ($user->banned) {
|
||||
abort(500, '该账户已被停止使用');
|
||||
}
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
if ($user->is_admin) {
|
||||
$request->session()->put('is_admin', true);
|
||||
}
|
||||
Cache::forget($key);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function check(Request $request)
|
||||
{
|
||||
$data = [
|
||||
'is_login' => $request->session()->get('id') ? true : false
|
||||
];
|
||||
if ($request->session()->get('is_admin')) {
|
||||
$data['is_admin'] = true;
|
||||
}
|
||||
return response([
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function forget(AuthForget $request)
|
||||
{
|
||||
if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
}
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
if (!$user) {
|
||||
abort(500, '该邮箱不存在系统中');
|
||||
}
|
||||
$user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, '重置失败');
|
||||
}
|
||||
Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -7,52 +7,81 @@ use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Models\InviteCode;
|
||||
use App\Utils\Dict;
|
||||
use App\Utils\CacheKey;
|
||||
|
||||
class CommController extends Controller
|
||||
{
|
||||
public function config () {
|
||||
public function config()
|
||||
{
|
||||
return response([
|
||||
'data' => [
|
||||
'isEmailVerify' => (int)config('v2board.email_verify', 0) ? 1 : 0,
|
||||
'isInviteForce' => (int)config('v2board.invite_force', 0) ? 1 : 0
|
||||
'isInviteForce' => (int)config('v2board.invite_force', 0) ? 1 : 0,
|
||||
'emailWhitelistSuffix' => (int)config('v2board.email_whitelist_enable', 0)
|
||||
? $this->getEmailSuffix()
|
||||
: 0
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
private function isEmailVerify () {
|
||||
private function isEmailVerify()
|
||||
{
|
||||
return response([
|
||||
'data' => (int)config('v2board.email_verify', 0) ? 1 : 0
|
||||
]);
|
||||
}
|
||||
|
||||
public function sendEmailVerify (CommSendEmailVerify $request) {
|
||||
public function sendEmailVerify(CommSendEmailVerify $request)
|
||||
{
|
||||
$email = $request->input('email');
|
||||
$redisKey = 'sendEmailVerify:' . $email;
|
||||
if (Redis::get($redisKey)) {
|
||||
abort(500, '验证码已发送,请过一会在请求');
|
||||
if (Cache::get(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email))) {
|
||||
abort(500, '验证码已发送,请过一会再请求');
|
||||
}
|
||||
$code = rand(100000, 999999);
|
||||
$subject = config('v2board.app_name', 'V2Board') . '邮箱验证码';
|
||||
Mail::send(
|
||||
'mail.sendEmailVerify',
|
||||
[
|
||||
'code' => $code,
|
||||
'name' => config('v2board.app_name', 'V2Board')
|
||||
],
|
||||
function ($message) use($email, $subject) {
|
||||
$message->to($email)->subject($subject);
|
||||
}
|
||||
);
|
||||
if (count(Mail::failures()) >= 1) {
|
||||
// 发送失败
|
||||
abort(500, '发送失败');
|
||||
}
|
||||
|
||||
Redis::set($redisKey, $code);
|
||||
Redis::expire($redisKey, 600);
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $email,
|
||||
'subject' => $subject,
|
||||
'template_name' => 'verify',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'code' => $code,
|
||||
'url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
|
||||
Cache::put(CacheKey::get('EMAIL_VERIFY_CODE', $email), $code, 300);
|
||||
Cache::put(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email), time(), 60);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function pv(Request $request)
|
||||
{
|
||||
$inviteCode = InviteCode::where('code', $request->input('invite_code'))->first();
|
||||
if ($inviteCode) {
|
||||
$inviteCode->pv = $inviteCode->pv + 1;
|
||||
$inviteCode->save();
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
private function getEmailSuffix()
|
||||
{
|
||||
$suffix = config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT);
|
||||
if (!is_array($suffix)) {
|
||||
return preg_split('/,/', $suffix);
|
||||
}
|
||||
return $suffix;
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Passport;
|
||||
|
||||
use App\Http\Requests\Passport\ForgetIndex;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
|
||||
class ForgetController extends Controller
|
||||
{
|
||||
public function index (ForgetIndex $request) {
|
||||
$redisKey = 'sendEmailVerify:' . $request->input('email');
|
||||
if (Redis::get($redisKey) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
}
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
$user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
|
||||
if (!$user->save()) {
|
||||
abort(500, '重置失败');
|
||||
}
|
||||
Redis::del($redisKey);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Passport;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\Passport\LoginIndex;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
public function index (LoginIndex $request) {
|
||||
$email = $request->input('email');
|
||||
$password = $request->input('password');
|
||||
|
||||
$user = User::where('email', $email)->first();
|
||||
if (!$user) {
|
||||
abort(500, '用户名或密码错误');
|
||||
}
|
||||
if (!password_verify($password, $user->password)) {
|
||||
abort(500, '用户名或密码错误');
|
||||
}
|
||||
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
if ($user->is_admin) {
|
||||
$request->session()->put('is_admin', true);
|
||||
}
|
||||
return response([
|
||||
'data' => [
|
||||
'is_admin' => $user->is_admin ? 2 : 1,
|
||||
'token' => $user->token
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function token2Login (Request $request) {
|
||||
if (empty($request->input('token'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$redirect = $request->input('redirect') ? $request->input('redirect') : 'dashboard';
|
||||
$user = User::where('token', $request->input('token'))->first();
|
||||
if ($user) {
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
if ($user->is_admin) {
|
||||
$request->session()->put('is_admin', true);
|
||||
}
|
||||
}
|
||||
if (config('v2board.app_url')) {
|
||||
$location = config('v2board.app_url') . '/#/' . $redirect;
|
||||
} else {
|
||||
$location = url('/#/' . $redirect);
|
||||
}
|
||||
header('Location:' . $location);
|
||||
}
|
||||
|
||||
public function check (Request $request) {
|
||||
return response([
|
||||
'data' => $request->session()->get('id') ? true : false
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Passport;
|
||||
|
||||
use App\Http\Requests\Passport\RegisterIndex;
|
||||
use App\Http\Requests\Passport\RegisterSendEmailVerify;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\Utils\Helper;
|
||||
use App\Models\InviteCode;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
public function index (RegisterIndex $request) {
|
||||
if ((int)config('v2board.stop_register', 0)) {
|
||||
abort(500, '本站已关闭注册');
|
||||
}
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
if (empty($request->input('invite_code'))) {
|
||||
abort(500, '必须使用邀请码才可以注册');
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
$redisKey = 'sendEmailVerify:' . $request->input('email');
|
||||
if (empty($request->input('email_code'))) {
|
||||
abort(500, '邮箱验证码不能为空');
|
||||
}
|
||||
if (Redis::get($redisKey) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
}
|
||||
}
|
||||
$email = $request->input('email');
|
||||
$password = $request->input('password');
|
||||
$exist = User::where('email', $email)->first();
|
||||
if ($exist) {
|
||||
abort(500, '邮箱已存在系统中');
|
||||
}
|
||||
$user = new User();
|
||||
$user->email = $email;
|
||||
$user->password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$user->v2ray_uuid = Helper::guid(true);
|
||||
$user->token = Helper::guid();
|
||||
if ($request->input('invite_code')) {
|
||||
$inviteCode = InviteCode::where('code', $request->input('invite_code'))
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$inviteCode) {
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
abort(500, '邀请码无效');
|
||||
}
|
||||
} else {
|
||||
$user->invite_user_id = $inviteCode->user_id ? $inviteCode->user_id : null;
|
||||
if (!(int)config('v2board.invite_never_expire', env('V2BOARD_INVITE_NEVER_EXPIRE'))) {
|
||||
$inviteCode->status = 1;
|
||||
$inviteCode->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$user->save()) {
|
||||
abort(500, '注册失败');
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
Redis::del($redisKey);
|
||||
}
|
||||
return response()->json([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Plan;
|
||||
|
||||
class PlanController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
if (empty($request->input('plan_id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
if (!$plan) {
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
return response([
|
||||
'data' => $plan
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
public function __construct(Request $request) {
|
||||
$token = $request->input('token');
|
||||
if (empty($token)) {
|
||||
abort(500, 'token is null');
|
||||
}
|
||||
if ($token !== config('v2board.server_token')) {
|
||||
abort(500, 'token is error');
|
||||
}
|
||||
}
|
||||
}
|
@ -2,45 +2,48 @@
|
||||
|
||||
namespace App\Http\Controllers\Server;
|
||||
|
||||
use App\Services\ServerService;
|
||||
use App\Services\UserService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Server\Controller;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class DeepbworkController extends Controller
|
||||
{
|
||||
CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"routing":{"settings":{"rules":[{"inboundTag":["api"],"outboundTag":"api","type":"field"}]},"strategy":"rules"},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
|
||||
CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled": true,"destOverride": ["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$token = $request->input('token');
|
||||
if (empty($token)) {
|
||||
abort(500, 'token is null');
|
||||
}
|
||||
if ($token !== config('v2board.server_token')) {
|
||||
abort(500, 'token is error');
|
||||
}
|
||||
}
|
||||
|
||||
// 后端获取用户
|
||||
public function user (Request $request) {
|
||||
public function user(Request $request)
|
||||
{
|
||||
$nodeId = $request->input('node_id');
|
||||
$server = Server::find($nodeId);
|
||||
if (!$server) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
Redis::set('server_last_check_at_' . $server->id, time());
|
||||
$users = User::whereIn('group_id', json_decode($server->group_id))
|
||||
->select([
|
||||
'id',
|
||||
'email',
|
||||
't',
|
||||
'u',
|
||||
'd',
|
||||
'transfer_enable',
|
||||
'enable',
|
||||
'v2ray_uuid',
|
||||
'v2ray_alter_id',
|
||||
'v2ray_level'
|
||||
])
|
||||
->get();
|
||||
Cache::put('server_last_check_at_' . $server->id, time());
|
||||
$serverService = new ServerService();
|
||||
$users = $serverService->getAvailableUsers(json_decode($server->group_id));
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
$user->v2ray_user = [
|
||||
"uuid" => $user->v2ray_uuid,
|
||||
"email" => sprintf("%s@v2panel.user", $user->v2ray_uuid),
|
||||
"email" => sprintf("%s@v2board.user", $user->v2ray_uuid),
|
||||
"alter_id" => $user->v2ray_alter_id,
|
||||
"level" => $user->v2ray_level,
|
||||
];
|
||||
@ -56,33 +59,37 @@ class DeepbworkController extends Controller
|
||||
}
|
||||
|
||||
// 后端提交数据
|
||||
public function submit (Request $request) {
|
||||
Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
|
||||
public function submit(Request $request)
|
||||
{
|
||||
// Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
|
||||
$server = Server::find($request->input('node_id'));
|
||||
if (!$server) {
|
||||
return response([
|
||||
'ret' => 1,
|
||||
'msg' => 'ok'
|
||||
'ret' => 0,
|
||||
'msg' => 'server is not found'
|
||||
]);
|
||||
}
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
$serverService = new ServerService();
|
||||
$userService = new UserService();
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
$d = $item['d'] * $server->rate;
|
||||
$user = User::find($item['user_id']);
|
||||
$user->t = time();
|
||||
$user->u = $user->u + $u;
|
||||
$user->d = $user->d + $d;
|
||||
$user->save();
|
||||
if (!$userService->trafficFetch($u, $d, $item['user_id'])) {
|
||||
return response([
|
||||
'ret' => 0,
|
||||
'msg' => 'user fetch fail'
|
||||
]);
|
||||
}
|
||||
|
||||
$serverLog = new ServerLog();
|
||||
$serverLog->user_id = $item['user_id'];
|
||||
$serverLog->server_id = $request->input('node_id');
|
||||
$serverLog->u = $item['u'];
|
||||
$serverLog->d = $item['d'];
|
||||
$serverLog->rate = $server->rate;
|
||||
$serverLog->save();
|
||||
$serverService->log(
|
||||
$item['user_id'],
|
||||
$request->input('node_id'),
|
||||
$item['u'],
|
||||
$item['d'],
|
||||
$server->rate
|
||||
);
|
||||
}
|
||||
|
||||
return response([
|
||||
@ -92,40 +99,18 @@ class DeepbworkController extends Controller
|
||||
}
|
||||
|
||||
// 后端获取配置
|
||||
public function config (Request $request) {
|
||||
public function config(Request $request)
|
||||
{
|
||||
$nodeId = $request->input('node_id');
|
||||
$localPort = $request->input('local_port');
|
||||
if (empty($nodeId) || empty($localPort)) {
|
||||
abort(1000, '参数错误');
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$server = Server::find($nodeId);
|
||||
if (!$server) {
|
||||
abort(1001, '节点不存在');
|
||||
}
|
||||
$json = json_decode(self::SERVER_CONFIG);
|
||||
$json->inboundDetour[0]->port = (int)$localPort;
|
||||
$json->inbound->port = (int)$server->server_port;
|
||||
$json->inbound->streamSettings->network = $server->network;
|
||||
if ($server->settings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp': $json->inbound->streamSettings->tcpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'kcp': $json->inbound->streamSettings->kcpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'ws': $json->inbound->streamSettings->wsSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'http': $json->inbound->streamSettings->httpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'domainsocket': $json->inbound->streamSettings->dsSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'quic': $json->inbound->streamSettings->quicSettings = json_decode($server->settings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((int)$server->tls) {
|
||||
$json->inbound->streamSettings->security = "tls";
|
||||
$tls = (object) array("certificateFile" => "/home/v2ray.crt", "keyFile" => "/home/v2ray.key");
|
||||
$json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
|
||||
$serverService = new ServerService();
|
||||
try {
|
||||
$json = $serverService->getConfig($nodeId, $localPort);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, $e->getMessage());
|
||||
}
|
||||
|
||||
die(json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
|
151
app/Http/Controllers/Server/PoseidonController.php
Normal file
151
app/Http/Controllers/Server/PoseidonController.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Server;
|
||||
|
||||
use App\Services\ServerService;
|
||||
use App\Services\UserService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class PoseidonController extends Controller
|
||||
{
|
||||
CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled": true,"destOverride": ["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
|
||||
|
||||
public $poseidonVersion;
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->poseidonVersion = $request->input('poseidon_version');
|
||||
}
|
||||
|
||||
// 后端获取用户
|
||||
public function user(Request $request)
|
||||
{
|
||||
if ($r = $this->verifyToken($request)) { return $r; }
|
||||
|
||||
$nodeId = $request->input('node_id');
|
||||
$server = Server::find($nodeId);
|
||||
if (!$server) {
|
||||
return $this->error("server could not be found", 404);
|
||||
}
|
||||
Cache::put('server_last_check_at_' . $server->id, time());
|
||||
$serverService = new ServerService();
|
||||
$users = $serverService->getAvailableUsers(json_decode($server->group_id));
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
$user->v2ray_user = [
|
||||
"uuid" => $user->v2ray_uuid,
|
||||
"email" => sprintf("%s@v2board.user", $user->v2ray_uuid),
|
||||
"alter_id" => $user->v2ray_alter_id,
|
||||
"level" => $user->v2ray_level,
|
||||
];
|
||||
unset($user['v2ray_uuid']);
|
||||
unset($user['v2ray_alter_id']);
|
||||
unset($user['v2ray_level']);
|
||||
array_push($result, $user);
|
||||
}
|
||||
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
// 后端提交数据
|
||||
public function submit(Request $request)
|
||||
{
|
||||
if ($r = $this->verifyToken($request)) { return $r; }
|
||||
|
||||
// Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
|
||||
$server = Server::find($request->input('node_id'));
|
||||
if (!$server) {
|
||||
return $this->error("server could not be found", 404);
|
||||
}
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
$serverService = new ServerService();
|
||||
$userService = new UserService();
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
$d = $item['d'] * $server->rate;
|
||||
if (!$userService->trafficFetch($u, $d, $item['user_id'])) {
|
||||
return $this->error("user fetch fail", 500);
|
||||
}
|
||||
|
||||
$serverService->log(
|
||||
$item['user_id'],
|
||||
$request->input('node_id'),
|
||||
$item['u'],
|
||||
$item['d'],
|
||||
$server->rate
|
||||
);
|
||||
}
|
||||
|
||||
return $this->success('');
|
||||
}
|
||||
|
||||
// 后端获取配置
|
||||
public function config(Request $request)
|
||||
{
|
||||
if ($r = $this->verifyToken($request)) { return $r; }
|
||||
|
||||
$nodeId = $request->input('node_id');
|
||||
$localPort = $request->input('local_port');
|
||||
if (empty($nodeId) || empty($localPort)) {
|
||||
return $this->error('invalid parameters', 400);
|
||||
}
|
||||
|
||||
$serverService = new ServerService();
|
||||
try {
|
||||
$json = $serverService->getConfig($nodeId, $localPort);
|
||||
$json->poseidon = [
|
||||
'license_key' => (string)config('v2board.server_license'),
|
||||
];
|
||||
if ($this->poseidonVersion >= 'v1.5.0') {
|
||||
// don't need it after v1.5.0
|
||||
unset($json->inboundDetour);
|
||||
unset($json->stats);
|
||||
unset($json->api);
|
||||
array_shift($json->routing->rules);
|
||||
}
|
||||
|
||||
foreach($json->policy->levels as &$level) {
|
||||
$level->handshake = 2;
|
||||
$level->uplinkOnly = 2;
|
||||
$level->downlinkOnly = 2;
|
||||
$level->connIdle = 60;
|
||||
}
|
||||
|
||||
return $this->success($json);
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
protected function verifyToken(Request $request)
|
||||
{
|
||||
$token = $request->input('token');
|
||||
if (empty($token)) {
|
||||
return $this->error("token must be set");
|
||||
}
|
||||
if ($token !== config('v2board.server_token')) {
|
||||
return $this->error("invalid token");
|
||||
}
|
||||
}
|
||||
|
||||
protected function error($msg, int $status = 400) {
|
||||
return response([
|
||||
'msg' => $msg,
|
||||
], $status);
|
||||
}
|
||||
|
||||
protected function success($data) {
|
||||
return response([
|
||||
'msg' => 'ok',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
|
||||
class TutorialController extends Controller
|
||||
{
|
||||
public function getSubscribeUrl (Request $request) {
|
||||
$user = User::find($request->session()->get('id'));
|
||||
return response([
|
||||
'data' => [
|
||||
'subscribe_url' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token']
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAppleID (Request $request) {
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if ($user->expired_at < time()) {
|
||||
return response([
|
||||
'data' => [
|
||||
]
|
||||
]);
|
||||
}
|
||||
return response([
|
||||
'data' => [
|
||||
'apple_id' => config('v2board.apple_id'),
|
||||
'apple_id_password' => config('v2board.apple_id_password')
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
18
app/Http/Controllers/User/CommController.php
Normal file
18
app/Http/Controllers/User/CommController.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class CommController extends Controller
|
||||
{
|
||||
public function config()
|
||||
{
|
||||
return response([
|
||||
'data' => [
|
||||
'isTelegram' => (int)config('v2board.telegram_bot_enable', 0)
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
33
app/Http/Controllers/User/CouponController.php
Normal file
33
app/Http/Controllers/User/CouponController.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Coupon;
|
||||
|
||||
class CouponController extends Controller
|
||||
{
|
||||
public function check(Request $request)
|
||||
{
|
||||
if (empty($request->input('code'))) {
|
||||
abort(500, '优惠券码不能为空');
|
||||
}
|
||||
$coupon = Coupon::where('code', $request->input('code'))->first();
|
||||
if (!$coupon) {
|
||||
abort(500, '优惠券无效');
|
||||
}
|
||||
if ($coupon->limit_use <= 0 && $coupon->limit_use !== NULL) {
|
||||
abort(500, '优惠券已无可用次数');
|
||||
}
|
||||
if (time() < $coupon->started_at) {
|
||||
abort(500, '优惠券还未到可用时间');
|
||||
}
|
||||
if (time() > $coupon->ended_at) {
|
||||
abort(500, '优惠券已过期');
|
||||
}
|
||||
return response([
|
||||
'data' => $coupon
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Order;
|
||||
use App\Models\InviteCode;
|
||||
@ -11,7 +11,8 @@ use App\Utils\Helper;
|
||||
|
||||
class InviteController extends Controller
|
||||
{
|
||||
public function save (Request $request) {
|
||||
public function save(Request $request)
|
||||
{
|
||||
if (InviteCode::where('user_id', $request->session()->get('id'))->where('status', 0)->count() >= config('v2board.invite_gen_limit', 5)) {
|
||||
abort(500, '已达到创建数量上限');
|
||||
}
|
||||
@ -23,9 +24,11 @@ class InviteController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function details (Request $request) {
|
||||
public function details(Request $request)
|
||||
{
|
||||
return response([
|
||||
'data' => Order::where('invite_user_id', $request->session()->get('id'))
|
||||
->where('commission_balance', '>', 0)
|
||||
->where('status', 3)
|
||||
->select([
|
||||
'id',
|
||||
@ -38,11 +41,12 @@ class InviteController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$codes = InviteCode::where('user_id', $request->session()->get('id'))
|
||||
->where('status', 0)
|
||||
->get();
|
||||
$commission_rate = config('v2board.invite_commission');
|
||||
$commission_rate = config('v2board.invite_commission', 10);
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if ($user->commission_rate) {
|
||||
$commission_rate = $user->commission_rate;
|
||||
@ -52,7 +56,7 @@ class InviteController extends Controller
|
||||
(int)User::where('invite_user_id', $request->session()->get('id'))->count(),
|
||||
//有效的佣金
|
||||
(int)Order::where('status', 3)
|
||||
->where('commission_status', 1)
|
||||
->where('commission_status', 2)
|
||||
->where('invite_user_id', $request->session()->get('id'))
|
||||
->sum('commission_balance'),
|
||||
//确认中的佣金
|
||||
@ -61,7 +65,9 @@ class InviteController extends Controller
|
||||
->where('invite_user_id', $request->session()->get('id'))
|
||||
->sum('commission_balance'),
|
||||
//佣金比例
|
||||
(int)$commission_rate
|
||||
(int)$commission_rate,
|
||||
//可用佣金
|
||||
(int)$user->commission_balance
|
||||
];
|
||||
return response([
|
||||
'data' => [
|
25
app/Http/Controllers/User/NoticeController.php
Normal file
25
app/Http/Controllers/User/NoticeController.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Notice;
|
||||
use App\Utils\Helper;
|
||||
|
||||
class NoticeController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = 5;
|
||||
$model = Notice::orderBy('created_at', 'DESC');
|
||||
$total = $model->count();
|
||||
$res = $model->forPage($current, $pageSize)
|
||||
->get();
|
||||
return response([
|
||||
'data' => $res,
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,30 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Requests\OrderSave;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\Http\Requests\User\OrderSave;
|
||||
use App\Services\CouponService;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\UserService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Models\Coupon;
|
||||
use App\Utils\Helper;
|
||||
use Omnipay\Omnipay;
|
||||
use Stripe\Stripe;
|
||||
use Stripe\Source;
|
||||
use Library\BitpayX;
|
||||
use Library\PayTaro;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
$order = Order::where('user_id', $request->session()->get('id'))
|
||||
->orderBy('created_at', 'DESC')
|
||||
->get();
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$model = Order::where('user_id', $request->session()->get('id'))
|
||||
->orderBy('created_at', 'DESC');
|
||||
if ($request->input('status') !== null) {
|
||||
$model->where('status', $request->input('status'));
|
||||
}
|
||||
$order = $model->get();
|
||||
$plan = Plan::get();
|
||||
for($i = 0; $i < count($order); $i++) {
|
||||
for($x = 0; $x < count($plan); $x++) {
|
||||
for ($i = 0; $i < count($order); $i++) {
|
||||
for ($x = 0; $x < count($plan); $x++) {
|
||||
if ($order[$i]['plan_id'] === $plan[$x]['id']) {
|
||||
$order[$i]['plan'] = $plan[$x];
|
||||
}
|
||||
@ -35,7 +45,8 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function details (Request $request) {
|
||||
public function details(Request $request)
|
||||
{
|
||||
$order = Order::where('user_id', $request->session()->get('id'))
|
||||
->where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
@ -43,7 +54,7 @@ class OrderController extends Controller
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
$order['plan'] = Plan::find($order->plan_id);
|
||||
$order['update_fee'] = config('v2board.plan_update_fee', 0.5);
|
||||
$order['try_out_plan_id'] = (int)config('v2board.try_out_plan_id');
|
||||
if (!$order['plan']) {
|
||||
abort(500, '订阅不存在');
|
||||
}
|
||||
@ -52,7 +63,13 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (OrderSave $request) {
|
||||
public function save(OrderSave $request)
|
||||
{
|
||||
$userService = new UserService();
|
||||
if ($userService->isNotCompleteOrderByUserId($request->session()->get('id'))) {
|
||||
abort(500, '您有未付款或开通中的订单,请稍后或取消再试');
|
||||
}
|
||||
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
$user = User::find($request->session()->get('id'));
|
||||
|
||||
@ -60,52 +77,82 @@ class OrderController extends Controller
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
|
||||
if (!($plan->show || $user->plan_id == $plan->id)) {
|
||||
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
|
||||
if ($request->input('cycle') !== 'reset_price') {
|
||||
abort(500, '该订阅已售罄');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$plan->show && !$plan->renew) {
|
||||
if (!$plan->renew && $user->plan_id == $plan->id) {
|
||||
abort(500, '该订阅无法续费,请更换其他订阅');
|
||||
}
|
||||
|
||||
if (!(int)$plan[$request->input('cycle')]) {
|
||||
if ($plan[$request->input('cycle')] === NULL) {
|
||||
if ($request->input('cycle') === 'reset_price') {
|
||||
abort(500, '该订阅当前不支持重置流量');
|
||||
}
|
||||
abort(500, '该订阅周期无法进行购买,请选择其他周期');
|
||||
}
|
||||
|
||||
if ($request->input('cycle') === 'reset_price' && !$user->plan_id) {
|
||||
abort(500, '必须存在订阅才可以购买流量重置包');
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
$order = new Order();
|
||||
$orderService = new OrderService($order);
|
||||
$order->user_id = $request->session()->get('id');
|
||||
$order->plan_id = $plan->id;
|
||||
$order->cycle = $request->input('cycle');
|
||||
$order->trade_no = Helper::guid();
|
||||
$order->total_amount = $plan[$request->input('cycle')];
|
||||
if ($user->expired_at > time() && $order->plan_id !== $user->plan_id) {
|
||||
$order->type = 3;
|
||||
if (!(int)config('v2board.plan_is_update', 1)) abort(500, '目前不允许更改订阅,请联系管理员');
|
||||
$order->total_amount = $order->total_amount + (ceil(($user->expired_at - time()) / 86400) * config('v2board.plan_update_fee', 0.5) * 100);
|
||||
} else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) {
|
||||
$order->type = 2;
|
||||
|
||||
if ($request->input('coupon_code')) {
|
||||
$couponService = new CouponService($request->input('coupon_code'));
|
||||
if (!$couponService->use($order)) {
|
||||
DB::rollBack();
|
||||
abort(500, '优惠券使用失败');
|
||||
}
|
||||
}
|
||||
|
||||
$orderService->setVipDiscount($user);
|
||||
$orderService->setOrderType($user);
|
||||
$orderService->setInvite($user);
|
||||
|
||||
if ($user->balance && $order->total_amount > 0) {
|
||||
$remainingBalance = $user->balance - $order->total_amount;
|
||||
$userService = new UserService();
|
||||
if ($remainingBalance > 0) {
|
||||
if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
|
||||
DB::rollBack();
|
||||
abort(500, '余额不足');
|
||||
}
|
||||
$order->balance_amount = $order->total_amount;
|
||||
$order->total_amount = 0;
|
||||
} else {
|
||||
$order->type = 1;
|
||||
if (!$userService->addBalance($order->user_id, - $user->balance)) {
|
||||
DB::rollBack();
|
||||
abort(500, '余额不足');
|
||||
}
|
||||
if ($user->invite_user_id) {
|
||||
$order->invite_user_id = $user->invite_user_id;
|
||||
$inviter = User::find($user->invite_user_id);
|
||||
if ($inviter && $inviter->commission_rate) {
|
||||
$order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
|
||||
} else {
|
||||
$order->commission_balance = $order->total_amount * (config('v2board.invite_commission', 10) / 100);
|
||||
$order->balance_amount = $user->balance;
|
||||
$order->total_amount = $order->total_amount - $user->balance;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$order->save()) {
|
||||
DB::rollback();
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response([
|
||||
'data' => $order->trade_no
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkout (Request $request) {
|
||||
public function checkout(Request $request)
|
||||
{
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$method = $request->input('method');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
@ -115,6 +162,16 @@ class OrderController extends Controller
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在或已支付');
|
||||
}
|
||||
// free process
|
||||
if ($order->total_amount <= 0) {
|
||||
$order->total_amount = 0;
|
||||
$order->status = 1;
|
||||
$order->save();
|
||||
return response([
|
||||
'type' => -1,
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
switch ($method) {
|
||||
// return type => 0: QRCode / 1: URL
|
||||
case 0:
|
||||
@ -153,12 +210,21 @@ class OrderController extends Controller
|
||||
'type' => 1,
|
||||
'data' => $this->bitpayX($order)
|
||||
]);
|
||||
case 5:
|
||||
if (!(int)config('v2board.paytaro_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->payTaro($order)
|
||||
]);
|
||||
default:
|
||||
abort(500, '支付方式不存在');
|
||||
}
|
||||
}
|
||||
|
||||
public function check (Request $request) {
|
||||
public function check(Request $request)
|
||||
{
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
@ -171,7 +237,8 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPaymentMethod () {
|
||||
public function getPaymentMethod()
|
||||
{
|
||||
$data = [];
|
||||
if ((int)config('v2board.alipay_enable')) {
|
||||
$alipayF2F = new \StdClass();
|
||||
@ -199,18 +266,50 @@ class OrderController extends Controller
|
||||
|
||||
if ((int)config('v2board.bitpayx_enable')) {
|
||||
$bitpayX = new \StdClass();
|
||||
$bitpayX->name = '虚拟货币';
|
||||
$bitpayX->name = config('v2board.bitpayx_name', '聚合支付');
|
||||
$bitpayX->method = 4;
|
||||
$bitpayX->icon = 'bitcoin';
|
||||
$bitpayX->icon = 'wallet';
|
||||
array_push($data, $bitpayX);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.paytaro_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = config('v2board.paytaro_name', '聚合支付');
|
||||
$obj->method = 5;
|
||||
$obj->icon = 'wallet';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
private function alipayF2F ($tradeNo, $totalAmount) {
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
if (empty($request->input('trade_no'))) {
|
||||
abort(500, '参数有误');
|
||||
}
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
if ($order->status !== 0) {
|
||||
abort(500, '只可以取消待支付订单');
|
||||
}
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->cancel()) {
|
||||
abort(500, '取消失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
private function alipayF2F($tradeNo, $totalAmount)
|
||||
{
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId(config('v2board.alipay_appid'));
|
||||
@ -233,16 +332,24 @@ class OrderController extends Controller
|
||||
return $response->getQrCode();
|
||||
}
|
||||
|
||||
private function stripeAlipay ($order) {
|
||||
$exchange = Helper::exchange('CNY', 'HKD');
|
||||
private function stripeAlipay($order)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, '货币转换超时,请稍后再试');
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => 'hkd',
|
||||
'currency' => $currency,
|
||||
'type' => 'alipay',
|
||||
'statement_descriptor' => $order->trade_no,
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'invoice_id' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
@ -251,23 +358,29 @@ class OrderController extends Controller
|
||||
abort(500, '支付网关请求失败');
|
||||
}
|
||||
|
||||
if (!Redis::set($source['id'], $order->trade_no)) {
|
||||
if (!Cache::put($source['id'], $order->trade_no, 3600)) {
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
Redis::expire($source['id'], 3600);
|
||||
return $source['redirect']['url'];
|
||||
}
|
||||
|
||||
private function stripeWepay ($order) {
|
||||
$exchange = Helper::exchange('CNY', 'HKD');
|
||||
private function stripeWepay($order)
|
||||
{
|
||||
$currency = config('v2board.stripe_currency', 'hkd');
|
||||
$exchange = Helper::exchange('CNY', strtoupper($currency));
|
||||
if (!$exchange) {
|
||||
abort(500, '货币转换超时,请稍后再试');
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => 'hkd',
|
||||
'currency' => $currency,
|
||||
'type' => 'wechat',
|
||||
'metadata' => [
|
||||
'user_id' => $order->user_id,
|
||||
'invoice_id' => $order->trade_no,
|
||||
'identifier' => ''
|
||||
],
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
@ -275,17 +388,17 @@ class OrderController extends Controller
|
||||
if (!$source['wechat']['qr_code_url']) {
|
||||
abort(500, '支付网关请求失败');
|
||||
}
|
||||
if (!Redis::set($source['id'], $order->trade_no)) {
|
||||
if (!Cache::put($source['id'], $order->trade_no, 3600)) {
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
Redis::expire($source['id'], 3600);
|
||||
return $source['wechat']['qr_code_url'];
|
||||
}
|
||||
|
||||
private function bitpayX ($order) {
|
||||
private function bitpayX($order)
|
||||
{
|
||||
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));
|
||||
$params = [
|
||||
'merchant_order_id' => 'V2Board_' . $order->trade_no,
|
||||
'merchant_order_id' => $order->trade_no,
|
||||
'price_amount' => $order->total_amount / 100,
|
||||
'price_currency' => 'CNY',
|
||||
'title' => '支付单号:' . $order->trade_no,
|
||||
@ -297,7 +410,20 @@ class OrderController extends Controller
|
||||
$strToSign = $bitpayX->prepareSignId($params['merchant_order_id']);
|
||||
$params['token'] = $bitpayX->sign($strToSign);
|
||||
$result = $bitpayX->mprequest($params);
|
||||
Log::info('bitpayXSubmit: ' . json_encode($result));
|
||||
// Log::info('bitpayXSubmit: ' . json_encode($result));
|
||||
return isset($result['payment_url']) ? $result['payment_url'] : false;
|
||||
}
|
||||
|
||||
private function payTaro($order)
|
||||
{
|
||||
$payTaro = new PayTaro(config('v2board.paytaro_app_id'), config('v2board.paytaro_app_secret'));
|
||||
$result = $payTaro->pay([
|
||||
'app_id' => config('v2board.paytaro_app_id'),
|
||||
'out_trade_no' => $order->trade_no,
|
||||
'total_amount' => $order->total_amount,
|
||||
'notify_url' => url('/api/v1/guest/order/payTaroNotify'),
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]);
|
||||
return $result;
|
||||
}
|
||||
}
|
30
app/Http/Controllers/User/PlanController.php
Executable file
30
app/Http/Controllers/User/PlanController.php
Executable file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Plan;
|
||||
|
||||
class PlanController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$plan = Plan::where('id', $request->input('id'))
|
||||
->first();
|
||||
if (!$plan) {
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
return response([
|
||||
'data' => $plan
|
||||
]);
|
||||
}
|
||||
$plan = Plan::where('show', 1)
|
||||
->orderBy('sort', 'ASC')
|
||||
->get();
|
||||
return response([
|
||||
'data' => $plan
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,23 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\UserService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use App\Models\User;
|
||||
|
||||
use App\Utils\Helper;
|
||||
|
||||
class ServerController extends Controller {
|
||||
public function fetch (Request $request) {
|
||||
class ServerController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$server = [];
|
||||
if ($user->expired_at > time()) {
|
||||
$userService = new UserService();
|
||||
if ($userService->isAvailable($user)) {
|
||||
$servers = Server::where('show', 1)
|
||||
->orderBy('name')
|
||||
->orderBy('sort', 'ASC')
|
||||
->get();
|
||||
foreach ($servers as $item) {
|
||||
$groupId = json_decode($item['group_id']);
|
||||
@ -28,37 +32,40 @@ class ServerController extends Controller {
|
||||
}
|
||||
for ($i = 0; $i < count($server); $i++) {
|
||||
$server[$i]['link'] = Helper::buildVmessLink($server[$i], $user);
|
||||
$server[$i]['last_check_at'] = Redis::get('server_last_check_at_' . $server[$i]['id']);
|
||||
if ($server[$i]['parent_id']) {
|
||||
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['parent_id']);
|
||||
} else {
|
||||
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['id']);
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $server
|
||||
]);
|
||||
}
|
||||
|
||||
public function logFetch (Request $request) {
|
||||
public function logFetch(Request $request)
|
||||
{
|
||||
$type = $request->input('type') ? $request->input('type') : 0;
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$serverLogModel = ServerLog::where('user_id', $request->session()->get('id'))
|
||||
->orderBy('created_at', 'DESC');
|
||||
switch ($type) {
|
||||
case 0: $serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')));
|
||||
case 0:
|
||||
$serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')));
|
||||
break;
|
||||
case 1: $serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')) - 604800);
|
||||
case 1:
|
||||
$serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')) - 604800);
|
||||
break;
|
||||
case 2: $serverLogModel->where('created_at', '>=', strtotime(date('Y-m-1')));
|
||||
case 2:
|
||||
$serverLogModel->where('created_at', '>=', strtotime(date('Y-m-1')));
|
||||
}
|
||||
$sum = [
|
||||
'u' => $serverLogModel->sum('u'),
|
||||
'd' => $serverLogModel->sum('d')
|
||||
];
|
||||
$total = $serverLogModel->count();
|
||||
$res = $serverLogModel->forPage($current, $pageSize)
|
||||
->get();
|
||||
return response([
|
||||
'data' => $res,
|
||||
'total' => $total,
|
||||
'sum' => $sum
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
}
|
20
app/Http/Controllers/User/TelegramController.php
Normal file
20
app/Http/Controllers/User/TelegramController.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\TelegramService;
|
||||
|
||||
class TelegramController extends Controller
|
||||
{
|
||||
public function getBotInfo()
|
||||
{
|
||||
$telegramService = new TelegramService();
|
||||
$response = $telegramService->getMe();
|
||||
return response([
|
||||
'data' => [
|
||||
'username' => $response->result->username
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,18 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Requests\TicketSave;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\TicketSave;
|
||||
use App\Http\Requests\User\TicketWithdraw;
|
||||
use App\Jobs\SendTelegramJob;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketMessage;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
@ -47,8 +50,12 @@ class TicketController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (TicketSave $request) {
|
||||
public function save(TicketSave $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
if ((int)Ticket::where('status', 0)->where('user_id', $request->session()->get('id'))->count()) {
|
||||
abort(500, '存在其他工单尚未处理');
|
||||
}
|
||||
$ticket = Ticket::create(array_merge($request->only([
|
||||
'subject',
|
||||
'level'
|
||||
@ -70,12 +77,14 @@ class TicketController extends Controller
|
||||
abort(500, '工单创建失败');
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function reply (Request $request) {
|
||||
public function reply(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -106,13 +115,15 @@ class TicketController extends Controller
|
||||
abort(500, '工单回复失败');
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function close (Request $request) {
|
||||
public function close(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -131,9 +142,59 @@ class TicketController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function getLastMessage ($ticketId) {
|
||||
private function getLastMessage($ticketId)
|
||||
{
|
||||
return TicketMessage::where('ticket_id', $ticketId)
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
}
|
||||
|
||||
public function withdraw(TicketWithdraw $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
$subject = '[提现申请]本工单由系统发出';
|
||||
$ticket = Ticket::create([
|
||||
'subject' => $subject,
|
||||
'level' => 2,
|
||||
'user_id' => $request->session()->get('id'),
|
||||
'last_reply_user_id' => $request->session()->get('id')
|
||||
]);
|
||||
if (!$ticket) {
|
||||
DB::rollback();
|
||||
abort(500, '工单创建失败');
|
||||
}
|
||||
$methodText = [
|
||||
'alipay' => '支付宝',
|
||||
'paypal' => '贝宝(Paypal)',
|
||||
'usdt' => 'USDT',
|
||||
'btc' => '比特币'
|
||||
];
|
||||
$message = "提现方式:{$methodText[$request->input('withdraw_method')]}\r\n提现账号:{$request->input('withdraw_account')}\r\n";
|
||||
$ticketMessage = TicketMessage::create([
|
||||
'user_id' => $request->session()->get('id'),
|
||||
'ticket_id' => $ticket->id,
|
||||
'message' => $message
|
||||
]);
|
||||
if (!$ticketMessage) {
|
||||
DB::rollback();
|
||||
abort(500, '工单创建失败');
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
private function sendNotify(Ticket $ticket, TicketMessage $ticketMessage)
|
||||
{
|
||||
if (!config('v2board.telegram_bot_enable', 0)) return;
|
||||
$users = User::where('is_admin', 1)
|
||||
->where('telegram_id', '!=', NULL)
|
||||
->get();
|
||||
foreach ($users as $user) {
|
||||
$text = "📮工单提醒 #{$ticket->id}\n———————————————\n主题:\n`{$ticket->subject}`\n内容:\n`{$ticketMessage->message}`";
|
||||
SendTelegramJob::dispatch($user->telegram_id, $text);
|
||||
}
|
||||
}
|
||||
}
|
82
app/Http/Controllers/User/TutorialController.php
Normal file
82
app/Http/Controllers/User/TutorialController.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Tutorial;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TutorialController extends Controller
|
||||
{
|
||||
public function getSubscribeUrl(Request $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
return response([
|
||||
'data' => [
|
||||
'subscribe_url' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token']
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAppleID(Request $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if ($user->expired_at < time()) {
|
||||
return response([
|
||||
'data' => [
|
||||
]
|
||||
]);
|
||||
}
|
||||
return response([
|
||||
'data' => [
|
||||
'apple_id' => config('v2board.apple_id'),
|
||||
'apple_id_password' => config('v2board.apple_id_password')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$tutorial = Tutorial::where('show', 1)
|
||||
->where('id', $request->input('id'))
|
||||
->first();
|
||||
if (!$tutorial) {
|
||||
abort(500, '教程不存在');
|
||||
}
|
||||
return response([
|
||||
'data' => $tutorial
|
||||
]);
|
||||
}
|
||||
$tutorial = Tutorial::select(['id', 'category_id', 'title'])
|
||||
->where('show', 1)
|
||||
->orderBy('sort', 'ASC')
|
||||
->get()
|
||||
->groupBy('category_id');
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$response = [
|
||||
'data' => [
|
||||
'tutorials' => $tutorial,
|
||||
'safe_area_var' => [
|
||||
'subscribe_url' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'],
|
||||
'app_name' => config('v2board.app_name', 'V2board'),
|
||||
'apple_id' => $user->expired_at > time() || $user->expired_at === NULL ? config('v2board.apple_id', '本站暂无提供AppleID信息') : '账号过期或未订阅',
|
||||
'apple_id_password' => $user->expired_at > time() || $user->expired_at === NULL ? config('v2board.apple_id_password', '本站暂无提供AppleID信息') : '账号过期或未订阅'
|
||||
]
|
||||
]
|
||||
];
|
||||
// fuck support shadowrocket urlsafeb64 subscribe
|
||||
$response['data']['safe_area_var']['b64_subscribe_url'] = str_replace(
|
||||
array('+', '/', '='),
|
||||
array('-', '_', ''),
|
||||
base64_encode($response['data']['safe_area_var']['subscribe_url'])
|
||||
);
|
||||
// end
|
||||
// fuck support surge urlencode subscribe
|
||||
$response['data']['safe_area_var']['ue_subscribe_url'] = urlencode($response['data']['safe_area_var']['subscribe_url']);
|
||||
// end
|
||||
return response($response);
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Requests\UserUpdate;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\UserUpdate;
|
||||
use App\Http\Requests\User\UserChangePassword;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
@ -15,24 +16,26 @@ use App\Models\ServerLog;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function logout (Request $request) {
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$request->session()->flush();
|
||||
return response([
|
||||
'data' => $request->session()->flush()
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function changePassword (Request $request) {
|
||||
if (empty($request->input('old_password'))) {
|
||||
abort(500, '旧密码不能为空');
|
||||
}
|
||||
if (empty($request->input('new_password'))) {
|
||||
abort(500, '新密码不能为空');
|
||||
}
|
||||
public function changePassword(UserChangePassword $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!password_verify($request->input('old_password'), $user->password)) {
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$request->input('old_password'),
|
||||
$user->password)
|
||||
) {
|
||||
abort(500, '旧密码有误');
|
||||
}
|
||||
$user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
@ -42,19 +45,25 @@ class UserController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function info (Request $request) {
|
||||
public function info(Request $request)
|
||||
{
|
||||
$user = User::where('id', $request->session()->get('id'))
|
||||
->select([
|
||||
'email',
|
||||
'transfer_enable',
|
||||
'last_login_at',
|
||||
'created_at',
|
||||
'enable',
|
||||
'banned',
|
||||
'is_admin',
|
||||
'remind_expire',
|
||||
'remind_traffic',
|
||||
'expired_at',
|
||||
'balance',
|
||||
'commission_balance'
|
||||
'commission_balance',
|
||||
'plan_id',
|
||||
'discount',
|
||||
'commission_rate',
|
||||
'telegram_id'
|
||||
])
|
||||
->first();
|
||||
$user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon';
|
||||
@ -63,7 +72,8 @@ class UserController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function getStat (Request $request) {
|
||||
public function getStat(Request $request)
|
||||
{
|
||||
$stat = [
|
||||
Order::where('status', 0)
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
@ -79,7 +89,8 @@ class UserController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function getSubscribe (Request $request) {
|
||||
public function getSubscribe(Request $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if ($user->plan_id) {
|
||||
$user['plan'] = Plan::find($user->plan_id);
|
||||
@ -93,7 +104,8 @@ class UserController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function resetSecurity (Request $request) {
|
||||
public function resetSecurity(Request $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$user->v2ray_uuid = Helper::guid(true);
|
||||
$user->token = Helper::guid();
|
||||
@ -101,11 +113,12 @@ class UserController extends Controller
|
||||
abort(500, '重置失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
'data' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user->token
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (UserUpdate $request) {
|
||||
public function update(UserUpdate $request)
|
||||
{
|
||||
$updateData = $request->only([
|
||||
'remind_expire',
|
||||
'remind_traffic'
|
||||
@ -115,7 +128,9 @@ class UserController extends Controller
|
||||
if (!$user) {
|
||||
abort(500, '该用户不存在');
|
||||
}
|
||||
if (!$user->update($updateData)) {
|
||||
try {
|
||||
$user->update($updateData);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
|
||||
@ -123,4 +138,26 @@ class UserController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function transfer(Request $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, '该用户不存在');
|
||||
}
|
||||
if ($request->input('transfer_amount') <= 0) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
if ($request->input('transfer_amount') > $user->commission_balance) {
|
||||
abort(500, '推广佣金余额不足');
|
||||
}
|
||||
$user->commission_balance = $user->commission_balance - $request->input('transfer_amount');
|
||||
$user->balance = $user->balance + $request->input('transfer_amount');
|
||||
if (!$user->save()) {
|
||||
abort(500, '划转失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ class Kernel extends HttpKernel
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\App\Http\Middleware\ForceJson::class,
|
||||
\App\Http\Middleware\CORS::class,
|
||||
'throttle:60,1',
|
||||
'throttle:120,1',
|
||||
'bindings',
|
||||
],
|
||||
];
|
||||
|
@ -14,7 +14,7 @@ class Authenticate extends Middleware
|
||||
*/
|
||||
protected function redirectTo($request)
|
||||
{
|
||||
if (! $request->expectsJson()) {
|
||||
if (!$request->expectsJson()) {
|
||||
return route('login');
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ class CORS
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$origin = $request->header('origin');
|
||||
if(empty($origin)){
|
||||
if (empty($origin)) {
|
||||
$referer = $request->header('referer');
|
||||
if(!empty($referer)&&preg_match("/^((https|http):\/\/)?([^\/]+)/i", $referer, $matches)){
|
||||
if (!empty($referer) && preg_match("/^((https|http):\/\/)?([^\/]+)/i", $referer, $matches)) {
|
||||
$origin = $matches[0];
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use App\Models\User;
|
||||
|
||||
class Server
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$token = $request->input('token');
|
||||
if (empty($token)) {
|
||||
abort(500, 'token is null');
|
||||
}
|
||||
if ($token !== config('v2board.server_token')) {
|
||||
abort(500, 'token is error');
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -15,6 +15,13 @@ class User
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($request->input('access_token')) {
|
||||
$user = \App\Models\User::where('token', $request->input('access_token'))->first();
|
||||
if ($user) {
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
}
|
||||
}
|
||||
if (!$request->session()->get('id')) {
|
||||
abort(403, '未登录或登陆已过期');
|
||||
}
|
||||
|
@ -6,40 +6,74 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ConfigSave extends FormRequest
|
||||
{
|
||||
public static function filter() {
|
||||
return [
|
||||
'invite_force',
|
||||
'invite_commission',
|
||||
'invite_gen_limit',
|
||||
'invite_never_expire',
|
||||
'stop_register',
|
||||
'email_verify',
|
||||
'app_name',
|
||||
'app_url',
|
||||
'subscribe_url',
|
||||
'plan_update_fee',
|
||||
'plan_is_update',
|
||||
CONST RULES = [
|
||||
// invite & commission
|
||||
'safe_mode_enable' => 'in:0,1',
|
||||
'invite_force' => 'in:0,1',
|
||||
'invite_commission' => 'integer',
|
||||
'invite_gen_limit' => 'integer',
|
||||
'invite_never_expire' => 'in:0,1',
|
||||
'commission_first_time_enable' => 'in:0,1',
|
||||
'commission_auto_check_enable' => 'in:0,1',
|
||||
// site
|
||||
'stop_register' => 'in:0,1',
|
||||
'email_verify' => 'in:0,1',
|
||||
'app_name' => '',
|
||||
'app_description' => '',
|
||||
'app_url' => 'nullable|url',
|
||||
'subscribe_url' => 'nullable|url',
|
||||
'try_out_enable' => 'in:0,1',
|
||||
'try_out_plan_id' => 'integer',
|
||||
'try_out_hour' => 'numeric',
|
||||
'email_whitelist_enable' => 'in:0,1',
|
||||
'email_whitelist_suffix' => '',
|
||||
'email_gmail_limit_enable' => 'in:0,1',
|
||||
// subscribe
|
||||
'plan_change_enable' => 'in:0,1',
|
||||
'reset_traffic_method' => 'in:0,1',
|
||||
'renew_reset_traffic_enable' => 'in:0,1',
|
||||
// server
|
||||
'server_token',
|
||||
'server_token' => 'nullable|min:16',
|
||||
'server_license' => 'nullable',
|
||||
'server_log_level' => 'nullable|in:debug,info,warning,error,none',
|
||||
// alipay
|
||||
'alipay_enable',
|
||||
'alipay_appid',
|
||||
'alipay_pubkey',
|
||||
'alipay_privkey',
|
||||
'alipay_enable' => 'in:0,1',
|
||||
'alipay_appid' => 'nullable|integer|min:16',
|
||||
'alipay_pubkey' => 'max:2048',
|
||||
'alipay_privkey' => 'max:2048',
|
||||
// stripe
|
||||
'stripe_sk_live',
|
||||
'stripe_pk_live',
|
||||
'stripe_alipay_enable',
|
||||
'stripe_wepay_enable',
|
||||
'stripe_webhook_key',
|
||||
// bitpayx,
|
||||
'bitpayx_enable',
|
||||
'bitpayx_appsecret',
|
||||
'stripe_alipay_enable' => 'in:0,1',
|
||||
'stripe_wepay_enable' => 'in:0,1',
|
||||
'stripe_sk_live' => '',
|
||||
'stripe_pk_live' => '',
|
||||
'stripe_webhook_key' => '',
|
||||
'stripe_currency' => 'in:hkd,usd,sgd,eur,gbp',
|
||||
// bitpayx
|
||||
'bitpayx_name' => '',
|
||||
'bitpayx_enable' => 'in:0,1',
|
||||
'bitpayx_appsecret' => '',
|
||||
// paytaro
|
||||
'paytaro_name' => '',
|
||||
'paytaro_enable' => 'in:0,1',
|
||||
'paytaro_app_id' => '',
|
||||
'paytaro_app_secret' => '',
|
||||
// frontend
|
||||
'frontend_theme_sidebar' => 'in:dark,light',
|
||||
'frontend_theme_header' => 'in:dark,light',
|
||||
'frontend_theme_color' => 'in:default,darkblue,black',
|
||||
'frontend_background_url' => 'nullable|url',
|
||||
// tutorial
|
||||
'apple_id',
|
||||
'apple_id_password'
|
||||
'apple_id' => 'email',
|
||||
'apple_id_password' => '',
|
||||
// email
|
||||
'email_template' => '',
|
||||
// telegram
|
||||
'telegram_bot_enable' => 'in:0,1',
|
||||
'telegram_bot_token' => '',
|
||||
'telegram_discuss_id' => '',
|
||||
'telegram_channel_id' => ''
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
@ -47,37 +81,15 @@ class ConfigSave extends FormRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'invite_force' => 'in:0,1',
|
||||
'invite_commission' => 'integer',
|
||||
'invite_gen_limit' => 'integer',
|
||||
'invite_never_expire' => 'in:0,1',
|
||||
'stop_register' => 'in:0,1',
|
||||
'email_verify' => 'in:0,1',
|
||||
'app_url' => 'url',
|
||||
'subscribe_url' => 'url',
|
||||
'plan_update_fee' => 'numeric',
|
||||
'plan_is_update' => 'in:0,1',
|
||||
// server
|
||||
'server_token' => 'min:16',
|
||||
// alipay
|
||||
'alipay_enable' => 'in:0,1',
|
||||
'alipay_appid' => 'integer|min:16',
|
||||
'alipay_pubkey' => 'max:2048',
|
||||
'alipay_privkey' => 'max:2048',
|
||||
// stripe
|
||||
'stripe_alipay_enable' => 'in:0,1',
|
||||
'stripe_wepay_enable' => 'in:0,1',
|
||||
// bitpayx
|
||||
'bitpayx_enable' => 'in:0,1',
|
||||
// tutorial
|
||||
'apple_id' => 'email'
|
||||
];
|
||||
return self::RULES;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
// illiteracy prompt
|
||||
return [
|
||||
'app_url.url' => '站点URL格式不正确,必须携带http(s)://',
|
||||
'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
41
app/Http/Requests/Admin/CouponSave.php
Normal file
41
app/Http/Requests/Admin/CouponSave.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CouponSave extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'type' => 'required|in:1,2',
|
||||
'value' => 'required|integer',
|
||||
'started_at' => 'required|integer',
|
||||
'ended_at' => 'required|integer',
|
||||
'limit_use' => 'nullable|integer'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'name.required' => '名称不能为空',
|
||||
'type.required' => '类型不能为空',
|
||||
'type.in' => '类型格式有误',
|
||||
'value.required' => '金额或比例不能为空',
|
||||
'value.integer' => '金额或比例格式有误',
|
||||
'started_at.required' => '开始时间不能为空',
|
||||
'started_at.integer' => '开始时间格式有误',
|
||||
'ended_at.required' => '结束时间不能为空',
|
||||
'ended_at.integer' => '结束时间格式有误',
|
||||
'limit_use.integer' => '使用次数格式有误'
|
||||
];
|
||||
}
|
||||
}
|
34
app/Http/Requests/Admin/MailSend.php
Normal file
34
app/Http/Requests/Admin/MailSend.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MailSend extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'type' => 'required|in:1,2,3,4',
|
||||
'subject' => 'required',
|
||||
'content' => 'required',
|
||||
'receiver' => 'array'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'type.required' => '发送类型不能为空',
|
||||
'type.in' => '发送类型格式有误',
|
||||
'subject.required' => '主题不能为空',
|
||||
'content.required' => '内容不能为空',
|
||||
'receiver.array' => '收件人格式有误'
|
||||
];
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ class NoticeSave extends FormRequest
|
||||
return [
|
||||
'title' => 'required',
|
||||
'content' => 'required',
|
||||
'img_url' => 'url'
|
||||
'img_url' => 'nullable|url'
|
||||
];
|
||||
}
|
||||
|
||||
|
34
app/Http/Requests/Admin/OrderAssign.php
Normal file
34
app/Http/Requests/Admin/OrderAssign.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderAssign extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'plan_id' => 'required',
|
||||
'email' => 'required',
|
||||
'total_amount' => 'required',
|
||||
'cycle' => 'required|in:month_price,quarter_price,half_year_price,year_price,onetime_price,reset_price'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'plan_id.required' => '订阅不能为空',
|
||||
'email.required' => '邮箱不能为空',
|
||||
'total_amount.required' => '支付金额不能为空',
|
||||
'cycle.required' => '订阅周期不能为空',
|
||||
'cycle.in' => '订阅周期格式有误'
|
||||
];
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ class OrderUpdate extends FormRequest
|
||||
{
|
||||
return [
|
||||
'status' => 'in:0,1,2,3',
|
||||
'commission_status' => 'in:0,1'
|
||||
'commission_status' => 'in:0,1,2'
|
||||
];
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ class OrderUpdate extends FormRequest
|
||||
{
|
||||
return [
|
||||
'status.in' => '销售状态格式不正确',
|
||||
'commission_status.in' => '续费状态格式不正确'
|
||||
'commission_status.in' => '佣金状态格式不正确'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,18 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PlanSave extends FormRequest
|
||||
{
|
||||
CONST RULES = [
|
||||
'name' => 'required',
|
||||
'content' => '',
|
||||
'group_id' => 'required',
|
||||
'transfer_enable' => 'required',
|
||||
'month_price' => 'nullable|integer',
|
||||
'quarter_price' => 'nullable|integer',
|
||||
'half_year_price' => 'nullable|integer',
|
||||
'year_price' => 'nullable|integer',
|
||||
'onetime_price' => 'nullable|integer',
|
||||
'reset_price' => 'nullable|integer'
|
||||
];
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
@ -13,27 +25,23 @@ class PlanSave extends FormRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'group_id' => 'required',
|
||||
'transfer_enable' => 'required',
|
||||
'month_price' => 'required|numeric',
|
||||
'quarter_price' => 'required|numeric',
|
||||
'half_year_price' => 'required|numeric',
|
||||
'year_price' => 'required|numeric'
|
||||
];
|
||||
return self::RULES;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'name.required' => '套餐名称不能为空',
|
||||
'type.required' => '套餐类型不能为空',
|
||||
'type.in' => '套餐类型格式有误',
|
||||
'group_id.required' => '权限组不能为空',
|
||||
'transfer_enable.required' => '流量不能为空',
|
||||
'month_price.required' => '月付金额不能为空',
|
||||
'quarter_price.required' => '季付金额不能为空',
|
||||
'half_year_price.required' => '半年付金额不能为空',
|
||||
'year_price.required' => '年付金额不能为空'
|
||||
'month_price.integer' => '月付金额格式有误',
|
||||
'quarter_price.integer' => '季付金额格式有误',
|
||||
'half_year_price.integer' => '半年付金额格式有误',
|
||||
'year_price.integer' => '年付金额格式有误',
|
||||
'onetime_price.integer' => '一次性金额有误',
|
||||
'reset_price.integer' => '流量重置包金额有误'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
28
app/Http/Requests/Admin/PlanSort.php
Normal file
28
app/Http/Requests/Admin/PlanSort.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PlanSort extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'plan_ids' => 'required|array'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'plan_ids.required' => '订阅计划ID不能为空',
|
||||
'plan_ids.array' => '订阅计划ID格式有误'
|
||||
];
|
||||
}
|
||||
}
|
@ -6,6 +6,23 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ServerSave extends FormRequest
|
||||
{
|
||||
CONST RULES = [
|
||||
'show' => '',
|
||||
'name' => 'required',
|
||||
'group_id' => 'required|array',
|
||||
'parent_id' => 'nullable|integer',
|
||||
'host' => 'required',
|
||||
'port' => 'required',
|
||||
'server_port' => 'required',
|
||||
'tls' => 'required',
|
||||
'tags' => 'nullable|array',
|
||||
'rate' => 'required|numeric',
|
||||
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic',
|
||||
'networkSettings' => '',
|
||||
'ruleSettings' => '',
|
||||
'tlsSettings' => '',
|
||||
'dnsSettings' => ''
|
||||
];
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
@ -13,17 +30,7 @@ class ServerSave extends FormRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'group_id' => 'required|array',
|
||||
'host' => 'required',
|
||||
'port' => 'required',
|
||||
'server_port' => 'required',
|
||||
'tls' => 'required',
|
||||
'tags' => 'array',
|
||||
'rate' => 'required|numeric',
|
||||
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic'
|
||||
];
|
||||
return self::RULES;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
@ -32,6 +39,7 @@ class ServerSave extends FormRequest
|
||||
'name.required' => '节点名称不能为空',
|
||||
'group_id.required' => '权限组不能为空',
|
||||
'group_id.array' => '权限组格式不正确',
|
||||
'parent_id.integer' => '父ID格式不正确',
|
||||
'host.required' => '节点地址不能为空',
|
||||
'port.required' => '连接端口不能为空',
|
||||
'server_port.required' => '后端服务端口不能为空',
|
||||
|
28
app/Http/Requests/Admin/ServerSort.php
Normal file
28
app/Http/Requests/Admin/ServerSort.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ServerSort extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'server_ids' => 'required|array'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'server_ids.required' => '服务器ID不能为空',
|
||||
'server_ids.array' => '服务器ID格式有误'
|
||||
];
|
||||
}
|
||||
}
|
34
app/Http/Requests/Admin/TutorialSave.php
Normal file
34
app/Http/Requests/Admin/TutorialSave.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class TutorialSave extends FormRequest
|
||||
{
|
||||
CONST RULES = [
|
||||
'title' => 'required',
|
||||
// 1:windows 2:macos 3:ios 4:android 5:linux 6:router
|
||||
'category_id' => 'required|in:1,2,3,4,5,6',
|
||||
'steps' => 'required'
|
||||
];
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return self::RULES;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'title.required' => '标题不能为空',
|
||||
'category_id.required' => '分类不能为空',
|
||||
'category_id.in' => '分类格式不正确',
|
||||
'steps.required' => '教程步骤不能为空'
|
||||
];
|
||||
}
|
||||
}
|
28
app/Http/Requests/Admin/TutorialSort.php
Normal file
28
app/Http/Requests/Admin/TutorialSort.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class TutorialSort extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'tutorial_ids' => 'required|array'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'tutorial_ids.required' => '教程ID不能为空',
|
||||
'tutorial_ids.array' => '教程ID格式有误'
|
||||
];
|
||||
}
|
||||
}
|
@ -6,6 +6,21 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserUpdate extends FormRequest
|
||||
{
|
||||
CONST RULES = [
|
||||
'email' => 'required|email',
|
||||
'password' => 'nullable',
|
||||
'transfer_enable' => 'numeric',
|
||||
'expired_at' => 'nullable|integer',
|
||||
'banned' => 'required|in:0,1',
|
||||
'plan_id' => 'nullable|integer',
|
||||
'commission_rate' => 'nullable|integer|min:0|max:100',
|
||||
'discount' => 'nullable|integer|min:0|max:100',
|
||||
'is_admin' => 'required|in:0,1',
|
||||
'u' => 'integer',
|
||||
'd' => 'integer',
|
||||
'balance' => 'integer',
|
||||
'commission_balance' => 'integer'
|
||||
];
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
@ -13,15 +28,7 @@ class UserUpdate extends FormRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => 'required|email',
|
||||
'transfer_enable' => 'numeric',
|
||||
'expired_at' => 'integer',
|
||||
'banned' => 'required|in:0,1',
|
||||
'is_admin' => 'required|in:0,1',
|
||||
'plan_id' => 'integer',
|
||||
'commission_rate' => 'nullable|integer|min:0|max:100'
|
||||
];
|
||||
return self::RULES;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
@ -39,7 +46,15 @@ class UserUpdate extends FormRequest
|
||||
'commission_rate.integer' => '推荐返利比例格式不正确',
|
||||
'commission_rate.nullable' => '推荐返利比例格式不正确',
|
||||
'commission_rate.min' => '推荐返利比例最小为0',
|
||||
'commission_rate.max' => '推荐返利比例最大为100'
|
||||
'commission_rate.max' => '推荐返利比例最大为100',
|
||||
'discount.integer' => '专属折扣比例格式不正确',
|
||||
'discount.nullable' => '专属折扣比例格式不正确',
|
||||
'discount.min' => '专属折扣比例最小为0',
|
||||
'discount.max' => '专属折扣比例最大为100',
|
||||
'u.integer' => '上行流量格式不正确',
|
||||
'd.integer' => '下行流量格式不正确',
|
||||
'balance.integer' => '余额格式不正确',
|
||||
'commission_balance.integer' => '佣金格式不正确'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ namespace App\Http\Requests\Passport;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ForgetIndex extends FormRequest
|
||||
class AuthForget extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
@ -4,7 +4,7 @@ namespace App\Http\Requests\Passport;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoginIndex extends FormRequest
|
||||
class AuthLogin extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
@ -4,7 +4,7 @@ namespace App\Http\Requests\Passport;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class RegisterIndex extends FormRequest
|
||||
class AuthRegister extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
@ -15,7 +15,7 @@ class OrderSave extends FormRequest
|
||||
{
|
||||
return [
|
||||
'plan_id' => 'required',
|
||||
'cycle' => 'required|in:month_price,quarter_price,half_year_price,year_price'
|
||||
'cycle' => 'required|in:month_price,quarter_price,half_year_price,year_price,onetime_price,reset_price'
|
||||
];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
30
app/Http/Requests/User/TicketWithdraw.php
Normal file
30
app/Http/Requests/User/TicketWithdraw.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class TicketWithdraw extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'withdraw_method' => 'required|in:alipay,paypal,usdt,btc',
|
||||
'withdraw_account' => 'required'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'withdraw_method.required' => '提现方式不能为空',
|
||||
'withdraw_method.in' => '提现方式不支持',
|
||||
'withdraw_account.required' => '提现账号不能为空'
|
||||
];
|
||||
}
|
||||
}
|
30
app/Http/Requests/User/UserChangePassword.php
Normal file
30
app/Http/Requests/User/UserChangePassword.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserChangePassword extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'old_password' => 'required',
|
||||
'new_password' => 'required|min:8'
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'old_password.required' => '旧密码不能为空',
|
||||
'new_password.required' => '新密码不能为空',
|
||||
'new_password.min' => '密码必须大于8位数'
|
||||
];
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
70
app/Http/Routes/AdminRoute.php
Normal file
70
app/Http/Routes/AdminRoute.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace App\Http\Routes;
|
||||
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
|
||||
class AdminRoute
|
||||
{
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group([
|
||||
'prefix' => 'admin',
|
||||
'middleware' => 'admin'
|
||||
], function ($router) {
|
||||
// Config
|
||||
$router->get ('/config/fetch', 'Admin\\ConfigController@fetch');
|
||||
$router->post('/config/save', 'Admin\\ConfigController@save');
|
||||
$router->get ('/config/getEmailTemplate', 'Admin\\ConfigController@getEmailTemplate');
|
||||
$router->post('/config/setTelegramWebhook', 'Admin\\ConfigController@setTelegramWebhook');
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'Admin\\PlanController@fetch');
|
||||
$router->post('/plan/save', 'Admin\\PlanController@save');
|
||||
$router->post('/plan/drop', 'Admin\\PlanController@drop');
|
||||
$router->post('/plan/update', 'Admin\\PlanController@update');
|
||||
$router->post('/plan/sort', 'Admin\\PlanController@sort');
|
||||
// Server
|
||||
$router->get ('/server/fetch', 'Admin\\ServerController@fetch');
|
||||
$router->post('/server/save', 'Admin\\ServerController@save');
|
||||
$router->get ('/server/group/fetch', 'Admin\\ServerController@groupFetch');
|
||||
$router->post('/server/group/save', 'Admin\\ServerController@groupSave');
|
||||
$router->post('/server/group/drop', 'Admin\\ServerController@groupDrop');
|
||||
$router->post('/server/drop', 'Admin\\ServerController@drop');
|
||||
$router->post('/server/update', 'Admin\\ServerController@update');
|
||||
$router->post('/server/copy', 'Admin\\ServerController@copy');
|
||||
$router->post('/server/viewConfig', 'Admin\\ServerController@viewConfig');
|
||||
$router->post('/server/sort', 'Admin\\ServerController@sort');
|
||||
// Order
|
||||
$router->get ('/order/fetch', 'Admin\\OrderController@fetch');
|
||||
$router->post('/order/repair', 'Admin\\OrderController@repair');
|
||||
$router->post('/order/update', 'Admin\\OrderController@update');
|
||||
$router->post('/order/assign', 'Admin\\OrderController@assign');
|
||||
// User
|
||||
$router->get ('/user/fetch', 'Admin\\UserController@fetch');
|
||||
$router->post('/user/update', 'Admin\\UserController@update');
|
||||
$router->get ('/user/getUserInfoById', 'Admin\\UserController@getUserInfoById');
|
||||
// Stat
|
||||
$router->get ('/stat/getOverride', 'Admin\\StatController@getOverride');
|
||||
// Notice
|
||||
$router->get ('/notice/fetch', 'Admin\\NoticeController@fetch');
|
||||
$router->post('/notice/save', 'Admin\\NoticeController@save');
|
||||
$router->post('/notice/update', 'Admin\\NoticeController@update');
|
||||
$router->post('/notice/drop', 'Admin\\NoticeController@drop');
|
||||
// Ticket
|
||||
$router->get ('/ticket/fetch', 'Admin\\TicketController@fetch');
|
||||
$router->post('/ticket/reply', 'Admin\\TicketController@reply');
|
||||
$router->post('/ticket/close', 'Admin\\TicketController@close');
|
||||
// Mail
|
||||
$router->post('/mail/send', 'Admin\\MailController@send');
|
||||
// Coupon
|
||||
$router->get ('/coupon/fetch', 'Admin\\CouponController@fetch');
|
||||
$router->post('/coupon/save', 'Admin\\CouponController@save');
|
||||
$router->post('/coupon/drop', 'Admin\\CouponController@drop');
|
||||
// Tutorial
|
||||
$router->get ('/tutorial/fetch', 'Admin\\TutorialController@fetch');
|
||||
$router->post('/tutorial/save', 'Admin\\TutorialController@save');
|
||||
$router->post('/tutorial/show', 'Admin\\TutorialController@show');
|
||||
$router->post('/tutorial/drop', 'Admin\\TutorialController@drop');
|
||||
$router->post('/tutorial/sort', 'Admin\\TutorialController@sort');
|
||||
});
|
||||
}
|
||||
}
|
21
app/Http/Routes/ClientRoute.php
Normal file
21
app/Http/Routes/ClientRoute.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace App\Http\Routes;
|
||||
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
|
||||
class ClientRoute
|
||||
{
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group([
|
||||
'prefix' => 'client',
|
||||
'middleware' => 'client'
|
||||
], function ($router) {
|
||||
// Client
|
||||
$router->get('/subscribe', 'Client\\ClientController@subscribe');
|
||||
// App
|
||||
$router->get('/app/data', 'Client\\AppController@data');
|
||||
$router->get('/app/config', 'Client\\AppController@config');
|
||||
});
|
||||
}
|
||||
}
|
24
app/Http/Routes/GuestRoute.php
Normal file
24
app/Http/Routes/GuestRoute.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace App\Http\Routes;
|
||||
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
|
||||
class GuestRoute
|
||||
{
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group([
|
||||
'prefix' => 'guest'
|
||||
], function ($router) {
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'Guest\\PlanController@fetch');
|
||||
// Order
|
||||
$router->post('/order/alipayNotify', 'Guest\\OrderController@alipayNotify');
|
||||
$router->post('/order/stripeNotify', 'Guest\\OrderController@stripeNotify');
|
||||
$router->post('/order/bitpayXNotify', 'Guest\\OrderController@bitpayXNotify');
|
||||
$router->post('/order/payTaroNotify', 'Guest\\OrderController@payTaroNotify');
|
||||
// Telegram
|
||||
$router->post('/telegram/webhook', 'Guest\\TelegramController@webhook');
|
||||
});
|
||||
}
|
||||
}
|
27
app/Http/Routes/PassportRoute.php
Normal file
27
app/Http/Routes/PassportRoute.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace App\Http\Routes;
|
||||
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
|
||||
class PassportRoute
|
||||
{
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group([
|
||||
'prefix' => 'passport'
|
||||
], function ($router) {
|
||||
// TODO: 1.1.1 abolish
|
||||
$router->post('/login', 'Passport\\AuthController@login');
|
||||
// Auth
|
||||
$router->post('/auth/register', 'Passport\\AuthController@register');
|
||||
$router->post('/auth/login', 'Passport\\AuthController@login');
|
||||
$router->get ('/auth/token2Login', 'Passport\\AuthController@token2Login');
|
||||
$router->get ('/auth/check', 'Passport\\AuthController@check');
|
||||
$router->post('/auth/forget', 'Passport\\AuthController@forget');
|
||||
// Comm
|
||||
$router->get ('/comm/config', 'Passport\\CommController@config');
|
||||
$router->post('/comm/sendEmailVerify', 'Passport\\CommController@sendEmailVerify');
|
||||
$router->post('/comm/pv', 'Passport\\CommController@pv');
|
||||
});
|
||||
}
|
||||
}
|
19
app/Http/Routes/ServerRoute.php
Normal file
19
app/Http/Routes/ServerRoute.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace App\Http\Routes;
|
||||
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
|
||||
class ServerRoute
|
||||
{
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group([
|
||||
'prefix' => 'server'
|
||||
], function ($router) {
|
||||
$router->any('/{class}/{action}', function($class, $action) {
|
||||
$ctrl = \App::make("\\App\\Http\\Controllers\\Server\\" . ucfirst($class) . "Controller");
|
||||
return \App::call([$ctrl, $action]);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
60
app/Http/Routes/UserRoute.php
Normal file
60
app/Http/Routes/UserRoute.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
namespace App\Http\Routes;
|
||||
|
||||
use Illuminate\Contracts\Routing\Registrar;
|
||||
|
||||
class UserRoute
|
||||
{
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group([
|
||||
'prefix' => 'user',
|
||||
'middleware' => 'user'
|
||||
], function ($router) {
|
||||
// User
|
||||
$router->get ('/resetSecurity', 'User\\UserController@resetSecurity');
|
||||
$router->get ('/logout', 'User\\UserController@logout');
|
||||
$router->get ('/info', 'User\\UserController@info');
|
||||
$router->post('/changePassword', 'User\\UserController@changePassword');
|
||||
$router->post('/update', 'User\\UserController@update');
|
||||
$router->get ('/getSubscribe', 'User\\UserController@getSubscribe');
|
||||
$router->get ('/getStat', 'User\\UserController@getStat');
|
||||
$router->post('/transfer', 'User\\UserController@transfer');
|
||||
// Order
|
||||
$router->post('/order/save', 'User\\OrderController@save');
|
||||
$router->post('/order/checkout', 'User\\OrderController@checkout');
|
||||
$router->get ('/order/check', 'User\\OrderController@check');
|
||||
$router->get ('/order/details', 'User\\OrderController@details');
|
||||
$router->get ('/order/fetch', 'User\\OrderController@fetch');
|
||||
$router->get ('/order/getPaymentMethod', 'User\\OrderController@getPaymentMethod');
|
||||
$router->post('/order/cancel', 'User\\OrderController@cancel');
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'User\\PlanController@fetch');
|
||||
// Invite
|
||||
$router->get ('/invite/save', 'User\\InviteController@save');
|
||||
$router->get ('/invite/fetch', 'User\\InviteController@fetch');
|
||||
$router->get ('/invite/details', 'User\\InviteController@details');
|
||||
// Tutorial
|
||||
$router->get ('/tutorial/getSubscribeUrl', 'User\\TutorialController@getSubscribeUrl');
|
||||
$router->get ('/tutorial/getAppleID', 'User\\TutorialController@getAppleID');
|
||||
$router->get ('/tutorial/fetch', 'User\\TutorialController@fetch');
|
||||
// Notice
|
||||
$router->get ('/notice/fetch', 'User\\NoticeController@fetch');
|
||||
// Ticket
|
||||
$router->post('/ticket/reply', 'User\\TicketController@reply');
|
||||
$router->post('/ticket/close', 'User\\TicketController@close');
|
||||
$router->post('/ticket/save', 'User\\TicketController@save');
|
||||
$router->get ('/ticket/fetch', 'User\\TicketController@fetch');
|
||||
$router->post('/ticket/withdraw', 'User\\TicketController@withdraw');
|
||||
// Server
|
||||
$router->get ('/server/fetch', 'User\\ServerController@fetch');
|
||||
$router->get ('/server/log/fetch', 'User\\ServerController@logFetch');
|
||||
// Coupon
|
||||
$router->post('/coupon/check', 'User\\CouponController@check');
|
||||
// Telegram
|
||||
$router->get ('/telegram/getBotInfo', 'User\\TelegramController@getBotInfo');
|
||||
// Comm
|
||||
$router->get ('/comm/config', 'User\\CommController@config');
|
||||
});
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendEmail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
protected $params;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$params = $this->params;
|
||||
$email = $params['email'];
|
||||
$subject = $params['subject'];
|
||||
Mail::send(
|
||||
$params['template_name'],
|
||||
$params['template_value'],
|
||||
function ($message) use($email, $subject) {
|
||||
$message->to($email)->subject($subject);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
60
app/Jobs/SendEmailJob.php
Normal file
60
app/Jobs/SendEmailJob.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use App\Models\MailLog;
|
||||
|
||||
class SendEmailJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
$this->delay(now()->addSecond(2));
|
||||
$this->onQueue('send_email');
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$params = $this->params;
|
||||
$email = $params['email'];
|
||||
$subject = $params['subject'];
|
||||
$params['template_name'] = 'mail.' . config('v2board.email_template', 'default') . '.' . $params['template_name'];
|
||||
try {
|
||||
Mail::send(
|
||||
$params['template_name'],
|
||||
$params['template_value'],
|
||||
function ($message) use ($email, $subject) {
|
||||
$message->to($email)->subject($subject);
|
||||
}
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
MailLog::create([
|
||||
'email' => $params['email'],
|
||||
'subject' => $params['subject'],
|
||||
'template_name' => $params['template_name'],
|
||||
'error' => isset($error) ? $error : NULL
|
||||
]);
|
||||
}
|
||||
}
|
43
app/Jobs/SendTelegramJob.php
Normal file
43
app/Jobs/SendTelegramJob.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SendTelegramJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
protected $telegramId;
|
||||
protected $text;
|
||||
|
||||
public $tries = 3;
|
||||
public $timeout = 5;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(int $telegramId, string $text)
|
||||
{
|
||||
$this->onQueue('send_telegram');
|
||||
$this->telegramId = $telegramId;
|
||||
$this->text = $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$telegramService = new TelegramService();
|
||||
$telegramService->sendMessage($this->telegramId, $this->text, 'markdown');
|
||||
}
|
||||
}
|
12
app/Models/Coupon.php
Normal file
12
app/Models/Coupon.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Coupon extends Model
|
||||
{
|
||||
protected $table = 'v2_coupon';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
}
|
12
app/Models/MailLog.php
Normal file
12
app/Models/MailLog.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class MailLog extends Model
|
||||
{
|
||||
protected $table = 'v2_mail_log';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ServerLog extends Model
|
||||
{
|
||||
|
12
app/Models/Tutorial.php
Normal file
12
app/Models/Tutorial.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Tutorial extends Model
|
||||
{
|
||||
protected $table = 'v2_tutorial';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
}
|
@ -36,7 +36,6 @@ class RouteServiceProvider extends ServiceProvider
|
||||
public function map()
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
|
||||
//
|
||||
@ -65,9 +64,14 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected function mapApiRoutes()
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
Route::group([
|
||||
'prefix' => '/api/v1',
|
||||
'middleware' => 'api',
|
||||
'namespace' => $this->namespace
|
||||
], function ($router) {
|
||||
foreach (glob(app_path('Http//Routes') . '/*.php') as $file) {
|
||||
$this->app->make('App\\Http\\Routes\\' . basename($file, '.php'))->map($router);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
48
app/Services/CouponService.php
Normal file
48
app/Services/CouponService.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Coupon;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CouponService
|
||||
{
|
||||
public $order;
|
||||
|
||||
public function __construct($code)
|
||||
{
|
||||
$this->coupon = Coupon::where('code', $code)->first();
|
||||
if (!$this->coupon) {
|
||||
abort(500, '优惠券无效');
|
||||
}
|
||||
if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) {
|
||||
abort(500, '优惠券已无可用次数');
|
||||
}
|
||||
if (time() < $this->coupon->started_at) {
|
||||
abort(500, '优惠券还未到可用时间');
|
||||
}
|
||||
if (time() > $this->coupon->ended_at) {
|
||||
abort(500, '优惠券已过期');
|
||||
}
|
||||
}
|
||||
|
||||
public function use(Order $order)
|
||||
{
|
||||
switch ($this->coupon->type) {
|
||||
case 1:
|
||||
$order->discount_amount = $this->coupon->value;
|
||||
break;
|
||||
case 2:
|
||||
$order->discount_amount = $order->total_amount * ($this->coupon->value / 100);
|
||||
break;
|
||||
}
|
||||
if ($this->coupon->limit_use !== NULL) {
|
||||
$this->coupon->limit_use = $this->coupon->limit_use - 1;
|
||||
if (!$this->coupon->save()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
7
app/Services/MailService.php
Normal file
7
app/Services/MailService.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class MailService
|
||||
{
|
||||
}
|
143
app/Services/OrderService.php
Normal file
143
app/Services/OrderService.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class OrderService
|
||||
{
|
||||
public $order;
|
||||
|
||||
public function __construct(Order $order)
|
||||
{
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
public function cancel():bool
|
||||
{
|
||||
$order = $this->order;
|
||||
DB::beginTransaction();
|
||||
$order->status = 2;
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
if ($order->balance_amount) {
|
||||
$userService = new UserService();
|
||||
if (!$userService->addBalance($order->user_id, $order->balance_amount)) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function setOrderType(User $user)
|
||||
{
|
||||
$order = $this->order;
|
||||
if ($order->cycle === 'reset_price') {
|
||||
$order->type = 4;
|
||||
} else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
|
||||
if (!(int)config('v2board.plan_change_enable', 1)) abort(500, '目前不允许更改订阅,请联系客服或提交工单操作');
|
||||
$order->type = 3;
|
||||
$this->getSurplusValue($user, $order);
|
||||
if ($order->surplus_amount >= $order->total_amount) {
|
||||
$order->refund_amount = $order->surplus_amount - $order->total_amount;
|
||||
$order->total_amount = 0;
|
||||
} else {
|
||||
$order->total_amount = $order->total_amount - $order->surplus_amount;
|
||||
}
|
||||
} else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) {
|
||||
$order->type = 2;
|
||||
} else {
|
||||
$order->type = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function setVipDiscount(User $user)
|
||||
{
|
||||
$order = $this->order;
|
||||
if ($user->discount) {
|
||||
$order->discount_amount = $order->discount_amount + ($order->total_amount * ($user->discount / 100));
|
||||
}
|
||||
$order->total_amount = $order->total_amount - $order->discount_amount;
|
||||
}
|
||||
|
||||
public function setInvite(User $user)
|
||||
{
|
||||
$order = $this->order;
|
||||
if ($user->invite_user_id && $order->total_amount > 0) {
|
||||
$order->invite_user_id = $user->invite_user_id;
|
||||
$commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
|
||||
if (!$commissionFirstTime || ($commissionFirstTime && !Order::where('user_id', $user->id)->where('status', 3)->first())) {
|
||||
$inviter = User::find($user->invite_user_id);
|
||||
if ($inviter && $inviter->commission_rate) {
|
||||
$order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
|
||||
} else {
|
||||
$order->commission_balance = $order->total_amount * (config('v2board.invite_commission', 10) / 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getSurplusValue(User $user, Order $order)
|
||||
{
|
||||
if ($user->expired_at === NULL) {
|
||||
$this->getSurplusValueByOneTime($user, $order);
|
||||
} else {
|
||||
$this->getSurplusValueByCycle($user, $order);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function getSurplusValueByOneTime(User $user, Order $order)
|
||||
{
|
||||
$plan = Plan::find($user->plan_id);
|
||||
$trafficUnitPrice = $plan->onetime_price / $plan->transfer_enable;
|
||||
if ($user->discount && $trafficUnitPrice) {
|
||||
$trafficUnitPrice = $trafficUnitPrice - ($trafficUnitPrice * $user->discount / 100);
|
||||
}
|
||||
$notUsedTrafficPrice = $plan->transfer_enable - (($user->u + $user->d) / 1073741824);
|
||||
$result = $trafficUnitPrice * $notUsedTrafficPrice;
|
||||
$orderModel = Order::where('user_id', $user->id)->where('cycle', '!=', 'reset_price')->where('status', 3);
|
||||
$order->surplus_amount = $result > 0 ? $result : 0;
|
||||
$order->surplus_order_ids = json_encode(array_map(function ($v) { return $v['id'];}, $orderModel->get()->toArray()));
|
||||
}
|
||||
|
||||
private function getSurplusValueByCycle(User $user, Order $order)
|
||||
{
|
||||
$strToMonth = [
|
||||
'month_price' => 1,
|
||||
'quarter_price' => 3,
|
||||
'half_year_price' => 6,
|
||||
'year_price' => 12,
|
||||
'onetime_price' => 0
|
||||
];
|
||||
$orderModel = Order::where('user_id', $user->id)
|
||||
->where('cycle', '!=', 'reset_price')
|
||||
->where('status', 3);
|
||||
$surplusAmount = 0;
|
||||
foreach ($orderModel->get() as $item) {
|
||||
$surplusMonth = strtotime("+ {$strToMonth[$item->cycle]}month", $item->created_at->format('U'));
|
||||
if (!$surplusMonth) continue;
|
||||
$surplusMonth = ($surplusMonth - time()) / 2678400 / $strToMonth[$item->cycle];
|
||||
if ($surplusMonth > 0) {
|
||||
$surplusAmount = $surplusAmount + ($item['total_amount'] + $item['balance_amount']) * $surplusMonth;
|
||||
}
|
||||
}
|
||||
if (!$surplusAmount) {
|
||||
return;
|
||||
}
|
||||
$order->surplus_amount = $surplusAmount > 0 ? $surplusAmount : 0;
|
||||
$order->surplus_order_ids = json_encode(array_map(function ($v) { return $v['id'];}, $orderModel->get()->toArray()));
|
||||
}
|
||||
}
|
165
app/Services/ServerService.php
Normal file
165
app/Services/ServerService.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\ServerLog;
|
||||
use App\Models\User;
|
||||
use App\Models\Server;
|
||||
|
||||
class ServerService
|
||||
{
|
||||
|
||||
CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"dns":{},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled":true,"destOverride":["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
|
||||
|
||||
public function getAvailableUsers($groupId)
|
||||
{
|
||||
return User::whereIn('group_id', $groupId)
|
||||
->whereRaw('u + d < transfer_enable')
|
||||
->where(function ($query) {
|
||||
$query->where('expired_at', '>=', time())
|
||||
->orWhere('expired_at', NULL);
|
||||
})
|
||||
->where('banned', 0)
|
||||
->select([
|
||||
'id',
|
||||
'email',
|
||||
't',
|
||||
'u',
|
||||
'd',
|
||||
'transfer_enable',
|
||||
'v2ray_uuid',
|
||||
'v2ray_alter_id',
|
||||
'v2ray_level'
|
||||
])
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getConfig(int $nodeId, int $localPort)
|
||||
{
|
||||
$server = Server::find($nodeId);
|
||||
if (!$server) {
|
||||
abort(500, '节点不存在');
|
||||
}
|
||||
$json = json_decode(self::SERVER_CONFIG);
|
||||
$json->log->loglevel = config('v2board.server_log_level', 'none');
|
||||
$json->inboundDetour[0]->port = (int)$localPort;
|
||||
$json->inbound->port = (int)$server->server_port;
|
||||
$json->inbound->streamSettings->network = $server->network;
|
||||
$this->setDns($server, $json);
|
||||
$this->setNetwork($server, $json);
|
||||
$this->setRule($server, $json);
|
||||
$this->setTls($server, $json);
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
private function setDns(Server $server, object $json)
|
||||
{
|
||||
if ($server->dnsSettings) {
|
||||
$dns = json_decode($server->dnsSettings);
|
||||
if (isset($dns->servers)) {
|
||||
array_push($dns->servers, '1.1.1.1');
|
||||
array_push($dns->servers, 'localhost');
|
||||
}
|
||||
$json->dns = $dns;
|
||||
$json->outbound->settings->domainStrategy = 'UseIP';
|
||||
}
|
||||
}
|
||||
|
||||
private function setNetwork(Server $server, object $json)
|
||||
{
|
||||
if ($server->networkSettings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp':
|
||||
$json->inbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'kcp':
|
||||
$json->inbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'ws':
|
||||
$json->inbound->streamSettings->wsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'http':
|
||||
$json->inbound->streamSettings->httpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'domainsocket':
|
||||
$json->inbound->streamSettings->dsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'quic':
|
||||
$json->inbound->streamSettings->quicSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setRule(Server $server, object $json)
|
||||
{
|
||||
if ($server->ruleSettings) {
|
||||
$rules = json_decode($server->ruleSettings);
|
||||
// domain
|
||||
if (isset($rules->domain) && !empty($rules->domain)) {
|
||||
$rules->domain = array_filter($rules->domain);
|
||||
$domainObj = new \StdClass();
|
||||
$domainObj->type = 'field';
|
||||
$domainObj->domain = $rules->domain;
|
||||
$domainObj->outboundTag = 'block';
|
||||
array_push($json->routing->rules, $domainObj);
|
||||
}
|
||||
// protocol
|
||||
if (isset($rules->protocol) && !empty($rules->protocol)) {
|
||||
$rules->protocol = array_filter($rules->protocol);
|
||||
$protocolObj = new \StdClass();
|
||||
$protocolObj->type = 'field';
|
||||
$protocolObj->protocol = $rules->protocol;
|
||||
$protocolObj->outboundTag = 'block';
|
||||
array_push($json->routing->rules, $protocolObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setTls(Server $server, object $json)
|
||||
{
|
||||
if ((int)$server->tls) {
|
||||
$tlsSettings = json_decode($server->tlsSettings);
|
||||
$json->inbound->streamSettings->security = 'tls';
|
||||
$tls = (object)[
|
||||
'certificateFile' => '/home/v2ray.crt',
|
||||
'keyFile' => '/home/v2ray.key'
|
||||
];
|
||||
$json->inbound->streamSettings->tlsSettings = new \StdClass();
|
||||
if (isset($tlsSettings->serverName)) {
|
||||
$json->inbound->streamSettings->tlsSettings->serverName = (string)$tlsSettings->serverName;
|
||||
}
|
||||
if (isset($tlsSettings->allowInsecure)) {
|
||||
$json->inbound->streamSettings->tlsSettings->allowInsecure = (int)$tlsSettings->allowInsecure ? true : false;
|
||||
}
|
||||
$json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
|
||||
}
|
||||
}
|
||||
|
||||
public function log(int $userId, int $serverId, int $u, int $d, float $rate)
|
||||
{
|
||||
if (($u + $d) <= 10240) return;
|
||||
$timestamp = strtotime(date('Y-m-d H:0'));
|
||||
$serverLog = ServerLog::where('log_at', '>=', $timestamp)
|
||||
->where('log_at', '<', $timestamp + 3600)
|
||||
->where('server_id', $serverId)
|
||||
->where('user_id', $userId)
|
||||
->where('rate', $rate)
|
||||
->first();
|
||||
if ($serverLog) {
|
||||
$serverLog->u = $serverLog->u + $u;
|
||||
$serverLog->d = $serverLog->d + $d;
|
||||
$serverLog->save();
|
||||
} else {
|
||||
$serverLog = new ServerLog();
|
||||
$serverLog->user_id = $userId;
|
||||
$serverLog->server_id = $serverId;
|
||||
$serverLog->u = $u;
|
||||
$serverLog->d = $d;
|
||||
$serverLog->rate = $rate;
|
||||
$serverLog->log_at = $timestamp;
|
||||
$serverLog->save();
|
||||
}
|
||||
}
|
||||
}
|
46
app/Services/TelegramService.php
Normal file
46
app/Services/TelegramService.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace App\Services;
|
||||
|
||||
use \Curl\Curl;
|
||||
|
||||
class TelegramService {
|
||||
protected $api;
|
||||
|
||||
public function __construct($token = '')
|
||||
{
|
||||
$this->api = 'http://dev.v2board.com/bot' . config('v2board.telegram_bot_token', $token) . '/';
|
||||
}
|
||||
|
||||
public function sendMessage(int $chatId, string $text, string $parseMode = '')
|
||||
{
|
||||
$this->request('sendMessage', [
|
||||
'chat_id' => $chatId,
|
||||
'text' => $text,
|
||||
'parse_mode' => $parseMode
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMe()
|
||||
{
|
||||
return $this->request('getMe');
|
||||
}
|
||||
|
||||
public function setWebhook(string $url)
|
||||
{
|
||||
return $this->request('setWebhook', [
|
||||
'url' => $url
|
||||
]);
|
||||
}
|
||||
|
||||
private function request(string $method, array $params = [])
|
||||
{
|
||||
$curl = new Curl();
|
||||
$curl->get($this->api . $method . '?' . http_build_query($params));
|
||||
$response = $curl->response;
|
||||
$curl->close();
|
||||
if (!$response->ok) {
|
||||
abort(500, '来自TG的错误:' . $response->description);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
93
app/Services/UserService.php
Normal file
93
app/Services/UserService.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
|
||||
class UserService
|
||||
{
|
||||
public function isAvailable(User $user)
|
||||
{
|
||||
if (!$user->banned && $user->transfer_enable && ($user->expired_at > time() || $user->expired_at === NULL)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getAvailableUsers()
|
||||
{
|
||||
return User::whereRaw('u + d < transfer_enable')
|
||||
->where(function ($query) {
|
||||
$query->where('expired_at', '>=', time())
|
||||
->orWhere('expired_at', NULL);
|
||||
})
|
||||
->where('banned', 0)
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getUnAvailbaleUsers()
|
||||
{
|
||||
return User::where(function ($query) {
|
||||
$query->where('expired_at', '<', time())
|
||||
->orWhere('expired_at', 0);
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->where('plan_id', NULL)
|
||||
->orWhere('transfer_enable', 0);
|
||||
})
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getUsersByIds($ids)
|
||||
{
|
||||
return User::whereIn('id', $ids)->get();
|
||||
}
|
||||
|
||||
public function getAllUsers()
|
||||
{
|
||||
return User::all();
|
||||
}
|
||||
|
||||
public function addBalance(int $userId, int $balance):bool
|
||||
{
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
$user->balance = $user->balance + $balance;
|
||||
if ($user->balance < 0) {
|
||||
return false;
|
||||
}
|
||||
if (!$user->save()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isNotCompleteOrderByUserId(int $userId):bool
|
||||
{
|
||||
$order = Order::whereIn('status', [0, 1])
|
||||
->where('user_id', $userId)
|
||||
->first();
|
||||
if (!$order) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function trafficFetch(int $u, int $d, int $userId):bool
|
||||
{
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
$user->t = time();
|
||||
$user->u = $user->u + $u;
|
||||
$user->d = $user->d + $d;
|
||||
if (!$user->save()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
19
app/Utils/CacheKey.php
Normal file
19
app/Utils/CacheKey.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
class CacheKey
|
||||
{
|
||||
CONST KEYS = [
|
||||
'EMAIL_VERIFY_CODE' => '邮箱验证吗',
|
||||
'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间'
|
||||
];
|
||||
|
||||
public static function get(string $key, $uniqueValue)
|
||||
{
|
||||
if (!in_array($key, array_keys(self::KEYS))) {
|
||||
abort(500, 'key is not in cache key list');
|
||||
}
|
||||
return $key . '_' . $uniqueValue;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user