UserController.php 11 KB

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