UploadController.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <?php
  2. namespace App\Controllers;
  3. use App\Database\DB;
  4. use App\Exceptions\NotFoundException;
  5. use App\Traits\SingletonController;
  6. use App\Web\Log;
  7. use App\Web\Session;
  8. use Flight;
  9. use League\Flysystem\FileExistsException;
  10. use League\Flysystem\FileNotFoundException;
  11. use League\Flysystem\Filesystem;
  12. class UploadController extends Controller
  13. {
  14. use SingletonController;
  15. public function upload(): void
  16. {
  17. $requestData = Flight::request()->data;
  18. $response = [
  19. 'message' => null,
  20. ];
  21. if (!isset($requestData->token)) {
  22. $response['message'] = 'Token not specified.';
  23. Flight::json($response, 400);
  24. return;
  25. }
  26. $user = DB::query('SELECT * FROM `users` WHERE `token` = ? LIMIT 1', $requestData->token)->fetch();
  27. if (!$user) {
  28. $response['message'] = 'Token specified not found.';
  29. Flight::json($response, 404);
  30. return;
  31. }
  32. if (!$user->active) {
  33. $response['message'] = 'Account disabled.';
  34. Flight::json($response, 401);
  35. return;
  36. }
  37. do {
  38. $code = uniqid();
  39. } while (DB::query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `code` = ?', $code)->fetch()->count > 0);
  40. $file = Flight::request()->files->current();
  41. $fileInfo = pathinfo($file['name']);
  42. $storagePath = "$user->user_code/$code.$fileInfo[extension]";
  43. $stream = fopen($file['tmp_name'], 'r+');
  44. $filesystem = $this->getStorage();
  45. try {
  46. $filesystem->writeStream($storagePath, $stream);
  47. } catch (FileExistsException $e) {
  48. Flight::halt(500);
  49. return;
  50. } finally {
  51. fclose($stream);
  52. }
  53. DB::query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`) VALUES (?, ?, ?, ?)', [
  54. $user->id,
  55. $code,
  56. $file['name'],
  57. $storagePath
  58. ]);
  59. $base_url = Flight::get('config')['base_url'];
  60. $response['message'] = 'OK.';
  61. $response['url'] = "$base_url/$user->user_code/$code.$fileInfo[extension]";
  62. Flight::json($response, 201);
  63. Log::info("User $user->username uploaded new media.", [DB::raw()->lastInsertId()]);
  64. }
  65. public function show($userCode, $mediaCode): void
  66. {
  67. $media = $this->getMedia($userCode, $mediaCode);
  68. if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) {
  69. Flight::error(new NotFoundException());
  70. return;
  71. }
  72. $filesystem = $this->getStorage();
  73. if (stristr(Flight::request()->user_agent, 'TelegramBot')) {
  74. $this->streamMedia($filesystem, $media);
  75. } else {
  76. try {
  77. $mime = $filesystem->getMimetype($media->storage_path);
  78. $type = explode('/', $mime)[0];
  79. if ($type === 'text') {
  80. $media->text = $filesystem->read($media->storage_path);
  81. } elseif (in_array($type, ['image', 'video'])) {
  82. $this->http2push(Flight::get('config')['base_url'] . "/$userCode/$mediaCode/raw");
  83. }
  84. } catch (FileNotFoundException $e) {
  85. Flight::error($e);
  86. return;
  87. }
  88. Flight::render('upload/public.twig', [
  89. 'media' => $media,
  90. 'type' => $mime,
  91. 'extension' => pathinfo($media->filename, PATHINFO_EXTENSION)
  92. ]);
  93. }
  94. }
  95. public function getRawById($id): void
  96. {
  97. $this->checkAdmin();
  98. $media = DB::query('SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1', $id)->fetch();
  99. if (!$media) {
  100. Flight::error(new NotFoundException());
  101. return;
  102. }
  103. $this->streamMedia($this->getStorage(), $media);
  104. }
  105. public function showRaw($userCode, $mediaCode): void
  106. {
  107. $media = $this->getMedia($userCode, $mediaCode);
  108. if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) {
  109. Flight::error(new NotFoundException());
  110. return;
  111. }
  112. $this->streamMedia($this->getStorage(), $media);
  113. }
  114. public function download($userCode, $mediaCode): void
  115. {
  116. $media = $this->getMedia($userCode, $mediaCode);
  117. if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) {
  118. Flight::error(new NotFoundException());
  119. return;
  120. }
  121. $this->streamMedia($this->getStorage(), $media, 'attachment');
  122. }
  123. public function togglePublish($id): void
  124. {
  125. $this->checkLogin();
  126. if (Session::get('admin')) {
  127. $media = DB::query('SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1', $id)->fetch();
  128. } else {
  129. $media = DB::query('SELECT * FROM `uploads` WHERE `id` = ? AND `user_id` = ? LIMIT 1', [$id, Session::get('user_id')])->fetch();
  130. }
  131. if (!$media) {
  132. Flight::halt(404);
  133. return;
  134. }
  135. DB::query('UPDATE `uploads` SET `published`=? WHERE `id`=?', [!$media->published, $media->id]);
  136. }
  137. public function delete($id): void
  138. {
  139. $this->checkLogin();
  140. $media = DB::query('SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1', $id)->fetch();
  141. if (Session::get('admin', false) || $media->user_id === Session::get('user_id')) {
  142. $filesystem = $this->getStorage();
  143. try {
  144. $filesystem->delete($media->storage_path);
  145. } catch (FileNotFoundException $e) {
  146. Flight::halt(404);
  147. return;
  148. } finally {
  149. DB::query('DELETE FROM `uploads` WHERE `id` = ?', $id);
  150. Log::info('User ' . Session::get('username') . " deleted media $id");
  151. }
  152. } else {
  153. Flight::halt(403);
  154. }
  155. }
  156. protected function getMedia($userCode, $mediaCode)
  157. {
  158. $mediaCode = pathinfo($mediaCode)['filename'];
  159. $media = DB::query('SELECT * FROM `uploads` INNER JOIN `users` ON `uploads`.`user_id` = `users`.`id` WHERE `user_code` = ? AND `uploads`.`code` = ? LIMIT 1', [
  160. $userCode,
  161. $mediaCode
  162. ])->fetch();
  163. return $media;
  164. }
  165. protected function streamMedia(Filesystem $storage, $media, string $disposition = 'inline'): void
  166. {
  167. try {
  168. Flight::response()->header('Content-Type', $storage->getMimetype($media->storage_path));
  169. Flight::response()->header('Content-Disposition', $disposition . ';filename="' . $media->filename . '"');
  170. Flight::response()->header('Content-Length', $storage->getSize($media->storage_path));
  171. Flight::response()->sendHeaders();
  172. ob_end_clean();
  173. fpassthru($storage->readStream($media->storage_path));
  174. } catch (FileNotFoundException $e) {
  175. Flight::error($e);
  176. }
  177. }
  178. }