mirror of
https://github.com/v2board/v2board.git
synced 2025-08-02 21:38:49 +08:00
Compare commits
453 Commits
Author | SHA1 | Date | |
---|---|---|---|
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=
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
/node_modules
|
||||
/config/v2panel.php
|
||||
/config/v2board.php
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/env.example.js
|
||||
|
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
|
||||
|
@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
|
||||
class CheckExpire extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'check:expire';
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
$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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Utils\Helper;
|
||||
use App\Models\Coupon;
|
||||
|
||||
class CheckOrder extends Command
|
||||
{
|
||||
@ -59,15 +60,30 @@ 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->cycle === 'onetime_price') {
|
||||
return $this->buyByOneTime($order, $user, $plan);
|
||||
}
|
||||
return $this->buyByCycle($order, $user, $plan);
|
||||
}
|
||||
|
||||
private function buy ($order, $user) {
|
||||
$plan = Plan::find($order->plan_id);
|
||||
private function buyByCycle(Order $order, User $user, Plan $plan)
|
||||
{
|
||||
// change plan process
|
||||
if ($order->type == 3) {
|
||||
$user->expired_at = time();
|
||||
}
|
||||
if ($order->refund_amount) {
|
||||
$user->balance = $user->balance + $order->refund_amount;
|
||||
}
|
||||
$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);
|
||||
@ -77,15 +93,37 @@ class CheckOrder extends Command
|
||||
}
|
||||
}
|
||||
|
||||
private function getTime ($str, $timestamp) {
|
||||
private function buyByOneTime(Order $order, User $user, Plan $plan)
|
||||
{
|
||||
if ($order->refund_amount) {
|
||||
$user->balance = $user->balance + $order->refund_amount;
|
||||
}
|
||||
$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;
|
||||
if ($user->save()) {
|
||||
$order->status = 3;
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
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,45 @@ class ResetTraffic extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
DB::table('v2_user')->update([
|
||||
$user = User::where('expired_at', '!=', NULL);
|
||||
$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
|
||||
{
|
||||
$date = date('Y-m-d', time());
|
||||
$startAt = strtotime((string)$date);
|
||||
$endAt = (int)$startAt + 24 * 3600;
|
||||
$lastDay = date('d', strtotime('last day of +0 months'));
|
||||
if ((string)$lastDay === '29') {
|
||||
$endAt = (int)$startAt + 72 * 3600;
|
||||
}
|
||||
if ((string)$lastDay === '30') {
|
||||
$endAt = (int)$startAt + 48 * 3600;
|
||||
}
|
||||
$user->where('expired_at', '>=', (int)$startAt)
|
||||
->where('expired_at', '<', (int)$endAt)
|
||||
->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\SendEmail;
|
||||
|
||||
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 - 86400) < time() && $user->expired_at > time()) {
|
||||
SendEmail::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => '在' . config('v2board.app_name', 'V2board') . '的服务即将到期',
|
||||
'template_name' => 'mail.sendRemindExpire',
|
||||
'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', 'mail.sendRemindTraffic')
|
||||
->count();
|
||||
if ($sendCount > 0) return;
|
||||
SendEmail::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => '在' . config('v2board.app_name', 'V2board') . '的流量使用已达到80%',
|
||||
'template_name' => 'mail.sendRemindTraffic',
|
||||
'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()
|
||||
);
|
||||
}
|
||||
}
|
@ -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, '数据库文件不存在');
|
||||
}
|
||||
@ -60,8 +82,10 @@ class V2boardInstall extends Command
|
||||
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, '数据库文件不存在');
|
||||
}
|
||||
@ -53,7 +53,8 @@ class V2boardUpdate extends Command
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,14 @@ namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\ConfigSave;
|
||||
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 fetch () {
|
||||
public function fetch()
|
||||
{
|
||||
// TODO: default should be in Dict
|
||||
return response([
|
||||
'data' => [
|
||||
'invite' => [
|
||||
@ -25,13 +21,22 @@ class ConfigController extends Controller
|
||||
'invite_never_expire' => config('v2board.invite_never_expire', 0)
|
||||
],
|
||||
'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)
|
||||
],
|
||||
'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,17 +45,29 @@ 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_enable' => (int)config('v2board.bitpayx_enable', 0),
|
||||
'bitpayx_appsecret' => config('v2board.bitpayx_appsecret'),
|
||||
// paytaro
|
||||
'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')
|
||||
],
|
||||
'tutorial' => [
|
||||
'apple_id' => config('v2board.apple_id')
|
||||
@ -59,12 +76,13 @@ class ConfigController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
64
app/Http/Controllers/Admin/CouponController.php
Normal file
64
app/Http/Controllers/Admin/CouponController.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?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 {
|
||||
if (!Coupon::find($request->input('id'))->update($params)) {
|
||||
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\SendEmail;
|
||||
|
||||
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) {
|
||||
SendEmail::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => $request->input('subject'),
|
||||
'template_name' => 'mail.sendEmailCustom',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'url' => config('v2board.app_url'),
|
||||
'content' => $request->input('content')
|
||||
]
|
||||
])->onQueue('other_mail');
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
}
|
@ -6,45 +6,40 @@ 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 {
|
||||
if (!Notice::find($request->input('id'))->update($data)) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function drop (Request $request) {
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ use App\Models\Plan;
|
||||
|
||||
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');
|
||||
@ -25,6 +26,9 @@ class OrderController extends Controller
|
||||
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,7 +46,8 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (OrderUpdate $request) {
|
||||
public function update(OrderUpdate $request)
|
||||
{
|
||||
$updateData = $request->only([
|
||||
'status',
|
||||
'commission_status'
|
||||
@ -63,7 +68,8 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function repair (Request $request) {
|
||||
public function repair(Request $request)
|
||||
{
|
||||
if (empty($request->input('trade_no'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
|
@ -9,42 +9,48 @@ 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()
|
||||
]);
|
||||
}
|
||||
|
||||
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
|
||||
User::where('plan_id', $plan->id)
|
||||
->update(['group_id' => $plan->group_id]);
|
||||
if (!$plan->update($params)) {
|
||||
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 +68,8 @@ class PlanController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function update (PlanUpdate $request) {
|
||||
public function update(PlanUpdate $request)
|
||||
{
|
||||
$updateData = $request->only([
|
||||
'show',
|
||||
'renew'
|
||||
|
@ -10,54 +10,72 @@ 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;
|
||||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$server = Server::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['rules'])) {
|
||||
if (!is_object(json_decode($params['rules']))) {
|
||||
abort(500, '审计规则配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['settings'])) {
|
||||
if (!is_object(json_decode($params['settings']))) {
|
||||
abort(500, '传输协议配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
if (!$server->update($params)) {
|
||||
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 +86,8 @@ class ServerController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function groupSave (Request $request) {
|
||||
public function groupSave(Request $request)
|
||||
{
|
||||
if (empty($request->input('name'))) {
|
||||
abort(500, '组名不能为空');
|
||||
}
|
||||
@ -85,7 +104,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 +132,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 +145,18 @@ 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)) {
|
||||
if (!$server->update($params)) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
|
||||
|
@ -10,22 +10,27 @@ 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())
|
||||
->where('status', '3')
|
||||
->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)
|
||||
->count(),
|
||||
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ 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();
|
||||
@ -43,7 +44,8 @@ class TicketController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function reply (Request $request) {
|
||||
public function reply(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -75,7 +77,8 @@ class TicketController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function close (Request $request) {
|
||||
public function close(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
|
74
app/Http/Controllers/Admin/TutorialController.php
Normal file
74
app/Http/Controllers/Admin/TutorialController.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Requests\Admin\TutorialSave;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Tutorial;
|
||||
|
||||
class TutorialController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
return response([
|
||||
'data' => Tutorial::get()
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(TutorialSave $request)
|
||||
{
|
||||
$params = $request->only(array_keys(TutorialSave::RULES));
|
||||
|
||||
if (!$request->input('id')) {
|
||||
if (!Tutorial::create($params)) {
|
||||
abort(500, '创建失败');
|
||||
}
|
||||
} else {
|
||||
if (!Tutorial::find($request->input('id'))->update($params)) {
|
||||
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 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,8 +41,9 @@ 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([
|
||||
@ -46,42 +53,33 @@ class UserController extends Controller
|
||||
'd',
|
||||
'transfer_enable',
|
||||
'expired_at'
|
||||
])->find($id)
|
||||
])->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);
|
||||
} 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)) {
|
||||
if (!$user->update($params)) {
|
||||
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,7 +51,8 @@ class AppController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function config (Request $request) {
|
||||
public function config(Request $request)
|
||||
{
|
||||
if (empty($request->input('server_id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -77,22 +80,28 @@ class AppController extends Controller
|
||||
$json->outbound->streamSettings->network = $server->network;
|
||||
if ($server->settings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp': $json->outbound->streamSettings->tcpSettings = json_decode($server->settings);
|
||||
case 'tcp':
|
||||
$json->outbound->streamSettings->tcpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'kcp': $json->outbound->streamSettings->kcpSettings = json_decode($server->settings);
|
||||
case 'kcp':
|
||||
$json->outbound->streamSettings->kcpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'ws': $json->outbound->streamSettings->wsSettings = json_decode($server->settings);
|
||||
case 'ws':
|
||||
$json->outbound->streamSettings->wsSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'http': $json->outbound->streamSettings->httpSettings = json_decode($server->settings);
|
||||
case 'http':
|
||||
$json->outbound->streamSettings->httpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'domainsocket': $json->outbound->streamSettings->dsSettings = json_decode($server->settings);
|
||||
case 'domainsocket':
|
||||
$json->outbound->streamSettings->dsSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'quic': $json->outbound->streamSettings->quicSettings = json_decode($server->settings);
|
||||
case 'quic':
|
||||
$json->outbound->streamSettings->quicSettings = json_decode($server->settings);
|
||||
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";
|
171
app/Http/Controllers/Client/ClientController.php
Executable file
171
app/Http/Controllers/Client/ClientController.php
Executable file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Middleware\User;
|
||||
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('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 (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->settings) {
|
||||
$wsSettings = json_decode($item->settings);
|
||||
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 clash($user, $server)
|
||||
{
|
||||
$proxy = [];
|
||||
$proxyGroup = [];
|
||||
$proxies = [];
|
||||
$rules = [];
|
||||
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;
|
||||
$array['skip-cert-verify'] = true;
|
||||
}
|
||||
if ($item->network == 'ws') {
|
||||
$array['network'] = $item->network;
|
||||
if ($item->settings) {
|
||||
$wsSettings = json_decode($item->settings);
|
||||
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);
|
||||
}
|
||||
|
||||
array_push($proxyGroup, [
|
||||
'name' => 'auto',
|
||||
'type' => 'url-test',
|
||||
'proxies' => $proxies,
|
||||
'url' => 'https://www.bing.com',
|
||||
'interval' => 300
|
||||
]);
|
||||
array_push($proxyGroup, [
|
||||
'name' => 'fallback-auto',
|
||||
'type' => 'fallback',
|
||||
'proxies' => $proxies,
|
||||
'url' => 'https://www.bing.com',
|
||||
'interval' => 300
|
||||
]);
|
||||
array_push($proxyGroup, [
|
||||
'name' => 'select',
|
||||
'type' => 'select',
|
||||
'proxies' => array_merge($proxies, [
|
||||
'auto',
|
||||
'fallback-auto'
|
||||
])
|
||||
]);
|
||||
|
||||
try {
|
||||
$rules = [];
|
||||
foreach (glob(base_path() . '/resources/rules/' . '*.clash.yaml') as $file) {
|
||||
$rules = array_merge($rules, Yaml::parseFile($file)['Rule']);
|
||||
}
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
$config = [
|
||||
'port' => 7890,
|
||||
'socks-port' => 7891,
|
||||
'allow-lan' => false,
|
||||
'mode' => 'Rule',
|
||||
'log-level' => 'info',
|
||||
'external-controller' => '0.0.0.0:9090',
|
||||
'secret' => '',
|
||||
'Proxy' => $proxy,
|
||||
'Proxy Group' => $proxyGroup,
|
||||
'Rule' => $rules
|
||||
];
|
||||
|
||||
return Yaml::dump($config);
|
||||
}
|
||||
}
|
@ -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'));
|
||||
@ -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
|
||||
|
@ -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()
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,303 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\OrderSave;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Utils\Helper;
|
||||
use Omnipay\Omnipay;
|
||||
use Stripe\Stripe;
|
||||
use Stripe\Source;
|
||||
use Library\BitpayX;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function fetch (Request $request) {
|
||||
$order = Order::where('user_id', $request->session()->get('id'))
|
||||
->orderBy('created_at', 'DESC')
|
||||
->get();
|
||||
$plan = Plan::get();
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
]);
|
||||
}
|
||||
|
||||
public function details (Request $request) {
|
||||
$order = Order::where('user_id', $request->session()->get('id'))
|
||||
->where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
$order['plan'] = Plan::find($order->plan_id);
|
||||
$order['update_fee'] = config('v2board.plan_update_fee', 0.5);
|
||||
if (!$order['plan']) {
|
||||
abort(500, '订阅不存在');
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (OrderSave $request) {
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
$user = User::find($request->session()->get('id'));
|
||||
|
||||
if (!$plan) {
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
|
||||
if (!($plan->show || $user->plan_id == $plan->id)) {
|
||||
abort(500, '该订阅已售罄');
|
||||
}
|
||||
|
||||
if (!$plan->show && !$plan->renew) {
|
||||
abort(500, '该订阅无法续费,请更换其他订阅');
|
||||
}
|
||||
|
||||
if (!(int)$plan[$request->input('cycle')]) {
|
||||
abort(500, '该订阅周期无法进行购买,请选择其他周期');
|
||||
}
|
||||
|
||||
$order = new 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;
|
||||
} else {
|
||||
$order->type = 1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (!$order->save()) {
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => $order->trade_no
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkout (Request $request) {
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$method = $request->input('method');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在或已支付');
|
||||
}
|
||||
switch ($method) {
|
||||
// return type => 0: QRCode / 1: URL
|
||||
case 0:
|
||||
// alipayF2F
|
||||
if (!(int)config('v2board.alipay_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->alipayF2F($tradeNo, $order->total_amount)
|
||||
]);
|
||||
case 2:
|
||||
// stripeAlipay
|
||||
if (!(int)config('v2board.stripe_alipay_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->stripeAlipay($order)
|
||||
]);
|
||||
case 3:
|
||||
// stripeWepay
|
||||
if (!(int)config('v2board.stripe_wepay_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->stripeWepay($order)
|
||||
]);
|
||||
case 4:
|
||||
// bitpayX
|
||||
if (!(int)config('v2board.bitpayx_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->bitpayX($order)
|
||||
]);
|
||||
default:
|
||||
abort(500, '支付方式不存在');
|
||||
}
|
||||
}
|
||||
|
||||
public function check (Request $request) {
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
return response([
|
||||
'data' => $order->status
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPaymentMethod () {
|
||||
$data = [];
|
||||
if ((int)config('v2board.alipay_enable')) {
|
||||
$alipayF2F = new \StdClass();
|
||||
$alipayF2F->name = '支付宝';
|
||||
$alipayF2F->method = 0;
|
||||
$alipayF2F->icon = 'alipay';
|
||||
array_push($data, $alipayF2F);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_alipay_enable')) {
|
||||
$stripeAlipay = new \StdClass();
|
||||
$stripeAlipay->name = '支付宝';
|
||||
$stripeAlipay->method = 2;
|
||||
$stripeAlipay->icon = 'alipay';
|
||||
array_push($data, $stripeAlipay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_wepay_enable')) {
|
||||
$stripeWepay = new \StdClass();
|
||||
$stripeWepay->name = '微信';
|
||||
$stripeWepay->method = 3;
|
||||
$stripeWepay->icon = 'wechat';
|
||||
array_push($data, $stripeWepay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.bitpayx_enable')) {
|
||||
$bitpayX = new \StdClass();
|
||||
$bitpayX->name = '虚拟货币';
|
||||
$bitpayX->method = 4;
|
||||
$bitpayX->icon = 'bitcoin';
|
||||
array_push($data, $bitpayX);
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
private function alipayF2F ($tradeNo, $totalAmount) {
|
||||
$gateway = Omnipay::create('Alipay_AopF2F');
|
||||
$gateway->setSignType('RSA2'); //RSA/RSA2
|
||||
$gateway->setAppId(config('v2board.alipay_appid'));
|
||||
$gateway->setPrivateKey(config('v2board.alipay_privkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey(config('v2board.alipay_pubkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setNotifyUrl(url('/api/v1/guest/order/alipayNotify'));
|
||||
$request = $gateway->purchase();
|
||||
$request->setBizContent([
|
||||
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
|
||||
'out_trade_no' => $tradeNo,
|
||||
'total_amount' => $totalAmount / 100
|
||||
]);
|
||||
/** @var \Omnipay\Alipay\Responses\AopTradePreCreateResponse $response */
|
||||
$response = $request->send();
|
||||
$result = $response->getAlipayResponse();
|
||||
if ($result['code'] !== '10000') {
|
||||
abort(500, $result['sub_msg']);
|
||||
}
|
||||
// 获取收款二维码内容
|
||||
return $response->getQrCode();
|
||||
}
|
||||
|
||||
private function stripeAlipay ($order) {
|
||||
$exchange = Helper::exchange('CNY', 'HKD');
|
||||
if (!$exchange) {
|
||||
abort(500, '货币转换超时,请稍后再试');
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => 'hkd',
|
||||
'type' => 'alipay',
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
]);
|
||||
if (!$source['redirect']['url']) {
|
||||
abort(500, '支付网关请求失败');
|
||||
}
|
||||
|
||||
if (!Redis::set($source['id'], $order->trade_no)) {
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
Redis::expire($source['id'], 3600);
|
||||
return $source['redirect']['url'];
|
||||
}
|
||||
|
||||
private function stripeWepay ($order) {
|
||||
$exchange = Helper::exchange('CNY', 'HKD');
|
||||
if (!$exchange) {
|
||||
abort(500, '货币转换超时,请稍后再试');
|
||||
}
|
||||
Stripe::setApiKey(config('v2board.stripe_sk_live'));
|
||||
$source = Source::create([
|
||||
'amount' => floor($order->total_amount * $exchange),
|
||||
'currency' => 'hkd',
|
||||
'type' => 'wechat',
|
||||
'redirect' => [
|
||||
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
]
|
||||
]);
|
||||
if (!$source['wechat']['qr_code_url']) {
|
||||
abort(500, '支付网关请求失败');
|
||||
}
|
||||
if (!Redis::set($source['id'], $order->trade_no)) {
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
Redis::expire($source['id'], 3600);
|
||||
return $source['wechat']['qr_code_url'];
|
||||
}
|
||||
|
||||
private function bitpayX ($order) {
|
||||
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));
|
||||
$params = [
|
||||
'merchant_order_id' => 'V2Board_' . $order->trade_no,
|
||||
'price_amount' => $order->total_amount / 100,
|
||||
'price_currency' => 'CNY',
|
||||
'title' => '支付单号:' . $order->trade_no,
|
||||
'description' => '充值:' . $order->total_amount / 100 . ' 元',
|
||||
'callback_url' => url('/api/v1/guest/order/bitpayXNotify'),
|
||||
'success_url' => config('v2board.app_url', env('APP_URL')) . '/#/order',
|
||||
'cancel_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
];
|
||||
$strToSign = $bitpayX->prepareSignId($params['merchant_order_id']);
|
||||
$params['token'] = $bitpayX->sign($strToSign);
|
||||
$result = $bitpayX->mprequest($params);
|
||||
Log::info('bitpayXSubmit: ' . json_encode($result));
|
||||
return isset($result['payment_url']) ? $result['payment_url'] : false;
|
||||
}
|
||||
}
|
211
app/Http/Controllers/Passport/AuthController.php
Normal file
211
app/Http/Controllers/Passport/AuthController.php
Normal file
@ -0,0 +1,211 @@
|
||||
<?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;
|
||||
|
||||
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.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 (Cache::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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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($redisKey);
|
||||
}
|
||||
$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)
|
||||
{
|
||||
$redisKey = 'sendEmailVerify:' . $request->input('email');
|
||||
if (Cache::get($redisKey) !== $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($redisKey);
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -7,52 +7,80 @@ 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\SendEmail;
|
||||
use App\Models\InviteCode;
|
||||
use App\Utils\Dict;
|
||||
|
||||
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, '验证码已发送,请过一会在请求');
|
||||
$cacheKey = 'sendEmailVerify:' . $email;
|
||||
if (Cache::get($cacheKey)) {
|
||||
abort(500, '验证码已发送,请过一会再请求');
|
||||
}
|
||||
$code = rand(100000, 999999);
|
||||
$code = Helper::randomChar(6);
|
||||
$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);
|
||||
SendEmail::dispatch([
|
||||
'email' => $email,
|
||||
'subject' => $subject,
|
||||
'template_name' => 'mail.sendEmailVerify',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'code' => $code,
|
||||
'url' => config('v2board.app_url')
|
||||
]
|
||||
])->onQueue('verify_mail');
|
||||
|
||||
Cache::put($cacheKey, $code, 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,46 @@
|
||||
|
||||
namespace App\Http\Controllers\Server;
|
||||
|
||||
use App\Services\ServerService;
|
||||
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\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,8 +57,9 @@ 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([
|
||||
@ -92,15 +94,16 @@ 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, '节点不存在');
|
||||
abort(500, '节点不存在');
|
||||
}
|
||||
$json = json_decode(self::SERVER_CONFIG);
|
||||
$json->inboundDetour[0]->port = (int)$localPort;
|
||||
@ -108,23 +111,54 @@ class DeepbworkController extends Controller
|
||||
$json->inbound->streamSettings->network = $server->network;
|
||||
if ($server->settings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp': $json->inbound->streamSettings->tcpSettings = json_decode($server->settings);
|
||||
case 'tcp':
|
||||
$json->inbound->streamSettings->tcpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'kcp': $json->inbound->streamSettings->kcpSettings = json_decode($server->settings);
|
||||
case 'kcp':
|
||||
$json->inbound->streamSettings->kcpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'ws': $json->inbound->streamSettings->wsSettings = json_decode($server->settings);
|
||||
case 'ws':
|
||||
$json->inbound->streamSettings->wsSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'http': $json->inbound->streamSettings->httpSettings = json_decode($server->settings);
|
||||
case 'http':
|
||||
$json->inbound->streamSettings->httpSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'domainsocket': $json->inbound->streamSettings->dsSettings = json_decode($server->settings);
|
||||
case 'domainsocket':
|
||||
$json->inbound->streamSettings->dsSettings = json_decode($server->settings);
|
||||
break;
|
||||
case 'quic': $json->inbound->streamSettings->quicSettings = json_decode($server->settings);
|
||||
case 'quic':
|
||||
$json->inbound->streamSettings->quicSettings = json_decode($server->settings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($server->rules) {
|
||||
$rules = json_decode($server->rules);
|
||||
// domain
|
||||
if (isset($rules->domain) && !empty($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)) {
|
||||
$protocolObj = new \StdClass();
|
||||
$protocolObj->type = 'field';
|
||||
$protocolObj->protocol = $rules->protocol;
|
||||
$protocolObj->outboundTag = 'block';
|
||||
array_push($json->routing->rules, $protocolObj);
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)$server->tls) {
|
||||
$json->inbound->streamSettings->security = "tls";
|
||||
$tls = (object) array("certificateFile" => "/home/v2ray.crt", "keyFile" => "/home/v2ray.key");
|
||||
$json->inbound->streamSettings->security = 'tls';
|
||||
$tls = (object)[
|
||||
'certificateFile' => '/home/v2ray.crt',
|
||||
'keyFile' => '/home/v2ray.key'
|
||||
];
|
||||
$json->inbound->streamSettings->tlsSettings = new \StdClass();
|
||||
$json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
|
||||
}
|
||||
|
||||
|
183
app/Http/Controllers/Server/PoseidonController.php
Normal file
183
app/Http/Controllers/Server/PoseidonController.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Server;
|
||||
|
||||
use App\Services\ServerService;
|
||||
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 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);
|
||||
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();
|
||||
|
||||
$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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
$server = Server::find($nodeId);
|
||||
if (!$server) {
|
||||
return $this->error("server could not be found", 404);
|
||||
}
|
||||
$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 ($server->rules) {
|
||||
$rules = json_decode($server->rules);
|
||||
// domain
|
||||
if (isset($rules->domain) && !empty($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)) {
|
||||
$protocolObj = new \StdClass();
|
||||
$protocolObj->type = 'field';
|
||||
$protocolObj->protocol = $rules->protocol;
|
||||
$protocolObj->outboundTag = 'block';
|
||||
array_push($json->routing->rules, $protocolObj);
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)$server->tls) {
|
||||
$json->inbound->streamSettings->security = 'tls';
|
||||
$tls = (object)[
|
||||
'certificateFile' => '/home/v2ray.crt',
|
||||
'keyFile' => '/home/v2ray.key'
|
||||
];
|
||||
$json->inbound->streamSettings->tlsSettings = new \StdClass();
|
||||
$json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
|
||||
}
|
||||
|
||||
$json->poseidon = [
|
||||
'license_key' => (string)config('v2board.server_license'),
|
||||
];
|
||||
|
||||
return $this->success($json);
|
||||
}
|
||||
|
||||
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')
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
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,7 +24,8 @@ 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('status', 3)
|
||||
@ -38,7 +40,8 @@ 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();
|
||||
@ -52,7 +55,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 +64,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
|
||||
]);
|
||||
}
|
||||
}
|
501
app/Http/Controllers/User/OrderController.php
Executable file
501
app/Http/Controllers/User/OrderController.php
Executable file
@ -0,0 +1,501 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\OrderSave;
|
||||
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)
|
||||
{
|
||||
$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++) {
|
||||
if ($order[$i]['plan_id'] === $plan[$x]['id']) {
|
||||
$order[$i]['plan'] = $plan[$x];
|
||||
}
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
]);
|
||||
}
|
||||
|
||||
public function details(Request $request)
|
||||
{
|
||||
$order = Order::where('user_id', $request->session()->get('id'))
|
||||
->where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
$order['plan'] = Plan::find($order->plan_id);
|
||||
$order['try_out_plan_id'] = (int)config('v2board.try_out_plan_id');
|
||||
if (!$order['plan']) {
|
||||
abort(500, '订阅不存在');
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
]);
|
||||
}
|
||||
|
||||
private function isNotCompleteOrderByUserId($userId)
|
||||
{
|
||||
$order = Order::whereIn('status', [0, 1])
|
||||
->where('user_id', $userId)
|
||||
->first();
|
||||
if (!$order) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// surplus value
|
||||
private function getSurplusValue(User $user)
|
||||
{
|
||||
$plan = Plan::find($user->plan_id);
|
||||
switch ($plan->type) {
|
||||
case 0: return $this->getSurplusValueByCycle($user, $plan);
|
||||
case 1: return $this->getSurplusValueByOneTime($user, $plan);
|
||||
}
|
||||
}
|
||||
|
||||
private function getSurplusValueByOneTime(User $user, Plan $plan)
|
||||
{
|
||||
$trafficUnitPrice = 0;
|
||||
$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;
|
||||
return $result > 0 ? $result : 0;
|
||||
}
|
||||
|
||||
private function getSurplusValueByCycle(User $user, Plan $plan)
|
||||
{
|
||||
$dayPrice = 0;
|
||||
if ($plan->month_price) {
|
||||
$dayPrice = $plan->month_price / 2592000;
|
||||
} else if ($plan->quarter_price) {
|
||||
$dayPrice = $plan->quarter_price / 7862400;
|
||||
} else if ($plan->half_year_price) {
|
||||
$dayPrice = $plan->half_year_price / 15811200;
|
||||
} else if ($plan->year_price) {
|
||||
$dayPrice = $plan->year_price / 31536000;
|
||||
}
|
||||
// exclude discount
|
||||
if ($user->discount && $dayPrice) {
|
||||
$dayPrice = $dayPrice - ($dayPrice * $user->discount / 100);
|
||||
}
|
||||
$remainingDay = $user->expired_at - time();
|
||||
$result = $remainingDay * $dayPrice;
|
||||
return $result > 0 ? $result : 0;
|
||||
}
|
||||
|
||||
public function save(OrderSave $request)
|
||||
{
|
||||
if ($this->isNotCompleteOrderByUserId($request->session()->get('id'))) {
|
||||
abort(500, '存在未付款订单,请取消后再试');
|
||||
}
|
||||
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
$user = User::find($request->session()->get('id'));
|
||||
|
||||
if (!$plan) {
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
|
||||
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
|
||||
abort(500, '该订阅已售罄');
|
||||
}
|
||||
|
||||
if (!$plan->renew && $user->plan_id == $plan->id) {
|
||||
abort(500, '该订阅无法续费,请更换其他订阅');
|
||||
}
|
||||
|
||||
if ($plan[$request->input('cycle')] === NULL) {
|
||||
abort(500, '该订阅周期无法进行购买,请选择其他周期');
|
||||
}
|
||||
|
||||
if ($request->input('coupon_code')) {
|
||||
$coupon = Coupon::where('code', $request->input('coupon_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, '优惠券已过期');
|
||||
}
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
$order = new 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')];
|
||||
// discount start
|
||||
// coupon
|
||||
if (isset($coupon)) {
|
||||
switch ($coupon->type) {
|
||||
case 1:
|
||||
$order->discount_amount = $coupon->value;
|
||||
break;
|
||||
case 2:
|
||||
$order->discount_amount = $order->total_amount * ($coupon->value / 100);
|
||||
break;
|
||||
}
|
||||
if ($coupon->limit_use !== NULL) {
|
||||
$coupon->limit_use = $coupon->limit_use - 1;
|
||||
if (!$coupon->save()) {
|
||||
DB::rollback();
|
||||
abort(500, '优惠券使用失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
// user
|
||||
if ($user->discount) {
|
||||
$order->discount_amount = $order->discount_amount + ($order->total_amount * ($user->discount / 100));
|
||||
}
|
||||
// discount complete
|
||||
$order->total_amount = $order->total_amount - $order->discount_amount;
|
||||
// discount end
|
||||
// renew and change subscribe process
|
||||
if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
|
||||
if (!(int)config('v2board.plan_change_enable', 1)) abort(500, '目前不允许更改订阅,请联系客服或提交工单');
|
||||
$order->type = 3;
|
||||
$order->surplus_amount = $this->getSurplusValue($user);
|
||||
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;
|
||||
}
|
||||
// invite process
|
||||
if ($user->invite_user_id && $order->total_amount > 0) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
if (!$order->save()) {
|
||||
DB::rollback();
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response([
|
||||
'data' => $order->trade_no
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkout(Request $request)
|
||||
{
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$method = $request->input('method');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在或已支付');
|
||||
}
|
||||
// free process
|
||||
if ($order->total_amount <= 0) {
|
||||
$order->total_amount = 0;
|
||||
$order->status = 1;
|
||||
$order->save();
|
||||
exit();
|
||||
}
|
||||
switch ($method) {
|
||||
// return type => 0: QRCode / 1: URL
|
||||
case 0:
|
||||
// alipayF2F
|
||||
if (!(int)config('v2board.alipay_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->alipayF2F($tradeNo, $order->total_amount)
|
||||
]);
|
||||
case 2:
|
||||
// stripeAlipay
|
||||
if (!(int)config('v2board.stripe_alipay_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 1,
|
||||
'data' => $this->stripeAlipay($order)
|
||||
]);
|
||||
case 3:
|
||||
// stripeWepay
|
||||
if (!(int)config('v2board.stripe_wepay_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'type' => 0,
|
||||
'data' => $this->stripeWepay($order)
|
||||
]);
|
||||
case 4:
|
||||
// bitpayX
|
||||
if (!(int)config('v2board.bitpayx_enable')) {
|
||||
abort(500, '支付方式不可用');
|
||||
}
|
||||
return response([
|
||||
'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)
|
||||
{
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
return response([
|
||||
'data' => $order->status
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPaymentMethod()
|
||||
{
|
||||
$data = [];
|
||||
if ((int)config('v2board.alipay_enable')) {
|
||||
$alipayF2F = new \StdClass();
|
||||
$alipayF2F->name = '支付宝';
|
||||
$alipayF2F->method = 0;
|
||||
$alipayF2F->icon = 'alipay';
|
||||
array_push($data, $alipayF2F);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_alipay_enable')) {
|
||||
$stripeAlipay = new \StdClass();
|
||||
$stripeAlipay->name = '支付宝';
|
||||
$stripeAlipay->method = 2;
|
||||
$stripeAlipay->icon = 'alipay';
|
||||
array_push($data, $stripeAlipay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.stripe_wepay_enable')) {
|
||||
$stripeWepay = new \StdClass();
|
||||
$stripeWepay->name = '微信';
|
||||
$stripeWepay->method = 3;
|
||||
$stripeWepay->icon = 'wechat';
|
||||
array_push($data, $stripeWepay);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.bitpayx_enable')) {
|
||||
$bitpayX = new \StdClass();
|
||||
$bitpayX->name = '聚合支付';
|
||||
$bitpayX->method = 4;
|
||||
$bitpayX->icon = 'wallet';
|
||||
array_push($data, $bitpayX);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.paytaro_enable')) {
|
||||
$obj = new \StdClass();
|
||||
$obj->name = '聚合支付';
|
||||
$obj->method = 5;
|
||||
$obj->icon = 'wallet';
|
||||
array_push($data, $obj);
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
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, '只可以取消待支付订单');
|
||||
}
|
||||
$order->status = 2;
|
||||
if (!$order->save()) {
|
||||
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'));
|
||||
$gateway->setPrivateKey(config('v2board.alipay_privkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setAlipayPublicKey(config('v2board.alipay_pubkey')); // 可以是路径,也可以是密钥内容
|
||||
$gateway->setNotifyUrl(url('/api/v1/guest/order/alipayNotify'));
|
||||
$request = $gateway->purchase();
|
||||
$request->setBizContent([
|
||||
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
|
||||
'out_trade_no' => $tradeNo,
|
||||
'total_amount' => $totalAmount / 100
|
||||
]);
|
||||
/** @var \Omnipay\Alipay\Responses\AopTradePreCreateResponse $response */
|
||||
$response = $request->send();
|
||||
$result = $response->getAlipayResponse();
|
||||
if ($result['code'] !== '10000') {
|
||||
abort(500, $result['sub_msg']);
|
||||
}
|
||||
// 获取收款二维码内容
|
||||
return $response->getQrCode();
|
||||
}
|
||||
|
||||
private function stripeAlipay($order)
|
||||
{
|
||||
$currency = config('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' => $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'
|
||||
]
|
||||
]);
|
||||
if (!$source['redirect']['url']) {
|
||||
abort(500, '支付网关请求失败');
|
||||
}
|
||||
|
||||
if (!Cache::put($source['id'], $order->trade_no, 3600)) {
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
return $source['redirect']['url'];
|
||||
}
|
||||
|
||||
private function stripeWepay($order)
|
||||
{
|
||||
$currency = config('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' => $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'
|
||||
]
|
||||
]);
|
||||
if (!$source['wechat']['qr_code_url']) {
|
||||
abort(500, '支付网关请求失败');
|
||||
}
|
||||
if (!Cache::put($source['id'], $order->trade_no, 3600)) {
|
||||
abort(500, '订单创建失败');
|
||||
}
|
||||
return $source['wechat']['qr_code_url'];
|
||||
}
|
||||
|
||||
private function bitpayX($order)
|
||||
{
|
||||
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));
|
||||
$params = [
|
||||
'merchant_order_id' => $order->trade_no,
|
||||
'price_amount' => $order->total_amount / 100,
|
||||
'price_currency' => 'CNY',
|
||||
'title' => '支付单号:' . $order->trade_no,
|
||||
'description' => '充值:' . $order->total_amount / 100 . ' 元',
|
||||
'callback_url' => url('/api/v1/guest/order/bitpayXNotify'),
|
||||
'success_url' => config('v2board.app_url', env('APP_URL')) . '/#/order',
|
||||
'cancel_url' => config('v2board.app_url', env('APP_URL')) . '/#/order'
|
||||
];
|
||||
$strToSign = $bitpayX->prepareSignId($params['merchant_order_id']);
|
||||
$params['token'] = $bitpayX->sign($strToSign);
|
||||
$result = $bitpayX->mprequest($params);
|
||||
// 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;
|
||||
}
|
||||
}
|
29
app/Http/Controllers/User/PlanController.php
Executable file
29
app/Http/Controllers/User/PlanController.php
Executable file
@ -0,0 +1,29 @@
|
||||
<?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)
|
||||
->get();
|
||||
return response([
|
||||
'data' => $plan
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,21 +1,25 @@
|
||||
<?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')
|
||||
->get();
|
||||
@ -28,25 +32,33 @@ 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'),
|
@ -1,10 +1,10 @@
|
||||
<?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 Illuminate\Http\Request;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketMessage;
|
||||
use App\Utils\Helper;
|
||||
@ -12,7 +12,8 @@ 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,7 +48,8 @@ class TicketController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function save (TicketSave $request) {
|
||||
public function save(TicketSave $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
$ticket = Ticket::create(array_merge($request->only([
|
||||
'subject',
|
||||
@ -75,7 +77,8 @@ class TicketController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function reply (Request $request) {
|
||||
public function reply(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -112,7 +115,8 @@ class TicketController extends Controller
|
||||
}
|
||||
|
||||
|
||||
public function close (Request $request) {
|
||||
public function close(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
@ -131,7 +135,8 @@ class TicketController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function getLastMessage ($ticketId) {
|
||||
private function getLastMessage($ticketId)
|
||||
{
|
||||
return TicketMessage::where('ticket_id', $ticketId)
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
77
app/Http/Controllers/User/TutorialController.php
Normal file
77
app/Http/Controllers/User/TutorialController.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Tutorial;
|
||||
|
||||
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)
|
||||
->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
|
||||
return response($response);
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
<?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 Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
@ -15,13 +15,16 @@ 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) {
|
||||
public function changePassword(Request $request)
|
||||
{
|
||||
if (empty($request->input('old_password'))) {
|
||||
abort(500, '旧密码不能为空');
|
||||
}
|
||||
@ -29,10 +32,15 @@ class UserController extends Controller
|
||||
abort(500, '新密码不能为空');
|
||||
}
|
||||
$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 +50,24 @@ 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'
|
||||
])
|
||||
->first();
|
||||
$user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon';
|
||||
@ -63,7 +76,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 +93,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 +108,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 +117,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'
|
@ -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',
|
||||
],
|
||||
];
|
||||
|
@ -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,59 @@ 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 = [
|
||||
'safe_mode_enable' => 'in:0,1',
|
||||
'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_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' => '',
|
||||
// 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',
|
||||
// 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',
|
||||
// bitpayx
|
||||
'bitpayx_enable' => 'in:0,1',
|
||||
'bitpayx_appsecret' => '',
|
||||
// paytaro
|
||||
'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' => ''
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
@ -47,37 +66,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'
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -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,17 @@ 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'
|
||||
];
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
@ -13,27 +24,22 @@ 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' => '一次性金额有误'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,21 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ServerSave extends FormRequest
|
||||
{
|
||||
CONST RULES = [
|
||||
'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',
|
||||
'settings' => ''
|
||||
];
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
@ -13,17 +28,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 +37,7 @@ class ServerSave extends FormRequest
|
||||
'name.required' => '节点名称不能为空',
|
||||
'group_id.required' => '权限组不能为空',
|
||||
'group_id.array' => '权限组格式不正确',
|
||||
'parent_id.integer' => '父ID格式不正确',
|
||||
'host.required' => '节点地址不能为空',
|
||||
'port.required' => '连接端口不能为空',
|
||||
'server_port.required' => '后端服务端口不能为空',
|
||||
|
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' => '教程步骤不能为空'
|
||||
];
|
||||
}
|
||||
}
|
@ -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'
|
||||
];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
62
app/Http/Routes/AdminRoute.php
Normal file
62
app/Http/Routes/AdminRoute.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?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');
|
||||
// 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');
|
||||
// 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');
|
||||
// Order
|
||||
$router->get ('/order/fetch', 'Admin\\OrderController@fetch');
|
||||
$router->post('/order/repair', 'Admin\\OrderController@repair');
|
||||
$router->post('/order/update', 'Admin\\OrderController@update');
|
||||
// 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');
|
||||
});
|
||||
}
|
||||
}
|
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');
|
||||
});
|
||||
}
|
||||
}
|
22
app/Http/Routes/GuestRoute.php
Normal file
22
app/Http/Routes/GuestRoute.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?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');
|
||||
});
|
||||
}
|
||||
}
|
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]);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
54
app/Http/Routes/UserRoute.php
Normal file
54
app/Http/Routes/UserRoute.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?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');
|
||||
// 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');
|
||||
// Server
|
||||
$router->get ('/server/fetch', 'User\\ServerController@fetch');
|
||||
$router->get ('/server/log/fetch', 'User\\ServerController@logFetch');
|
||||
// Coupon
|
||||
$router->post('/coupon/check', 'User\\CouponController@check');
|
||||
});
|
||||
}
|
||||
}
|
@ -8,11 +8,13 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use App\Models\MailLog;
|
||||
|
||||
class SendEmail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
@ -33,6 +35,7 @@ class SendEmail implements ShouldQueue
|
||||
$params = $this->params;
|
||||
$email = $params['email'];
|
||||
$subject = $params['subject'];
|
||||
try {
|
||||
Mail::send(
|
||||
$params['template_name'],
|
||||
$params['template_value'],
|
||||
@ -40,5 +43,15 @@ class SendEmail implements ShouldQueue
|
||||
$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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
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'];
|
||||
}
|
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
31
app/Services/ServerService.php
Normal file
31
app/Services/ServerService.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
class ServerService
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
50
app/Services/UserService.php
Normal file
50
app/Services/UserService.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
8
app/Utils/CacheKey.php
Normal file
8
app/Utils/CacheKey.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
class CacheKey
|
||||
{
|
||||
|
||||
}
|
18
app/Utils/Dict.php
Normal file
18
app/Utils/Dict.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
class Dict
|
||||
{
|
||||
CONST EMAIL_WHITELIST_SUFFIX_DEFAULT = [
|
||||
'gmail.com',
|
||||
'qq.com',
|
||||
'163.com',
|
||||
'yahoo.com',
|
||||
'sina.com',
|
||||
'126.com',
|
||||
'outlook.com',
|
||||
'yeah.net',
|
||||
'foxmail.com'
|
||||
];
|
||||
}
|
@ -4,7 +4,8 @@ namespace App\Utils;
|
||||
|
||||
class Helper
|
||||
{
|
||||
public static function guid ($format = false) {
|
||||
public static function guid($format = false)
|
||||
{
|
||||
if (function_exists('com_create_guid') === true) {
|
||||
return md5(trim(com_create_guid(), '{}'));
|
||||
}
|
||||
@ -17,13 +18,15 @@ class Helper
|
||||
return md5(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)) . '-' . time());
|
||||
}
|
||||
|
||||
public static function exchange ($from, $to) {
|
||||
public static function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangeratesapi.io/latest?symbols=' . $to . '&base=' . $from);
|
||||
$result = json_decode($result, true);
|
||||
return $result['rates'][$to];
|
||||
}
|
||||
|
||||
public static function randomChar($len, $special=false){
|
||||
public static function randomChar($len, $special = false)
|
||||
{
|
||||
$chars = array(
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
|
||||
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
|
||||
@ -50,22 +53,46 @@ class Helper
|
||||
return $str;
|
||||
}
|
||||
|
||||
public static function buildVmessLink($item, $user) {
|
||||
public static function buildVmessLink($item, $user)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $item->name,
|
||||
"add" => $item->host,
|
||||
"port" => $item->port,
|
||||
"id" => $user->v2ray_uuid,
|
||||
"aid" => "2",
|
||||
"net" => $item->network,
|
||||
"type" => "chacha20-poly1305",
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"tls" => $item->tls?"tls":"",
|
||||
"path" => "",
|
||||
"tls" => $item->tls ? "tls" : ""
|
||||
];
|
||||
if ($item->network == 'ws') {
|
||||
$wsSettings = json_decode($item->settings);
|
||||
if ($wsSettings->path) $config['path'] = $wsSettings->path;
|
||||
if (isset($wsSettings->path)) $config['path'] = $wsSettings->path;
|
||||
if (isset($wsSettings->headers->Host)) $config['host'] = $wsSettings->headers->Host;
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function multiPasswordVerify($algo, $password, $hash)
|
||||
{
|
||||
switch($algo) {
|
||||
case 'md5': return md5($password) === $hash;
|
||||
case 'sha256': return hash('sha256', $password) === $hash;
|
||||
default: return password_verify($password, $hash);
|
||||
}
|
||||
}
|
||||
|
||||
public static function emailSuffixVerify($email, $suffixs)
|
||||
{
|
||||
$suffix = preg_split('/@/', $email)[1];
|
||||
if (!$suffix) return false;
|
||||
if (!is_array($suffixs)) {
|
||||
$suffixs = preg_split('/,/', $suffixs);
|
||||
}
|
||||
if (!in_array($suffix, $suffixs)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
"laravel/framework": "^6.0",
|
||||
"laravel/tinker": "^1.0",
|
||||
"lokielse/omnipay-alipay": "^3.0",
|
||||
"php-curl-class/php-curl-class": "^8.6",
|
||||
"stripe/stripe-php": "^7.5",
|
||||
"symfony/yaml": "^4.3"
|
||||
},
|
||||
|
@ -228,4 +228,13 @@ return [
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| V2board version
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The only modification by laravel config
|
||||
|
|
||||
*/
|
||||
'version' => '1.2.2'
|
||||
];
|
||||
|
0
database/.gitignore
vendored
Executable file → Normal file
0
database/.gitignore
vendored
Executable file → Normal file
1
database/factories/UserFactory.php
Executable file → Normal file
1
database/factories/UserFactory.php
Executable file → Normal file
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Factory $factory */
|
||||
|
||||
use App\User;
|
||||
use Faker\Generator as Faker;
|
||||
use Illuminate\Support\Str;
|
||||
|
242
database/install.sql
Normal file
242
database/install.sql
Normal file
@ -0,0 +1,242 @@
|
||||
-- Adminer 4.7.3 MySQL dump
|
||||
|
||||
SET NAMES utf8;
|
||||
SET time_zone = '+00:00';
|
||||
SET foreign_key_checks = 0;
|
||||
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `failed_jobs`;
|
||||
CREATE TABLE `failed_jobs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_coupon`;
|
||||
CREATE TABLE `v2_coupon` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`code` char(8) NOT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`type` tinyint(1) NOT NULL,
|
||||
`value` int(11) NOT NULL,
|
||||
`limit_use` int(11) DEFAULT NULL,
|
||||
`started_at` int(11) NOT NULL,
|
||||
`ended_at` int(11) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_invite_code`;
|
||||
CREATE TABLE `v2_invite_code` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`code` char(32) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`pv` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_mail_log`;
|
||||
CREATE TABLE `v2_mail_log` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(64) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`template_name` varchar(255) NOT NULL,
|
||||
`error` varchar(255) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_notice`;
|
||||
CREATE TABLE `v2_notice` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`img_url` varchar(255) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_order`;
|
||||
CREATE TABLE `v2_order` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`invite_user_id` int(11) DEFAULT NULL,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`plan_id` int(11) NOT NULL,
|
||||
`type` int(11) NOT NULL COMMENT '1新购2续费3升级',
|
||||
`cycle` varchar(255) NOT NULL,
|
||||
`trade_no` varchar(36) NOT NULL,
|
||||
`callback_no` varchar(255) DEFAULT NULL,
|
||||
`total_amount` int(11) NOT NULL,
|
||||
`discount_amount` int(11) DEFAULT NULL,
|
||||
`surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
|
||||
`refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`commission_status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_plan`;
|
||||
CREATE TABLE `v2_plan` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` int(11) NOT NULL,
|
||||
`transfer_enable` int(11) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`renew` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`content` text,
|
||||
`month_price` int(11) DEFAULT '0',
|
||||
`quarter_price` int(11) DEFAULT '0',
|
||||
`half_year_price` int(11) DEFAULT '0',
|
||||
`year_price` int(11) DEFAULT '0',
|
||||
`onetime_price` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server`;
|
||||
CREATE TABLE `v2_server` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` varchar(255) NOT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`parent_id` int(11) DEFAULT NULL,
|
||||
`host` varchar(255) NOT NULL,
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`tls` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`tags` varchar(255) DEFAULT NULL,
|
||||
`rate` varchar(11) NOT NULL,
|
||||
`network` varchar(11) NOT NULL,
|
||||
`settings` text,
|
||||
`rules` text,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server_group`;
|
||||
CREATE TABLE `v2_server_group` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server_log`;
|
||||
CREATE TABLE `v2_server_log` (
|
||||
`user_id` int(11) NOT NULL,
|
||||
`server_id` int(11) NOT NULL,
|
||||
`u` varchar(255) NOT NULL,
|
||||
`d` varchar(255) NOT NULL,
|
||||
`rate` decimal(10,2) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_ticket`;
|
||||
CREATE TABLE `v2_ticket` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`last_reply_user_id` int(11) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`level` tinyint(1) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:已开启 1:已关闭',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_ticket_message`;
|
||||
CREATE TABLE `v2_ticket_message` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`ticket_id` int(11) NOT NULL,
|
||||
`message` varchar(255) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_tutorial`;
|
||||
CREATE TABLE `v2_tutorial` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`category_id` int(11) NOT NULL,
|
||||
`title` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`steps` text,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `v2_tutorial` (`id`, `category_id`, `title`, `steps`, `show`, `created_at`, `updated_at`) VALUES
|
||||
(1, 1, 'V2rayN', '[{\"default_area\":\"<div><div>下载 V2rayN 客户端。</div><div>下载完成后解压,解压完成后运行V2rayN</div><div>运行时请右键,以管理员身份运行</div></div>\",\"download_url\":\"/downloads/V2rayN.zip\"},{\"default_area\":\"<div>点击订阅按钮,选择订阅设置点击添加,输入如下内容后点击确定保存</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url):<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UkcHNtERTnjLVS8.jpg\"},{\"default_area\":\"<div>点击订阅后,从服务器列表选择服务器</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/BgPGFQ3kCSuIRjJ.jpg\"},{\"default_area\":\"<div>点击参数设置,找到Http代理,选择PAC模式后按确定保存即启动代理。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/vnVykKEFT8Lzo3f.jpg\"}]', 1, 1577972408, 1577980882),
|
||||
(2, 4, 'V2rayNG', '[{\"default_area\":\"<div>下载 V2rayNG 客户端。</div>\",\"safe_area\":\"\",\"download_url\":\"/downloads/V2rayNG.apk\"},{\"default_area\":\"<div>打开 V2rayNG 点击左上角的菜单图标打开侧边栏,随后点击 订阅设置,点击右上角的➕按钮新增订阅。</div><div>按照下方内容进行填写,填写完毕后点击右上角的☑️按钮。</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url):<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"download_url\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ghuVkTe6LBqRxSO.jpg\"},{\"default_area\":\"<div>再次从侧边栏进入 设置 页面,点击 路由模式 将其更改为 \\b绕过局域网及大陆地址。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/Tf1AGoXZuhJrwOq.jpg\"},{\"default_area\":\"<div>随后从侧边栏回到 配置文件 页面,点击右上角的省略号图标选择更新订阅。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UtfPShQXupRmB4L.jpg\"},{\"img_url\":\"https://i.loli.net/2019/11/21/ZkbNsSrAg3m5Dny.jpg\",\"default_area\":\"<div>点击选择您需要的节点,点击右下角的V字按钮即可连接。</div>\"}]', 1, 1577972534, 1577981610),
|
||||
(3, 2, 'ClashX', '[{\"default_area\":\"<div>下载 ClashX 客户端,安装后运行。</div>\",\"download_url\":\"/downloads/ClashX.dmg\",\"img_url\":\"https://i.loli.net/2019/11/20/uNGrjl2noCL1f5B.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+M(订阅快捷键),在弹出的窗口点击添加输入下方信息</div>\",\"safe_area\":\"<div>Url:<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\\n<div>Config Name:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/20/8eB13mRbFuszwxg.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+S(设置为系统代理快捷键),即连接完成</div>\"}]', 1, 1577979855, 1577981646),
|
||||
(4, 3, 'Shadowrocket', '[{\"default_area\":\"<div>iOS上使用请在iOS浏览器中打开本页</div>\"},{\"default_area\":\"<div>在 App Store 登录本站提供的美区 Apple ID 下载客户端。</div><div>为了保护您的隐私,请勿在手机设置里直接登录,仅在 App Store 登录即可。</div><div>登陆完成后点击下方下载会自动唤起下载。</div>\",\"safe_area\":\"<div>Apple ID:<code onclick=\\\"safeAreaCopy(\'{{$apple_id}}\')\\\">{{$apple_id}}</code></div><div>密码:<code onclick=\\\"safeAreaCopy(\'{{$apple_id_password}}\')\\\">点击复制密码</code></div>\",\"download_url\":\"https://apps.apple.com/us/app/shadowrocket/id932747118\",\"img_url\":\"https://i.loli.net/2019/11/21/5idkjJ61stWgREV.jpg\"},{\"default_area\":\"<div>待客户端安装完成后,点击下方一键订阅按钮会自动唤起并进行订阅</div>\",\"safe_area\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ZcqlNMb3eg5Uhxd.jpg\",\"download_url\":\"shadowrocket://add/sub://{{$b64_subscribe_url}}?remark={{$app_name}}\"},{\"default_area\":\"<div>选择节点进行链接,首次链接过程授权窗口请一路允许。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/9Zdxksr7Ey6hjlm.jpg\"}]', 1, 1577982016, 1577983283);
|
||||
|
||||
DROP TABLE IF EXISTS `v2_user`;
|
||||
CREATE TABLE `v2_user` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`invite_user_id` int(11) DEFAULT NULL,
|
||||
`email` varchar(64) NOT NULL,
|
||||
`password` varchar(64) NOT NULL,
|
||||
`password_algo` char(10) DEFAULT NULL,
|
||||
`balance` int(11) NOT NULL DEFAULT '0',
|
||||
`discount` int(11) DEFAULT NULL,
|
||||
`commission_rate` int(11) DEFAULT NULL,
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`t` int(11) NOT NULL DEFAULT '0',
|
||||
`u` bigint(20) NOT NULL DEFAULT '0',
|
||||
`d` bigint(20) NOT NULL DEFAULT '0',
|
||||
`transfer_enable` bigint(20) NOT NULL DEFAULT '0',
|
||||
`banned` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`last_login_at` int(11) DEFAULT NULL,
|
||||
`last_login_ip` int(11) DEFAULT NULL,
|
||||
`v2ray_uuid` varchar(36) NOT NULL,
|
||||
`v2ray_alter_id` tinyint(4) NOT NULL DEFAULT '2',
|
||||
`v2ray_level` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`group_id` int(11) DEFAULT NULL,
|
||||
`plan_id` int(11) DEFAULT NULL,
|
||||
`remind_expire` tinyint(4) DEFAULT '1',
|
||||
`remind_traffic` tinyint(4) DEFAULT '1',
|
||||
`token` char(32) NOT NULL,
|
||||
`expired_at` bigint(20) DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- 2020-03-05 14:10:26
|
0
database/migrations/2014_10_12_000000_create_users_table.php
Executable file → Normal file
0
database/migrations/2014_10_12_000000_create_users_table.php
Executable file → Normal file
0
database/migrations/2014_10_12_100000_create_password_resets_table.php
Executable file → Normal file
0
database/migrations/2014_10_12_100000_create_password_resets_table.php
Executable file → Normal file
0
database/migrations/2019_08_19_000000_create_failed_jobs_table.php
Executable file → Normal file
0
database/migrations/2019_08_19_000000_create_failed_jobs_table.php
Executable file → Normal file
0
database/seeds/DatabaseSeeder.php
Executable file → Normal file
0
database/seeds/DatabaseSeeder.php
Executable file → Normal file
187
database/update.sql
Normal file
187
database/update.sql
Normal file
@ -0,0 +1,187 @@
|
||||
ALTER TABLE `v2_server`
|
||||
ADD `last_check_at` int(11) NULL AFTER `rate`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
ADD `network` varchar(11) COLLATE 'utf8_general_ci' NOT NULL AFTER `rate`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
ADD `settings` text COLLATE 'utf8_general_ci' NULL AFTER `network`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
ADD `show` tinyint(1) NOT NULL DEFAULT '0' AFTER `settings`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
CHANGE `enable` `enable` tinyint(1) NOT NULL DEFAULT '1' AFTER `transfer_enable`;
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
ADD `type` int(11) NOT NULL COMMENT '1新购2续费3升级' AFTER `plan_id`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `commission_rate` int(11) NULL AFTER `password`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `balance` int(11) NOT NULL DEFAULT '0' AFTER `password`;
|
||||
|
||||
CREATE TABLE `v2_notice` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE `v2_notice`
|
||||
ADD `img_url` varchar(255) COLLATE 'utf8_general_ci' NULL AFTER `content`;
|
||||
|
||||
CREATE TABLE `v2_ticket` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`level` tinyint(1) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `v2_ticket_message` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`ticket_id` int(11) NOT NULL,
|
||||
`message` varchar(255) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
ALTER TABLE `v2_ticket`
|
||||
ADD `last_reply_user_id` int(11) NOT NULL AFTER `user_id`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
CHANGE `last_login_at` `last_login_at` int(11) NULL AFTER `is_admin`;
|
||||
|
||||
ALTER TABLE `v2_server_log`
|
||||
CHANGE `node_id` `server_id` int(11) NOT NULL AFTER `user_id`,
|
||||
CHANGE `u` `u` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `server_id`,
|
||||
CHANGE `d` `d` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `u`,
|
||||
CHANGE `rate` `rate` int(11) NOT NULL AFTER `d`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
DROP `last_check_at`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
CHANGE `name` `name` varchar(255) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `group_id`;
|
||||
|
||||
ALTER TABLE `v2_plan`
|
||||
CHANGE `month_price` `month_price` int(11) NULL DEFAULT '0' AFTER `content`,
|
||||
CHANGE `quarter_price` `quarter_price` int(11) NULL DEFAULT '0' AFTER `month_price`,
|
||||
CHANGE `half_year_price` `half_year_price` int(11) NULL DEFAULT '0' AFTER `quarter_price`,
|
||||
CHANGE `year_price` `year_price` int(11) NULL DEFAULT '0' AFTER `half_year_price`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
ADD `parent_id` int(11) NULL AFTER `group_id`;
|
||||
|
||||
CREATE TABLE `v2_mail_log` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`email` varchar(64) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`template_name` varchar(255) NOT NULL,
|
||||
`error` varchar(255) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE `v2_coupon` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`code` char(32) NOT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`type` tinyint(1) NOT NULL,
|
||||
`value` int(11) NOT NULL,
|
||||
`limit_use` int(11) DEFAULT NULL,
|
||||
`started_at` int(11) NOT NULL,
|
||||
`ended_at` int(11) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
ADD `discount_amount` int(11) NULL AFTER `total_amount`;
|
||||
|
||||
CREATE TABLE `v2_tutorial` (
|
||||
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`title` varchar(255) COLLATE 'utf8mb4_general_ci' NOT NULL,
|
||||
`description` varchar(255) COLLATE 'utf8mb4_general_ci' NOT NULL,
|
||||
`icon` varchar(255) COLLATE 'utf8mb4_general_ci' NOT NULL,
|
||||
`steps` text NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
);
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT INTO `v2_tutorial` (`id`, `title`, `description`, `icon`, `steps`, `show`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'Windows', '兼容 Windows 7 以上的版本', 'fab fa-2x fa-windows', '[{\"default_area\":\"<div><div>下载 V2rayN 客户端。</div><div>下载完成后解压,解压完成后运行V2rayN</div><div>运行时请右键,以管理员身份运行</div></div>\",\"download_url\":\"/downloads/V2rayN.zip\"},{\"default_area\":\"<div>点击订阅按钮,选择订阅设置点击添加,输入如下内容后点击确定保存</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url):<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UkcHNtERTnjLVS8.jpg\"},{\"default_area\":\"<div>点击订阅后,从服务器列表选择服务器</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/BgPGFQ3kCSuIRjJ.jpg\"},{\"default_area\":\"<div>点击参数设置,找到Http代理,选择PAC模式后按确定保存即启动代理。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/vnVykKEFT8Lzo3f.jpg\"}]', 1, 1577972408, 1577980882),
|
||||
(2, 'Android', '兼容 Android 6 以上的版本', 'fab fa-2x fa-android', '[{\"default_area\":\"<div>下载 V2rayNG 客户端。</div>\",\"safe_area\":\"\",\"download_url\":\"/downloads/V2rayNG.apk\"},{\"default_area\":\"<div>打开 V2rayNG 点击左上角的菜单图标打开侧边栏,随后点击 订阅设置,点击右上角的➕按钮新增订阅。</div><div>按照下方内容进行填写,填写完毕后点击右上角的☑️按钮。</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url):<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"download_url\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ghuVkTe6LBqRxSO.jpg\"},{\"default_area\":\"<div>再次从侧边栏进入 设置 页面,点击 路由模式 将其更改为 \\b绕过局域网及大陆地址。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/Tf1AGoXZuhJrwOq.jpg\"},{\"default_area\":\"<div>随后从侧边栏回到 配置文件 页面,点击右上角的省略号图标选择更新订阅。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UtfPShQXupRmB4L.jpg\"},{\"img_url\":\"https://i.loli.net/2019/11/21/ZkbNsSrAg3m5Dny.jpg\",\"default_area\":\"<div>点击选择您需要的节点,点击右下角的V字按钮即可连接。</div>\"}]', 1, 1577972534, 1577981610),
|
||||
(3, 'macOS', '兼容 Yosemite 以上的版本', 'fab fa-2x fa-apple', '[{\"default_area\":\"<div>下载 ClashX 客户端,安装后运行。</div>\",\"download_url\":\"/downloads/ClashX.dmg\",\"img_url\":\"https://i.loli.net/2019/11/20/uNGrjl2noCL1f5B.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+M(订阅快捷键),在弹出的窗口点击添加输入下方信息</div>\",\"safe_area\":\"<div>Url:<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\\n<div>Config Name:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/20/8eB13mRbFuszwxg.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+S(设置为系统代理快捷键),即连接完成</div>\"}]', 1, 1577979855, 1577981646),
|
||||
(4, 'iOS', '兼容 iOS 9 以上的版本', 'fab fa-2x fa-apple', '[{\"default_area\":\"<div>iOS上使用请在iOS浏览器中打开本页</div>\"},{\"default_area\":\"<div>在 App Store 登录本站提供的美区 Apple ID 下载客户端。</div><div>为了保护您的隐私,请勿在手机设置里直接登录,仅在 App Store 登录即可。</div><div>登陆完成后点击下方下载会自动唤起下载。</div>\",\"safe_area\":\"<div>Apple ID:<code onclick=\\\"safeAreaCopy(\'{{$apple_id}}\')\\\">{{$apple_id}}</code></div><div>密码:<code onclick=\\\"safeAreaCopy(\'{{$apple_id_password}}\')\\\">点击复制密码</code></div>\",\"download_url\":\"https://apps.apple.com/us/app/shadowrocket/id932747118\",\"img_url\":\"https://i.loli.net/2019/11/21/5idkjJ61stWgREV.jpg\"},{\"default_area\":\"<div>待客户端安装完成后,点击下方一键订阅按钮会自动唤起并进行订阅</div>\",\"safe_area\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ZcqlNMb3eg5Uhxd.jpg\",\"download_url\":\"shadowrocket://add/sub://{{$b64_subscribe_url}}?remark={{$app_name}}\"},{\"default_area\":\"<div>选择节点进行链接,首次链接过程授权窗口请一路允许。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/9Zdxksr7Ey6hjlm.jpg\"}]', 1, 1577982016, 1577983283);
|
||||
|
||||
ALTER TABLE `v2_server_log`
|
||||
CHANGE `rate` `rate` decimal(10,2) NOT NULL AFTER `d`;
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
DROP `method`;
|
||||
|
||||
ALTER TABLE `v2_invite_code`
|
||||
ADD `pv` int(11) NOT NULL DEFAULT '0' AFTER `status`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `password_algo` char(10) COLLATE 'utf8_general_ci' NULL AFTER `password`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
CHANGE `tls` `tls` tinyint(4) NOT NULL DEFAULT '0' AFTER `server_port`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
ADD `rules` text COLLATE 'utf8_general_ci' NULL AFTER `settings`;
|
||||
|
||||
CREATE TABLE `failed_jobs` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `discount` int(11) NULL AFTER `balance`;
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
ADD `surplus_amount` int(11) NULL COMMENT '剩余价值' AFTER `discount_amount`;
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
ADD `refund_amount` int(11) NULL COMMENT '退款金额' AFTER `surplus_amount`;
|
||||
|
||||
ALTER TABLE `v2_tutorial`
|
||||
ADD `category_id` int(11) NOT NULL AFTER `id`;
|
||||
|
||||
ALTER TABLE `v2_tutorial`
|
||||
DROP `description`;
|
||||
|
||||
ALTER TABLE `v2_plan`
|
||||
CHANGE `month_price` `month_price` int(11) NULL AFTER `content`,
|
||||
CHANGE `quarter_price` `quarter_price` int(11) NULL AFTER `month_price`,
|
||||
CHANGE `half_year_price` `half_year_price` int(11) NULL AFTER `quarter_price`,
|
||||
CHANGE `year_price` `year_price` int(11) NULL AFTER `half_year_price`,
|
||||
ADD `onetime_price` int(11) NULL AFTER `year_price`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
DROP `enable`,
|
||||
ADD `banned` tinyint(1) NOT NULL DEFAULT '0' AFTER `transfer_enable`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
CHANGE `expired_at` `expired_at` bigint(20) NULL DEFAULT '0' AFTER `token`;
|
||||
|
||||
ALTER TABLE `v2_tutorial`
|
||||
DROP `icon`;
|
175
install.sql
175
install.sql
@ -1,175 +0,0 @@
|
||||
-- Adminer 4.7.3 MySQL dump
|
||||
|
||||
SET NAMES utf8;
|
||||
SET time_zone = '+00:00';
|
||||
SET foreign_key_checks = 0;
|
||||
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
|
||||
|
||||
DROP TABLE IF EXISTS `v2_invite_code`;
|
||||
CREATE TABLE `v2_invite_code` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`code` char(32) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_notice`;
|
||||
CREATE TABLE `v2_notice` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`img_url` varchar(255) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_order`;
|
||||
CREATE TABLE `v2_order` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`invite_user_id` int(11) DEFAULT NULL,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`plan_id` int(11) NOT NULL,
|
||||
`type` int(11) NOT NULL COMMENT '1新购2续费3升级',
|
||||
`cycle` varchar(255) NOT NULL,
|
||||
`trade_no` varchar(36) NOT NULL,
|
||||
`callback_no` varchar(255) DEFAULT NULL,
|
||||
`total_amount` int(11) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`commission_status` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_plan`;
|
||||
CREATE TABLE `v2_plan` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` int(11) NOT NULL,
|
||||
`transfer_enable` int(11) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`renew` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`content` text,
|
||||
`month_price` int(11) NOT NULL DEFAULT '0',
|
||||
`quarter_price` int(11) NOT NULL DEFAULT '0',
|
||||
`half_year_price` int(11) NOT NULL DEFAULT '0',
|
||||
`year_price` int(11) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server`;
|
||||
CREATE TABLE `v2_server` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` varchar(255) NOT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`host` varchar(255) NOT NULL,
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`tls` tinyint(4) NOT NULL,
|
||||
`tags` varchar(255) DEFAULT NULL,
|
||||
`rate` varchar(11) NOT NULL,
|
||||
`network` varchar(11) NOT NULL,
|
||||
`settings` text,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server_group`;
|
||||
CREATE TABLE `v2_server_group` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server_log`;
|
||||
CREATE TABLE `v2_server_log` (
|
||||
`user_id` int(11) NOT NULL,
|
||||
`server_id` int(11) NOT NULL,
|
||||
`u` varchar(255) NOT NULL,
|
||||
`d` varchar(255) NOT NULL,
|
||||
`rate` int(11) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_ticket`;
|
||||
CREATE TABLE `v2_ticket` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`last_reply_user_id` int(11) NOT NULL,
|
||||
`subject` varchar(255) NOT NULL,
|
||||
`level` tinyint(1) NOT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:已开启 1:已关闭',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_ticket_message`;
|
||||
CREATE TABLE `v2_ticket_message` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`ticket_id` int(11) NOT NULL,
|
||||
`message` varchar(255) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_user`;
|
||||
CREATE TABLE `v2_user` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`invite_user_id` int(11) DEFAULT NULL,
|
||||
`email` varchar(64) NOT NULL,
|
||||
`password` varchar(64) NOT NULL,
|
||||
`balance` int(11) NOT NULL DEFAULT '0',
|
||||
`commission_rate` int(11) DEFAULT NULL,
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`t` int(11) NOT NULL DEFAULT '0',
|
||||
`u` bigint(20) NOT NULL DEFAULT '0',
|
||||
`d` bigint(20) NOT NULL DEFAULT '0',
|
||||
`transfer_enable` bigint(20) NOT NULL DEFAULT '0',
|
||||
`enable` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`banned` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`last_login_at` int(11) DEFAULT NULL,
|
||||
`last_login_ip` int(11) DEFAULT NULL,
|
||||
`v2ray_uuid` varchar(36) NOT NULL,
|
||||
`v2ray_alter_id` tinyint(4) NOT NULL DEFAULT '2',
|
||||
`v2ray_level` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`group_id` int(11) DEFAULT NULL,
|
||||
`plan_id` int(11) DEFAULT NULL,
|
||||
`remind_expire` tinyint(4) DEFAULT '1',
|
||||
`remind_traffic` tinyint(4) DEFAULT '1',
|
||||
`token` char(32) NOT NULL,
|
||||
`expired_at` bigint(20) NOT NULL DEFAULT '0',
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- 2019-12-27 07:14:40
|
@ -1,14 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
class BitpayX {
|
||||
class BitpayX
|
||||
{
|
||||
private $bitpayxAppSecret;
|
||||
private $bitpayxGatewayUri;
|
||||
|
||||
/**
|
||||
* 签名初始化
|
||||
* @param merKey 签名密钥
|
||||
*/
|
||||
public function __construct($bitpayxAppSecret) {
|
||||
public function __construct($bitpayxAppSecret)
|
||||
{
|
||||
$this->bitpayxAppSecret = $bitpayxAppSecret;
|
||||
$this->bitpayxGatewayUri = 'https://api.mugglepay.com/v1/';
|
||||
}
|
||||
@ -22,15 +26,18 @@ class BitpayX {
|
||||
ksort($data_sign);
|
||||
return http_build_query($data_sign);
|
||||
}
|
||||
|
||||
public function sign($data)
|
||||
{
|
||||
return strtolower(md5(md5($data) . $this->bitpayxAppSecret));
|
||||
}
|
||||
|
||||
public function verify($data, $signature)
|
||||
{
|
||||
$mySign = $this->sign($data);
|
||||
return $mySign === $signature;
|
||||
}
|
||||
|
||||
public function mprequest($data)
|
||||
{
|
||||
$headers = array('content-type: application/json', 'token: ' . $this->bitpayxAppSecret);
|
||||
@ -48,6 +55,7 @@ class BitpayX {
|
||||
curl_close($curl);
|
||||
return json_decode($data, true);
|
||||
}
|
||||
|
||||
public function mpcheckout($orderId, $data)
|
||||
{
|
||||
$headers = array('content-type: application/json', 'token: ' . $this->bitpayxAppSecret);
|
||||
@ -65,11 +73,15 @@ class BitpayX {
|
||||
curl_close($curl);
|
||||
return json_decode($data, true);
|
||||
}
|
||||
public function refund($merchantTradeNo) {
|
||||
|
||||
public function refund($merchantTradeNo)
|
||||
{
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
public function buildHtml($params, $method = 'post', $target = '_self'){
|
||||
|
||||
public function buildHtml($params, $method = 'post', $target = '_self')
|
||||
{
|
||||
// var_dump($params);exit;
|
||||
$html = "<form id='submit' name='submit' action='" . $this->gatewayUri . "' method='$method' target='$target'>";
|
||||
foreach ($params as $key => $value) {
|
||||
|
49
library/PayTaro.php
Normal file
49
library/PayTaro.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
use \Curl\Curl;
|
||||
|
||||
class PayTaro
|
||||
{
|
||||
private $appId;
|
||||
private $appSecret;
|
||||
|
||||
public function __construct($appId, $appSecret)
|
||||
{
|
||||
$this->appId = $appId;
|
||||
$this->appSecret = $appSecret;
|
||||
}
|
||||
|
||||
public function pay($params)
|
||||
{
|
||||
ksort($params);
|
||||
$str = http_build_query($params) . $this->appSecret;
|
||||
$params['sign'] = md5($str);
|
||||
$curl = new Curl();
|
||||
$curl->post('https://api.paytaro.com/v1/gateway/fetch', http_build_query($params));
|
||||
$result = $curl->response;
|
||||
if ($curl->error) {
|
||||
$errors = (array)$result->errors;
|
||||
abort(500, $errors[array_keys($errors)[0]][0]);
|
||||
}
|
||||
$curl->close();
|
||||
if (!isset($result->data->trade_no)) {
|
||||
abort(500, '接口请求失败');
|
||||
}
|
||||
return $result->data->pay_url;
|
||||
}
|
||||
|
||||
public function verify($params)
|
||||
{
|
||||
$sign = $params['sign'];
|
||||
unset($params['sign']);
|
||||
ksort($params);
|
||||
reset($params);
|
||||
$str = http_build_query($params) . $this->appSecret;
|
||||
if ($sign !== md5($str)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
63
library/TomatoPay.php
Normal file
63
library/TomatoPay.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Library;
|
||||
|
||||
class TomatoPay
|
||||
{
|
||||
private $mchid;
|
||||
private $account;
|
||||
private $key;
|
||||
|
||||
public function __construct($mchid, $account, $key)
|
||||
{
|
||||
$this->mchid = $mchid;
|
||||
$this->account = $account;
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
public function alipay($cny, $trade)
|
||||
{
|
||||
$params = [
|
||||
'mchid' => $this->mchid,
|
||||
'account' => $this->account,
|
||||
'cny' => $cny,
|
||||
'type' => '1',
|
||||
'trade' => $trade
|
||||
];
|
||||
$params['signs'] = $this->sign($params);
|
||||
return $this->buildHtml('https://b.fanqieui.com/gateways/alipay.php', $params);
|
||||
}
|
||||
|
||||
public function wxpay($cny, $trade)
|
||||
{
|
||||
$params = [
|
||||
'mchid' => $this->mchid,
|
||||
'account' => $this->account,
|
||||
'cny' => $cny,
|
||||
'type' => '1',
|
||||
'trade' => $trade
|
||||
];
|
||||
$params['signs'] = $this->sign($params);
|
||||
return $this->buildHtml('https://b.fanqieui.com/gateways/wxpay.php', $params);
|
||||
}
|
||||
|
||||
public function sign($params)
|
||||
{
|
||||
$o = '';
|
||||
foreach ($params as $k => $v) {
|
||||
$o .= "$k=" . ($v) . "&";
|
||||
}
|
||||
return md5(substr($o, 0, -1) . $this->key);
|
||||
}
|
||||
|
||||
public function buildHtml($url, $params, $method = 'post', $target = '_self')
|
||||
{
|
||||
// return var_dump($params);
|
||||
$html = "<form id='submit' name='submit' action='" . $url . "' method='$method' target='$target'>";
|
||||
foreach ($params as $key => $value) {
|
||||
$html .= "<input type='hidden' name='$key' value='$value'/>";
|
||||
}
|
||||
$html .= "</form><script>document.forms['submit'].submit();</script>";
|
||||
return $html;
|
||||
}
|
||||
}
|
5
pm2.yaml
Normal file
5
pm2.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
apps:
|
||||
- name : 'V2Board'
|
||||
script : 'php artisan queue:work --queue=verify_mail,other_mail'
|
||||
instances: 4
|
||||
out_file : './storage/logs/queue/queue.log'
|
1
public/assets/admin/antd.async.js
vendored
Normal file
1
public/assets/admin/antd.async.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user