AccountController.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <?php
  2. namespace app\Controllers;
  3. use app\Exceptions\FollowNotFoundException;
  4. use app\Exceptions\InvalidRequestException;
  5. use app\Exceptions\UnauthenticatedException;
  6. use app\Exceptions\VideoNotFoundException;
  7. use app\Hajeebtok;
  8. use app\Interfaces\IRouteController;
  9. use app\Logger;
  10. use app\Types\DatabaseObjects\Account;
  11. use app\Types\DatabaseObjects\Follow;
  12. use app\Types\DatabaseObjects\Link;
  13. use app\Types\DatabaseObjects\Session;
  14. use app\Types\LinkEnum;
  15. use Exception;
  16. use app\Exceptions\SecurityFaultException;
  17. use Mimey\MimeTypes;
  18. use Pecee\SimpleRouter\SimpleRouter;
  19. class AccountController implements IRouteController
  20. {
  21. public static function getToken(): string
  22. {
  23. $username = input("username");
  24. $password = password_hash(input("password"), PASSWORD_DEFAULT);
  25. $account = new Account(
  26. username: $username,
  27. password: $password,
  28. picture_hash: "default",
  29. verified: false
  30. );
  31. if ($account->Exists()) { // Account already exists
  32. $account->Load();
  33. if (password_verify($password, $account->password)) throw new UnauthenticatedException($account->id, 401);
  34. $session = new Session(account_id: $account->id);
  35. } else { // Create a new account
  36. $session = new Session(account_id: $account->Save());
  37. }
  38. CORSHelper();
  39. $token = $session->Save();
  40. $session->Load();
  41. return api_json([
  42. "token" => $token,
  43. "auth_date" => $session->date_authenticated
  44. ]);
  45. }
  46. public static function getAccount($id): string
  47. {
  48. if (!signed_in(request())) {
  49. $usernames = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeUsernames"));
  50. CORSHelper();
  51. return api_json([
  52. "id" => $id,
  53. "password" => "password1234",
  54. "username" => $usernames[rand(0, count($usernames) - 1)],
  55. "verified" => rand(1, 10) > 8,
  56. "bio" => "SIGN IN to see this EPIC content!!!",
  57. "followers" => rand(5, 38203),
  58. "following" => rand(5, 6243),
  59. "myself" => false,
  60. "links" => [],
  61. "socialCredit" => rand(-20223, 20223),
  62. "followed" => false
  63. ]);
  64. }
  65. if ($id === "myself") $id = get_token_id(request());
  66. Logger::Debug($id);
  67. Logger::Debug("Getting account id ($id).");
  68. $account = new Account(id: $id);
  69. $account->Load();
  70. $followers = count(new Follow(followee_id: $id)->LoadMany());
  71. $following = count(new Follow(follower_id: $id)->LoadMany());
  72. try {
  73. $links = new Link(account_id: $id)->LoadMany();
  74. } catch (Exception $e) {
  75. $links = [];
  76. }
  77. $followed = new Follow(followee_id: $id, follower_id: get_token_id(request()))->Exists();
  78. CORSHelper();
  79. return api_json([
  80. "id" => $id,
  81. "username" => $account->username,
  82. "verified" => $account->verified,
  83. "bio" => $account->bio,
  84. "followers" => $followers,
  85. "following" => $following,
  86. "myself" => $id == get_token_id(request()),
  87. "links" => $links,
  88. "socialCredit" => $account->social_credit,
  89. "followed" => $followed
  90. ]);
  91. }
  92. public static function getVideos($id): string
  93. {
  94. if(!signed_in(request())) {
  95. $titles = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeTitles"));
  96. $descriptions = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeDescriptions"));
  97. $usernames = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeUsernames"));
  98. $feed = [];
  99. for($i = 0; $i < rand(6, 20); $i++) {
  100. $feed[] = [
  101. "id" => rand(1, 10),
  102. "title" => $titles[rand(0, count($titles) - 1)],
  103. "description" => $descriptions[rand(0, count($descriptions) - 1)],
  104. "likes" => rand(20000, 37020),
  105. "dislikes" => rand(2, 12343),
  106. "comments" => rand(2, 1029),
  107. "shares" => rand(2, 200000),
  108. "author" => [
  109. "id" => rand(2, 59),
  110. "verified" => rand(1, 10) > 8,
  111. "username" => $usernames[rand(0, count($usernames) - 1)],
  112. ],
  113. ];
  114. }
  115. CORSHelper();
  116. return api_json($feed);
  117. }
  118. if ($id === "myself") $id = get_token_id(request());
  119. $account = new Account(id: $id);
  120. $video_number = intval(input("video") ?? "0");
  121. $data = Hajeebtok::$Database->Query("SELECT * FROM videos WHERE author_id = :author_id", ["author_id" => $account->id]);
  122. if (empty($data)) throw new VideoNotFoundException(0, 404);
  123. $videos = [];
  124. $valid_video = false;
  125. foreach ($data as $video) {
  126. if($video_number === 0) $valid_video = true;
  127. if($video["id"] === $video_number) $valid_video = true;
  128. if(!$valid_video) continue;
  129. $account = new Account($video["author_id"]);
  130. $account->Load();
  131. $videos[] = ["id" => $video["id"],
  132. "title" => $video["title"],
  133. "description" => $video["description"],
  134. "likes" => $video["likes"],
  135. "dislikes" => $video["dislikes"],
  136. "comments" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM comments WHERE video_id = :id", ["id" => $video["id"]]),
  137. "shares" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM messages INNER JOIN videos ON messages.video_id = videos.id WHERE videos.id = :id", ["id" => $video["id"]]),
  138. "author" => [
  139. "id" => $video["author_id"],
  140. "verified" => $account->verified,
  141. "username" => $account->username,
  142. ],
  143. ];
  144. }
  145. CORSHelper();
  146. return api_json($videos);
  147. }
  148. public static function getPicture($id): string
  149. {
  150. $signed_in = signed_in(request());
  151. if ($id === "myself") $id = get_token_id(request());
  152. if ($signed_in) {
  153. try {
  154. $account = new Account(id: $id);
  155. $account->Load();
  156. $picture_path = APP_ROOT . "/usercontent/pictures/$account->picture_hash.png";
  157. } catch (Exception $e) {
  158. $picture_path = APP_ROOT . "/usercontent/pictures/not_found.png";
  159. }
  160. } else {
  161. // this is hardcoded because i dont care
  162. $picture_path = APP_ROOT . "/usercontent/pictures/premium_" . rand(1, 57) . ".png";
  163. }
  164. $mimeTypes = new MimeTypes();
  165. $picture_contents = file_get_contents($picture_path);
  166. $picture_size = filesize($picture_path);
  167. $mime = $mimeTypes->getMimeType(pathinfo($picture_path, PATHINFO_EXTENSION));
  168. $response = response();
  169. $response->header("Content-Type: $mime");
  170. $response->header("Content-Length: $picture_size");
  171. $response->header("Cache-Control: max-age=1800, public");
  172. return $picture_contents;
  173. }
  174. public static function getAvailablePremiumProfilePictures(): string
  175. {
  176. $pictures = [];
  177. for ($i = 1; $i <= 47; $i++) {
  178. $pictures[] = $i;
  179. }
  180. CORSHelper();
  181. return api_json($pictures);
  182. }
  183. public static function getPremiumProfilePicture($id): string
  184. {
  185. $picture_path = APP_ROOT . "/usercontent/pictures/premium_$id.png";
  186. $mime_types = new MimeTypes();
  187. $picture_contents = file_get_contents($picture_path);
  188. $picture_size = filesize($picture_path);
  189. $mime = $mime_types->getMimeType(pathinfo($picture_path, PATHINFO_EXTENSION));
  190. $response = response();
  191. $response->header("Content-Type: $mime");
  192. $response->header("Content-Length: $picture_size");
  193. $response->header("Cache-Control: max-age=3600, public");
  194. return $picture_contents;
  195. }
  196. public static function updateAccount(): string
  197. {
  198. if (!signed_in(request())) throw new UnauthenticatedException(0, 401);
  199. $id = get_token_id(request());
  200. $picture_hash = input("picture_hash");
  201. $picture = request()->getInputHandler()->file("picture");
  202. $bio = input("bio");
  203. $valid_picture_hash_list = [
  204. "default",
  205. "premium_1",
  206. "premium_2",
  207. "premium_3",
  208. "premium_4",
  209. "premium_5",
  210. "premium_6",
  211. "premium_7",
  212. "premium_8",
  213. "premium_9",
  214. "premium_10",
  215. "premium_11",
  216. "premium_12",
  217. "premium_13",
  218. "premium_14",
  219. "premium_15",
  220. "premium_16",
  221. "premium_17",
  222. "premium_18",
  223. "premium_19",
  224. "premium_20",
  225. "premium_21",
  226. "premium_22",
  227. "premium_23",
  228. "premium_24",
  229. "premium_25",
  230. "premium_26",
  231. "premium_27",
  232. "premium_28",
  233. "premium_29",
  234. "premium_30",
  235. "premium_31",
  236. "premium_32",
  237. "premium_33",
  238. "premium_34",
  239. "premium_35",
  240. "premium_36",
  241. "premium_37",
  242. "premium_38",
  243. "premium_39",
  244. "premium_40",
  245. "premium_41",
  246. "premium_42",
  247. "premium_43",
  248. "premium_44",
  249. "premium_45",
  250. "premium_46",
  251. "premium_47",
  252. "premium_48",
  253. "premium_49",
  254. "premium_50",
  255. "premium_51",
  256. "premium_52",
  257. "premium_53",
  258. "premium_54",
  259. "premium_55",
  260. "premium_56",
  261. "premium_57"
  262. ];
  263. if (empty($picture) && !empty($picture_hash)) {
  264. if (!in_array($picture_hash, $valid_picture_hash_list)) throw new SecurityFaultException("Attempt to path trace on /update endpoint.", 400);
  265. } else if (!empty($picture) && empty($picture_hash)) {
  266. $picture_hash = hash("sha256", $picture);
  267. $picture_path = APP_ROOT . "/usercontent/pictures/$picture_hash.png";
  268. $size = getimagesize($picture);
  269. $crop = min($size[0], $size[1]);
  270. $image_contents = file_get_contents($picture);
  271. $image_string = imagecreatefromstring($image_contents);
  272. $cropped_image = imagecrop($image_string, [
  273. "x" => 0,
  274. "y" => 0,
  275. "width" => $crop,
  276. "height" => $crop
  277. ]);
  278. imagepng($cropped_image, $picture_path); // save image and crop and turn into png (i love php)
  279. } else if (!empty($picture) && !empty($picture_hash)) {
  280. throw new InvalidRequestException(400);
  281. }
  282. $old_account = new Account(id: $id);
  283. $old_account->Load();
  284. $new_account = new Account(
  285. id: $id,
  286. username: $old_account->username,
  287. password: $old_account->password,
  288. picture_hash: $picture_hash ?? $old_account->picture_hash,
  289. verified: $old_account->verified,
  290. bio: $bio ?? $old_account->bio,
  291. social_credit: $old_account->social_credit,
  292. );
  293. CORSHelper();
  294. $new_account->Update();
  295. return api_json([
  296. "id" => $new_account->id,
  297. "username" => $new_account->username,
  298. "pictureHash" => in_array($new_account->picture_hash, $valid_picture_hash_list) ? $new_account->picture_hash : null,
  299. "bio" => $new_account->bio,
  300. "verified" => $new_account->verified,
  301. "social_credit" => $old_account->social_credit,
  302. ]);
  303. }
  304. public static function addLink(): string
  305. {
  306. if (!signed_in(request())) throw new UnauthenticatedException(0, 401);
  307. $account_id = get_token_id(request());
  308. $link = input("link");
  309. $link_type = input("linkType");
  310. $link_enum_type = LinkEnum::tryFrom($link_type);
  311. // todo: add enum type filtering
  312. $link = new Link(account_id: $account_id, type: $link_enum_type, url: $link);
  313. $link->Save();
  314. return api_json([
  315. "id" => $link->id,
  316. "url" => $link->url,
  317. "type" => $link->type->value,
  318. "account_id" => $link->account_id
  319. ]);
  320. }
  321. public static function getFollowers($id): string
  322. {
  323. if ($id === "myself") $id = get_token_id(request());
  324. $followers = new Follow(followee_id: $id)->LoadMany();
  325. if (empty($followers)) throw new FollowNotFoundException(0, 404);
  326. for ($i = 0; $i < count($followers); $i++) {
  327. $follower = $followers[$i]["follower_id"];
  328. $account = new Account(id: $follower);
  329. $account->Load();
  330. $followers[$i] = [
  331. "followee_id" => (int)$id,
  332. "follower_id" => $follower,
  333. "username" => $account->username,
  334. "verified" => $account->verified
  335. ];
  336. }
  337. return api_json($followers);
  338. }
  339. public static function getFollowing($id): string
  340. {
  341. if ($id === "myself") $id = get_token_id(request());
  342. $following = new Follow(follower_id: $id)->LoadMany();
  343. if (empty($following)) throw new FollowNotFoundException(0, 404);
  344. for ($i = 0; $i < count($following); $i++) {
  345. $followee = $following[$i]["followee_id"];
  346. $account = new Account(id: $followee);
  347. $account->Load();
  348. $following[$i] = [
  349. "followee_id" => $followee,
  350. "follower_id" => (int)$id,
  351. "username" => $account->username,
  352. "verified" => $account->verified
  353. ];
  354. }
  355. return api_json($following);
  356. }
  357. public static function search(): string {
  358. $signed_in = signed_in(request());
  359. if(!$signed_in) {
  360. $data = [];
  361. $usernames = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeUsernames"));
  362. for($i = 0; $i < rand(1, 13); $i++) {
  363. $data[] = [
  364. "id" => rand(2, 10232),
  365. "username" => $usernames[rand(0, count($usernames) - 1)],
  366. "socialCredit" => rand(-2032, 1209312),
  367. "following" => rand(0, 10232),
  368. "followers" => rand(0, 10232),
  369. "bio" => "SIGN IN to see this EPIC content!!",
  370. "verified" => rand(1, 10) > 8,
  371. ];
  372. }
  373. CORSHelper();
  374. return api_json($data);
  375. }
  376. $query = input("query");
  377. $account = new Account(username: $query);
  378. $data = $account->LoadMany();
  379. $accounts = [];
  380. foreach($data as $account) {
  381. $followers = count(new Follow(followee_id: $account["id"])->LoadMany());
  382. $following = count(new Follow(follower_id: $account["id"])->LoadMany());
  383. $accounts[] = [
  384. "id" => $account["id"],
  385. "username" => $account["username"],
  386. "socialCredit" => $account["social_credit"],
  387. "following" => $following,
  388. "followers" => $followers,
  389. "bio" => $account["bio"],
  390. "verified" => $account["verified"] === 1,
  391. ];
  392. }
  393. CORSHelper();
  394. return api_json($accounts);
  395. }
  396. public static function follow($id): string {
  397. $signed_in = signed_in(request());
  398. if(!$signed_in) throw new UnauthenticatedException(0, 401);
  399. $follower_id = get_token_id(request());
  400. $message = "Successful";
  401. $successful = true;
  402. $follow = new Follow(follower_id: $follower_id, followee_id: $id);
  403. if($follow->Exists()) {
  404. $successful = false;
  405. $message = "You already follow this account...";
  406. } else {
  407. $follow->Save();
  408. }
  409. CORSHelper();
  410. return api_json([
  411. "successful" => $successful,
  412. "message" => $message,
  413. ]);
  414. }
  415. public static function RegisterRoutes(): void
  416. {
  417. SimpleRouter::group([
  418. "prefix" => "/account/",
  419. ], function () {
  420. SimpleRouter::get("/availablePremiumProfilePictures", [AccountController::class, "getAvailablePremiumProfilePictures"]);
  421. SimpleRouter::get("/getPremiumProfilePicture/{id}", [AccountController::class, "getPremiumProfilePicture"]);
  422. SimpleRouter::get("/{id}/get", [AccountController::class, "getAccount"]);
  423. SimpleRouter::get("/{id}/videos", [AccountController::class, "getVideos"]);
  424. SimpleRouter::get("/{id}/picture", [AccountController::class, "getPicture"]);
  425. //SimpleRouter::get("/{id}/followers", [AccountController::class, "getFollowers"]);
  426. //SimpleRouter::get("/{id}/following", [AccountController::class, "getFollowing"]);
  427. SimpleRouter::post("/{id}/follow", [AccountController::class, "follow"]);
  428. SimpleRouter::post("/token", [AccountController::class, "getToken"]);
  429. SimpleRouter::post("/update", [AccountController::class, "updateAccount"]);
  430. SimpleRouter::post("/addLink", [AccountController::class, "addLink"]);
  431. SimpleRouter::post("/search", [AccountController::class, "search"]);
  432. SimpleRouter::options("/update", "CORSHelper");
  433. SimpleRouter::options("/token", "CORSHelper");
  434. SimpleRouter::options("/{id}/get", "CORSHelper");
  435. SimpleRouter::options("/{id}/videos", "CORSHelper");
  436. SimpleRouter::options("/update", "CORSHelper");
  437. SimpleRouter::options("/availablePremiumProfilePictures", "CORSHelper");
  438. SimpleRouter::options("/getPremiumProfilePicture/{id}","CORSHelper");
  439. SimpleRouter::options("/search", "CORSHelper");
  440. });
  441. }
  442. }