AccountController.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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($id === "myself") $id = get_token_id(request());
  49. Logger::Debug($id);
  50. Logger::Debug("Getting account id ($id).");
  51. $account = new Account(id: $id);
  52. $account->Load();
  53. $followers = count(new Follow(followee_id: $id)->LoadMany());
  54. $following = count(new Follow(follower_id: $id)->LoadMany());
  55. try {
  56. $links = new Link(account_id: $id)->LoadMany();
  57. } catch (Exception $e) {
  58. $links = [];
  59. }
  60. CORSHelper();
  61. return api_json([
  62. "id" => $id,
  63. "username" => $account->username,
  64. "verified" => $account->verified,
  65. "bio" => $account->bio,
  66. "pictureHash" => $account->picture_hash,
  67. "followers" => $followers,
  68. "following" => $following,
  69. "myself" => $id == get_token_id(request()),
  70. "links" => $links
  71. ]);
  72. }
  73. public static function search(): string
  74. {
  75. $query = input("query");
  76. $account = new Account(username: $query);
  77. $accounts = $account->LoadMany();
  78. return api_json($accounts);
  79. }
  80. public static function getVideos($id): string
  81. {
  82. if($id === "myself") $id = get_token_id(request());
  83. $account = new Account(id: $id);
  84. $data = Hajeebtok::$Database->Query("SELECT * FROM videos WHERE author_id = :author_id", ["author_id" => $account->id]);
  85. if (empty($data)) throw new VideoNotFoundException(0, 404);
  86. // todo: make it return in feed format!!!!
  87. CORSHelper();
  88. return api_json($data);
  89. }
  90. public static function getPicture($id): string
  91. {
  92. $signed_in = signed_in(request());
  93. if($id === "myself") $id = get_token_id(request());
  94. if($signed_in) {
  95. try {
  96. $account = new Account(id: $id);
  97. $account->Load();
  98. $picture_path = APP_ROOT . "/usercontent/pictures/$account->picture_hash.png";
  99. } catch (Exception $e) {
  100. $picture_path = APP_ROOT . "/usercontent/pictures/not_found.png";
  101. }
  102. } else {
  103. // this is hardcoded because i dont care
  104. $picture_path = APP_ROOT . "/usercontent/pictures/premium_" . rand(1, 57) . ".png";
  105. }
  106. $mimeTypes = new MimeTypes();
  107. $picture_contents = file_get_contents($picture_path);
  108. $picture_size = filesize($picture_path);
  109. $mime = $mimeTypes->getMimeType(pathinfo($picture_path, PATHINFO_EXTENSION));
  110. $response = response();
  111. $response->header("Content-Type: $mime");
  112. $response->header("Content-Length: $picture_size");
  113. $response->header("Cache-Control: max-age=3600, public");
  114. return $picture_contents;
  115. }
  116. public static function getAvailablePremiumProfilePictures(): string
  117. {
  118. $pictures = [];
  119. for ($i = 1; $i <= 57; $i++) {
  120. $pictures[] = $i;
  121. }
  122. return api_json($pictures);
  123. }
  124. public static function getPremiumProfilePicture($id): string
  125. {
  126. $picture_path = APP_ROOT . "/usercontent/pictures/premium_$id.png";
  127. $mime_types = new MimeTypes();
  128. $picture_contents = file_get_contents($picture_path);
  129. $picture_size = filesize($picture_path);
  130. $mime = $mime_types->getMimeType(pathinfo($picture_path, PATHINFO_EXTENSION));
  131. $response = response();
  132. $response->header("Content-Type: $mime");
  133. $response->header("Content-Length: $picture_size");
  134. $response->header("Cache-Control: max-age=3600, public");
  135. return $picture_contents;
  136. }
  137. public static function updateAccount(): string
  138. {
  139. if(!signed_in(request())) throw new UnauthenticatedException(0, 401);
  140. $id = get_token_id(request());
  141. $picture_hash = input("picture_hash");
  142. $picture = request()->getInputHandler()->file("picture");
  143. $bio = input("bio");
  144. if(empty($picture) && !empty($picture_hash)) {
  145. $valid_picture_hash_list = [
  146. "default",
  147. "premium_1",
  148. "premium_2",
  149. "premium_3",
  150. "premium_4",
  151. "premium_5",
  152. "premium_6",
  153. "premium_7",
  154. "premium_8",
  155. "premium_9",
  156. "premium_10",
  157. "premium_11",
  158. "premium_12",
  159. "premium_13",
  160. "premium_14",
  161. "premium_15",
  162. "premium_16",
  163. "premium_17",
  164. "premium_18",
  165. "premium_19",
  166. "premium_20",
  167. "premium_21",
  168. "premium_22",
  169. "premium_23",
  170. "premium_24",
  171. "premium_25",
  172. "premium_26",
  173. "premium_27",
  174. "premium_28",
  175. "premium_29",
  176. "premium_30",
  177. "premium_31",
  178. "premium_32",
  179. "premium_33",
  180. "premium_34",
  181. "premium_35",
  182. "premium_36",
  183. "premium_37",
  184. "premium_38",
  185. "premium_39",
  186. "premium_40",
  187. "premium_41",
  188. "premium_42",
  189. "premium_43",
  190. "premium_44",
  191. "premium_45",
  192. "premium_46",
  193. "premium_47",
  194. "premium_48",
  195. "premium_49",
  196. "premium_50",
  197. "premium_51",
  198. "premium_52",
  199. "premium_53",
  200. "premium_54",
  201. "premium_55",
  202. "premium_56",
  203. "premium_57"
  204. ];
  205. if(!in_array($picture_hash, $valid_picture_hash_list)) throw new SecurityFaultException("Attempt to path trace on /update endpoint.",400);
  206. } else if (!empty($picture) && empty($picture_hash)) {
  207. $picture_hash = hash("sha256", $picture);
  208. $picture_path = APP_ROOT . "/usercontent/pictures/$picture_hash.png";
  209. $size = getimagesize($picture);
  210. $crop = min($size[0], $size[1]);
  211. $image_contents = file_get_contents($picture);
  212. $image_string = imagecreatefromstring($image_contents);
  213. $cropped_image = imagecrop($image_string, [
  214. "x" => 0,
  215. "y" => 0,
  216. "width" => $crop,
  217. "height" => $crop
  218. ]);
  219. imagepng($cropped_image, $picture_path); // save image and crop and turn into png (i love php)
  220. } else if(!empty($picture) && !empty($picture_hash)) {
  221. throw new InvalidRequestException(400);
  222. }
  223. $old_account = new Account(id: $id);
  224. $old_account->Load();
  225. $new_account = new Account(
  226. id: $id,
  227. username: $old_account->username,
  228. password: $old_account->password,
  229. picture_hash: $picture_hash ?? $old_account->picture_hash,
  230. verified: $old_account->verified,
  231. bio: $bio ?? $old_account->bio,
  232. );
  233. CORSHelper();
  234. $new_account->Update();
  235. return api_json([
  236. "id" => $new_account->id,
  237. "username" => $new_account->username,
  238. "pictureHash" => $new_account->picture_hash,
  239. "bio" => $new_account->bio,
  240. "verified" => $new_account->verified,
  241. ]);
  242. }
  243. public static function addLink(): string
  244. {
  245. if(!signed_in(request())) throw new UnauthenticatedException(0, 401);
  246. $account_id = get_token_id(request());
  247. $link = input("link");
  248. $link_type = input("linkType");
  249. $link_enum_type = LinkEnum::tryFrom($link_type);
  250. // todo: add enum type filtering
  251. $link = new Link(account_id: $account_id, type: $link_enum_type, url: $link);
  252. $link->Save();
  253. return api_json([
  254. "id" => $link->id,
  255. "url" => $link->url,
  256. "type" => $link->type->value,
  257. "account_id" => $link->account_id
  258. ]);
  259. }
  260. public static function getFollowers($id): string {
  261. if($id === "myself") $id = get_token_id(request());
  262. $followers = new Follow(followee_id: $id)->LoadMany();
  263. if(empty($followers)) throw new FollowNotFoundException(0, 404);
  264. for ($i = 0; $i < count($followers); $i++) {
  265. $follower = $followers[$i]["follower_id"];
  266. $account = new Account(id: $follower);
  267. $account->Load();
  268. $followers[$i] = [
  269. "followee_id" => (int)$id,
  270. "follower_id" => $follower,
  271. "username" => $account->username,
  272. "pictureHash" => $account->picture_hash,
  273. "verified" => $account->verified
  274. ];
  275. }
  276. return api_json($followers);
  277. }
  278. public static function getFollowing($id): string {
  279. if($id === "myself") $id = get_token_id(request());
  280. $following = new Follow(follower_id: $id)->LoadMany();
  281. if(empty($following)) throw new FollowNotFoundException(0, 404);
  282. for ($i = 0; $i < count($following); $i++) {
  283. $followee = $following[$i]["followee_id"];
  284. $account = new Account(id: $followee);
  285. $account->Load();
  286. $following[$i] = [
  287. "followee_id" => $followee,
  288. "follower_id" => (int)$id,
  289. "username" => $account->username,
  290. "pictureHash" => $account->picture_hash,
  291. "verified" => $account->verified
  292. ];
  293. }
  294. return api_json($following);
  295. }
  296. public static function RegisterRoutes(): void
  297. {
  298. SimpleRouter::group([
  299. "prefix" => "/account/",
  300. ], function () {
  301. SimpleRouter::get("/availablePremiumProfilePictures", [AccountController::class, "getAvailablePremiumProfilePictures"]);
  302. SimpleRouter::get("/getPremiumProfilePicture/{id}", [AccountController::class, "getPremiumProfilePicture"]);
  303. SimpleRouter::get("/{id}/get", [AccountController::class, "getAccount"]);
  304. SimpleRouter::get("/{id}/videos", [AccountController::class, "getVideos"]);
  305. SimpleRouter::get("/{id}/picture", [AccountController::class, "getPicture"]);
  306. SimpleRouter::get("/{id}/followers", [AccountController::class, "getFollowers"]);
  307. SimpleRouter::get("/{id}/following", [AccountController::class, "getFollowing"]);
  308. SimpleRouter::post("/token", [AccountController::class, "getToken"]);
  309. SimpleRouter::post("/search", [AccountController::class, "search"]);
  310. SimpleRouter::post("/update", [AccountController::class, "updateAccount"]);
  311. SimpleRouter::post("/addLink", [AccountController::class, "addLink"]);
  312. SimpleRouter::options("/update", "CORSHelper");
  313. SimpleRouter::options("/token", "CORSHelper");
  314. SimpleRouter::options("/{id}/get", "CORSHelper");
  315. SimpleRouter::options("/{id}/videos", "CORSHelper");
  316. });
  317. }
  318. }