UserController.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Classes\Pterodactyl;
  4. use App\Events\UserUpdateCreditsEvent;
  5. use App\Http\Controllers\Controller;
  6. use App\Models\DiscordUser;
  7. use App\Models\Settings;
  8. use App\Models\User;
  9. use App\Notifications\ReferralNotification;
  10. use Carbon\Carbon;
  11. use Illuminate\Contracts\Foundation\Application;
  12. use Illuminate\Contracts\Pagination\LengthAwarePaginator;
  13. use Illuminate\Contracts\Routing\ResponseFactory;
  14. use Illuminate\Database\Eloquent\Builder;
  15. use Illuminate\Database\Eloquent\Collection;
  16. use Illuminate\Database\Eloquent\Model;
  17. use Illuminate\Http\Request;
  18. use Illuminate\Http\Response;
  19. use Illuminate\Support\Facades\App;
  20. use Illuminate\Support\Facades\DB;
  21. use Illuminate\Support\Facades\Hash;
  22. use Illuminate\Support\Str;
  23. use Illuminate\Validation\Rule;
  24. use Illuminate\Validation\ValidationException;
  25. use Spatie\QueryBuilder\QueryBuilder;
  26. class UserController extends Controller
  27. {
  28. const ALLOWED_INCLUDES = ['servers', 'notifications', 'payments', 'vouchers', 'discordUser'];
  29. const ALLOWED_FILTERS = ['name', 'server_limit', 'email', 'pterodactyl_id', 'role', 'suspended'];
  30. /**
  31. * Display a listing of the resource.
  32. *
  33. * @param Request $request
  34. * @return LengthAwarePaginator
  35. */
  36. public function index(Request $request)
  37. {
  38. $query = QueryBuilder::for(User::class)
  39. ->allowedIncludes(self::ALLOWED_INCLUDES)
  40. ->allowedFilters(self::ALLOWED_FILTERS);
  41. return $query->paginate($request->input('per_page') ?? 50);
  42. }
  43. /**
  44. * Display the specified resource.
  45. *
  46. * @param int $id
  47. *
  48. * @return User|Builder|Collection|Model
  49. */
  50. public function show(int $id)
  51. {
  52. $discordUser = DiscordUser::find($id);
  53. $user = $discordUser ? $discordUser->user : User::findOrFail($id);
  54. $query = QueryBuilder::for($user)
  55. ->with('discordUser')
  56. ->allowedIncludes(self::ALLOWED_INCLUDES)
  57. ->where('users.id', '=', $id)
  58. ->orWhereHas('discordUser', function (Builder $builder) use ($id) {
  59. $builder->where('id', '=', $id);
  60. });
  61. return $query->firstOrFail();
  62. }
  63. /**
  64. * Update the specified resource in storage.
  65. *
  66. * @param Request $request
  67. * @param int $id
  68. * @return User
  69. */
  70. public function update(Request $request, int $id)
  71. {
  72. $discordUser = DiscordUser::find($id);
  73. $user = $discordUser ? $discordUser->user : User::findOrFail($id);
  74. $request->validate([
  75. "name" => "sometimes|string|min:4|max:30",
  76. "email" => "sometimes|string|email",
  77. "credits" => "sometimes|numeric|min:0|max:1000000",
  78. "server_limit" => "sometimes|numeric|min:0|max:1000000",
  79. "role" => ['sometimes', Rule::in(['admin', 'moderator', 'client', 'member'])],
  80. ]);
  81. event(new UserUpdateCreditsEvent($user));
  82. //Update Users Password on Pterodactyl
  83. //Username,Mail,First and Lastname are required aswell
  84. $response = Pterodactyl::client()->patch('/application/users/' . $user->pterodactyl_id, [
  85. "username" => $request->name,
  86. "first_name" => $request->name,
  87. "last_name" => $request->name,
  88. "email" => $request->email,
  89. ]);
  90. if ($response->failed()) {
  91. throw ValidationException::withMessages([
  92. 'pterodactyl_error_message' => $response->toException()->getMessage(),
  93. 'pterodactyl_error_status' => $response->toException()->getCode()
  94. ]);
  95. }
  96. $user->update($request->all());
  97. return $user;
  98. }
  99. /**
  100. * increments the users credits or/and server_limit
  101. *
  102. * @param Request $request
  103. * @param int $id
  104. * @return User
  105. * @throws ValidationException
  106. */
  107. public function increment(Request $request, int $id)
  108. {
  109. $discordUser = DiscordUser::find($id);
  110. $user = $discordUser ? $discordUser->user : User::findOrFail($id);
  111. $request->validate([
  112. "credits" => "sometimes|numeric|min:0|max:1000000",
  113. "server_limit" => "sometimes|numeric|min:0|max:1000000",
  114. ]);
  115. if ($request->credits) {
  116. if ($user->credits + $request->credits >= 99999999) throw ValidationException::withMessages([
  117. 'credits' => "You can't add this amount of credits because you would exceed the credit limit"
  118. ]);
  119. event(new UserUpdateCreditsEvent($user));
  120. $user->increment('credits', $request->credits);
  121. }
  122. if ($request->server_limit) {
  123. if ($user->server_limit + $request->server_limit >= 2147483647) throw ValidationException::withMessages([
  124. 'server_limit' => "You cannot add this amount of servers because it would exceed the server limit."
  125. ]);
  126. $user->increment('server_limit', $request->server_limit);
  127. }
  128. return $user;
  129. }
  130. /**
  131. * decrements the users credits or/and server_limit
  132. *
  133. * @param Request $request
  134. * @param int $id
  135. * @return User
  136. * @throws ValidationException
  137. */
  138. public function decrement(Request $request, int $id)
  139. {
  140. $discordUser = DiscordUser::find($id);
  141. $user = $discordUser ? $discordUser->user : User::findOrFail($id);
  142. $request->validate([
  143. "credits" => "sometimes|numeric|min:0|max:1000000",
  144. "server_limit" => "sometimes|numeric|min:0|max:1000000",
  145. ]);
  146. if ($request->credits) {
  147. if ($user->credits - $request->credits < 0) throw ValidationException::withMessages([
  148. 'credits' => "You can't remove this amount of credits because you would exceed the minimum credit limit"
  149. ]);
  150. $user->decrement('credits', $request->credits);
  151. }
  152. if ($request->server_limit) {
  153. if ($user->server_limit - $request->server_limit < 0) throw ValidationException::withMessages([
  154. 'server_limit' => "You cannot remove this amount of servers because it would exceed the minimum server."
  155. ]);
  156. $user->decrement('server_limit', $request->server_limit);
  157. }
  158. return $user;
  159. }
  160. /**
  161. * Suspends the user
  162. *
  163. * @param Request $request
  164. * @param int $id
  165. * @return bool
  166. * @throws ValidationException
  167. */
  168. public function suspend(Request $request, int $id)
  169. {
  170. $discordUser = DiscordUser::find($id);
  171. $user = $discordUser ? $discordUser->user : User::findOrFail($id);
  172. if ($user->isSuspended()) {
  173. throw ValidationException::withMessages([
  174. 'error' => 'The user is already suspended',
  175. ]);
  176. }
  177. $user->suspend();
  178. return $user;
  179. }
  180. /**
  181. * Unsuspend the user
  182. *
  183. * @param Request $request
  184. * @param int $id
  185. * @return bool
  186. * @throws ValidationException
  187. */
  188. public function unsuspend(Request $request, int $id)
  189. {
  190. $discordUser = DiscordUser::find($id);
  191. $user = $discordUser ? $discordUser->user : User::findOrFail($id);
  192. if (!$user->isSuspended()) {
  193. throw ValidationException::withMessages([
  194. 'error' => "You cannot unsuspend an User who is not suspended."
  195. ]);
  196. }
  197. $user->unSuspend();
  198. return $user;
  199. }
  200. /**
  201. * Create a unique Referral Code for User
  202. * @return string
  203. */
  204. protected function createReferralCode(){
  205. $referralcode = STR::random(8);
  206. if (User::where('referral_code', '=', $referralcode)->exists()) {
  207. $this->createReferralCode();
  208. }
  209. return $referralcode;
  210. }
  211. /**
  212. * @throws ValidationException
  213. */
  214. public function store(Request $request)
  215. {
  216. $request->validate([
  217. 'name' => ['required', 'string', 'max:30', 'min:4', 'alpha_num', 'unique:users'],
  218. 'email' => ['required', 'string', 'email', 'max:64', 'unique:users'],
  219. 'password' => ['required', 'string', 'min:8', 'max:191'],
  220. ]);
  221. // Prevent the creation of new users via API if this is enabled.
  222. if (!config('SETTINGS::SYSTEM:CREATION_OF_NEW_USERS', 'true')) {
  223. throw ValidationException::withMessages([
  224. 'error' => "The creation of new users has been blocked by the system administrator."
  225. ]);
  226. }
  227. $user = User::create([
  228. 'name' => $request->input('name'),
  229. 'email' => $request->input('email'),
  230. 'credits' => config('SETTINGS::USER:INITIAL_CREDITS', 150),
  231. 'server_limit' => config('SETTINGS::USER:INITIAL_SERVER_LIMIT', 1),
  232. 'password' => Hash::make($request->input('password')),
  233. 'referral_code' => $this->createReferralCode(),
  234. ]);
  235. $response = Pterodactyl::client()->post('/application/users', [
  236. "external_id" => App::environment('local') ? Str::random(16) : (string)$user->id,
  237. "username" => $user->name,
  238. "email" => $user->email,
  239. "first_name" => $user->name,
  240. "last_name" => $user->name,
  241. "password" => $request->input('password'),
  242. "root_admin" => false,
  243. "language" => "en"
  244. ]);
  245. if ($response->failed()) {
  246. $user->delete();
  247. throw ValidationException::withMessages([
  248. 'pterodactyl_error_message' => $response->toException()->getMessage(),
  249. 'pterodactyl_error_status' => $response->toException()->getCode()
  250. ]);
  251. }
  252. $user->update([
  253. 'pterodactyl_id' => $response->json()['attributes']['id']
  254. ]);
  255. //INCREMENT REFERRAL-USER CREDITS
  256. if(!empty($request->input("referral_code"))){
  257. $ref_code = $request->input("referral_code");
  258. $new_user = $user->id;
  259. if($ref_user = User::query()->where('referral_code', '=', $ref_code)->first()) {
  260. if(config("SETTINGS::REFERRAL:MODE") == "register" || config("SETTINGS::REFERRAL:MODE") == "both") {
  261. $ref_user->increment('credits', config("SETTINGS::REFERRAL::REWARD"));
  262. $ref_user->notify(new ReferralNotification($ref_user->id, $new_user));
  263. }
  264. //INSERT INTO USER_REFERRALS TABLE
  265. DB::table('user_referrals')->insert([
  266. 'referral_id' => $ref_user->id,
  267. 'registered_user_id' => $user->id,
  268. 'created_at' => Carbon::now(),
  269. 'updated_at' => Carbon::now()
  270. ]);
  271. }
  272. }
  273. $user->sendEmailVerificationNotification();
  274. return $user;
  275. }
  276. /**
  277. * Remove the specified resource from storage.
  278. *
  279. * @param int $id
  280. * @return Application|Response|ResponseFactory
  281. */
  282. public function destroy(int $id)
  283. {
  284. $discordUser = DiscordUser::find($id);
  285. $user = $discordUser ? $discordUser->user : User::findOrFail($id);
  286. $user->delete();
  287. return response($user, 200);
  288. }
  289. }