UserController.php 11 KB

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